0%

Android动态调试so之dump内存数据

配置环境

一台已root手机

  • IDA pro6.6
  • Android SDK

准备工作:

  1. 把Android SDK添加到环境变量中
  2. 把已root手机的系统中关键so拖到本地,必要时可以静态读取,获取系统函数的偏移地址。 例如把手机系统的system/lib的文件拖到本地debugging文件夹中。
    adb pull /system/lib .\debugging\lib
  3. 在IDA pro6.6中找出android_server,并把android_server放到手机system/bin中。push的时候没有root权限,可以如下操作:
    1
    2
    3
    4
    adb push android_server /data/local/tmp
    adb shell
    su
    cp /data/local/tmp/android_server /system/lib

环境启动

由于需要调试程序启动加载的so,所以步骤比较多。

  1. 进入adb shell,在手机中执行android_server
    1
    2
    3
    adb shell
    su
    android_server
  2. 进行端口转发
    adb forward tcp:23946 tcp:23946
  3. 使用调试模式启动程序,adb shell am start -D -n 包名/类名,以上一篇博文的dexunshellram为例,dexunshellram动态加载了crackme0201,启动命令如下:
    adb shell am start -D -n com.droider.dexunshellram/com.droider.crackme0201.MainActivity
  4. 打开IDA6.6,打开需要调试的so,然后选择Debugger->Select debugger,选择Remote ARM Linux/Android debugger
    然后选择Debugger->Debugger options,勾选所有loggingEvents里的Suspend on thread start/exitSuspend on library load/unload
    然后选择Debugger->process options,在Hostname中填写127.0.0.1,端口默认为23946
    然后选择Debugger->attach a process,选择com.droider.dexunshellram
    然后可能出现如下图所示,需要add map,由于之前把手机系统的system/lib复制到电脑中,这样可以指定到电脑的相应文件夹中,这样就可以静态调试系统so。如果你觉得你用不着调试系统函数,可以不用指定。
    image0
  5. 在Android SDK中找出ddms.bat,双击打开,为了后面执行jdb。
  6. 在IDA中点击运行或者按F9,然后在cmd中运行一下命令启动jdb
    jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
  7. IDA在linker中停下了,然后在path中找到相应调试的libunshellram.so,选择函数Java_com_droider_dexunshellram_unshellram_loadDex,这里是内存动态加载dex的地方。在else语句之前进行断点,就是loc_前一行插入断点,按F2,然后再运行程序,按F9。

dump内存

如下图所示,R0寄存器BE8F9144-48保存着dex文件的地址。
image1
可以看出寄存器保存的地址为0x7653E008
image2
查看0x7653E018地址可以看出存储的就是dex文件,dex文件是dex.035开头的。而0x7653E038-3B存储着是dex文件的长度25C5BC
image3
把以下脚本保存成dump.py,通过在file->Script File,选择dump.py。然后输入0x7653E018,输入长度0x25C5BC,最后输入保存的文件C:\\classes.dex

1
2
3
4
5
6
7
8
9
10
11
12
13
import struct
def dumpdex(start, len, target):
rawdex = idaapi.dbg_read_memory(start, len)
fd = open(target, 'wb')
fd.write(rawdex)
fd.close()

def getdexlen(start):
pos = start + 0x20
mem = idaapi.dbg_read_memory(pos, 4)
len = struct.unpack(' 0 and start > 0x0 and target and AskYN(1, 'start is 0x%0x, len is %d, dump to %s' % (start, len, target)) == 1:
dumpdex(start, len, target)
print('Dump Finish')

把dex文件dump到C盘中,由于是crackme0201classes.dex,如果把dex放回之前博文的crackme0201的apk中,可以正常启动应用。