了解反汇编的C代码:dec%eax和movl $ 0x0,-0x8(%ebp)

时间:2019-07-10 15:30:31

标签: assembly x86 disassembly att

我试图理解一段反汇编代码中的行,如下所示。我想知道以下内容:

  • dec%eax:为什么eax寄存器递减? eax寄存器的初始值是什么?
  • movl $ 0x0,-0x8(%ebp):为什么我们将值0x0移动到堆栈上? movl不存储32位值(4个字节)吗?如果是这样,为什么将值存储在基指针下方8个字节而不是4个字节?

这是反汇编的二进制文件:

Contents of section .text:
 0000 554889e5 48c745f8 00000000 905dc3    UH..H.E......]. 
Contents of section .rodata:
 0000 48656c6c 6f00                        Hello.          
Contents of section .comment:
 0000 00474343 3a202855 62756e74 7520352e  .GCC: (Ubuntu 5.
 0010 342e302d 36756275 6e747531 7e31362e  4.0-6ubuntu1~16.
 0020 30342e31 30292035 2e342e30 20323031  04.10) 5.4.0 201
 0030 36303630 3900                        60609.          
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 01781001  .........zR..x..
 0010 1b0c0708 90010000 1c000000 1c000000  ................
 0020 00000000 0f000000 00410e10 8602430d  .........A....C.
 0030 064a0c07 08000000                    .J......        

Disassembly of section .text:
0000000000000000 <my_function>:
   0:   55                      push   %ebp
   1:   48                      dec    %eax
   2:   89 e5                   mov    %esp,%ebp
   4:   48                      dec    %eax
   5:   c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%ebp)
   c:   90                      nop
   d:   5d                      pop    %ebp
   e:   c3                      ret    

这是C代码:

void my_function () { 
   char* my_string = "Hello";
}

2 个答案:

答案 0 :(得分:4)

您正在分解64位代码,就好像它是32位代码一样。 REX.W前缀(仅存在于64位代码中)是32位代码中的DEC EAX指令。

答案 1 :(得分:2)

您正在分解64位代码,就好像它是32位代码一样。除非您专门重写了反汇编程序或使用objcopy或其他将64位机器代码复制到32位ELF目标文件中,否则通常无法实现。

x86-64将0x40..f字节重新用作REX前缀,而不是inc / dec的1字节编码。 DEC EAX实际上是REX.W前缀,因此对于正常的帧指针设置,该指令为mov %rsp, %rbp

这也说明了堆栈指针下方红色区域的前8个字节的使用。 (x86-64系统V有一个红色区域,i386系统V没有红色区域;在存储在它下面之前,它会移动ESP。)并且它为指针解释了-8而不是-4,因为x86 -64有8个字节的指针。


0字节是因为您要拆卸未链接的.o。链接程序将使用字符串的绝对地址填充这四个字节的零。

GCC在这里使用mov r/m64, sign_extended_imm32使用32位绝对地址作为立即数来存储指向内存的8字节指针。

要把它放在寄存器中,我们将为非PIE可执行文件获得常规的mov r32, imm32(隐式零扩展到64位)。但是此代码(带有默认的-O0“调试模式”)需要内存中的整个8字节指针。它仍然可以使用32位绝对地址来代替相对于RIP的LEA到寄存器+单独的存储区中,但是必须将其显式符号扩展为64位。