调试环境
PC操作系统:Linux——内核信息:5.13.19-2-MANJARO(64位)、发行版信息:ManjaroLinux-21.1.6、桌面信息:KDE Plasma-5.22.5
PC处理器架构:Intel amd64
IDA版本:7.5(基于deepin-wine环境运行,deepin-wine6-stable版本为6.0)
手机系统版本:Android 9
手机环境:root(必须)、ro.debuggable=1
建议ida安装好后测试一下64位的F5功能,很多7.0以后的泄漏版本是没有64位F5插件的
基本使用方法
1、软件的基本介绍
开始页面

共有三个选项,其含义和作用如下
New:在本地打开一个文件进行分析
Go:不打开文件直接进入程序
Previous:打开一个历史分析项目
ida的数据库文件
在ida打开某个文件后,会在与其相同目录下创建一个数据库将打开的可执行文件的各种信息分别保存在.id0 、.id1 、.id2 、.nam 和.til这5个文件中。当可执行文件被关闭时,这五个文件可以被打包为一个.idb文件(64位打包格式为.i64),若后续分析仅需要静态分析,只需只用ida打开保存的idb文件即可,不再需要可执行文件本身(但是不能进行动态调试)
三种基本视图
ida在打开一个可执行文件后会有三种视图可以选择,其所适用的场景不同。三种视图的切换都可以通过在汇编窗口右键菜单中找到
text view
这种试图将打开文件的所有的汇编代码和数据完整的显示。这种视图可以比较方便的找到临近函数的代码,并且可以看到指令的相关地址,可以比较方便的处理部分分析错误(如花指令)

graph view
这种视图可以看见单个函数的代码间流转关系和整个函数的执行流程,对于单个函数的详细分析是比较合适的

proximity browser
这种视图展示的是函数或数据间的关系,以图形化的方式展示了交叉引用

动态调试so
对so的动态调试分为两种情况,第一种为常规的so函数调试,另一种为较早期的so调试(例如jni_Load调试)
常规动态调试
针对于常规的函数调试,其操作相对简单,流程如下:
1、打开server
ida针对常见平台都提供了远程调试能力,ida存放远程调试服务的目录位于ida根目录的dbgsrv文件夹内,其中安卓so调试所需要用到的服务端为android_server或android_server64(x86架构的安卓平台使用android_x64_server),其使用方式和frida的服务端类似
将服务端可执行文件存放进手机的某个目录中并给予可执行权限,在root权限下执行即可。服务端正常的状态如下,其监听端口为手机的23946端口

在ida中选择Debugger-Attach-Remote Linux/Android debugger,选择后会弹出一个设置页面,在设置页面中填写手机的ip和上一步中监听的端口即可

可以使用adb的端口转发功能将手机的监听端口转发至pc上,即
adb forward tcp:23946 tcp:23946,然后就可以直接设定为本地ip和转发到的端口了
3、选择调试进程
在上一步设定正确后点击OK后,会出现一个选择进程的窗口,找到需要调试的程序点击OK即可,也可以使用搜索对进程进行搜索。
窗口左侧为进程端口,右侧为进程名

若出现如下图这种只出现极少进程的情况需要注意服务端是否是在root权限下执行
4、attach进程
在选择进程并attach后,调试器会停留在libc,这是app会进入没有响应的状态,点击运行(快捷键F9),程序即可正常执行

5、so代码定位与断点
只有找到so中相应的代码位置并且下了断点,ida才会在想要的位置暂停
这时候要关注两个问题,app有没有加载so和加载的so中下了断点的函数会什么时候执行
对于第一个问题,有下面两种确认方式
通过shell命令确认
cat /proc/[pid]/maps | grep /data/app
这个命令可以输出pid对应的进程加载了什么模块(应用自己的)。如下图可见应用加载了apk和so。同一个模块出现了多次是因为模块中的数据和代码等不同内容会分别加载

通过ida确认
在ida附加了程序后,会在右侧modules窗口显示所加载的所有模块

使用右键菜单的过滤功能(快捷键ctrl+f)可以进行搜索。下图

这个时候会经常出现程序正在正常运行但是无法找到其所加载的so的情况,这是需要注意一个问题:应用的am文件中是否android:extractNativeLibs字段。若该字段为false,app会从apk中加载so,所以so不会被直接加载进内存。这时需要把该字段的值设置为true。
在ida中找到已经加载的so后,可以双击进入其代码函数列表,这是用相同的操作搜索所需要的函数,双击即可看到其汇编代码

这是在需要停下的代码左侧点击或右键选择Add Breakpoint(快捷键F2)即可添加断点。这时在在应用中执行会调用这段代码的操作即可断下

