为什么gdb上的x / i给出不同的结果然后反汇编?

时间:2018-02-12 05:16:41

标签: assembly gdb

我有一个反汇编的程序,有一段这样的代码:

   │0x804873b <main>        push   %ebp                                        
   │0x804873c <main+1>      mov    %esp,%ebp                                   │
   │0x804873e <main+3>      and    $0xfffffff0,%esp                            │
   │0x8048741 <main+6>      sub    $0x50,%esp                                  │
   │0x8048744 <main+9>      cmpl   $0x2,0x8(%ebp)                              │
   │0x8048748 <main+13>     je     0x8048770 <main+53>                         │
   │0x804874a <main+15>     mov    0xc(%ebp),%eax   

我认为mov是一个2字节长的指令,但如果我这样做

x/i 0x804873d 0x804873d <main+2>:  in     $0x83,%eax

为什么我会得到这个呢?这些是非伪指令还是Gdb的错误?

1 个答案:

答案 0 :(得分:1)

是的,mov长2个字节,但0x...c开始,因此下一个指令从0x...e开始。

mov0x...d)的第2个字节开始反汇编,而不是在函数顶部开始解码时执行的下一条指令的开始。

mov的ModR / M字节开头的字节确实表示有效指令(与大多数字节序列一样),因为x86操作码空间大部分都是满的。通常,当以下字节代码与操作数不兼容的操作数时,您只会获得无效指令;这种情况很少见,因为大多数操作码都没有任何不兼容的操作数模式。

TL:DR:如果您解码“不同步”,您通常会获得有效的x86指令,并且可能永远不会与编译器发出的内容“同步”。这同样适用于跳转后的CPU解码指令;一种代码混淆技术是将指令隐藏在即时数据或其他内容中,然后跳转到该数据。

或者甚至出于优化目的:要使循环的第一条指令在第一次迭代时不执行,请将这些字节作为立即消耗到mov eax, imm32或其他东西。这比跳过指令或剥离第一次迭代要小,如果它不会混淆uop缓存/循环缓冲区,则可能不会慢。