【问题标题】:Reading x86 assembly code读取 x86 汇编代码
【发布时间】:2016-04-16 12:22:50
【问题描述】:

我正在通过一个实验室工作,我必须通过为每个阶段提供正确的输入来拆除“炸弹”。我无权访问源代码,因此我必须使用 GDB 逐步完成汇编代码。现在,我被困在第 2 阶段,非常感谢一些帮助。这是 x86 汇编代码 - 我添加了一些描述我认为正在发生的事情的 cmets,但这些可能是非常错误的,因为我们几天前才开始学习汇编代码,我仍然很不稳定。据我现在所知,这个阶段从用户那里读取了 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 代码的建议。提前致谢!

【问题讨论】:

  • 你有什么问题?
  • 不要按字面意思把它翻译成 C 语言。学会根据寄存器和内存的变化来思考算法是如何实现的。 C 和 asm 只是表达您实际希望机器做什么的不同方式。每条指令都会对机器的体系结构状态进行明确定义的修改,因此只需按照该步骤链查看结果即可。
  • 对不起,如果我不清楚。我只需要帮助了解汇编代码在做什么。
  • '我只需要帮助了解汇编代码在做什么' - 只是?这就是整个练习。

标签: c assembly x86


【解决方案1】:

特别是关于如何将这样的东西翻译成 C 代码的建议

不要把它直接翻译成 C。

学习如何根据寄存器和内存的更改来实现算法。 C 和 asm 只是表达您真正希望机器做什么的不同方式。

每条指令都会对机器的体系结构状态进行明确定义的修改,因此只需按照该步骤链查看结果即可。任何好的调试器(例如layout reg 模式下的gdb)都可以向您显示在您单步时修改了哪个寄存器。 insn 参考手册( 标签 wiki 中的链接)包含关于每条指令的确切作用的完整文档。

如果您对某事感到惊讶,请查一下。没有这样做的人提出了许多 SO 问题,然后在他们没有首先设置 rdx 时发布了关于 div 结果的愚蠢问题。


您需要找到修改或覆盖寄存器或内存位置的 insn 与从该寄存器或内存位置读取的后续指令之间的连接。


您通常可以从寄存器的使用方式中获得线索,例如add $0x4,%rbx 可能是指向int * 的指针增量。如果 64 位整数不是指针,或者以某种方式涉及内存寻址,则很少将其递增 4。

如果您查看周围的代码并找到mov 0xc(%rbx),%eax(从%rbx 的偏移量加载4B),这证实了它是一个指针的理论。

cmp %r12,%rbx / jcc 告诉你它也是循环条件的一部分,%r12 是结束指针。通过验证 %r12 在循环中没有被修改,并且在循环之前它被初始化为合理的东西,您可以检查它只是一个简单的 do{}while(p &lt; end) 循环。


mov $0x0,%ebp 告诉你这是来自-O0-O1 的编译器输出,因为每个x86 编译器都知道xor %ebp,%ebp is the best way to zero registers 的“窥孔”优化。幸运的是,这看起来像 -O1 编译器输出,因此它不会在每个 C 语句之后将所有内容存储到内存中并在之后重新加载。这使得代码难以理解,因为一个值不会在同一个寄存器中长期存在。


如果您对该二进制炸弹代码有任何具体问题,请询问他们。我刚刚回答了“如何阅读 asm”部分。

【讨论】: