标题: 使用Smalidea对无源码APK调试简介 https://scz.617.cn/android/201701041805.txt 最近正好也用了Smalidea,就ZZ的原贴做一些补充。 0. 可调试APP 如果Android的系统属性ro.debuggable等于1(用getprop ro.debuggable验证),则所 有APP都可调试。如果ro.debuggable等于0,某APP的AndroidManifest.xml中有 android:debuggable="true",该APP可调试。 对于模拟器,ro.debuggable等于1: $ adb -s emulator-5554 shell "getprop ro.debuggable" 1 真实手机上ro.debuggable一般等于0,其上绝大多数APP的AndroidManifest.xml中没 有android:debuggable="true",为了在真实手机上调试这些APP,必须用些歪招,不 在此文范围。 ZZ用了: $ adb -s emulator-5554 shell "getprop | grep ro.debuggable" [ro.debuggable]: [1] 没必要,可以直接"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. 安装 Android Studio->Configure->Plugins->Install plugin from disk ->选中smalidea-0.03.zip->Restart Android Studio 3. 用baksmali反编译apk ZZ用Apktool反编译,实际上Smalidea自带反编译工具 $ java -jar baksmali-2.2b4.jar d -o out some.apk 反编译结果出现在out子目录中 $ java -jar baksmali-2.2b4.jar help d baksmali有很多参数,下面这个参数你可能会感兴趣: -a,--api The numeric api level of the file being disassembled (default: 15) 4. 在baksmali反编译基础上新建工程 创建目录: X:\\SOME_Smalidea 将baksmali得到的out子目录复制到上述目录,更名为src子目录: X:\\SOME_Smalidea\src Android Studio->Import project (Eclipse ADT, Gradle, etc.) ->X:\\SOME_Smalidea->Create project from existing sources Project name SOME_Smalidea Project location X:\\SOME_Smalidea 在AS左上角选择"Project Files" SOME_Smalidea->src->右键菜单->Mark Directory As->Sources Root 5. 建立调试通道 对于可调试APP,有两种办法建立调试通道,一种是用DDMS,另一种是用ADB,其实前 者也隐式使用ADB。 5.1 启动DDMS X:\\sdk\tools\monitor.bat 过去用ddms.bat启动DDMS,这个已经过时了,现在建议用monitor.bat启动DDMS。 monitor.bat实际执行: lib\monitor-x86\monitor.exe lib\monitor-x86_64\monitor.exe 选择哪种CPU架构,由如下命令控制: java -jar X:\\sdk\tools\lib\archquery.jar 最终受java的位数控制,如果用64-bits Java,将执行: X:\\sdk\tools\lib\monitor-x86_64\monitor.exe 最好用"where java"检查一下当前Java路径,调整环境变量,比如: set path=X:\\Java\jre8\bin;%path% X:\\sdk\tools\monitor.bat X:\\sdk\tools\lib\monitor-x86_64\monitor.exe 假设启动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: jdwp: 关掉DDMS,直接用ADB建立调试通道 $ adb -s emulator-5554 shell "ps | grep com.anything.some" USER PID PPID VSIZE RSS WCHAN PC NAME u0_a47 10140 795 187052 28200 ffffffff b7ede827 S com.anything.some 注意双引号的使用,这样可以确保执行Android系统中的grep,而不是PC上的grep, 从而使得上述命令在Windows、Linux上都可用。 $ adb -s emulator-5554 forward tcp:8700 jdwp:10140 $ adb -s emulator-5554 forward --list emulator-5554 tcp:8700 jdwp:10140 关闭ADB建立的调试通道: $ adb -s emulator-5554 forward --remove-all 上述命令的Windows实现可能有BUG,事后用Tcpview查看,adb仍在侦听8700/TCP,必 须: $ adb kill-server 我这里举例是以模拟器为目标,实际上可以是真实手机。 直接用ADB建立调试通道时,不一定使用8700/TCP。此处之所以仍然使用8700,是为 了固化Android Studio调试配置。 5.3 启动APP之初就开始调试 前面假设APP已经启动,待调试代码仍可路过。如果待调试代码位于APP启动之初,需 要额外操作。 假设目标是模拟器,检查AndroidManifest.xml $ adb -s emulator-5554 shell am start -D -W -n com.anything.some/.SomeActivity 在模拟器中会看到: Waiting For Debugger Application SOME (process com.anything.some) is waiting for the debugger to attach 然后启动DDMS或"adb forward tcp: jdwp:",建立调试通道。 假设目标是真实手机: 设置->开发者选项->USB调试 设置->开发者选项->选择调试应用->SOME 设置->开发者选项->等待调试器 去真实手机中正常启动SOME,也会停在"Waiting For Debugger"。 然后启动DDMS或"adb forward tcp: jdwp:",建立调试通道。 6. 在Android Studio中调试 Run->Edit Configurations->点击左上角+号->Remote Name : DebugVia8700(这个名字可以任意) Port : 8700(如果与DDMS配合,可以设成另一个端口,但每次都得改) SOME_Smalidea->src->com/anything/some->b.smali 第72行附近代码: -------------------------------------------------------------------------- if-nez v0, :cond_3c iget-object v0, p0, Lcom/anything/some/b;->a:Lcom/anything/some/SomeActivity; const v1, 0x7f060025 -------------------------------------------------------------------------- 在if-nez左侧空白处单击或按Ctrl-F8(参看Run菜单)设置断点 Run->Debug DebugVia8700 在Console面板中会看到: Connected to the target VM, address: 'localhost:8700', transport: 'socket' 去模拟器中正常操作SOME,触发断点,调用栈回溯: com.anything.some.b.onClick(Unknown Source:-1) android.view.View.performClick(View.java:4204) android.view.View$PerformClick.run(View.java:17355) android.os.Handler.handleCallback(Handler.java:725) android.os.Handler.dispatchMessage(Handler.java:92) android.os.Looper.loop(Looper.java:137) android.app.ActivityThread.main(ActivityThread.java:5041) java.lang.reflect.Method.invokeNative(Method.java:-1) java.lang.reflect.Method.invoke(Method.java:511) com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) dalvik.system.NativeStart.main(NativeStart.java:-1) 可以单步跟踪,可以修改由this指针定位的成员变量。 对于JVM,我一直梦想着有这样一个Java字节码级别的调试器,可以看调用栈回溯、 看单条Bytecode指令的中间状态(比如ifeq是否满足)、修改变量等等,一直没找到。 想不到对于DVM,有Smalidea这样的东西,不错。 有些文章提到一个配置操作: File->Project Structure->Project->Project SDK 对于在baksmali反编译基础上新建工程,此处显示。这些文章认为必须在此 设置SDK版本,完全是胡说八道,根本不需要。 7. 排错 注意,"am start -D"仍然要求ro.debuggable=1或android:debuggable="true" 假设在真实手机上调试一个不可调试APP,"am start -D"不会报错,但这个进程中不 会出现JDWP线程,可用如下命令验证: $ adb -s XXX shell "ps -t | grep -A 8 com.anything.some" $ adb -s XXX shell "ps -t | grep -B 6 JDWP" 对于这种情况,DDMS里看不到"com.anything.some"。如果用"adb forward tcp:8700 jdwp:" 强行建立调试通道,只是看上去成功了,Tcpview看到adb侦听127.0.0.1:8700/TCP, 在Android Studio中尝试调试时,报错: Error running DebugVia8700: Unable to open debugger port (localhost:8700): java.net.SocketException "Connection reset" 简单点说,如果APP所在进程没有出现JDWP线程,不管你怎么折腾,都不要想着调试 了。