在信安大赛的准备过程中,主要通过 Android Studio 动态调试 apk 反编译出来的 smali 代码的方式来对我们分析的执行流程进行验证。该技巧的主要流程在此记录。以下过程使用 Android Studio 3.0.1 和 smalidea 0.0.5 进行演示。
smali动态调试简述
使用 Android Studio 调试 apk 反编译出的 smali 代码,需要 PC 端和移动端的合作。在 PC 端通过 Android Studio 导入反编译出的完整的 smali 代码,并设置好调试所需的条件,并设置断点。随后将移动设备连接至 PC 端,并正常运行调试的 apk 应用,当待调试的 apk 应用执行流程运行至 PC 端上对应的 smali 代码设置的断点处时,则会触发断点,从而可以进行进一步的分析和调试等。
上述方法可以与静态流程分析相结合,以确认所分析流程的正确性( 会触发选定的断点 )。
动态调试所需的条件
想要通过 Android Studio 进行 smali 代码的动态调试,至少需要满足以下条件之一:
(1) 将待调试的应用设置为可调试的。apk 的 release 版本均为不可调试的,故需将 apk 中的 AndroidManifest.xml 文件中的 android:debuggable 设置为 true ,再将 apk 重打包,再进行安装后,即可进行调试。若原 AndroidManifest.xml 文件不含有 debuggable 字段,则在合适的位置添加即可;( 但该方法会破坏源apk的完整性,可能无法通过 apk 的完整性检查,且仅针对一款应用 )
(2) 将移动设备的 / /default.prop 中的 ro.debuggable 设置为 1;(需要手机 root 权限 ,设置后即可对所有应用进行调试 )
笔者采用的是第二种方案,该方案相对来说最简单,且不会破坏源 apk 的完整性,设置借助的是 xposed 框架,简单过程可见文末。
安装 smalidea
(1)在这里下载最新的 smalidea 安装包(.zip)。
(2)在 Android Studio 的 File -> Settings -> Plugins 中,选择 Install plugin from disk ,选中之前下载的 .zip 格式的文件,确定;
(3)重新启动 Android Studio 即可;
不同的Android Studio 版本可能上述路径有所不同,但过程大致相同。
获取 apk 反编译出的完整的 smali 代码
一般借助 apktools 进行完整 apk 的反编译操作。
在 Android Studio 中导入和配置项目
(1) 选择 import project -> 选择 apk 反编译出的完整的 smali 文件夹 -> Create project from existing sources ,并一路选择 next 即可。导入完成后,将目录结构切换为 project 。
(2) 选中项目目录,右键 -> Mark Directory as -> Sources root.
(3) 配置项目使用的 JDK 版本,选中 项目目录 -> 右键 -> Open Module Settings -> Project ,在右侧 Project SDK 栏中选择安装待调试 apk 的移动设备所使用的 JDK 版本。(在笔者使用的 Android Studio 3.0.1 版本的导入过程中,Android Studio 会询问项目将会使用的 SDK 版本,直接在该提示界面中选择所需的 JDK 版本即可.)
(4) 对调试选项进行编辑,点击 Android Studio 界面上的图示按钮,选择 Edit Configurations。
通过添加按钮 添加一个 Remote 调试。可根据需要对该调试的 Name 和 Port 进行修改,通过 apply 进行确认。
启动调试
调试的方法有两种,一种是通过 DDMS 启动调试,一种是通过命令行 adb 命令启动调试。( 以下为 Android Studio 3.0.1 中的操作)
通过 DDMS 启动调试
(1) 通过 Tools -> Android -> Android Device Monitor ,在 DDMS 左侧的列表中,选中所需要调试的包名;
(2) 切回 Android Studio 界面,点击选择菜单 run -> debug "xxx" (xxx 为设置的 remote 调试的名字),即开始调试过程,此时 DDMS 界面的包名前面会出现一个绿色小虫标志;
使用该方法进行调试的缺点在于,需要将 remote 调试中的端口号设置为 DDMS 包名后面显示的两个端口号之一。其中 8700 端口总是固定的,建议使用。
使用命令行启动调试
( 注:使用该方法时需要关闭 DDMS,因为 DDMS 也使用了 adb ,同时开启会有冲突 )
(1) 使用 adb shell 以调试模式运行待调试的应用,关于以命令行方式运行 adb shell ,可以参考笔者在这里的记录。
adb shell am start -D -S -W packageName/mainActivity //以调试状态运行目标应用,参数为 包名/主activiy 名。
所需找到的包名和主活动名在 apk 的 AndroidManifest.xml 中均有记录。在 AndroidManifest.xml 中, package 字段对应的值即为包名,而带有 LAUNCHER 关键字的 activity 即为主 activity。
a.包名:
b.主 activity 名
所以对于图示 apk ,需要执行的命令即为
adb shell am start -D -S -W com.tencent.mm/com.tencent.mm.ui.LauncherUI
若上述命令运行成功,则在手机终端上会显示 Waiting For Debugger 提示,不要关闭该提示框。
(2) 通过 adb shell ps 命令显示所有移动设备上运行的进程,并找到目标进程的pid ( 也就是刚启动的应用的 pid )。
adb shell ps | grep "com.tencent.mm" //非Windows环境下,可以使用 grep 命令以包名为参数进行字符串筛选 adb shell ps | findstr "com.tencent.mm" //Windows环境下,通过 findstr 命令以包名为参数进行字符串选择 adb shell "ps | grep "com.tencent.mm"" //使用 android 环境下的 grep 命令,而不是主机上的命令
查询结果如下,即找到对应进程的pid。
(3) 进行端口映射
adb forward tcp:port_A jdwp:pid //其中,port_A 为添加 remote 调试时指定的端口号,pid 即为第二步中获得对应调试进程的 pid
对于笔者的示例,即为
adb forward tcp:5005 jdwp:12144
由于执行了端口映射,所以 remote 调试中的端口号可以为任意未被占用的端口号。
(4) 使用 run -> attach debugger to Android Process 选项,根据提示选择待调试进程即可。正确操作时,移动设备上的 waiting for debugger 对话框会消失。
通过上述方法启动调试之后,即可进行正常的调试过程。常见的方案是在经过分析所得的可能执行路径上事先设置断点,之后在移动设备上进行对应的 app 操作,若分析正确,则 app 会执行对应的流程,从而触发断点,从而可以进行进一步的分析。
一些可能错误
通过 Android Studio 的菜单栏 run -> debug "xxx" 启动调试时,报错"unable to open debugger port java.net.socketexception connection reset"
网上相关资料较少,但出现该问题时,需要注意是否待调试应用是处于可调试状态的,对于以 adb shell am start -D 命令运行的应用,需要 ro.debuggable=1 或 android:debuggable=”true”,在不满足上述条件时,adb shell am start -D 命令不会报错,但是通过 adb shell "ps -t | grep -A 8 package" 进行验证时,会发现目标应用进程中不含有 JDWP 线程,此时不论如何进行设置,都无法使得调试顺序进行,Android Studio 中也会报上述错误。
将移动设备的 ro.debuggable 设置为 1 的方法,比较简单的无需刷机的方法是安装 xposed 框架( 需要 root 权限 ),之后通过已开发好的 xposed 模块如 BuildProp Enhancer 设置 ro.debuggable 位,该方法一次重启后即可生效,之后即可对应用进行调试。( 注意使用上述方法后,使用诸如 adb shell "getprop ro.debuggable"命令时,返回的结果仍为0,但是所有的应用确实处于可调试状态)
xposed 框架的安装可以参考 xposed 中文网站和 xposed 英文论坛,也可参考网上资料。
参考资料:
(1) 简要介绍:Smalidea+IntelliJ IDEA/Android Studio无源码调试
(2) 完整过程介绍 : 使用Smalidea对无源码APK调试简介