如果需要调试没有或无法被是放在本地的so,就需要在运行过程中在内存中找到需要调试的相应代码位置,再下断点,即可停住
早期流程的动态调试
上面介绍的动态调试方法适用于程序运行后还可以触发so中代码的情况,但是对于so中较早期运行的代码(如jni_OnLoad)调试上述方法就不适用了,因为在应用初始化时才会执行这种代码,但是后续一般不再会执行了,所以上述调试方法就无法直接断在较早期运行的函数。对于这种情况,需要采取以下策略来使程序初始化的时机可控
1、调试模式启动应用
使用 adb shell am start -D -n 包名/活动名命令来以调试模式启动应用,这时应用就会进入下图的状态,在运行时等待调试器附加后再进行初始化

2、ida调试准备
这时程序还未初始化,ida可以先attach上程序并在相关的地方下好断点(可以采用直接打开so文件的方式在还没加载so的时候下断点,但是so文件名需要和apk中相同)并等待程序执行
3、调试器附加
首先使用命令adb -d forward tcp:[port] jdwp:[pid]来利用等待进程的pid进行调试端口转发,映射到pc本地的端口
然后使用命令jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=[port]来将调试器附加在等待调试器的app上,附加成功会出现下图的状态

1、如果时采用本地打开so下断点的方法,ida会弹出如下提示,只是so路径不一样,点same即可
2、如果app正在处于待调试状态可以使用命令adb -d jdwp来查看其pid秒如果为刚刚启动的app往往最下方的一个pid就是刚刚启动的app
3、如果执行附加调试器命令时程序卡住或报致命错误,请检查是否开启了android Studio(或其他拥有安卓调试功能的软件),如果开启了需要关掉后重新附加jdb,否则会产生冲突导致无法成功附加
动态调试后恢复地址
进行动态调试后,ida中打开的地址会从偏移地址(基地址为0)变为上一次调试映射在内存中的地址,跟着函数名会一同修改,有时候会不方便,这时可以选择修改回来。
点击Edit-Segments-Rebase program...,在弹出窗口中,把Image base选项设定为0即可。

附录1:常用快捷键
| 名称 | 快捷键 |
|---|---|
| 反编译 | F5 |
| 交叉引用 | X |
| 地址定位 | G |
| 重命名 | N |
| 修改函数(变量)原型 | Y |
| 定义为函数 | P |
| 定义为结构体 | alt+Q |
| 添加备注(汇编) | ; |
| 添加备注(伪代码) | / |
| 伪代码与汇编切换 | tab |
| 运行 | F9 |
| 运行到光标处 | F4 |
| 单步步入 | F8 |
| 单步步过 | F7 |
| 打开字符串窗口 | shift+F12 |
| 文本搜索 | alt+t(text) |
| 二进制搜索 | alt+b(binary) |
| 下断点 | F2 |
附录2:关于android:extractNativeLibs字段和其默认值的说明
字段作用说明
android:extractNativeLibs的作用是决定是否将apk中的lib进行压缩,若值为true,apk中的lib将会被压缩,并且可以减小apk体积,若为false,apk中的lib将会被直接存储,所以apk的尺寸也会变大。下图为该字段开启前(左)和开启后(右)的lib对比。


该字段对调试影响
但是除了压缩lib减小体积外,其安装流程也会有区别。在未压缩的apk安装时是不会将so释放至本地的(加快安装时间),并且程序运行装载时会直接从apk中加载so,如下图,lib目录下并没有so

如果开启了lib压缩,apk安装时就会将so解压并且存放在本地,程序执行时加载的也是本地的so,如下图,可以看到开启了压缩本地就会存在so

所以如果未开启so压缩会直接导致程序执行的时候ida不能够识别到响应的so加载,导致无法定位到so中的代码从而无法停在断点。对于未开启so压缩的情况需要通过重打包来修改该字段以便可以定位到so。
字段的默认值
该字段会对so调试造成一定的影响,但是该字段在未进行设置时的默认值发生过一定改变,所以在未设定该字段时仍需要注意
若minSdkVersion<23或Android Gradle plugin < 3.6.0,打包时 android:extractNativeLibs=true;
若minSdkVersion>=23且Android Gradle plugin>=3.6.0,打包时android:extractNativeLibs=false;
附录3:常用相关指令
调试模式启动应用 adb shell am start -D -n 包名/活动名
转发23946端口 adb forward tcp:23946 tcp:23946
转发调试端口 adb -d forward tcp:[port] jdwp:[pid]
附加jdb jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=[port]
查看可调试的pid adb -d jdwp 或 adb shell su -c 'ps -A | grep 包名'
查看进程所加载的自身模块 cat /proc/[pid]/maps | grep /data/app
附录4:在Linux/Unix下使用wine运行IDA时python环境的处理方案
添加python环境只需要在ida所在的wine容器下使用安装包安装一个python环境即可,安装时需要注意勾选将python写入环境变量。
然后需要运行idapyswitch.exe,选择已经安装的python环境。不过此时ida会无法打开,这是因为idapyswitch.exe设置了一个错误的Python3TargetDLL值,只需要打开注册表,修改如下位置的键值:
HKEY_CURRENT_USER\Software\Hex-Rays\IDA,修改Python3TargetDLL为安装的python目录下的python3.dll绝对路径(idapyswitch.exe工具将路径设定为了当前版本的dll),修改之后即可正常打开ida。








