ida7.7 打开so文件 查看jni_onload
底部有个内联汇编, 跳转到X1, 但ida没识别出来.
__asm { BR X1 }
看看汇编代码
可以看到 BR X1
那x1来自哪里
x1 = x0 + x34
那X0呢, sub_29D9C 这个函数点进去看看
从图中可以看到, 不同的颜色是一个对称操作, 绿色箭头是开栈和清栈, 红色箭头是 push 和 pop, 只有这个黄色箭头 才是这个函数真正的目的.
这条指令相当于 x0 = X30, arm64 X30 就是 LR 寄存器. 此时LR 是多少呢?
BL 指令执行前就将下一条要执行的指令保存在 LR 中, 所以 LR = 0x29D94, X0 = LR = 0x29D94
接下来一条指令是 ADD X1, X0, #0x34 那 X1 = 0x29D94 + 0x34 = 0x29dc8 实际要跳转的地址就是 0x29dc8, 但是ida没有识别出来
我们来帮助 ida 进行识别.
现在我们知道了, 这三行其实就是花指令. 让ida 无法识别真实的跳转地址.
BL sub_29D9C 相当于 执行了 X0 = LR = pc + 4
BR X1 相当于 br 0x29D90 + 4 + #0x34
最终要跳转的地址就是 BR 0x29dc8
也就是说这三行就相当于 BR 0x29dc8, 我们直接用 ida 的插件 keypatch 来patch 第一行, 第2, 3行 进行 nop
将第一行改为 B 0x29dc8
剩下两行nop
然后再次 F5
好嘛, 从 XR X1 变成 BR X6了, 继续来看看汇编
好像跟刚才花指令类似, 一个函数, 然后加上一个数, 然后跳转.
我们看看函数 sub_29D9C
跟刚才的确实类似, SUB 开栈, ADD 清栈, STP 压栈, LDP 出栈. 只有中间的 LDR X0, [SP,#8] 有用, 相当于 X0 = X30 = LR
手动计算一下X6的地址.
X6 = 0x29DC8 + 4 + 8 = 0x29dd4
我们像刚才一样使用 keypatch 进行patch
将第一行改为 B 0x29dd4 第2, 3行 nop
改完后再次 F5
发现有效果了, 正确识别出一些函数了, 但是下面还是有 BR x1
我们跳转到 BR X1 处看看。
好家伙跟刚才如出一辙, 只是这次加的是 0x38
我们按照相同的方法计算出 X0 = LR + 0x38 = 0x29e68
利用keypath 进行patch. patch 完后再次 F5. 效果如下
可以看到又识别了很多函数, 但是又遇到了 BR X1, 我们点进去看看
跟刚才一模一样. 开始思考人生,这么patch 下去什么时候是个头呢?
这里总结一下, 这里遇到了三种花指令
一种是 BR LR + 8
一种是 BR LR + 0x34
一种是 BR LR + 0x38
我们看看 BR LR + 0x34 这种花指令在so中出现了多少次
设置一下ida显示指令的机器码.
将这里改为 4
我们来到 jni_onload 的第一处花指令
我们通过ida的二进制搜索功能搜索第2,3行对应的机器码 01 D0 00 91 20 00 1F D6 看看有多少处 0x34这种类型的混淆
继续勾选 find all
可以看到有122 处.
有122处匹配不代表有122处花指令, 可能其他的一些数据正好能匹配 01 D0 00 91 20 00 1F D6。
我们希望再精准一点, 不要匹配到不该匹配的内容. 怎么再精准一些呢.
第一行我们是不是也可以匹配一下呢?
BL sub_2A2BC 我们放在 https://armconverter.com/?code=BL+0x0C 这个网站中转换一下
跟ida中的一致.
BL 0x0C 对应的机器码是 03 00 00 94
那如果是 BL 0x1C 呢? 对应的机器码会是什么样子呢?
发现只是 03 00 00 94 变成了 07 00 00 94 只是第一个字节发生了变化. 可以看到小跳转只是改变了第一个字节, 我们假设花指令都是小跳转(双板小回转.jpg). 我们把 00 00 94 也加入到匹配中
在 ida中搜索 00 00 94 01 D0 00 91 20 00 1F D6
可以看到同样122处, 看来之前匹配到的就是全部的花指令.
那我们能不能通过脚本匹配这种花指令呢
通过python 脚本如下
1 | import ida_bytes |
执行过后, ida打印出 122处匹配, 通过脚本匹配的和ida搜索匹配的数量一样.
那花指令地址找到了, 怎么计算中真实跳转地址呢. 参考如下脚本
1 | import ida_bytes |
获取真实跳转地址的逻辑就是 第一行地址 + 4 + 第二行汇编的第3个操作数. 对应脚本中的 real_jump_adrr = addr + 4 + idc.get_operand_value(addr + 4, 2)
有了真实地址 如何patch呢
- 构造出 BL real_jump_addr 这个汇编代码
- 将这个汇编代码转换为机器码
- ida_bytes.patch_bytes 将机器码修改到对应的地址处.
接下来我给出三种花指令批量处理的脚本. 可以patch so中300多处的花指令
1 | import ida_bytes |
代码中要注意 0x8 这种花指令 都是出现在 0x34 之后的。 但是0x38跟 0x8, 0x34是分开的
整个脚本执行完后, 可以看到 jni_onload 好看了很多
完结!