工具
Android Studio,后面的内容简称AS.
Android Studio 是谷歌推出的一个Android集成开发工具,基于IntelliJ IDEA. 类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用于开发和调试。
系统app调试
开发系统app的时候,大多数基于makefile的,并且签名是platform的,因此不能直接通过源码进行调试。本文不打算拿系统app来讲解如何调试,不过会使用这里的调式技巧,那么调试系统app也很简单了(系统app的源码大都涉密,不方便拿来举例,AOSP的源码例外)。
这里主要是Java层的调试,native层的调试在后续文章中会讲解。
应用程序
源码很简单,主要包括一个MainActivity,部分代码如下:
代码语言:javascript复制 @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
password_et = (EditText) this.findViewById(R.id.password);
username_et = (EditText) this.findViewById(R.id.username);
message_tv = ((TextView) findViewById(R.id.textView));
registerReceiver(new MyReceiver(), new IntentFilter("send"));
...
}
通过objection查看安装目录,关于objection的使用在Frida进阶之内存漫游及简单抓包有所介绍。通过env命令可以查看安装目录。
使用adb pull 导出apk.
使用AS进行debug
点击File->Profile or Debug APK,选择导出的apk。
在右上角有Attach Kotlin/Java Source...,选择源代码路径,这时候相应的smali就编程源代码了。注意:在debug配置的时候选择Java Only。
选择Attach debugger to Android Process,选择相应的进程,在需要的地方下断点就可以正常调式了。
这就和和正常的app的调试是一样的了。
当然也可以通过Run->Debug来调试,这种和普通app的调试就一样了。
JDB调试Android程序
在App动态调试(1)-Radare2和lldb 中对JDB调试进行了简单的介绍,通过jdb调试来跟踪指定的动态库加载完成。
JDWP 协议
首先让我们认识一下什么是 JDWP(Java Debug Wire Protocol),说白了就是 JVM 或者类 JVM 的虚拟机都支持一种协议,通过该协议,Debugger 端可以和目标 VM 通信,可以获取目标 VM 的包括类、对象、线程等信息。
在调试 Android 应用程序这一场景,Debugger 一般是指你的 develop machine 的某一支持 JDWP 协议的工具例如 Android Studio 或者 JDB,而 Target JVM 是指运行在你 mobile 设备当中的各个 App(因为它们都是一个个虚拟机 Dalvik 或者 ART)。
JDWP Agent一般负责监听某一个端口,当有 Debugger 向这一个端口发起请求的时候,Agent 就转发该请求给 Target JVM 并最终由该 JVM 来处理请求,并把 reply 信息返回给 Debugger 端。
针对Android设备,可参考下面这个图, JDWP Agent 在 Android 手机上应该是指 adbd 进程。
JDWP 协议的报文格式,JDWP 协议中主要有两种报文:Command packet 和 Reply packet,command packet 就是我们上面所说的请求报文,reply 自然就是对 command 的回答。
如果想深入了解JDWP协议,可参考https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/introclientissues005.html。
关于JDWP曾经存在过远程命令执行漏洞,有兴趣可参考https://blog.csdn.net/wanzt123/article/details/82793023。
JDB命令列表
这个除了step、stop,其他的用的不太多,除非要自己开发调试器。 命令 | 作用 -------- | ----- connectors | 列出此 VM 中可用的连接器和传输 run [class [args]] | 开始执行应用程序的主类 threads [threadgroup] | 列出线程 suspend [thread id(s)] | 挂起线程 (默认值: all) resume [thread id(s)] | 恢复线程 (默认值: all) where [ | all] | 转储线程的堆栈 wherei [ | all] | 转储线程的堆栈, 以及 pc 信息 up [n frames] | 上移线程的堆栈 down [n frames] | 下移线程的堆栈 kill | 终止具有给定的异常错误对象的线程 interrupt | 中断线程 print | 输出表达式的值 dump | 输出所有对象信息 eval | 对表达式求值 (与 print 相同) set | 向字段/变量/数组元素分配新值 locals | 输出当前堆栈帧中的所有本地变量 classes | 列出当前已知的类 class | 显示已命名类的详细资料 methods | 列出类的方法 fields | 列出类的字段 threadgroups | 列出线程组 threadgroup | 设置当前线程组 stop in .[(argument_type,...)] | 在方法中设置断点 stop at : | 在行中设置断点 clear .[(argument_type,...)] | 清除方法中的断点 clear : | 清除行中的断点 clear | 列出断点 catch [uncaught caught all] | 出现指定的异常错误时中断 ignore [uncaught caught all] | 对于指定的异常错误, 取消 ‘catch’ watch [access all] . | 监视对字段的访问/修改 unwatch [access all] . | 停止监视对字段的访问/修改 trace [go] methods [thread] | 跟踪方法进入和退出。 | 除非指定 ‘go’, 否则挂起所有线程 trace [go] method exit exits [thread] | 跟踪当前方法的退出, 或者所有方法的退出 | 除非指定 ‘go’, 否则挂起所有线程 untrace [methods] | 停止跟踪方法进入和/或退出 step | 执行当前行 step up | 一直执行, 直到当前方法返回到其调用方 stepi | 执行当前指令 cont | 从断点处继续执行 list [line number method] | 输出源代码 use (或 sourcepath) [source file path] | 显示或更改源路径 exclude [, ... | "none"] | 对于指定的类, 不报告步骤或方法事件 classpath | 从目标 VM 输出类路径信息 monitor | 每次程序停止时执行命令 monitor | 列出监视器 unmonitor <monitor#> | 删除监视器 read | 读取并执行命令文件 lock | 输出对象的锁信息 threadlocks [thread id] | 输出线程的锁信息 pop | 通过当前帧出栈, 且包含当前帧 reenter | 与 pop 相同, 但重新进入当前帧 redefine | 重新定义类的代码 disablegc | 禁止对象的垃圾收集 enablegc | 允许对象的垃圾收集
调试程序
使用JDB调试上面的apk程序。 (1) 查看进程 adb shell ps | grep com.example.myapplication (2) 端口转发 adb forward tcp:12345 jdwp:15513(进程PID) (3)JDB和app之间建立联系 jdb -attach localhost:12345
(4)设置断点 stop in com.example.myapplication.MainActivity.onCreate(android.os.Bundle)
由于这个这个方法已经运行了,因此这里是不能够出发断点的,如果在改方法调用之前能够suspend进程就能解决这个问题了。在App动态调试(1)-Radare2和lldb 通过R2frida的spawan模式使得进程suspend的。
进入Debug模式
添加这段代码android.os.Debug.waitForDebugger() 是能够实现的。
UI定位
记录AS中的一个错误
记录AS中的一个错误,由于移除插件导致再次启动AS的时候出现了下面的错误:
missing essential plugin org.jetbrains.android please reinstall android studio from scratch
这个错误是和用户有关的,因此可以通过用户切换来解决。
在Ubuntu上的解决办法:
代码语言:javascript复制rm -rf ./.config/Google/AndroidStudio4.1/disabled_plugins.txt
在其他平台也类似,找到用户目录,搜索disabled_plugins.txt,然后删除就OK了。
布局探测
以某多多为例,根据这个关键字就能基本确定代码的位置了。
写在最后
Android中涉及的调试有很多,包括framework层的调试以及native层的调试,后续会持续更新调试相关的文章。熟练使用调试对阅读代码和定位问题都有很大的帮助。
公众号
更多内容,欢迎关注我的微信公众号: 无情剑客。