前面走马观花的看了几个模块的源码,看到是用python(会加载自定义的java类)写的。产生2个问题:在命令行中输入command,drozer是如何去执行的;python是如何与java交互的。

drozer console connect:

当我们在命令行输入以上字符串时,在drozer中是如此来执行的:

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

——>2  def run(self, argv=None)

——>3  self.__invokeCommand(arguments)

——>4 getattr(self, "do_" + command)(arguments)

——>5 def do_connect(self, arguments)

——> 6.1 response = server.startSession(device, password)

——>6.1.1 self.sendAndReceive(SystemRequestFactory.startSession(device_id))

——>6.1.1.1 startSession(device_id)

6.1.1.2self.sendAndReceive(builder)

——>6.1.1.2.1 message_id = self.send(message)

6.1.1.2.2 elif response.id == message_id:  return
response

6.2 if response.type == Message.SYSTEM_RESPONSE and response.system_response.status==Message.SystemResponse.SUCCESS: session_id
=response.system_response.session_id

——>6.2.1 session = Session(server, session_id, arguments)

6.2.2  session.cmdloop()

当输入drozer console connect时,首先会把提取argv[2::]即connect。在步骤2里的run函数里经过转换去执行do_connect方法即步骤5。上面这几步都是次要的,接下来才是主题。步骤6.1建立会话过程;若会话建立成功即步骤6.2,进入6.2.2等待命令的输入,至此drozer console connect 命令全部执行完毕。需要理解什么是会话?

看上面的执行步骤我们可以看到,会话是在步骤6.1.1过程中建立的。在步骤6.1.1.1中封装开始会话请求信息及加入设备ID,budiler。

<span style="white-space:pre">	</span>builder = SystemRequestFactory(Message.SystemRequest.START_SESSION)    #注意这里请求的参数是start_session

        builder.addDeviceId(device_id)

        return builder

至此,步骤6.1.1变成变成步骤6.1.1.2。sendAndReceive中的操作:发送msg和接收到对应的msg才返回。详细分析发送和接收过程:首先给msg自动分配唯一标识符message_id,并通过socket将msg发送给agent;接收msg,通过message_id来识别是否为之前发送出去msg的返回信息。ok,会话建立过程就是给agent发送Message.SystemRequest.START_SESSION。

继续步骤6.2.1,初始化session,在此期间会输出drozer的由字母组成的头像(在最后)。接着进入步骤6.2.2cmdloop(),在preloop()中输出drozer console的版本号,接着循环等待用户输入命令。

run app.service.send:

上面的cmdloop在等待用户的输入,输入以上的字符串。读取字符串到argv中argv[0] = run,argv[1] = app.service.send。根据list[0]会先进入Session类中的do_run方法(Session类中有很多do_xx方法)。然后判断argv[1]是不是模块命令(module.run(argv[1:]))进入drozer.module.base.py中Module类(每个命令都是继承自Module类:class
Send(Module, common.ServiceBinding))中的run方法:

<span style="font-size:18px;"> parser = self.__prepare_parser()

        parser.description = self.usage.formatted_description()
parser.usage = self.usage.formatted_usage(parser) if "-h" in args or "--help" in args:
return parser.print_help()
else:
arguments = parser.parse_args(args) if hasattr(self, 'execute'):
<span style="color:#ff0000;">result = self.execute</span></span>

至此,self.execute(arguments)就去执行类app.service.send下的execute方法了。之前分析过app.service.send的执行过程中会加载自定义的java类来发送和接收service的msg,注意send类的定义class Send(Module, common.ServiceBinding):

<span style="font-size:18px;"> def obtain_binder(self):
if self.binder == None:
ServiceBinder = self.context.loadClass("common/ServiceBinder.apk", "ServiceBinder") self.binder = self.context.new(ServiceBinder) return self.binder</span>

首先会调用loader.py文件中的ClassLoader里的loadclass方法(class ServiceBinding(loader.ClassLoader))。

def loadClass(self, source, klass, relative_to=None):
"""
Load a Class from a local apk file (source) on the running Dalvik VM.
""" if relative_to == None:
relative_to = os.path.join(os.path.dirname(__file__), "..")
elif relative_to.find(".py") >= 0 or relative_to.find(".pyc") >= 0:
relative_to = os.path.dirname(relative_to) if not Module.cached_klass(".".join([source, klass])):
loader = utils.ClassLoader(source, self.__get_cache_path(), self.__get_constructor(), self.klass('java.lang.ClassLoader').getSystemClassLoader(), relative_to=relative_to)
loader.android_path = lambda: Configuration.library("android.jar")
loader.dx_path = lambda: Configuration.executable("dx.bat") if platform.system() == "Windows" else Configuration.executable("dx")
loader.javac_path = lambda: Configuration.executable("javac") Module.cache_klass(".".join([source, klass]), loader.loadClass(klass)) return Module.get_cached_klass(".".join([source, klass]))

