我最近正在研究一个(看似)简单的 CTF 挑战,其中输入是一个 base64 编码的文件,二进制文件将检查一个函数调用的正确性。问题是检查函数大约有 88k 个基本块长,但完全是线性的。因此,每个块将检查单个字节,如果不正确,将分支到一个较小的块,该块将调用退出函数。每个块中的操作很简单:
if(RANDOM_CONST0 == mem[i] ^ RANDOM_CONST1){
goto next_basic_block;
}else{
exiting_function() //no return
}
每个基本块使用的常数不同。因此,它似乎是 angr 的绝佳目标!
尝试(使用call_state
):
1)
- 用途
SimulationManager.explore
- 每一步后的丢弃
avoid
和deadended
状态(使用step_func
) - 约 10 分钟后内存耗尽 (8GB)
2)
- 用途
SimulationManager.step
- 在每一步之后手动修剪“坏”状态(例如导致调用退出函数的状态)
- 在每个块之后评估解决方案的单个字节
- 将内存设置为具体值 0(用于减少内存)
- 20 小时后仍然搅动(65k 字节的解决方案)
- 此时显着放缓——大约 1 字节/3 秒
- 用于
SimulationManager.simplify
减少每一步的内存开销
其他杂项:
- 对符号内存绝对没有限制(包含解决方案)
- 缓冲区约为 88k,仅对这么多内存的限制就消耗了大量 RAM
问题:
- 为什么要占用这么多内存?
- 的唯一的符号寄存器是
eax
在每个基本块(因为它从存储器读取的符号字节),但它确实不复杂化合物与每个步骤(它被在下一基本块设置为另一符号值)
- 的唯一的符号寄存器是
- 为什么 angr 的速度如此之快?
我在这里为感兴趣的人上传了 CTF 二进制文件。