1. 程式人生 > >Android滲透工具之Drozer原始碼分析

Android滲透工具之Drozer原始碼分析

       最近時間比較充足,想找點事情來做做。再說Drozer這工具整天在手頭用得著,只知其玄乎,又聽大B哥說其架構是如何的NB。所以,就趁著這份熱盡,體會了Drozer的神乎。確實挺神乎的,本人小菜一枚,求各位大神輕虐,在此放過了;如有想體會Drozer的神乎的朋友,在這拋磚引玉,不求感激,只求不坑了各位,哈哈、、、、

1.Drozer簡介

       Drozer是一個由MWR安全團隊維護開源的軟體,該軟體可以說是針對Android平臺的安全測試框架。安全人員可以通過Drozer自身提供的一些module完成一些基礎的安全測試功能,同時也可以根據需求實現自己的

module,甚至可以在利用Drozer提供的框架實現一些自動化審計功能。於目標裝置上安裝Agent,可以通過Agent接收PC端傳來的指令或者程式碼並與Dalvik VM,其他app以及作業系統進行互動以達到某些安全審計功能。關於Drozer的原始碼可以從Githubhttps://github.com/mwrlabs)上獲取:

drozer: 包含了控制檯與服務端程式碼;

drozer-agent: 包含了運行於Android裝置的Agent程式碼;

jdiesel:反射和通訊協議的核心jar工程;

mwr-tls:安全通訊jar工程;

mwr-android: 

移動端介面jar工程;

2.Drozer通訊協議

       DrozerAgent之間的通訊採用google protocol buffer協議,這種協議是一種輕便高效的結構化資料儲存格式,可以用於結構化資料序列化,很適合做資料儲存或 RPC 資料交換格式。它可用於通訊協議、資料儲存等領域的語言無關、平臺無關、可擴充套件的序列化結構資料格式。目前提供了 C++JavaPython 三種語言的 API。完整協議可以參見github)上的原始碼,該協議是兩者協作的核心。

下面是對這個協議的簡單介紹:

該協議中包含4種訊息型別,其中包括SYSTEM_REQUEST

SYSTEM_RESPONSEREFLECTION_REQUESTREFLECTION_RESPONSE

message Message {
         enum MessageType {
                   SYSTEM_REQUEST= 1;
                   SYSTEM_RESPONSE= 2;
                   REFLECTION_REQUEST= 3;
                   REFLECTION_RESPONSE= 4;
         }
   }

SystemRequest:包含裝置資訊,會話資訊,連線控制

message SystemRequest {
                   enum RequestType {
                            PING= 1;
                            BIND_DEVICE= 2;
                            UNBIND_DEVICE= 3;
                            LIST_DEVICES= 4;
                            START_SESSION= 5;
                            STOP_SESSION= 6;
                            RESTART_SESSION= 7;
                            LIST_SESSIONS= 8;
                       }
                   required RequestType type = 1 [default = PING];
                   optional Device device = 5;
                   optional string session_id = 7;
                   optional string password = 8;
     }

SystemResponse:主要包括響應的型別(繫結服務,裝置列表,會話列表等)狀態資訊

message SystemResponse {
                   enum ResponseType {
                            PONG= 1;
                            BOUND= 2;
                            UNBOUND= 3;
                            DEVICE_LIST= 4;
                            SESSION_ID= 5;
                            SESSION_LIST= 6;
                   }

                   enum ResponseStatus {
                            SUCCESS= 1;
                            ERROR= 2;
                   }
                   required ResponseType type = 1;
                   required ResponseStatus status = 2;
                   repeated Device devices = 6;
                   optional string session_id = 7;
                   optional string error_message = 8;
                   repeated Session sessions = 9;
         }

ReflectionRequest:java反射請求,主要有Resolve(所反射的classname)Construct(物件引用ObjectReferenceint32型別),方法物件method,和方法引數Argument)Invoke呼叫方法(同樣包括物件引用ObjectReferenceint32型別),方法物件method,和方法引數Argument),以及對Propertygetset,還有Delete(物件引用ObjectReferenceint32型別))

