我在实验室工作,我必须通过为每个阶段提供正确的输入来拆除“炸弹”。我无权访问源代码,所以我必须使用 GDB 单步执行汇编代码。现在,我停留在第 2 阶段,非常感谢您的帮助。这是 x86 汇编代码——我添加了一些注释来描述我认为正在发生的事情,但这些可能是非常错误的,因为我们几天前才开始学习汇编代码,而且我仍然很不稳定。据我所知,这个阶段从用户那里读取了 6 个数字(这就是 read_six_numbers 所做的)并且似乎经历了某种类型的循环。
0000000000400f03 <phase_2>:
400f03: 41 55 push %r13 // save values
400f05: 41 54 push %r12
400f07: 55 push %rbp
400f08: 53 push %rbx
400f09: 48 83 ec 28 sub $0x28,%rsp // decrease stack pointer
400f0d: 48 89 e6 mov %rsp,%rsi // move rsp to rsi
400f10: e8 5a 07 00 00 callq 40166f <read_six_numbers> // read in six numbers from the user
400f15: 48 89 e3 mov %rsp,%rbx // move rsp to rbx
400f18: 4c 8d 64 24 0c lea 0xc(%rsp),%r12 // ?
400f1d: bd 00 00 00 00 mov $0x0,%ebp // set ebp to 0?
400f22: 49 89 dd mov %rbx,%r13 // move rbx to r13
400f25: 8b 43 0c mov 0xc(%rbx),%eax // ?
400f28: 39 03 cmp %eax,(%rbx) // compare eax and rbx
400f2a: 74 05 je 400f31 <phase_2+0x2e> // if equal, skip explode
400f2c: e8 1c 07 00 00 callq 40164d <explode_bomb> // bomb detonates (fail)
400f31: 41 03 6d 00 add 0x0(%r13),%ebp // add r13 and ebp (?)
400f35: 48 83 c3 04 add $0x4,%rbx // add 4 to rbx
400f39: 4c 39 e3 cmp %r12,%rbx // compare r12 and rbx
400f3c: 75 e4 jne 400f22 <phase_2+0x1f> // loop? if not equal, jump to 400f22
400f3e: 85 ed test %ebp,%ebp // compare ebp with itself?
400f40: 75 05 jne 400f47 <phase_2+0x44> // skip explosion if not equal
400f42: e8 06 07 00 00 callq 40164d <explode_bomb> // bomb detonates (fail)
400f47: 48 83 c4 28 add $0x28,%rsp
400f4b: 5b pop %rbx
400f4c: 5d pop %rbp
400f4d: 41 5c pop %r12
400f4f: 41 5d pop %r13
400f51: c3 retq
非常感谢任何帮助 - 特别是关于如何将类似的东西翻译成 C 代码的建议。提前致谢!
最佳答案
especially advice on how I would go about translating something like this into C code
不要直接将其翻译成 C。
学习根据寄存器和内存的变化来思考算法是如何实现的。 C 和 asm 只是表达您实际希望机器执行的操作的不同方式。
每条指令都会对机器的架构状态进行明确定义的修改,因此只需遵循该步骤链并查看结果。任何好的调试器(例如 gdb
模式下的 layout reg
)都可以显示在单步执行时修改了哪个寄存器。 insn ref 手册(x86 标签 wiki 中的链接)有关于每条指令确切作用的完整文档。
如果您对某事感到惊讶,请查找。没有这样做的人提出了很多 SO 问题,然后发布了关于 div
的愚蠢问题当他们没有设置 rdx
时的结果首先。
您需要找到修改或覆盖寄存器或内存位置的 insn 与后来从该寄存器或内存位置读取的指令之间的联系。
您通常可以从寄存器的使用方式中获得线索,例如add $0x4,%rbx
可能是指向 int *
的指针增量.如果 64 位整数不是指针或以某种方式涉及内存寻址,则很少将其递增 4。
如果您查看周围的代码并找到 mov 0xc(%rbx),%eax
(从 %rbx
的偏移量加载 4B),这证实了它是一个指针的理论。
cmp %r12,%rbx
/jcc
告诉你它也是循环条件的一部分,并且 %r12
是结束指针。你检查它只是一个简单的do{}while(p < end)
通过验证 %r12
循环在循环中没有被修改,并且在循环之前它被初始化为合理的东西。
mov $0x0,%ebp
告诉你这是 -O0
的编译器输出或 -O1
,因为每个 x86 编译器都知道 xor %ebp,%ebp
的“窥视孔”优化is the best way to zero registers .幸运的是,这看起来像 -O1
编译器输出,因此它不会在每个 C 语句之后将所有内容存储到内存中并在之后重新加载。这使得代码难以遵循,因为一个值不会在同一个寄存器中长期存在。
如果您对那个二进制炸弹代码有任何具体问题,您应该问他们。我刚刚回答了“如何阅读 asm”部分。
关于c - 阅读 x86 汇编代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36659225/