我有一个反汇编的程序,有一段这样的代码:
│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的错误?
答案 0 :(得分:1)
是的,mov
长2个字节,但在0x...c
开始,因此下一个指令从0x...e
开始。
从mov
(0x...d
)的第2个字节开始反汇编,而不是在函数顶部开始解码时执行的下一条指令的开始。
以mov
的ModR / M字节开头的字节确实表示有效指令(与大多数字节序列一样),因为x86操作码空间大部分都是满的。通常,当以下字节代码与操作数不兼容的操作数时,您只会获得无效指令;这种情况很少见,因为大多数操作码都没有任何不兼容的操作数模式。
TL:DR:如果您解码“不同步”,您通常会获得有效的x86指令,并且可能永远不会与编译器发出的内容“同步”。这同样适用于跳转后的CPU解码指令;一种代码混淆技术是将指令隐藏在即时数据或其他内容中,然后跳转到该数据。
或者甚至出于优化目的:要使循环的第一条指令在第一次迭代时不执行,请将这些字节作为立即消耗到mov eax, imm32
或其他东西。这比跳过指令或剥离第一次迭代要小,如果它不会混淆uop缓存/循环缓冲区,则可能不会慢。