message ReflectionRequest {
                   enum RequestType {
                            RESOLVE= 1;
                            CONSTRUCT= 2;
                            INVOKE= 3;
                            SET_PROPERTY= 4;
                            GET_PROPERTY= 5;
                            DELETE= 6;
                            DELETE_ALL= 7;
                   }

                   required string session_id = 1;
                   required RequestType type = 2;

                   message Resolve {
                            optional string classname = 1;
                   }

                   message Construct {
                            optional ObjectReference object = 1;
                            repeated Argument argument = 2;
                   }

                   message Invoke {
                            optional ObjectReference object = 1;
                            optional string method = 2;
                            repeated Argument argument = 3;
                   }

                   message SetProperty {
                            optional ObjectReference object = 1;
                            optional string property = 2;
                            optional Argument value = 3;
                   }

                   message GetProperty {
                            optional ObjectReference object = 1;
                            optional string property = 2;
                   }

                   message Delete {
                            optional ObjectReference object = 1;
                   }

                   optional Resolve resolve = 3;
                   optional Construct construct = 4;
                   optional Invoke invoke = 5;
                   optional SetProperty set_property = 6;
                   optional GetProperty get_property = 7;
                   optional Delete delete = 8;
         }

ReflectionResponse:主要是反射響應後的狀態和引數,還有一些錯誤資訊

message ReflectionResponse {
                   enum ResponseStatus {
                            SUCCESS= 1;
                            ERROR= 2;
                            FATAL= 3;
                   }

                   required string session_id = 1;
                   required ResponseStatus status = 2;
                   optional Argument result = 3;
                   optional string errormessage = 8;
         }

3. Drozer原始碼解析

        由於Drozer存在很多命令,在原始碼中會存在很多分支路徑,本文不打算全部分析,主要側重點在於我們常用的命令(連線Agent,執行模組)程式碼的分析,一般處理指令如下:

drozer console connect

run module_name argv

下面的內容就這兩類命令的執行流程進行原始碼分析。

3.1 Drozer consoleconnect命令執行過程

     首先以drozer console connect為切入點。這條命令是Drozer端與Agent建立console會話。

Drozer端:

找到執行這條命令的入口點,入口點在drozer\cli\console.py

from mwr.common import logger
from drozer.console import Console
logger.setLevel(logging.DEBUG)
logger.addStreamHandler()

#例項化Console,並執行run(),這裡取第3個及之後的引數,這裡取的是connect

Console().run(sys.argv[2::])

在這個模組中,執行Console().run(sys.argv[2::])Console這個類在drozer\console\console.py,這個類繼承cli.Basemwr\common\cli.py)。這個run()就在cli.Base這個類中實現,定位到cli.Base.run()

def run(self, argv=None):
        if argv == None:
            argv = []
        
        self.prepare_argument_parser(argv)
        # parse argv into arguments using the generated ArgumentParser
        #解析命令列引數

        arguments = self.parse_arguments(self._parser, argv)
       #根據命令列引數呼叫對應的方法
        try:

            self.__invokeCommand(arguments)
        except UsageError as e:
            self.__showUsage(e.message)
        except Exception as e:
            self.handle_error(e)

繼續跟蹤__invokeCommand():

def __invokeCommand(self, arguments):
   
        try:
            command = arguments.command

            if "do_" + command in dir(self):
                #呼叫do_xxx方法

                getattr(self, "do_" + command)(arguments)
            else:
                raise UsageError("unknown command: " + command)
        except IndexError:
            raise UsageError("incorrect usage")

run()最後呼叫__invokeCommand(),通過getattr()呼叫do_xxx(),由於傳入的引數connect,這裡執行Console類裡的do_connect()

def do_connect(self, arguments):
        if arguments.password:
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")

                password = getpass.getpass()
        else:
            password = None
        #獲取與Drozer繫結的裝置

        device = self.__get_device(arguments)
        #獲取與Agent連線的socket
        server = self.__getServerConnector(arguments)
        #獲取與Agent開始會話的響應
        response = server.startSession(device, password)
        
        if response.type == Message.SYSTEM_RESPONSE and\
            response.system_response.status == Message.SystemResponse.SUCCESS:
            session_id = response.system_response.session_id

            try:
                if(arguments.debug):
                    session = DebugSession(server, session_id, arguments)
                else:
                     #與Agent建立會話

                    session = Session(server, session_id, arguments)

                if len(arguments.file) > 0:
                    session.do_load(" ".join(arguments.file))
                    session.do_exit("")
                elif arguments.onecmd != None:
                    session.onecmd(arguments.onecmd)
                    session.do_exit("")
                else:
                    #進入控制檯,等待命令輸入並執行

                    session.cmdloop()
            except KeyboardInterrupt:
                print
                print "Caught SIGINT, terminating your session."
            finally:
                session.do_exit("")
                
            self.__getServerConnector(arguments).close()
        else:
            self.handle_error(RuntimeError(response.system_response.error_message), fatal=True)

這裡主要是實現DrozerAgent建立Session,進入控制檯,等待命令的輸入並執行。