loadClass()先利用反射得到ClassLoader来初始化loader,然后利用loader将klass(自定义的java类)装载进dvm。先看反射步骤:.klass('java.lang.ClassLoader')

Module类中的klass方法:

def klass(self, class_name):
"""
Resolves a class name, and returns an object reference for the class.
""" if not Module.cached_klass(class_name):
Module.cache_klass(class_name, self.reflector.resolve(class_name)) return Module.get_cached_klass(class_name)

通过Module.cached_klass判断当前类名是否已在Module__klass中,若不存在则调用self.reflector.resolve来加载并在cache_klass中记录(一个cached,一个是cache)。reflector.resolve: <—— reflector.py

    def resolve(self, class_name):
"""
Resolves a Java class, given its fully qualified name, and returns a
ReflectedObject that can be used to instantiate it with #construct.
""" response = self.sendAndReceive(ReflectionRequestFactory.resolve(class_name)) if response is None:
raise ReflectionException("expected a response to RESOLVE")
elif response.reflection_response.status == Message.ReflectionResponse.SUCCESS:
return ReflectedType.fromArgument(response.reflection_response.result, reflector=self)
else:
raise ReflectionException(response.reflection_response.errormessage)

分两步:resolve(class_name)组合msg;sendAndReceive发送msg要求Reflection calss_name。到此为止得到java.lang.ClassLoader,然后再loader.loadClass(klass)

    def loadClass(self, klass):
return self.getClassLoader().loadClass(klass); def getClassLoader(self):
"""
Gets a DexClassLoader on the agent, given compiled source or an apk
file from the local system.
""" self.source = self.__get_source(self.source_or_relative_path, relative_to=self.relative_to) if self.source != None:
file_path = "/".join([self.cache_path, self.__get_cached_apk_name()]) file_io = self.construct('java.io.File', file_path) if not self.__verify_file(file_io, self.source):
source_data = [ReflectedPrimitive("byte", (ord(i) if ord(i) < 128 else ord(i) - 0x100), reflector=None) for i in self.source] file_stream = self.construct("java.io.FileOutputStream", file_path)
file_stream.write(source_data, 0, len(source_data))
file_stream.close()
return self.construct('dalvik.system.DexClassLoader', file_path, self.cache_path, None, self.system_class_loader)
else:
raise RuntimeError("drozer could not find or compile a required extension library.\n")

