首页
社区
课程
招聘
[原创] 从0开始的二进制复习(学习)-理解执行流与溢出
发表于: 21小时前 71

[原创] 从0开始的二进制复习(学习)-理解执行流与溢出

21小时前
71

首先编译,为了理解执行流和溢出本身本次编译需要把system模块编译出来

练习目的是理解溢出和执行流

一开始需要在main的入口打个断点

pwndbg的信息比较详尽 如果凌乱那么使用gdb即可 考虑到后期需要转换Windows 而pwndbg和windbg的一些命令相同 所以继续用pwndbg

非常直观的看到断点位置

图片描述

简单理解一下汇编的部分

关于空间分配的部分,见注释1

为了直观看汇编部分

找几个关键点,要完成溢出关注sub实际分配的空间

图片描述

关注str的实际位置 解释查看注释2

在gets函数调用后打断点查看输入

图片描述

来查看传入的参数(eax)和此时str变量当中存储的值

图片描述

查看内存布局

图片描述

代码顺着执行接下来会走到printf的部分

图片描述

按照预期来说当程序走到这里会正常输出8个A

打个断点验证

图片描述

正常看到了这个输出

接下来分支有两条

第一条就是单纯的修改执行流,现在程序的逻辑是收到什么字符串就printf什么字符串,所以接下来的事情,是想要把收到的字符串当作命令执行,也就是理解执行流

重新让程序跑起来

这次跑起来以后不要再输入单纯的字符A

而是输入一个命令比如id

图片描述

在继续之前先来找找命令执行的相关函数,由于在编译时已经把system编译进去了 可以直接查询

图片描述

这里找到system相关的地址挑第一个来用do_system不用处理多余的东西直接就可以用

注意在本例当中system和do_system的传参原理虽然不通,但是都通过eax所以接下来的调试过程通用

当前代码在 call printf 处使用的是直接寻址,而不是通过 GOT 表做间接跳转

直接在这里打断点

在 32 位 x86 汇编中,直接调用函数的 call <target> 采用的是相对近调用

也就是这个指令是两个部分

call 指令 + 4字节的一个地址

然后call指令本身所在的地址是0x80498df

所以要修改call function的地址要在call指令+1的位置调整即可

这里要稍微标记一下 call function 这条指令本身不是 call后面的地址本身

而是 这样的一个公式call call指令本身地址-目的函数的相对距离

做个假设 比如 call指令本身的地址是0x1234 目标函数是0x1244那么想要调用目标函数 不是call 0x1244而是 call 0x10(0x1244-0x1234) 但是注意 call 0x1234这个指令本身要占5个字节 也就是说如果直接call 0x10那么是从第五个字节向后算的所以 call的最终公式应该如下(套用0x1234和0x1244)

call ( 0x1244(目的函数地址)-0x1234(call指令地址)-0x5(call指令长度) )

最终如果想要从0x1234的call指令地址成功调用位于0x1244的函数的话这个计算结果就是

call 0xb

之后查看当前调用

图片描述

然后让他运行下去

图片描述

中间的报错暂时不用管,看到最后这个地方就说明确实是调用了 do_system 这里报错是%s这个命令没有找到

这里的主要原因是printf 和 do_system 的函数原型与参数解析方式完全不同,看注释3

也就是说我们的command是在%s+4的位置存着 所以重新运行在这里也下一个断点

这里由于传参方式的不同所以需要set的点不一样

图片描述

然后还需要设置一下 否则会陷入一个循环 输出也不直观

设置 GDB 仅保留在父进程,子进程自由执行并脱离

图片描述

最终成功调用do_system执行命令

但是这样就发现一个事情完全没有用到get输入的参数,此时这个参数无论是什么都不影响我们后续的部分,反正后续通过eax传参

用于理解执行流

在现代 x86/GCC 中,出于性能优化以及满足 SSE 指令集的 16 字节对齐要求,编译器开辟局部变量空间时,会按照 16 字节(0x10)的倍数向上取整。

也就是说当前的代码

str分配的空间小于0x10 所以向上取证

但是当这个部分大于0x10时比如等于18那么还是会向上取证

编译

图片描述

注意此处的lea eax, [ebp-0x14]与lea -0x14(%ebp),%eax 在机器码层面完全相同,唯一的区别是汇编代码的语法风格(Syntax Flavor)不同:前者是 Intel 语法,后者是AT&T语法。

printf 是格式化输出函数

system 是命令执行函数

do_system 是 glibc 内部核心执行函数

理解了执行流以后来尝试溢出的部分 , 溢出首先就不能手动去更改执行流 , 而是需要通过输入被动修改执行流