下面根據drozer console connect這條命令的流程重點分析SystemRequest、ReflectionRequest兩類訊息的執行過程:

SystemRequest訊息執行過程

device = self.__get_device(arguments),這個方法獲取與Drozer建立連線的裝置。這裡以這個分支詳細分析SystemRequest訊息的執行過程。定位到__get_device():

def __get_device(self, arguments):

        if arguments.device == None:
            #獲取與Drozer建立連線的裝置

            devices = self.__getServerConnector(arguments).listDevices().system_response.devices

            if len(devices) == 1:
                device = devices[0].id

                print "Selecting %s (%s %s %s)\n" % (devices[0].id, devices[0].manufacturer, devices[0].model, devices[0].software)

             ......
             ......
            return arguments.device

__getServerConnector()是為了得到與Agent建立連線的socket。實現程式碼如下:

def __getServerConnector(self, arguments):

        if self.__server == None:
            #例項化ServerConnector類

            self.__server = ServerConnector(arguments, self.__manage_trust)

        return self.__server

ServerConnector這個類在drozer\connector\server_connector.py,核心程式碼是呼叫父類SocketTransport__init__,從而獲取與Agent31415埠建立的socket

class ServerConnector(SocketTransport):
    #連線的主機與埠

    DefaultHost = "127.0.0.1"
    DefaultPort = 31415

    
    def __init__(self, arguments, trust_callback=None):
        try:
             #呼叫SocketTransport.__init__,獲取與Agent的31415埠建立的socket

            SocketTransport.__init__(self, arguments, trust_callback)
        ......
        ......

展開SocketTransport.__init__():

class SocketTransport(Transport):
    
    def __init__(self, arguments, trust_callback=None):
        Transport.__init__(self)
        #socket例項化

        self.__socket = socket.socket()
        
      ......
      ......

得到與agent建立連線的socket,接下來執行__getServerConnector(arguments).listDevices()定位到listDevices()

def listDevices(self):

        try:
            #向Agent傳送LIST_DEVICES訊息並接收響應

            return self.sendAndReceive(SystemRequestFactory.listDevices())
        except RuntimeError as e:
            if e.message == 'Received an empty response from the Agent. This normally means the remote service has crashed.':
                raise ConnectionError(e)
            else:
                raise

這個方法呼叫sendAndReceive(SystemRequestFactory.listDevices()),而sendAndReceive主要是傳送一個messageagent,並獲取返回結果,定位到sendAndReceive

def sendAndReceive(self, message):
         #為message加上一個id,然後將這個message封裝為protocol buffer型別的Frame,最終傳送給Agent

        message_id = self.send(message)

        while(True):
           #獲取返回結果

            response = self.receive()

            if response == None:
                raise ConnectionError(RuntimeError('Received an empty response from the Agent.'))
            elif response.id == message_id:
                return response

展開send(message):

def send(self, message):
      
        try:
             #新增message id

            message_id = self.nextId()
            #封裝為protocol buffer類物件
            frame = Frame.fromMessage(message.setId(message_id).build())
            #將訊息傳送給Agent
            self.__socket.sendall(str(frame))
    
            return message_id
      .....
      ......

我們再來看下此處message的細節。執行SystemRequestFactory.listDevices(),這個方法是為了獲取與Drozer建立連線的Agent這樣一個SystemRequest訊息物件。定位到listDevices()

def listDevices(cls):
         #例項化SystemRequest型別的物件

        builder = SystemRequestFactory(Message.SystemRequest.LIST_DEVICES)

        return builder

到這裡,SystemRequest訊息在Drozer中執行完畢。接下來看Agent是如何接收這個SystemRequest.LIST-DEVICES這個請求,並如何處理的。

Agent端:

       Agent首先會通過一個Receiver啟動ServerService,這個ServerService就是與Drozer建立連線的服務。定位到Receiver

public void onReceive(Context context, Intent intent) {
                   Intent start_service = new Intent();
                   start_service.putExtras(intent);         
                   if(intent.getCategories().contains("com.mwr.dz.START_EMBEDDED")){
                            start_service.addCategory("com.mwr.dz.START_EMBEDDED");

                            #Intent繫結ServerService

                            start_service.setComponent(new ComponentName("com.mwr.dz",
"com.mwr.dz.services.ServerService"));

                   }
                   else {
                            if(intent.getCategories().contains("com.mwr.dz.CREATE_ENDPOINT"))
                                     start_service.addCategory("com.mwr.dz.CREATE_ENDPOINT");
                            if(intent.getCategories().contains("com.mwr.dz.START_ENDPOINT"))
                                     start_service.addCategory("com.mwr.dz.START_ENDPOINT");                          
                            start_service.setComponent(new
ComponentName("com.mwr.dz", "com.mwr.dz.services.ClientService"));
                   }

                   #啟動ServerService

                   context.startService(start_service);
         }

