0x00 利用x86汇编查找EIP
使用FPU指令
该方法首次利用是由Aaron Adams发表在fa4K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6k6h3y4D9K9i4y4@1M7#2)9J5k6h3!0J5k6#2)9J5c8Y4k6#2L8r3&6Q4x3X3c8V1k6i4k6Q4x3V1j5J5x3o6l9K6i4K6u0r3e0X3!0$3i4K6u0r3y4o6b7`.
实现原理就是利用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

使用的编辑环境是22eK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6p5L8h3q4F1z5e0g2Q4x3V1k6e0b7g2y4y4
上面的汇编指令中,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的前一个指令
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课