最近时间比较充足,想找点事情来做做。再说Drozer这工具整天在手头用得着,只知其玄乎,又听大B哥说其架构是如何的NB。所以,就趁着这份热尽,体会了Drozer的神乎。确实挺神乎的,本人小菜一枚,求各位大神轻虐,在此放过了;如有想体会Drozer的神乎的朋友,在这抛砖引玉,不求感激,只求不坑了各位,哈哈、、、、
1. Drozer简介
Drozer是一个由MWR安全团队维护开源的软件,该软件可以说是针对Android平台的安全测试框架。安全人员可以通过Drozer自身提供的一些module完成一些基础的安全测试功能,同时也可以根据需求实现自己的module,甚至可以在利用Drozer提供的框架实现一些自动化审计功能。于目标设备上安装Agent,可以通过Agent接收PC端传来的指令或者代码并与Dalvik VM,其他app以及操作系统进行交互以达到某些安全审计功能。关于Drozer的源码可以从Github(https://github.com/mwrlabs)上获取:
drozer: 包含了控制台与服务端代码;
drozer-agent: 包含了运行于Android设备的Agent代码;
jdiesel:反射和通信协议的核心jar工程;
mwr-tls:安全通信jar工程;
mwr-android: 移动端界面jar工程;
2. Drozer通信协议
Drozer和Agent之间的通信采用google protocol buffer协议,这种协议是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,很适合做数据存储或 RPC 数据交换格式。它可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。完整协议可以参见github(https://github.com/mwrlabs/mercury-common/blob/master/protobuf.proto)上的源码,该协议是两者协作的核心。
下面是对这个协议的简单介绍:
该协议中包含4种消息类型,其中包括SYSTEM_REQUEST、SYSTEM_RESPONSE、REFLECTION_REQUEST、REFLECTION_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 { |
---|
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; message Resolve { message Construct { message Invoke { message SetProperty { message GetProperty { message Delete { optional Resolve resolve = 3; |
---|
ReflectionResponse:主要是反射响应后的状态和参数,还有一些错误信息
message ReflectionResponse { enum ResponseStatus { SUCCESS= 1; ERROR= 2; FATAL= 3; } required string session_id = 1; |
---|
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): 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() 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: session = Session(server, session_id, arguments) if len(arguments.file) > 0: |
---|
这里主要是实现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: devices = self.__getServerConnector(arguments).listDevices().system_response.devices if len(devices) == 1: print “Selecting %s (%s %s %s)\n” % (devices[0].id, devices[0].manufacturer, devices[0].model, devices[0].software) …… |
---|
__getServerConnector()是为了得到与Agent建立连接的socket。实现代码如下:
def __getServerConnector(self, arguments): if self.__server == None: 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: 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): if response == None: |
---|
展开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(); |
---|
定位到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():
protected void createConnection(Transport transport) { if(transport.isLive()) { #实例化Connection对象 this.connection = new Connection(this,this.device_info, transport); this.connection.start(); } } |
---|
创建一个Connection实例,而后启动它。这个Connection也是间接继承Thread,也是一个线程类。定位到这个类的run(),而这个run()是继承来自它的父类AbstractConnection,真正负责接收Drozer的请求与处理相对应的请求就在这里实现的。具体代码如下:
public void run() { this.running = true; this.started = true; this.tryAndNotifyAll(); this.last_message_at =System.currentTimeMillis(); Message request = null; if(!this.bindToServer(this.device_info)) this.stopConnection(); while(this.running) { #获取Drozer发送过来的request request = this.receive(); if(request != null){ #处理request this.handleMessage(request); request =null; } this.checkForLiveness(); Thread.yield(); } |
---|
展开this.receive()这个函数,将请求转化为Frame对象,这里需要将request转化为Frame对象,是不是符合Drozer将message封装成Frame的过程?定位到receive():
protected Message receive() { try { #将request转化为Frame类型 Frame f = this.transport.receive(); return f != null ?f.getPayload() : null; } |
---|
再展开this.handleMessage(),代码如下:
private void handleMessage(Message message) { try { Message response = null; switch(message.getType()){ case REFLECTION_REQUEST: case REFLECTION_RESPONSE: #匹配message类型,并做相应的处理 case SYSTEM_REQUEST: response = this.handleSystemRequest(message); break; case SYSTEM_RESPONSE: default: #将处理后的结果返回给Drozer this.send(response); } |
---|
从这里可以看出,这里根据不同的消息类型进行处理。因为LIST_DEVICES属于SystemRequest,所以这个交给handleSystemRequest()处理。而handleSystemRequest()调用system_message_handler.handle()进行处理。而后将response返回给Drozer。
展开system_message_handler.handle(),system_message_handler属于SystemMessageHandler的实例。Handle()代码如下:
public Message handle(Message message) throws InvalidMessageException { if(message.getType() != Message.MessageType.SYSTEM_REQUEST) throw new InvalidMessageException(message); if(!message.hasSystemRequest()) throw new InvalidMessageException(message); switch(message.getSystemRequest().getType()){ #匹配具体的SystemRequest消息类型,并进行处理 case LIST_DEVICES: return this.handleListDevices(message); case LIST_SESSIONS: return this.handleListSessions(message); case PING: return this.handlePing(message); case START_SESSION: return this.startSession(message); case STOP_SESSION: return this.stopSession(message); default: throw new InvalidMessageException(message); } |
---|
到这里就看到LIST_DEVICES消息类型了。定位到handleListDevices()
protected Message handleListDevices(Message message) throws InvalidMessageException { MessageFactory factory = new MessageFactory(SystemResponseFactory.deviceList(message).addDevice( this.device_info.getAndroidID(), this.device_info.getManufacturer(), this.device_info.getModel(), this.device_info.getSoftware())); factory.inReplyTo(message); return factory.build(); } |
---|
至此,整个获取与Drozer建立连接的设备就结束了。
接着往下走,下面重点分析
ReflectionRequest消息执行过程
现在我们重点分析建立会话过程,定位到Console.do_connect():
def do_connect(self, arguments): if arguments.password: with warnings.catch_warnings(): warnings.simplefilter(“ignore”) password = getpass.getpass() 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: session = Session(server, session_id, arguments) if len(arguments.file) > 0: |
---|
我们从这行入手session = Session(server, session_id, arguments),初始化session对象,建立与Agent联系的session。
定位到Session类的__init__方法:
class Session(cmd.Cmd): def __init__(self, server, session_id, arguments): self.modules = collection.ModuleCollection(loader.ModuleLoader()) self.prompt = “dz> “ #反射对象 self.reflector = Reflector(self) if hasattr(arguments, ‘no_color’) and not arguments.no_color: self.stdout = ColouredStream(self.stdout) self.stderr = ColouredStream(self.stderr) else: self.stdout = DecolouredStream(self.stdout) self.stderr = DecolouredStream(self.stderr) #实例化Module对象m m = Module(self) if m.has_context(): self.variables = { ‘PATH’: dataDir +’/bin:/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin’, |
---|
如果Context不存在,获取com.mwr.dz.Agent的数据路径,当然这里就要用到消息反射了。定位到Modules的getContext():
def getContext(self): #利用反射得到com.mwr.dz.Agent的实例 return self.klass(‘com.mwr.dz.Agent’).getContext() |
---|
继续跟踪klass()
def klass(self, class_name): if not Module.cached_klass(class_name): Module.cache_klass(class_name, self.reflector.resolve(class_name)) return Module.get_cached_klass(class_name) |
---|
继续执行会跳转到Reflector.resolve方法:
def resolve(self, class_name): #将反射请求发送到Agent response = self.sendAndReceive(ReflectionRequestFactory.resolve(class_name)) if response is None: |
---|
到这里,我们又看到SendAndReceive(),接下来的执行过程可参考Drozer端的SystemRequest消息的执行过程。
Agent端:
Agent接收与处理ReflecionRequest类消息可参考上节讲的SystemReqeust。这里重点讲下ReflectionRequest的处理过程。最终执行过程如下:
public Message handle(Message message) throws InvalidMessageException { if(message.getType() != Message.MessageType.REFLECTION_REQUEST) throw new InvalidMessageException(message); if(!message.hasReflectionRequest()) throw new InvalidMessageException(message); try { switch(message.getReflectionRequest().getType()){ case CONSTRUCT: if(!message.getReflectionRequest().hasConstruct()) throw new InvalidMessageException(message); return this.handleConstruct(message); case DELETE: if(!message.getReflectionRequest().hasDelete()) throw new InvalidMessageException(message); return this.handleDelete(message); case DELETE_ALL: return this.handleDeleteAll(message); case GET_PROPERTY: if(!message.getReflectionRequest().hasGetProperty()) throw new InvalidMessageException(message); return this.handleGetProperty(message); case INVOKE: if(!message.getReflectionRequest().hasInvoke()) throw new InvalidMessageException(message); return this.handleInvoke(message); #执行resolve类的ReflectionRequest消息 case RESOLVE: if(!message.getReflectionRequest().hasResolve()) throw new InvalidMessageException(message) return this.handleResolve(message); case SET_PROPERTY: if(!message.getReflectionRequest().hasSetProperty()) throw new InvalidMessageException(message); return this.handleSetProperty(message); default: throw new InvalidMessageException(message); } } |
---|
定位到handResolve():
protected Message handleResolve(Message message) throws InvalidMessageException { #利用反射得到类的class Class<?>klass = Reflector.resolve(message.getReflectionRequest().getResolve().getClassname()); if(klass != null) { int ref = this.session.object_store.put(klass); return this.createResponse(message, ReflectionResponseFactory.object(ref)); } else { return this.handleError(message, “cannot resolve ” + message.getReflectionRequest().getResolve().getClassname()); } } |
---|
定位到Reflector.resolve:
public static Class<?> resolve(String className) { try { #反射得到类名的class return Class.forName(className); } catch(ClassNotFoundException e) { return null; } } |
---|
至此,ReflectonRequest整个请求与处理过程完成了。
3.2 runmodule_name argvs命令执行过程
为了方便,这里以run app.package. AttackSurface packageName为例说明。
现在我们重点分析在console中执行命令的过程,定位到Console.do_connect():
def do_connect(self, arguments): if arguments.password: with warnings.catch_warnings(): warnings.simplefilter(“ignore”) password = getpass.getpass() 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: session = Session(server, session_id, arguments) if len(arguments.file) > 0: |
---|
此处从session.cmdloop()入手,定位到cmdloop()方法:
def cmdloop(self, intro=None): self.preloop() if self.use_rawinput and self.completekey: self.push_completer(self.complete, self.history_file) try: stop = None while not stop: if self.cmdqueue: line = self.cmdqueue.pop(0) else: if self.use_rawinput: try: #得到用户输入的命令 line = raw_input(self.prompt) except EOFError: line = ‘EOF’ else: self.stdout.write(self.prompt) self.stdout.flush() line = self.stdin.readline() if not len(line): line = ‘EOF’ else: line = line.rstrip(‘\r\n’) try: line = self.precmd(line) #解析输入的命令 stop = self.onecmd(line) stop = self.postcmd(stop, line) ….. ….. |
---|
这里首先获取用户的输入命令,然后执行onecmd()对获取到的命令进行解析。定位到onecmd():
def onecmd(self, line): cmd, arg, line = self.parseline(line) if not line: return self.emptyline() if cmd is None: return self.default(line) self.lastcmd = line if line == ‘EOF’ : self.lastcmd = ” if cmd == ”: return self.default(line) else: try: #获取console当中的命令,调用do_xxx func = getattr(self, ‘do_’ + cmd) except AttributeError: return self.default(line) return func(arg) |
---|
这里,根据输入的命令跳转到对应do_xxx的方法进行解析。
此处分析 run命令的执行过程,定位到Session.do_run()方法:
def do_run(self, args): argv = shlex.split(args, comments=True) if len(argv) == 1 and (argv[0] == “-h” or argv[0] == “–help”): if len(argv) > 0: module = self.__module(argv[0]) module.push_completer = self.__push_module_completer module.pop_completer = self.__pop_module_completer self.__module_pushed_completers = 0 except KeyError as e: self.stderr.write(“unknown module: %s\n” % str(e)) return None try: module.run(argv[1:]) except KeyboardInterrupt: self.stderr.write(“\nCaught SIGINT. Interrupt again to terminate you session.\n”) except Exception as e: self.handleException(e) while self.__module_pushed_completers > 0: self.__pop_module_completer() else: self.do_help(“run”) |
---|
因为所以的modules都继承Module,而module.run()这个run()是在Module中实现的,定位到Module.run():
def run(self, args): #实例化parse对象 parser = self.__prepare_parser() parser.description = self.usage.formatted_description() if “-h” in args or “–help” in args: return result |
---|
定位到app.package. AttackSurface.execute():
def execute(self, arguments): if arguments.package != None: package = self.packageManager().getPackageInfo(arguments.package, common.PackageManager.GET_ACTIVITIES | common.PackageManager.GET_RECEIVERS | common.PackageManager.GET_PROVIDERS | common.PackageManager.GET_SERVICES) application = package.applicationInfo activities = self.match_filter(package.activities, ‘exported’, True) if (application.flags & application.FLAG_DEBUGGABLE) != 0: if package.sharedUserId != None: |
---|
至此,在console当中run命令的执行分析过程就结束了。
4. Drozer小结
Drozer可以看成是C/S结构的工具。PC端是用Python写的,Agent端是用java写的。利用Google Protocol Buffer协议作为消息载体。很重要的一点就是利用反射,很大一部分工作都是由Agent代理完成,最后由PC端处理解析。后面还有就是命令的具体实现部分,后面会陆续更新。
5. 参考链接
http://www.freebuf.com/articles/terminal/111863.html
http://bbs.pediy.com/showthread.php?t=190561