安全路透社
当前位置:安全路透社 > 网络转载 > 正文

Android安全测试工具Drozer之手机端agent源码解析

0×00 前言

Drozer是MWR Labs开发的一款具有pc端(主要python编写)和移动端(agent代理)的开源Android渗透测试框架。可以自定义python脚本,与通过代理agent与Android虚拟机进行交互。本文主要解析移动端agent代码,为各位同学后续写自己的安全工具起到抛砖引玉的个作用。

Drozer的通信框架如下图所示,它有两张通信方式,具体请看Android AppInjection&&Drozer Use

image002.gif

首先从个github https://github.com/mwrlabs 下载drozer-agentjdieselmwr-tlsmwr-android

1、drozer-agent:移动端界面工程

2、jdiesel:反射和通信协议的核心jar工程

3、mwr-tls:安全通信jar工程

4、mwr-android: 移动端界面jar工程

image004.jpg

0×01 pc与android端通信桥梁message

打开jdiesel->common中protobuf.proto文件,原来是ProtocolBuffers协议,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,很适合做数据存储或 RPC 数据交换格式。它可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。

优点:体积小,解析速度快。

缺点:二进制的方式存储,抓包后几乎不可读,调试起来可能不方便。

1 required 不可以增加或删除的字段,必须初始化

2 optional 可选字段,可删除,可以不初始化

3 repeated 可重复字段, 对应到java文件里,生成的是List

代码中常常用到Build,生成具体的java类时,例如protobuf.java,同时会存在build方法。意思是对于转化后的数据,具有唯一性,build提供了便利的方法来初始化这些数据。protobuf详情

先看看protobuf.proto文件,其中主要的架构是这样。

image005.png

消息类型除了id和消息类型(MessageType),就是消息体,是可有可无的分为四种:

SystemRequest

SystemRequest:包含设备信息,会话信息,连接控制

image007.png

SystemResponse

SystemResponse::主要包括响应的类型(绑定服务,设备列表,会话列表等)状态信息。

image009.png

ReflectionRequest

ReflectionRequest:java反射请求,主要有resolve(所反射的classname),Construct(对象引用ObjectReference(int32类型),方法对象method,和方法参数Argument),Invoke调用方法(同样包括对象引用ObjectReference(int32类型),方法对象method,和方法参数Argument),以及对Property的get与set,还有Delete(对象引用ObjectReference(int32类型))

image011.png

ReflectionResponse

ReflectionResponse:主要是反射响应后的状态和参数,还有一些错误信息。

image013.png

0×02 pc与android端通信方式

在jdiesel代码中com.mwr.jdiesel.api.transport包下四个文件

image016.jpgimage017.png

SecureTransport是加了ssl加密传输的,从Transport文件中可以看出,receive和send都是有Frame封装的数据格式,如下

public abstract class Transport {

   public abstract void close();
   protected abstract InputStream getInputStream() throws IOException;
   protected abstract OutputStream getOutputStream() throws IOException;
   public abstract boolean isLive();

   public Frame receive() throws APIVersionException, IOException,TransportDisconnectedException {
       if(this.getInputStream() != null)
            return Frame.readFrom(this.getInputStream());
       else
            throw newTransportDisconnectedException();
   }

   public void send(Frame frame) throws IOException {
       this.getOutputStream().write(frame.toByteArray());
   }

}

外部调用时只需new一个SocketTransport(socket)即可,再看看socket在哪儿传送过来的

在ServerSocketFactory类中有创建serversocket的方法

public class ServerSocketFactory {

