第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

使用 IDA Pro 调试 Android 原生程序

  • 和 gdb、lldb 一样,用 IDA 调试原生程序时也要在设备上通过调试服务端进行远程连接。IDA 的 dbdsrv 目录存放着所有远程调试服务端程序:

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 目前 IDA 支持调试 x86x86-64ARMARM64 四种类型的 Android 原生程序。android_server_nonpie 运行在低版本的设备上,用于调试没开启 PIE 的 ELF。因为设备是 64 位的,故执行如下命令 push

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 仍然调试已 push 的 app6

  • 将 app6 拖入 IDA,等待分析完成,然后在 main() 的 printf() 调用上按 F2 设断点

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 点击菜单栏 Debugger -> Select Debugger,打开调试器选择对话框,选择 Remote ARM Linux/Android debugger,点击 OK。再次点击 Debugger -> Debugger options...,勾选如下图所示选项,点击 OK

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 启动调试服务端

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 点击菜单栏 Debugger -> Process options...,如下图所示输入

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 端口转发

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 至此,配置完成

  • 点击菜单栏 Debugger -> Start process 或按 F9,开始调试。但此时会弹出一个提示直接执行程序可能会对设备有风险的提示,点击 Yes 即可

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 这还没完,接下来会提示找不到文件(忘记截图),点击 Yes 即可。然后又会弹出没找到远端的文件,并询问是否复制一份新的

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 点击 Use found,IDA 会返回主界面并切换到调试界面。至此,所有调试准备工作都已完成

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 由上图可看出,此时寄存器窗口尚未显示寄存器的值,按 F9 让程序运行。此时可能会弹出一个提示,关于调试路径和 linker64 有问题之类的,点击 Yes 即可。程序会中断在 linker64 的代码空间中,寄存器与堆栈都已被正确显示

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • Ctrl+S,打开段选择对话框,找到 .got 并双击,定位到第一项 printf 的内存地址 0x0000005559D82FB8

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 点击菜单栏 Debugger -> Debugger windows -> Watch view,打开监视面板。在其中单击右键,选择 Add watch,输入如下内容,监视该地址的内存变化

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 现在,该地址存放的值为 0x5A0。按 F9 继续运行,程序会再次断在 linker64 的代码空间,但上述地址的值已变化。从 Output window 窗口输出的内容可看出,linker64 在加载 /system/lib64/libc.so 后改写了 0x0000005559D82FB8 处的内容,改成了 0x0000007FA9DB22E4。在反汇编窗口按 G ,输入 0x0000007FA9DB22E4,点击 OK,将会看到此处的伪代码(书上是这么说,但此处的伪代码并没有识别为 printf,不过从内存窗口可看出)正是 printf() 的内存地址

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 通过上述操作,可得出结论,ELF 的 .got 的符号填充过程是在 linker64 加载和链接依赖库的过程中完成的,ELF 的所有外部函数符号的真实地址会在依赖库加载后全部设置完成,这意味着 Android 系统的链接器没有使用延迟绑定技术(Lazy Binding)

  • 要想知道具体的符号填充细节,要对 linker64 的代码进行跟踪

  • 也可按 Tab 或 F5 切换到伪代码模式进行调试。点击菜单栏 Debugger -> Debugger windows -> Locals,打开本地变量窗口,可查看动态调试过程中临时变量的信息

    第九章 Android 原生程序动态调试(三)(使用 IDA Pro 调试 Android 原生程序)

  • 接下来可按 F7 单步步入,F8 单步步过(伪代码窗口貌似还是会步入),配合伪代码和本地变量窗口,即可很方便地调试(源码级别动态调试)