反汇编
反汇编
- 反汇编:把目标代码转为汇编代码的过程。 通常,编写程序是利用高级语言如C,Pascal等高级语言进行编程的,然后再经过编译程序生成可以被计算机系统直接执行的文件。反汇编即是指将这些执行文件反编译还原成汇编语言或其他高级语言。但通常反编译出来的程序与原程序会存在许多不同,虽然执行效果相同.
- gdb相关操作:
b linenumber 设置断点
run 运行
disassemble 获取汇编代码
用i(nfo) r(egisters)查看各寄存器的值
用x查看内存地址中的值 - 具体步骤:
设置断点在main函数调用f函数的位置gdb> b main
gdb> run运行
gdb> disassemble反汇编
display /i $pc
i r
x查看内存中的内容
si执行下一条汇编 - 反汇编:
使用gcc - g example.c -o example -m32指令在64位的机器上产生32位汇编,然后使用gdb example指令进入gdb调试器: - 用gcc在64位机器上编译一个32位的程序,遇到报错,具体如下图:
- 这是因为编译64位Linux版本32位的二进制文件,需要安装一个库,使用指令
sudo apt-get install libc6-dev-i386
- 进入之后先在main函数处设置一个断点,再run一下,使用disassemble指令获取汇编代码,用i(info) r(registers)指令查看各寄存器的值:
- 可见此时主函数的栈基址为0xffffd1e8,用x(examine)指令查看内存地址中的值,但目前%esp所指堆栈内容为0,%ebp所指内容也为0
以上为入门小练习,接下来让我们直接进入f函数中,查看f函数的每一步汇编代码
“display /i $pc”
其中 $pc 代表当前汇编指令,/i 表示以十六进行显示。当需要关心汇编代码时,此命令相当有用。这样在每次执行下一条汇编语句时,都会显示出当前执行的语句。下面展示每一步时%esp、%ebp、%eip、%eax和堆栈内容的变化:
- movl 指令:把值0x1b存在%ebp-0x4中
- 将上一个函数的地址入栈,以%ebp+0x8作为基址 待考证
- call指令将下一条指令的地址入栈
- endbr32
这是Intel 为 CONTROL-FLOW ENFORCEMENT TECHNOLOGY 新加的指令:
The ENDBRANCH (see Section 73 for details) is a new instruction that is used to mark valid jump target addresses of indirect calls and jumps in the program. This instruction opcode is selected to be one that is a NOP on legacy machines such that programs compiled with ENDBRANCH new instruction continue to function on old machines without the CET enforcement. On processors that support CET the ENDBRANCH is still a NOP and is primarily used as a marker instruction by the processor pipeline to detect control flow violations. The CPU implements a state machine that tracks indirect jmp and call instructions. When one of these instructions is seen, the state machine moves from IDLE to WAIT_FOR_ENDBRANCH state. In WAIT_FOR_ENDBRANCH state the next instruction in the program stream must be an ENDBRANCH. If an ENDBRANCH is not seen the processor causes a control protection exception (#CP), else the state machine moves back to IDLE state.
给技术要求相对跳转的目标地址一定是一条 endbr32 或 endbr64 指令,否则就会异常。该指令并不执行任何操作,只是用于验证目标地址是期望的跳转目标。
- 将上一个函数的基址入栈,从当前%esp开始作为新基址
- 此处栈底地址改变,下图是新地址与原始地址的对比图:
- call下一条指令入栈
- 将%esp指向的地址中的值赋给%eax寄存器
- ret指令将栈顶弹给%eip
将$0x2e23寄存器中的值与%eax寄存器中的值相加,存在$0x2e23寄存器中
- 将%eax寄存器中的值存到0x8+%ebp地址中,也就是堆栈中
- %eax寄存器的值与$0x3寄存器的值相加后的值存到$0x3寄存器中
- pop %ebp指令将栈顶弹到%ebp中,同时%esp增加4字节
-
ret指令将栈顶弹给%eip
-
leave
在32位汇编下相当于:
mov esp,ebp;//将ebp指向(ebp内部应当保存一个地址,所谓指向即这个地址对应的空间)的值赋给esp
pop ebp/* leave指令将EBP寄存器的内容复制到ESP寄存器中,
以释放分配给该过程的所有堆栈空间。然后,它从堆栈恢复EBP寄存器的旧值。*/