ServerService這個服務相對Drozer可以稱為server端,主要負責監聽Drozer的請求連線,並處理相關請求。

ServerService間接繼承Service元件,onStartCommand()主要負責執行StartServer()這個方法。

public int onStartCommand(Intent intent, int flags, int startId){
                   int ret_val = super.onStartCommand(intent, flags, startId);
                   if(intent != null &&intent.getCategories() != null &&
intent.getCategories().contains("com.mwr.dz.START_EMBEDDED")) {
                            Agent.getInstance().setContext(this.getApplicationContext());


                            this.startServer();
                   }                 
                   return ret_val;
         }

定位到startServer()

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

                           #例項化Server物件,其中Server類間接繼承Thread

                            this.server = new Server(this.server_parameters, Agent.getInstance().getDeviceInfo());

                            this.server.setLogger(this.server_parameters.getLogger());
                            this.server_parameters.getLogger().addOnLogMessageListener(this);

                            #啟動Server這個例項化執行緒,執行run()

                            this.server.start();       
                    
                           Toast.makeText(this,String.format(Locale.ENGLISH, this.getString(R.string.embedded_server_started),this.server_parameters.getPort()), Toast.LENGTH_SHORT).show();
                   }
         }

程式碼中的this.serverServer類的物件,這個Server類繼承Link類,而Link類間接繼承Thread,所以說,Server類是一個執行緒類。當執行this.server.start()時,這個執行緒就啟動了。定位到Server類的run()

