0x01 smail的动态调试

使用的apk来自正己第四课的教程demo:教程demo

参考正己的《安卓逆向这档事》第五课操作:《安卓逆向这档事》五、1000-7=?&动态调试&Log插桩

教程使用mt管理器对xml文件进行更改,但很多时候该用电脑还得用电脑,所以这边提供电脑的操作

以下两种方法更改apk中的AndroidManifest.xml使其可被调试:

1.JEB

使用JEB进行电脑上的操作:https://www.pnfsoftware.com/jeb/manual/android-debugging/#generating-debuggable-apk

首先将apk转换为可动态调试的版本

1
jeb_wincon.bat -c --makeapkdebug -- file.apk

之后对apk进行签名

1
apksigner.bat sign -ks %USERPROFILE%\.android\debug.keystore --ks-pass pass:android file_debuggable.apk

这个debug.keystore需要先用Android Studio创建个项目然后生成调试apk才能得到

2.apktool

apktool提供了解包和打包的方法,总的来说还是挺方便的

1
apktool d file.apk

解压出来之后就可以改xml文件了,在<application>标签中加上

1
android:debuggable="true"

重新打包即可

1
apktool b filepath -o file.apk

别忘了重签名

1
apksigner.bat sign -ks %USERPROFILE%\.android\debug.keystore --ks-pass pass:android file.apk

之后就可以开始愉快调试了

首先使用adb以调试模式启动对应的包

1
adb shell am start -D -n 包名/类名

adb shell am start -D -n
adb shell am start -D -n 包名/类名
am start -n 表示启动一个activity
am start -D 表示将应用设置为可调试模式

之后再JEB上下断点然后attach上去就可以了

0x02 Native的动态调试

参考:IDA调试Android nativeIDA 动态调试原生层程序IDA7.5安卓10动态调试还有IDA动态调试so

考虑到手上的IDA8.3不支持Arm架构,用7.7来搞

首先要搞清楚位数

1
adb shell getprop ro.product.cpu.abilist

我的是arm64,所以决定使用android_sever64

1
adb push android_server64 /data/local/tmp/

push上去之后要设置为可执行

1
adb shell chmod 777 /data/local/tmp/android_server64

建立本地计算机 23496 端口与手机端口 23946 的通信。当 PC 有客户端连接 23946 端口时,其发送的信息都将被转发到手机的相应的端口,这时 android_server64就收到相应的数据,从而使得 IDA 可以调试程序。前面的 tcp 的端口指的是本地 PC 的端口,后面的指的是手机的端口。

1
adb forward tcp:23946 tcp:23946

