32位与64位 系统调用的区别:
1. 传参方式不同
2. 系统调用号 不同
3. 调用方式 不同
32位:
传参方式:首先将系统调用号 传入 eax,然后将参数 从左到右 依次存入 ebx,ecx,edx寄存器中,返回值存在eax寄存器
调用号:sys_read 的调用号 为 3 sys_write 的调用号 为 4
调用方式: 使用 int 80h 中断进行系统调用
64位:
传参方式:首先将系统调用号 传入 rax,然后将参数 从左到右 依次存入 rdi,rsi,rdx寄存器中,返回值存在rax寄存器
调用号:sys_read 的调用号 为 0 sys_write 的调用号 为 1
stub_execve 的调用号 为 59 stub_rt_sigreturn 的调用号 为 15
调用方式: 使用 syscall 进行系统调用
关于程序偏移计算
有了上面的知识,就可以做尝试的看这道题了。检查保护只开启了堆栈不可执行。
main函数调用了vuln函数,我们来看一下vuln的汇编。
发现首先利用系统调用,执行了read(0,buf,0x400),又执行了write(1,buf,0x30)。看一下buf的位置,发现距离rbp只有0x10大小,存在栈溢出。
除了这个其实我们还可以看到程序结束的时候是直接程序开始的时候,直接是:
pop rbp
mov rbp,rsp
程序结束的时候直接是:
retn
注意!!!这里的retn是0x400519的retn,执行这个其实就跳出这个函数了。
而retn是做什么的呢?retn的操作是内 pop eip,然后执行eip指向的指令。
函数调用开始,rbp==rsp,并且值也一直没变过,所以这里覆盖rbp的时候,其实就需要将rbp覆盖成你想要的返回地址。所以这道题的偏移其实就是0x10就可以了!
pwndbg stack查看rsp上面的栈分布
在调试这个程序的过程中,因为rsp==rbp,而pwndbg的stack指令直接看到的就是rsp下面的栈分布,这个时候应该怎么办呢?以前学过用pwndbg给地址和寄存器赋值,代码是:
Set *addr = value 给地址赋值 Set $rsp = value 给寄存器赋值
只要我们先让$rsp=$rsp-0x...,这样再用stack命令,就可以看到我们想要看的内存分布了!
咳咳,开始尝试做题了!!!
做法1 通过系统调用59对应的execve,然后想办法执行execve(“/bin/sh”,0,0) 接用leak stack!!!
上面说到了可以进行栈溢出,执行execve就需要给寄存器赋值,那大概的布局就是这样的:
$rax==59
$rdi==“/bin/sh”
$rsi==0
$rdx==0
syscall
ida中可以看到有一个函数叫做gadgets,我们看看汇编干了些什么。
下面的箭头是给rax赋值为0x3b,也就是59,后面还跟了一个retn。做到这里,真的佩服出题人,这道题出的真好!!!这样首先解决了rax,接下来是rdi,既然“/bin/sh”是字符串,我们可不可以写入栈呢?写入栈的话,就需要leak栈地址。这里我们用上面的知识点,来调试看看栈分布!
还记得write是会打印出0x30大小的数据,这里在打印到0x20的时候,接下来是打印出来一个地址,这个地址一看就是栈上面的,所以只要算出这个地址和binsh地址的相对偏移,就可以在程序每次执行的时候算出binsh的地址了!
这里是ca8-b90==0x118