public void run() {
                   this.running = true;                 
                   this.log(LogMessage.INFO,"Starting Server...");
                   while(this.running) {
                            try {
                                     if(this.connection== null) {               
                                               this.log(LogMessage.INFO,"Attempting to bind to port " +
((com.mwr.jdiesel.api.connectors.Server)this.parameters).getPort() +"...");

                                                #例項化ServerSocket物件

                                               this.server_socket= new
ServerSocketFactory().createSocket((com.mwr.jdiesel.api.connectors.Server)this.parameters);

                                              this.log(LogMessage.INFO,"Waiting for connections...");
                                               #接收Drozer的socket傳送過來的請求

                                               Socket socket =this.server_socket.accept();
                                             
                                               if(socket!= null) {                                                                                                             
                                                        this.log(LogMessage.INFO,"Accepted connection...");                                             
                                                        this.log(LogMessage.INFO,"Starting drozer thread...");

                                                        #處理Drozer發過來的請求

                                                        this.createConnection(new SocketTransport(socket));
                                               }
                                     }

server_socket.accept()負責接收Drozer的請求。而createConnection(new SocketTransport(socket))負責處理這個請求。定位到createConnection()

相關推薦

Android滲透工具Drozer原始碼分析

       最近時間比較充足,想找點事情來做做。再說Drozer這工具整天在手頭用得著,只知其玄乎,又聽大B哥說其架構是如何的NB。所以,就趁著這份熱盡,體會了Drozer的神乎。確實挺神乎的,本人小菜一枚,求各位大神輕虐,在此放過了;如有想體會Drozer

Android Architecture ComponentViewModel原始碼分析

前言 知識準備 重要知識介紹(後面用到) /** * Control whether a fragment instance is retained across Activity * re-creation (s

Android---網路程式設計Retrofit2原始碼分析

相關 前言 通過上篇的分析,大家應該對Retrofit2的大致情況有所瞭解吧,下面為我們一起看一下原始碼,沒看過的,可以看一下了解一下解析思路。 思路 我們現在知道了使用retrofit需要進行3步流程

Android 彈出式佈局Dialog原始碼分析

文章目錄 關於 Dialog的基本使用,可以看之前寫的一片文章 Android 彈出式佈局之Dialog的使用 官方不是不建議直接使用Dialog的,而我們工作中一般是使用AlertDialog或者DialogFragment實現彈框功能

android記憶體優化三記憶體分析工具的使用

 anroid記憶體分析工具的使用 一.Eclipse Heap分析記憶體洩露           Android開發中避免不了碰到記憶體洩露問題,這裡先大概講下記憶體洩露的基本概念:記憶體洩露官方的解釋是是用動態儲存分配函式動態開闢的空間,在使用完畢後未釋放,結果導致一直

Android服務startService原始碼分析

先看下繼承關係: startService是Context的抽象方法,呼叫startService時,會先呼叫到ContextWrapper的startService方法: @Override public ComponentName startService(Inten

Android原始碼分析Glide原始碼分析&基礎版ImageLoader框架

1 Glide原始碼分析   Glide是一款由Bump Technologies開發的圖片載入框架,使得我們可以在Android平臺上以極度簡單的方式載入和展示圖片。本部落格基於Glide 3.7.0版本來進行講解,這個版本的Glide相當成熟和穩定。

Android 65K問題Multidex原理分析及NoClassDefFoundError的解決方法

bottom mini ati ... types auto weight right for Android 65K問題相信困惑了不少人,盡管AS的出來能夠通過分dex高速解決65K問題,可是同一時候也easy由於某些代碼沒有打包到MainDex裏

【kubernetes/k8s原始碼分析】kubelet原始碼分析cdvisor原始碼分析

  資料流 UnsecuredDependencies -> run   1. cadvisor.New初始化 if kubeDeps.CAdvisorInterface == nil { imageFsInfoProvider := cadv

【kubernetes/k8s原始碼分析】 controller-managerreplicaset原始碼分析

ReplicaSet簡介     Kubernetes 中建議使用 ReplicaSet來取代 ReplicationController。ReplicaSet 跟 ReplicationController 沒有本質的不同, ReplicaSet 支援集合式的

【kubernetes/k8s原始碼分析】 client-go包Informer原始碼分析

Informer 簡介        Informer 是 Client-go 中的一個核心工具包。如果 Kubernetes 的某個元件,需要 List/Get Kubernetes 中的 Object(包括pod,service等等),可以直接使用

【go原始碼分析】go原始碼slice原始碼分析

Go 語言切片是對陣列的抽象。 Go 陣列的長度不可改變,與陣列相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大。 len() 和 cap() 函式     切片是可索引的,並且可以由 len() 方法獲取長度。    

【go原始碼分析】go原始碼list原始碼分析

本文針對go 1.11版本,路徑src/container/list/list.go 資料結構 Element結構體 Value 前驅 後繼 // Element is an element of a linked list. type Element st

android apk 簽名 原始碼環境裡如何完成

假設Android原始碼路徑為 ProPath/ 且cd到此路徑下,之後執行 1. cp  ./build/target/product/security/releasekey.x509.pem  ./ 2. cp  ./build/target/pr

RecyclerViewSnapHelper原始碼分析

很久沒有寫Android控制元件了,正好最近專案有個自定義控制元件的需求,整理了下做個總結,主要是實現類似於抖音翻頁的效果,但是有有點不同,需要在底部漏出後面的view,這樣說可能不好理解,看下Demo,按頁滑動,後面的View有放大縮放的動畫,滑動速度過小時會有回到原位的效果,下滑也是按頁滑動的效果。

springMVC原始碼學習addFlashAttribute原始碼分析

本文主要從falshMap初始化,存,取,消毀來進行原始碼分析,springmvc版本4.3.18。關於使用及驗證請參考另一篇https://www.cnblogs.com/pu20065226/p/10032048.html 1.初始化和呼叫,首先是入springMVC 入口webmvc包中org.spr

Android系統播放器MediaPlayer原始碼分析(一)

前言 對於MediaPlayer播放器的原始碼分析內容相對來說比較多,會從Java->JNI->C/C++慢慢分析,後面會慢慢更新。另外,部落格只作為自己學習記錄的一種方式,對於其他的不過多的評論。 MediaPlayerDemo public class MainA

java集合----ArrayList原始碼分析(基於jdk1.8)

一、ArrayList 1、ArrayList是什麼: ArrayList就是動態陣列,用MSDN中的說法,就是Array的複雜版本,它提供了動態的增加和減少元素,實現了ICollection和IList介面,靈活的設定陣列的大小等好處,實現了Randomaccess介面,支援快速隨

java集合----HashMap原始碼分析(基於JDK1.7與1.8)

一、什麼是HashMap 百度百科這樣解釋: 簡而言之,HashMap儲存的是鍵值對(key和value),通過key對映到value,具有很快的訪問速度。HashMap是非執行緒安全的,也就是說在多執行緒併發環境下會出現問題(死迴圈) 二、內部實現 (1)結構 HashM

ROS Navigation Stackdwa_local_planner原始碼分析

DWA和base_local_planner的關係 在base_local_planner包中有兩個檔案叫trajectory_planner.cpp 以及對應的ros實現,其和DWA是同一層的。 由於nav_core提供了統一的介面,因此我們可以先看看統一的介面有哪些,那我們便知