教程中的原生程序太过老旧了,实机上根本无法运行,主要是因为这个原生程序并没有使用开启PIE,导致连系统都无法执行它(参考Android5.0中的安全增强功能

因此执行原生程序这一步从/system/bin/里面找一个测试就行了

这边用了ls,可以成功执行,有警告,但不影响程序执行

重头戏在so库的调试上

教程中的apk也是老得不成样子了,实机上无法运行,这边整一个其他地方的来

首先将其改为能调试的,在application标签中加上

1
2
android:debuggable="true"
android:extractNativeLibs="true"

android:extractNativeLibs = "true" 时,gradle 打包时会对工程中的 so 库进行压缩,最终生成 apk 包的体积会减小。但用户在手机端进行 apk 安装时,系统会对压缩后的 so 库进行解压,一般解压到 /data/app/ 某一目录下,从而造成用户安装 apk 的时间变长。如果为 false 则不会解压到该目录。

minSdkVersion >= 23 并且 Android Gradle plugin >= 3.6.0 情况下,打包时默认android:extractNativeLibs=false, 如果该属性为false,虽然 IDA 可以正常附加,但是无法加载对应的 so 进行调试,所以如果未开启 so 压缩会直接导致程序执行的时候 ida 不能够识别到响应的 so 加载,导致无法定位到 so 中的代码从而无法停在断点。对于未开启 so 压缩的情况需要通过重打包来修改该字段以便可以定位到 so。
针对性的,也有相应的模块,用于 Hook 安卓的安装器,让其在安装时将该属性的值设为 true,从而能够正确的调试,例如项目ForceExtractNativeLibs即通过 Xposed 进行 Hook 在安装时重置该属性。

注:毕竟是个病毒样本,虽然音频被替换了但是还是很抽象,这边建议解包后顺便把asset里面的0.mp3换成无声的一段音频

之后签个名安装

1
adb install test_dbgable.apk

然后从apk里面提取so文件,用IDA打开,下个断点

之后启动手机的调试端

1
2
3
adb shell
su
./data/local/tmp/as

另开一个cmd窗口,端口转发

1
adb forward tcp:23946 tcp:23946

用调试模式开启对应Activity

1
adb shell am start -D -n com.sgzh.dt/com.androlua.Welcome

包名在开头

对应的activity在action.MAIN附近可以找到

之后在IDA中设置进程选项

然后调试器选项里面打开

1
2
3
进程入口点暂停
在线程启动/退出时挂起
在库加载/卸载时挂起

然后就可以选择对应包名,附加到进程了

之后执行

1
adb shell ps | findstr 包名

可以找到这个进程相关信息

各段含义如下

1
USER PID PPID USIZE RSS WCHAN PC NAME

adb中ps命令的详解

内容 含义
USER 进程当前所属的用户
PID 进程ID
PPID 父进程ID
VSIZE 进程的虚拟内存大小,以KB为单位
RSS 进程实际占用的内存大小,以KB为单位
WCHAN 进程正在睡眠的内核函数名称;
PC 计算机中提供要从“存储器”中取出的下一个指令地址的寄存器
NAME 进程状态(注:状态说明见下)和名称

NAME中进程状态的不同值如下

状态 含义
D 不可中断的睡眠态
R 运行态
S 睡眠态
T 被跟踪或已停止
Z 僵尸态
W 进入内存交换(从内核2.6开始无效)
X 死掉的进程
< 高优先级
N 低优先级
L 有些页被锁进内存
s 包含子进程
l 多线程,克隆线程
+ 位于后台的进程组

这里重点是获取PID(虽然好像在IDA里面都能直接看见)

之后执行如下命令,将 8700 端口转发到调试进程(8700是固定的,一般来说不会更改)

1
adb forward tcp:8700 jdwp:<PID>

借用以下ctfwiki的图片解释这个转发(虽然但是DDMS已经被废除了

执行完上面的那一条指令,我们的电脑已经与手机的 app 虚拟机之间建立了通信。

之后需要使用 jdb 在 java 层来将 apk 应用 attach 到我们的电脑上,这里使用如下的命令。

1
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700

此为固定的

之后在IDA中F9运行,直到这个出现

选择相同之后,就会断在下断点的位置了

至此调试完成了

(这边还是不太建议直接调so库的,因为问题很多,不稳定)

0x03 附录

附一下可以一键执行调试的脚本

1.杀死IDA的调试服务器as64进程

killIDAdbg.bat

1
adb shell < killIDAserver.sh

killIDAserver.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
su
cd /data/local/tmp

# lsof:列出系统中已经打开的文件
# grep 23946:获取含有"23946"字串的内容
# grep as64:获取含有"as64"字串的内容
# awk '{print $2}':awk是行处理器。按行,以空格分段的字符串,打印第二列信息;
pid=`lsof | grep 23946 | grep as64 | awk '{print $2}'`
# $(expression):获取表达式返回值,这里获取的是frida-s的PID
# -z:检查字符串变量的长度是否为 0
if [ -z "$pid" ]
then
echo "not running, no need to do anything"
else
kill -9 $pid
echo "kill success!"
fi

2.开启IDA的调试服务器as64

IDAdbg1.bat

1
2
3
4
5
@echo on   %关闭回显命令%
start "" cmd /c call killIDAdbg.bat %杀死原本有的IDAdbgserver(如果有的话)%
start "" cmd /k call IDAdbgD.bat %新打开一个cmd并运行IDAdbgD.bat,可以根据需求更改为IDAdbgnD.bat%
adb shell "su -c '/data/local/tmp/as64 -p 23946'" %-p参数用于指定端口,默认是23946,如果这里改了则后面的转发和IDA中都要改%
pause

3.dbg状态开启app

IDAdbgD.bat

1
2
3
4
5
6
@echo on %关闭回显命令%
adb shell am start -D -n 包名/类名 %debug状态启动手机端APP%
adb forward tcp:23946 tcp:23946 %端口转发%
adb forward tcp:8700 jdwp:PID %pid监听%
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700 %jdb挂起%
pause

4.非dbg状态开启app

IDAdbgnD.bat

1
2
3
4
@echo on %关闭回显命令%
adb forward tcp:23946 tcp:23946 %端口转发%
adb shell am start -n 包名/类名 %debug状态启动手机端APP%
pause

注意非dbg状态开启需要先进行端口转发