ok,仔细分析发现上面对于涉及到java的操作都是利用sendAndReceive来实现的(关于message如何定义查看https://github.com/mwrlabs/mercury-common/blob/master/protobuf.proto)。

startSession:sendAndReceive()——>Message.SystemRequest.START_SESSION

reflector.resolve:sendAndReceive()——>Message.ReflectionRequest.RESOLVE

construct :        sendAndReceive()——> Message.REFLECTION_REQUEST

.....

sendAndReceive是通过socket把msg发送给agent的,实际还是由agent来执行。

参考资料:Android开源审计框架drozer--源码浅析

版权声明:本文为博主原创文章,未经博主允许不得转载。

drozer浅析三:命令实现与交互的更多相关文章

  1. firefox 扩展开发笔记(三):高级ui交互编程

    firefox 扩展开发笔记(三):高级ui交互编程 前言 前两篇链接 1:firefox 扩展开发笔记(一):jpm 使用实践以及调试 2:firefox 扩展开发笔记(二):进阶开发之移动设备模拟 ...

  2. sshpass-Linux命令之非交互SSH密码验证

    sshpass-Linux命令之非交互SSH密码验证 参考网址:https://www.cnblogs.com/chenlaichao/p/7727554.html ssh登陆不能在命令行中指定密码. ...

  3. InnoDB的锁机制浅析(三)—幻读

    文章总共分为五个部分: InnoDB的锁机制浅析(一)-基本概念/兼容矩阵 InnoDB的锁机制浅析(二)-探索InnoDB中的锁(Record锁/Gap锁/Next-key锁/插入意向锁) Inno ...

  4. 【转】sshpass-Linux命令之非交互SSH密码验证

      sshpass-Linux命令之非交互SSH密码验证 ssh登陆不能在命令行中指定密码.sshpass的出现,解决了这一问题.sshpass用于非交互SSH的密码验证,一般用在sh脚本中,无须再次 ...

  5. Python 命令模式和交互模式

    命令模式 在系统CMD命名模式下执行 命令执行到脚本所在目录 执行python Test.py 可直接一次执行完脚本里面所有的语句 交互模式下 一行一行执行

  6. Python的命令模式和交互模式

    Python的命令行模式和交互模式 请注意区分命令行模式和Python交互模式. 在命令行模式下,可以执行python进入Python交互式环境,也可以执行python first.py运行一个.py ...

  7. iOS-静态库,动态库,framework浅析(三)

    创建framework静态库 第一步,新建项目 新建项目.png 第二步,删除系统默认创建的[FMDB.h]和[FMDB.m]文件,导入需要打包的源文件. 导入源码后的工程.png 第三步,修改项目配 ...

  8. 鸟哥的私房菜:Bash shell(三)-命令别名与历史指令

    一  命令别名设定: alias, unalias 命令别名是一个很有趣的东西,特别是你的惯用指令特别长的时候!还有, 增设预设的属性在一些惯用的指令上面,可以预防一些不小心误杀档案的情况发生的时候! ...

  9. Mysql浅析-基础命令(一)

    主要从以上篇幅来介绍mysql的一些知识点 一.Mysql简介 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品.MySQL 是最流行的关系型数 ...

随机推荐

  1. 八. SpringCloud消息总线

    1. 消息总线概述 1.1 分布式配置的动态刷新问题 Linux运维修改Github上的配置文件内容做调整 刷新3344,发现ConfigServer配置中心立刻响应 刷新3355,发现ConfigC ...

  2. WPF 基础 - DataTemplate 和 ControlTemplate 的关系和应用

    1. 关系 凡是 Template,最后都得作用到 控件 上,这个控件就是 Template 的目标控件(也称模板化控件): DataTemplate 一般是落实在一个 ContentPresente ...

  3. Cloudam云端,探索高性能计算在药物研究领域的解决方案

    近日,Cloudam云端与国内某知名药企与合作,通过接入Cloudam云端自主研发的云E云超算服务,计算效率提高的数百倍.这也是云算力在生命科学领域的又一次成功应用.Cloudam云端云E云超算服务是 ...

  4. POJ_2253 Frogger 【最短路变形】

    一.题目 Frogger 二.分析 题意关键点就是那个青蛙距离.就是所有1到2的点的路径中,每条路径都可以确定一个最大值,这个最大值就是青蛙要跳的青蛙距离,然后要求这个青蛙距离最小值. 其实就是最短路 ...

  5. Tomcat搭建配置

    Tomcat是Apache软件基金会( Apache Software Foundation )的Jakarta项目中的一个核心项目,由Apache.Sun和其他一些公司及个人共同开发而成.受Java ...

  6. Heron and His Triangle HDU - 6222

    题目链接:https://vjudge.net/problem/HDU-6222 思路:打表找规律. 然后因为数据范围较大可以考虑用字符串模拟,或者__int128要注意用一个快读快输模板. 1 #i ...

  7. P1049_装箱问题(JAVA语言)

    思路:动态规划的背包问题.使箱子剩余空间最小,也就是使箱内装的物品体积达到最大,我们可将物品的体积视为价值,然后按照01背包问题求解即可. //直接上模板 题目描述 有一个箱子容量为VV(正整数,0 ...

  8. PTA 链表逆置

    6-3 链表逆置 (20 分)   本题要求实现一个函数,将给定单向链表逆置,即表头置为表尾,表尾置为表头.链表结点定义如下: struct ListNode { int data; struct L ...

  9. .net core 和 WPF 开发升讯威在线客服系统【私有化部署免费版】发布

    希望 .net 和 WPF 技术时至今日,还能有一些存在感. 这个项目源于2015年前后,当时开发的初版,我使用了 ASP.NET MVC 做为后端,数据库使用原生 ADO.NET 进行操作.WPF ...

  10. 「HTML+CSS」--自定义加载动画【008】

    前言 Hello!小伙伴! 首先非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 哈哈 自我介绍一下 昵称:海轰 标签:程序猿一只|C++选手|学生 简介:因C语言结识编程,随后转入计算机 ...