关于ubuntu18版本以上调用64位程序中的system函数的栈对齐问题
有时候在做64位题目的时候会exp完全没问题,但就是获取不了shell。然后通过gdb调试发现是在最后的system函数执行的时候卡住了,然后就满脸疑惑,这也能卡???
为什么执行system函数要栈对齐
其实啊,64位ubuntu18以上系统调用system函数时是需要栈对齐的。再具体一点就是64位下system函数有个movaps指令,这个指令要求内存地址必须16字节对齐,如果你到system函数执行的时候,si单步进去就会发现,如果没对齐的话,最后就会卡在这里(如下图)。
对齐?怎么才算对齐?
因为64位程序的地址是8字节的,而十六进制又是满16就会进位,因此我们看到的栈地址末尾要么是0要么是8。如下图
只有当地址的末尾是0的时候,才算是与16字节对齐了,如果末尾是8的话,那就是没有对齐。而我们想要在ubuntu18以上的64位程序中执行system函数,必须要在执行system地址末尾是0。
下面两个图,分别是没对齐和对齐的情况。
如果执行system的时候没有对齐怎么办?
如果执行了一个对栈地址的操作指令(比如pop,ret,push等等,但如果是mov这样的则不算对栈的操作指令),那么栈地址就会+8或是-8。为使rsp对齐16字节,核心思想就是增加或减少栈内容,使rsp地址能相应的增加或减少8字节,这样就能够对齐16字节了。因为栈中地址都是以0或8结尾,0已经对齐16字节,因此只需要进行奇数次pop或push操作,就能把地址是8结尾的rsp变为0结尾,使其16字节对齐。
这时候有两种解决方法。
1、去将system函数地址+1,此处的+1,即是把地址+1,也可以理解为
+1是为了跳过一条栈操作指令(我们的目的就是跳过一条栈操作指令,使rsp十六字节对齐,跳过一条指令,自然就是把8变成0了)。但又一个问题就是,本来+1是为了跳过一条栈操作指令,但是你也不知道下一条指令是不是栈操作指令,如果不是栈操作指令的话(你加一之后有可能正好是mov这种指令,也有可能人家指令是好几个字节,你加一之后也没有到下一个指令呢),+1也是徒劳的,要么就继续+1,一直加到遇见一条栈操作指令为止(看别的师傅说最大加16次就能成功,不过我不知道为啥)
可以看见本来我们应该是用401186这个地址的,但是我们现在要跳过一条指令,那自然就是用401187,这样就跳过了push rbp这条指令。
2、直接在调用system函数地址之前去调用一个ret指令。因为本来现在是没有对齐的,那我现在直接执行一条对栈操作指令(ret指令等同于pop rip,该指令使得rsp+8,从而完成rsp16字节对齐),这样system地址所在的栈地址就是0结尾,从而完成了栈对齐。
因此payload有两种改法(下面我是以BUUCTF上的rip题目的exp为例)。
from pwn import *
p=remote("node4.buuoj.cn",28002)
payload=23*'A'+p64(0x401186+1)+p64(0)#加1去跳过一个栈操作指令,使其对齐16字节
#p.recvuntil("please input")#这里用recvuntil会报连接超时,因为nc上去发现服务器那边的程序上没有打印这句话
p.sendline(payload)
p.interactive()
from pwn import *
p=remote("node4.buuoj.cn",28002)
payload=23*'A'+p64(0x401016)+p64(0x401186)+p64(0)#0x401016是一个ret指令, p64(0)是system函数的返回地址
p.sendline(payload)
p.interactive()