目的是优化目标程序的 fibonacci 数列 从而快速解密
1 | (gdb) shell cat /proc/`pidof fibonacci`/maps |
先确定程序基址是 0x00400000
用 uc 的 map 去映射一些内存 增加 hook for debugging
然后开跑
1 | from unicorn import * |
报错
1 | 2023-03-15 20:11:26.366 | INFO | __main__:hook_code:39 - >>> Tracing instruction at 0x4004ef, instruction size = 0x7 |
1 | .text:00000000004004E0 ; __unwind { |
1 | .bss:0000000000601038 stdout dq ? ; DATA XREF: LOAD:0000000000400350↑o |
所以是映射太少了
但是我们不需要 glibc 对于所有的 glibc 模拟都可以跳过
1 | .text:00000000004004EF mov rdi, cs:stdout ; stream |
为了打印 flag 出来 也需要对特定的寄存器去处理
1 | .text:0000000000400558 movsx edi, dil ; c |
# re
这里用了一点混淆来干扰 ida
1 | .text:00000000004004E2 xor esi, esi ; buf |
1 | .text:000000000040054F mov rsi, cs:stdout ; fp |
rbp 就是指向加密的 flag 的指针
1 |
|
ghidra 这里更友好点
每轮里 do8 次 fibonacci 的第二个参数是用作计算的指针
这里计算结果是给到 ptr 的 而 ptr 的结果又是根据 fibonacci 的结果来的 所以可以缓存起来
fibonacci 的结果和 ptr 貌似没关系 但实际不然 当 fibonacci 的 N 为一个固定值的时候 ptr 指向的位置可以有不同的值 而新的 ptr 内容也需要 ptr 本身的内容
1 | __int64 __fastcall fibonacci(int n, _DWORD *ptr) |
总结:
- 进入函数时 rdi 可见 rsi 可见 rsi 指向的内容可见
- 返回的时候 rsi 和 rdi 和 rsi 的内容已经改变了(会用到这些寄存器) 我们需要 缓存新的 rax 和老 rsi 现在的内容
- 所以在程序入口的时候要保存 rdi rsi (为了取出内容) rsi 的内容 (为了做索引)
- 退出函数的时候 拿到 rax 和老的 rdi rsi rsi 指向的内容 用 (rdi, rsi 内容) 做索引 缓存掉返回值 rax 和新的内容
总感觉有一种状态机的感觉 但是我微薄的数理基础描绘不出来
# exp
1 | from unicorn import * |