使用Smalidea对无源码APK调试简介

最近正好也用了Smalidea,就ZZ的原贴做一些补充。

  1. 可调试APP

如果Android的系统属性ro.debuggable等于1(用getprop ro.debuggable验证),则所有APP都可调试。如果ro.debuggable等于0,某APP的AndroidManifest.xml中有android:debuggable=”true”,该APP可调试。

对于模拟器,ro.debuggable等于1:

真实手机上ro.debuggable一般等于0,其上绝大多数APP的AndroidManifest.xml中没有android:debuggable=”true”,为了在真实手机上调试这些APP,必须用些歪招,不在此文范围。

ZZ用了:

没必要,可以直接”getprop ro.debuggable”。

1. 下载

https://github.com/JesusFreke/smali/
https://github.com/JesusFreke/smali/wiki/smalidea
https://bitbucket.org/JesusFreke/smali/downloads

https://bitbucket.org/JesusFreke/smali/downloads/smalidea-0.03.zip
https://bitbucket.org/JesusFreke/smali/downloads/smali-2.2b4.jar
https://bitbucket.org/JesusFreke/smali/downloads/baksmali-2.2b4.jar

2. 安装

3. 用baksmali反编译apk

ZZ用Apktool反编译,实际上Smalidea自带反编译工具

反编译结果出现在out子目录中

baksmali有很多参数,下面这个参数你可能会感兴趣:

4. 在baksmali反编译基础上新建工程

创建目录:

将baksmali得到的out子目录复制到上述目录,更名为src子目录:

在AS左上角选择”Project Files”

SOME_Smalidea->src->右键菜单->Mark Directory As->Sources Root

5. 建立调试通道

对于可调试APP,有两种办法建立调试通道,一种是用DDMS,另一种是用ADB,其实前者也隐式使用ADB。

5.1 启动DDMS

过去用ddms.bat启动DDMS,这个已经过时了,现在建议用monitor.bat启动DDMS。monitor.bat实际执行:

选择哪种CPU架构,由如下命令控制:

最终受java的位数控制,如果用64-bits Java,将执行:

最好用”where java”检查一下当前Java路径,调整环境变量,比如:

假设启动DDMS失败,用Process Explorer检查是否存在多个monitor.exe,杀掉它们再试。此外可以用Tcpview检查8700/TCP被谁占用。

DDMS有GUI,在左上角区域可以看到所有可调试进程的PID及调试端口。从中选择待调试进程,会发现其多出一个调试端口,第一个端口(原有的)不固定,第二个端口(新增的)是固定的8700/TCP。这两个调试端口地位相当,都可以用于调试,只不过8700有利于固化Android Studio调试配置。8700始终跟着被选中的待调试进程,如果切换了待调试进程,8700将出现在新选中的进程行,上一个被选中的进程行不再出现8700。

关于DDMS、ADB、JDWP,有兴趣者参看:


Dalvik Debugger Support
dalvik/docs/debugger.html

Dalvik VM Debug Monitor
dalvik/docs/debugmon.html

system/core/adb/jdwp_service.c

《格蠢汇编》第20章《漫谈Android系统的调试模型》

5.2 adb forward tcp:hostport jdwp:pid

关掉DDMS,直接用ADB建立调试通道

注意双引号的使用,这样可以确保执行Android系统中的grep,而不是PC上的grep,从而使得上述命令在Windows、Linux上都可用。

关闭ADB建立的调试通道:

上述命令的Windows实现可能有BUG,事后用Tcpview查看,adb仍在侦听8700/TCP,必须:

我这里举例是以模拟器为目标,实际上可以是真实手机。

直接用ADB建立调试通道时,不一定使用8700/TCP。此处之所以仍然使用8700,是为了固化Android Studio调试配置。

5.3 启动APP之初就开始调试

前面假设APP已经启动,待调试代码仍可路过。如果待调试代码位于APP启动之初,需要额外操作。

假设目标是模拟器,检查AndroidManifest.xml

在模拟器中会看到:

然后启动DDMS或”adb forward tcp:hostport jdwp:pid”,建立调试通道。

假设目标是真实手机:

设置->开发者选项->USB调试
设置->开发者选项->选择调试应用->SOME
设置->开发者选项->等待调试器

去真实手机中正常启动SOME,也会停在”Waiting For Debugger”。

然后启动DDMS或”adb forward tcp:hostport jdwp:pid”,建立调试通道。

6. 在Android Studio中调试

Run->Edit Configurations->点击左上角+号->Remote

Name : DebugVia8700(这个名字可以任意)
Port : 8700(如果与DDMS配合,可以设成另一个端口,但每次都得改)

第72行附近代码:

在if-nez左侧空白处单击或按Ctrl-F8(参看Run菜单)设置断点

在Console面板中会看到:

去模拟器中正常操作SOME,触发断点,调用栈回溯:

可以单步跟踪,可以修改由this指针定位的成员变量。

对于JVM,我一直梦想着有这样一个Java字节码级别的调试器,可以看调用栈回溯、看单条Bytecode指令的中间状态(比如ifeq是否满足)、修改变量等等,一直没找到。想不到对于DVM,有Smalidea这样的东西,不错。

有些文章提到一个配置操作:

对于在baksmali反编译基础上新建工程,此处显示。这些文章认为必须在此设置SDK版本,完全是胡说八道,根本不需要。

7. 排错

注意,”am start -D”仍然要求ro.debuggable=1或android:debuggable=”true”

假设在真实手机上调试一个不可调试APP,”am start -D”不会报错,但这个进程中不会出现JDWP线程,可用如下命令验证:

对于这种情况,DDMS里看不到”com.anything.some”。如果用”adb forward tcp:8700 jdwp:pid”强行建立调试通道,只是看上去成功了,Tcpview看到adb侦听127.0.0.1:8700/TCP,在Android Studio中尝试调试时,报错:

简单点说,如果APP所在进程没有出现JDWP线程,不管你怎么折腾,都不要想着调试了。

如果您需要了解更多内容,可以
加入QQ群:570982169
直接询问:010-68438880

发表评论