直白的说就是 : 不可能直接调试目标程序 , 所以要从程序的输入当中破坏程序的预期执行流 , 让程序走向我的预期执行流

在基础理解部分已经写出了分配的空间大小 , 以及原因 , 直接看关键部分代码

这里分配了0x10空间大小 , 在gets调用结束后打断点

一个本办法找到当前的内存空间 esp和ebp是栈顶和栈底 那其实只需要查看一下esp 和 ebp 就知道范围

大略过一下地址情况

这个地址ffffcc30很明显是str字符串的存储位置

接下来的ffffcc40 + 8的位置是ebp 那么ffffcc40 + 12的位置是返回地址的位置

查看这个地方 稍微做一下注解

图片描述

在 Linux C 语言环境中,程序的第一个执行入口并不是 main 函数,而是编译器链接进去的 _start 汇编桩

在 __libc_start_call_main 执行 call main 的瞬间,CPU 硬件机制自动将紧接着的下一条指令地址(即 0x08049e45,也就是 __libc_start_call_main+101)压入栈中作为返回地址(Return Address)。

简单来说在这种情况下 把main函数看做一个普通函数这个函数返回会向着__libc_start_call_main这里去返回

溢出攻击的终极目标是覆盖掉返回地址(Return Address),让程序在 ret 时跳去执行预期的代码。

内存布局如下

所以这个部分的距离计算如下

0xffffcc4c - 0xffffcc34 = 0x18 = 24字节

可以理解一共分配了0x10(16)字节的空间大小那么ebp在0x10 + 4的位置然后返回地址在ebp+4

所以常规状态下 这个距离也可以这样算

分配大小 + ebp(4字节) + 一个到return address的padding 一般为(4字节)

也就是在本例当中0x10(16)+0x4(4)+0x4(4)=0x18(24)也就是从第25个字节开始覆盖到返回地址

按照这个逻辑输入到24个A看到的内存布局应该是A紧连返回地址

图片描述

来查看这个内存布局 继续向下执行一定是崩溃的

在崩溃之前有一个点需要理解

在这里ffffcc40+0xc这个位置原本的返回地址从08049e45->08049e00这里并不是返回地址变了

从这个变化恰恰应证了溢出是正常覆盖了的00是因为 gets() 会在读取完字符串末尾自动追加一个空字节 \0

综上 尝试28个A时应该会让返回地址变成0x41414141然后由于没有这样的地址所以报错

由于我们gcc编译器版本的原因 所以导致GCC 编译器生成了一种特殊的函数收尾机制,它不是直接 ret,而是:

所以预期如下

图片描述

图片描述

但是此刻无法完成溢出执行命令这样的执行流需要用到进阶的技术

目前到这里完成溢出练习

这里题目设计的问题所以这个命令执行是有一点复杂的

精准修复 ECX(Stack Pivoting / 栈迁移变种):

溢出时不能全填 A,必须把 0xffffcc40 位置精准填入一个合法的栈地址(比如原始的 0xffffcc60)。这样 lea esp, [ecx-4] 算出来的还是合法栈顶,程序就能活着执行 ret。

构造标准的 ret2libc 栈结构: 在 ret 后面不能只填函数地址,必须按照 32 位函数调用的栈格式来排布:

按照这样的payload结构如下

注意这里存在一个问题 : 硬编码栈地址的局限性

也就是说接下来我所有的操作都只针对我当前的环境和我本次调试

但是理论是正确的 , 理解理论是可以完成同样的操作的

如果按照这个payload 去写一个脚本 生成bin文件加载进去会发现报错 这个payload的理论只是我们推演的雏形 , 是有问题的 按照这个思路来说 , 写出来的payload大概这样

图片描述

但是 , 这样去加载就会发现有问题

一切如预期 但是崩溃了

图片描述

这里存在的问题有几个

大量文字来袭

1.栈帧布局错位

在内存中,SYSTEM_ADDR 之后紧接着写入了 EAX_ADDR(0xffffcc34)当 ret 跳转到 system 时,system 内部从栈顶读取数据:

system 接收到的参数指针不是 "id;",而是错误的垃圾地址,导致命令无法执行。


[招生]科锐逆向工程师培训(2026年11月13日实地,远程教学同时开班, 第57期)

最后于 21小时前 被Cypher.M编辑 ,原因: 需要上传相关源码和附件
收藏
点赞 ・2
打赏
分享
最新回复 (1)
雪    币: 4024
活跃值: (1265)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
2 楼
欢迎围观指正~
21小时前
0
游客
登录 | 注册 方可回帖
返回