ARM的栈回溯(三):在 ida 里实时进行 arm 栈回溯

本系列文章共三篇。本文是第三篇,使用已有的知识,实现 arm stack unwind,给本系列完美地画上句号。

ida arm unwind plugin

IDA 里有一个不常用的功能,叫打印栈回溯,使用的是常见的 ebp/esp 栈帧技术,没有对 ARM 进行适配,导致调试安卓 so 时完完全全是错的。本文编写一个 ida 插件,正确展示实时的 arm 栈回溯。

代码在:https://github.com/LeadroyaL/IDA_ARM_Unwind ,欢迎 star。

设计思路:

  1. 总目标,对当前断点进行栈回溯,得到 pc 序列,进一步可以得到 crash-log 一样的栈回溯展示
  2. 检查运行环境,需要是 ARM 架构,需要处于调试中的状态
  3. 将当前 pc 加入序列
  4. 初始化VRSStatus状态,将各个寄存器的值设置正确,为 unwind 做准备
  5. 递归 unwind
    1. 根据当前 pc 找到当前 ELF 头部的大概位置,解析头部的一些字节,使用 pyelftools 得到第一个 PT_LOAD
    2. .ARM.exidx.ARM.extab 一定在第一个 PT_LAOD 里,使用 pyelftools 解析它们的数据
    3. 根据当前 pc、List[EHABIEntry],找到对应的 EHABIEntry
    4. 解释执行对应的字节码,得到最终状态
    5. 最终状态的 LR 就是返回地址,判断 ARM 还是 THUMB,再决定是 -2 还是 -4,修正为上一条指令的地址
    6. 将计算好的 pc 加入序列
  6. 使用 pc 序列,寻找对应的 module、funcion,计算相对偏移,构造为 List[Frame]
    7: 绑定快捷键,画 GUI,抄的 https://github.com/ChiChou/IDA-ObjCExplorer 的代码

效果图,个人感觉还是非常好用的哈:


具体用途看仓库里的 readme 哈。

总结

三篇文章,虽然是按照开发的时间顺序写的,但发生顺序其实是反的,这个流程拖得挺长:

  1. 先发现 IDA 失效,于是准备写个插件;
  2. 写插件,研究栈回溯,发现 ARM 的栈回溯跟人不一样 (参考 原创andorid native栈回溯原理分析与思考
  3. pyelftools 不提供 arm ehabi 的解析,于是自己实现数据解析(参考 https://github.com/llvm/llvm-project/blob/master/llvm/tools/llvm-readobj/ARMEHABIPrinter.h
  4. libunwind 接入成本太高,于是自己用 python 写字节码的解释执行和 unwind(参考 https://github.com/llvm/llvm-project/blob/master/libunwind/src/Unwind-EHABI.cpp

把大量的规范从 c 移植为 python,整个下来花了我大量时间,但对 unwind 本身有了非常深刻的理解,希望 elftools.ehabiida-arm-unwind-plugin 这两个轮子能为行业做贡献吧。


作品链接:https://github.com/LeadroyaL/IDA_ARM_Unwind

pyelftools的commit 链接:https://github.com/eliben/pyelftools/commit/ee0facee32ae5fc91709c93f9a57a9a7683a3315

总结

本文讲了 ELF 里 arm ehabi 的存放和使用。

第一篇指路:https://leadroyal.cn/p/1125
第二篇指路:https://leadroyal.cn/p/1131
第三篇指路:https://leadroyal.cn/p/1135