Android滲透工具之Drozer原始碼分析
最近時間比較充足,想找點事情來做做。再說Drozer這工具整天在手頭用得著,只知其玄乎,又聽大B哥說其架構是如何的NB。所以,就趁著這份熱盡,體會了Drozer的神乎。確實挺神乎的,本人小菜一枚,求各位大神輕虐,在此放過了;如有想體會Drozer的神乎的朋友,在這拋磚引玉,不求感激,只求不坑了各位,哈哈、、、、
1.Drozer簡介
Drozer是一個由MWR安全團隊維護開源的軟體,該軟體可以說是針對Android平臺的安全測試框架。安全人員可以通過Drozer自身提供的一些module完成一些基礎的安全測試功能,同時也可以根據需求實現自己的
drozer: 包含了控制檯與服務端程式碼;
drozer-agent: 包含了運行於Android裝置的Agent程式碼;
jdiesel:反射和通訊協議的核心jar工程;
mwr-tls:安全通訊jar工程;
mwr-android:
2.Drozer通訊協議
Drozer和Agent之間的通訊採用google protocol buffer協議,這種協議是一種輕便高效的結構化資料儲存格式,可以用於結構化資料序列化,很適合做資料儲存或 RPC 資料交換格式。它可用於通訊協議、資料儲存等領域的語言無關、平臺無關、可擴充套件的序列化結構資料格式。目前提供了 C++、Java、Python 三種語言的 API。完整協議可以參見github()上的原始碼,該協議是兩者協作的核心。
下面是對這個協議的簡單介紹:
該協議中包含4種訊息型別,其中包括SYSTEM_REQUEST
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(物件引用ObjectReference(int32型別),方法物件method,和方法引數Argument),Invoke呼叫方法(同樣包括物件引用ObjectReference(int32型別),方法物件method,和方法引數Argument),以及對Property的get與set,還有Delete(物件引用ObjectReference(int32型別))
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.Base(mwr\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 "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) |
---|
這裡主要是實現Drozer與Agent建立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__,從而獲取與Agent的31415埠建立的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主要是傳送一個message給agent,並獲取返回結果,定位到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.server是Server類的物件,這個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 Component之ViewModel原始碼分析前言 知識準備 重要知識介紹(後面用到) /** * 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-manager之replicaset原始碼分析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 RecyclerView之SnapHelper原始碼分析很久沒有寫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 Stack之dwa_local_planner原始碼分析DWA和base_local_planner的關係 在base_local_planner包中有兩個檔案叫trajectory_planner.cpp 以及對應的ros實現,其和DWA是同一層的。 由於nav_core提供了統一的介面,因此我們可以先看看統一的介面有哪些,那我們便知 |
---|