0x00 利用x86汇编查找EIP
使用FPU指令
该方法首次利用是由Aaron Adams发表在https://seclists.org/vuln-dev/2003/Nov/44
实现原理就是利用x87浮点运算单元(FPU)来获取EIP的值
先大概介绍一下FPU:
FPU是专用于浮点运算的处理器,以前FPU是一种单独的芯片,从486开始集成在了CPU中,所以基于x86的处理器是继承了FPU处理器的。
此时可以通过如下的汇编代码来获取EIP:
section .text
BITS 32
global CMAIN
CMAIN:
fldz
fnstenv [esp-0xc]
pop eax
add al,0x09
xor eax, eax
ret
使用的编辑环境是https://github.com/Dman95/SASM
上面的汇编指令中,fldz用于激活FPU寄存器,fldz功能是把+0.0 压入压入堆栈中。这里指的就是FPU寄存器.
在因特尔手册中可以找到关于FPU寄存器的结构描述:
这里调用了fldz指令之后,便会初始化FPU寄存器,并将当前EIP的内容存放到FIP中。
第二条指令fnstenv [esp - 0xc]
所以该指令的功能是将FPU保存到指定地址,esp-0xc的位置这里为什么是0xc,我们再回过头来看FPU的定义:
可以看到FIP相对FPU的偏移刚好是12,也就是0xc
这里将FPU保存到exp-0xc,也就是将esp保存在FIP中。
接下来通过pop eax指令,将FIP(esp)的值弹出保存到eax
这个时候我们就可以通过这个esp去找到EIP,因为现在eax已经指向esp了,所以直接将eax加上已经执行过的命令的字节数,就可以得到EIP。这里本来应该是+7,但由于本身add al,7这条命令还要占2个字节,所以直接写add al,9。
将汇编代码编译生成exe
在调试器中断在fldz指令处:
目前fldz还未执行
往后执行两步,初始化FPU寄存器,并将FPU保存到esp-0xc
这里可以看到ESP(FIP)的值就是00401390
执行pop eax 将这个值弹出
现在eax指向00401390处
接下来将执行过的代码字节给加上,就可以使eax指向当前的EIP了
这里之所以是+9,是因为原本的命令应该是7个字节,但是这里还要往后执行语句,所以加起来应该是9个字节所以是al+9
通过jmp
先上代码:
%include "io.inc"
section .text
BITS 32
global CMAIN
CMAIN:
jmp labe12
labe11:
jmp getEIP
labe12:
call labe11
getEIP:
pop eax
调试器中:
这里pop eax执行之后,eax直接就指向了eip的前一条指令,即成功获取到EIP了。
0x01 x64架构
其实这两种方法原理是通用的,不同之处就在于32位寄存器应改为64位。
FPU
查了一下,x64处理器下FPU还是可以使用的,所以直接参照之前x86的方法尝试,直接在一个64位的进程中输入代码:
pop rax之后,RAX成功指到RSP
同样的方法add rax ,7 是的RAX指向RIP前一个位置
通过跳转
同样的,构建一个同x86的跳转代码:
pop rax之后,RAX指向的RIP的前一个指令
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)