   public ServerSocket createSocket(Server server) throwsCertificateException, IOException, KeyManagementException, KeyStoreException,UnrecoverableKeyException {
       if(server.isSSL())
            return this.createSSLSocket(server);
       else
            return new ServerSocket(server.getPort());
   }
   .
   .
   .

在Link包中,server.java代码中重载run方法中有accept()接受socket请求。

public class Server extends Link {
   @Override
   public void run() {
       this.running = true;

       this.log(LogMessage.INFO, "Starting Server...");
       while(this.running) {
            try {
                if(this.connection == null) {
                    this.parameters.setStatus(com.mwr.jdiesel.api.connectors.Server.Status.CONNECTING);

                    this.log(LogMessage.INFO, "Attempting tobind to port " + ((com.mwr.jdiesel.api.connectors.Server)this.parameters).getPort() + "...");
                    this.server_socket = new ServerSocketFactory().createSocket((com.mwr.jdiesel.api.connectors.Server)this.parameters);

                    this.log(LogMessage.INFO, "Waiting forconnections...");
                    Socket socket = this.server_socket.accept();

其中关于连接link的类关系图如下图所示。

image019.png

在jdiesel代码中server.java的run方法中有循环接受客户端的 Socket socket=this.server_socket accept();.

在agent代码中找到一个Service(此服务为android四大组件的服务),在消息循环中有startServer()方法被调用,就有server线程的开启。该server正是import com.mwr.jdiesel.api.links.Server的Server

public class ServerService extends ConnectorService {

   public void startServer() {
       if(this.server == null) {
            (new ServerSettings()).load(this.server_parameters);

            this.server_parameters.enabled = true;
            this.server = new Server(this.server_parameters, Agent.getInstance().getDeviceInfo());
            this.server.setLogger(this.server_parameters.getLogger());
            this.server_parameters.getLogger().addOnLogMessageListener(this);

            this.server.start();

   @Override
   public void handleMessage(Message msg) {
       switch(msg.what) {     
            caseMSG_START_SERVER:
            try {
                this.startServer();

                Message message = Message.obtain(null,MSG_GET_SERVER_STATUS);
                message.setData(this.getStatus());

                msg.replyTo.send(message);

上面所讲的是console连接手机server的通信方式,是比较常用的通信方式。

还有一种通信方式是PC端开启一个Server,手机端Client连接PC服务,以上两张通信方式都离不开adb forword tcp:[localport] tcp:[remoteport]

在jdiesel代码中Client.java的run方法中有连接PC服务端的代码

Socket socket= new EndpointSocketFactory().createSocket(endpoint);

0×03 手机端反射框架解析

在jdiesel代码中最关键的反射代码为Reflector.java

tu4.png

这其中有Construct、Field、Method、NativeArgument、invoke、Property等

在Construct 中

   public static Object construct(Class<?> klass, ReflectedType[] arguments) throws IllegalAccessException, InstantiationException,InvocationTargetException, NoSuchMethodException {
       Constructor<?> constructor = null;

       if(arguments.length == 0)
            constructor = klass.getConstructor();
       else
            constructor =getConstructor(klass, arguments);

       if(constructor != null)
            return constructor.newInstance(getNativeArguments(arguments));
       else
            throw newNoSuchMethodException();
   }

   private static Constructor<?> getConstructor(Class<?> object, ReflectedType[] arguments) {
       for(Constructor<?> constructor : object.getConstructors()) {
           if(hasCompatibleSignatures(constructor.getParameterTypes(),getNativeArguments(arguments)))
                return constructor;
       }

       return null;
   }
对引用实例化constructor.newInstance(getNativeArguments(arguments));
   private static Field getField(Object object, String property) throws NoSuchFieldException {
       if(object instanceof Class)
            return ((Class<?>)object).getField(property);
       else
            return object.getClass().getField(property);
   }

getField用于返回一个指定名称的属性,但是这个属性必须是公有的,这个属性可以在父类中定义。如果是私有属性或者是保护属性,那么都会抛出异常提示找不到这个属性。getFields则是返回类型中的所有公有属性,所有的私有属性和保护属性都找不到。

   private static Method lookupMethod(Class<?> klass, String method_name, ReflectedType[] arguments) throws NoSuchMethodException {
       for(Method method: klass.getMethods()) {
            if(method_name.equals(method.getName())) {
                if(hasCompatibleSignatures(method.getParameterTypes(),getNativeArguments(arguments)))
                    return method;
            }
       }

       throw new NoSuchMethodException(method_name + " for " + klass.toString());
   }
   private static Method getMethod(Object object, String method_name, ReflectedType[] arguments) throws NoSuchMethodException {
       if(object instanceof Class) {
            Method m = lookupMethod((Class<?>)object, method_name, arguments);

            if(!Modifier.isStatic(m.getModifiers()))
                return null;
            else
                return m;
       }
       else
            return lookupMethod(object.getClass(), method_name, arguments);
   }

获取方法getMethod在类方法中获取特定方法名的方法。

getDeclaredField获得在这个类型的声明中定义的指定名称的属性,这个属性必须是在这个类型的声明中定义,但可以使私有和保护的。

   private static boolean isWrapperTypeOf(Class<?> klass, Class<?> primitive) {
       try {
            return !klass.isPrimitive() && klass.getDeclaredField("TYPE").get(null).equals(primitive);
       }
       catch(IllegalAccessException e) {
            return false;
       }
       catch(NoSuchFieldException e) {
            return false;
       }

方法调用

public static Object invoke(Object object, String method_name, ReflectedType[] arguments) throws IllegalAccessException, IllegalArgumentException,InvocationTargetException, NoSuchMethodException {
       if(arguments.length == 0)
            return getMethod(object, method_name, arguments).invoke(object, (Object[])null);
       else
            return getMethod(object, method_name, arguments).invoke(object,getNativeArguments(arguments));
   }

通过类名获取类

   public static Class<?> resolve(String className) {
       try {
            return Class.forName(className);
       }
       catch(ClassNotFoundException e) {
            return null;
       }
   }

在反射类型如下

image023.pngimage025.png

这些类型在protobuf.java中message中定义。

0×04 结论

本文分析源码的主要目的是做android安全测试工具,可以看出在drozer工具中的重要的模块为jdiesel。

Protobuf基于TCP之上的通信协议。

反射框架。在Android安全测试工具中,反射无疑是最关键的,不可或缺的。

*本文作者:wangshu

未经允许不得转载:安全路透社 » Android安全测试工具Drozer之手机端agent源码解析

赞 (0)
分享到:更多 ()

评论 0

评论前必须登录!

登陆 注册