极简栈溢出程序逆向分析
极简栈溢出程序逆向分析
如下所示是本实验的源代码,为方便添加断点和查看栈,特对几个变量进行初始化。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PASSWORD "1234567"int verify_password(char *password){int authentication=0;char buffer[8]={0};authentication=strcmp(password, PASSWORD);strcpy(buffer,password);return authentication;
}void main(){int valid_flag=0;char password[1024]={0};while(1){printf("please input password: ");scanf("%s",password);valid_flag=verify_password(password);if(valid_flag){printf("incorrect password!\n\n");}else{printf("congratulations! you have passed the verification\n\n");break;}}
}
首先执行gcc -m32 -g sof.c -o sof -z execstack -fno-stack-protector
对源代码进行编译,开启栈执行并关闭栈保护,执行echo 0 > /proc/sys/kernel/randomize_va_space
关闭ubuntu的ASLR。
随后开启gdb调试,按r完成一边整体流程之后调试中的地址将变成物理地址,在代码7、8、9、10、15、16、22、23处添加断点方便调试,再按r,开始调试。主要观察红色箭头所指的EBP、ESP、汇编代码、源代码四个部分。此时执行到main函数的第一行,同时汇编代码执行到第12行,可见前十一行应为准备阶段。
如下是main函数的汇编代码1~11行,从第5行开始,重置栈顶和栈底此时二者相等,将edi、ebx、ecx压入栈中保存三者的值,同时栈空间增加12字节,随后栈顶增加0x41c即1052个字节以扩展栈空间,第10行将代码的位置加载到ebx
寄存器中,从而允许全局对象(与代码有固定的偏移量)作为该寄存器的偏移量来访问。ebx与本次分析关系不大。
0x56556236 <+0>: lea ecx,[esp+0x4]0x5655623a <+4>: and esp,0xfffffff00x5655623d <+7>: push DWORD PTR [ecx-0x4]0x56556240 <+10>: push ebp0x56556241 <+11>: mov ebp,esp0x56556243 <+13>: push edi0x56556244 <+14>: push ebx0x56556245 <+15>: push ecx0x56556246 <+16>: sub esp,0x41c0x5655624c <+22>: call 0x565560e0 <__x86.get_pc_thunk.bx>0x56556251 <+27>: add ebx,0x2d77
执行x/1064bx $ebp
输出栈空间,1064由开始压入的三个寄存器和拓展的1052字节构成,如下图是当前栈空间,可以看到ebp-0x1c的位置,栈空间非常杂乱,接下来将对栈空间中的变量初始化:
下面7行是对两个变量的初始化:
=> 0x56556257 <+33>: mov DWORD PTR [ebp-0x1c],0x00x5655625e <+40>: mov DWORD PTR [ebp-0x41c],0x00x56556268 <+50>: lea edx,[ebp-0x418]0x5655626e <+56>: mov eax,0x00x56556273 <+61>: mov ecx,0xff0x56556278 <+66>: mov edi,edx0x5655627a <+68>: rep stos DWORD PTR es:[edi],eax
首先对地址ebp-0x1c赋予四字节(int的大小)的0,执行si,可见结果:
接着对ebp-0x41c即0xffffd0fc处的四个字节赋值为零,此处应当是为了凑出255即ff的整倍数,先赋值4个字节剩余1020字节,在按双字赋值255(ff)次即可完成整个数组的初始化。故汇编代码中eax赋为0,ecx赋为0xff,edi保存目的串信息,es:[edi]为段超越前缀(一种寻址方式),最终完成赋值。
随后4行扩展12个字节栈空间,保存eax=[ebx-0x1fb8](0x56557010)即调用printf函数的参数(输出字符串)。
0x5655627c <+70>: sub esp,0xc0x5655627f <+73>: lea eax,[ebx-0x1fb8]0x56556285 <+79>: push eax0x56556286 <+80>: call 0x56556060 <printf@plt>
此时ESP 0xffffd0e0 —▸ 0x56557010 ◂— 'please input password: ',以下7行是scanf函数调用的准备阶段,回收刚才扩展的12字节和eax占用的4字节共16字节,扩展8字节,保存[ebp-0x41c]、[ebx-0x1f9f]两个值作为调用参数。
0x5655628b <+85>: add esp,0x100x5655628e <+88>: sub esp,0x80x56556291 <+91>: lea eax,[ebp-0x41c]0x56556297 <+97>: push eax0x56556298 <+98>: lea eax,[ebx-0x1f9f]0x5655629e <+104>: push eax0x5655629f <+105>: call 0x56556090 <__isoc99_scanf@plt>
随后输入1234567,执行ni,查看栈顶48个元素,如下图密码已经存入到恰当位置。
之后5行是调用verify_password函数的准备阶段,回收16字节的栈空间,扩展12字节保存[ebp-0x41c]即刚存储的password的起始地址,随即调用verify_password函数。
0x565562a4 <+110>: add esp,0x100x565562a7 <+113>: sub esp,0xc0x565562aa <+116>: lea eax,[ebp-0x41c]0x565562b0 <+122>: push eax0x565562b1 <+123>: call 0x565561dd <verify_password>
如下6行是新函数的准备阶段,保存ebp以备返回,重置ebp至栈顶,保存ebp,扩展20个字节栈空间待用,更新ebx。
0x565561dd <+0>: push ebp0x565561de <+1>: mov ebp,esp0x565561e0 <+3>: push ebx0x565561e1 <+4>: sub esp,0x140x565561e4 <+7>: call 0x565560e0 <__x86.get_pc_thunk.bx>0x565561e9 <+12>: add ebx,0x2ddf
如下3行初始化了两个变量,将上图所示三个区域赋值为0,结果如下:
0x565561ef <+18>: mov DWORD PTR [ebp-0xc],0x00x565561f6 <+25>: mov DWORD PTR [ebp-0x14],0x00x565561fd <+32>: mov DWORD PTR [ebp-0x10],0x0
如下5行是调用strcmp函数的准备阶段,扩展8个字节空间,保存[ebx-0x1fc0](宏定义的PASSWORD)、[ebp+0x8](调用该函数的参数地址,即输入的password的首地址)。
0x56556204 <+39>: sub esp,0x80x56556207 <+42>: lea eax,[ebx-0x1fc0]0x5655620d <+48>: push eax0x5655620e <+49>: push DWORD PTR [ebp+0x8]0x56556211 <+52>: call 0x56556040 <strcmp@plt>
如下7行是调用strcpy函数的准备阶段,回收16字节空间,给authentication赋strcmp的返回值(eax=0),扩展8字节空间,保存[ebp+0x8](输入的password的首地址)、[ebp-0x14](buffer的首地址),调用strcpy函数。
0x56556216 <+57>: add esp,0x100x56556219 <+60>: mov DWORD PTR [ebp-0xc],eax0x5655621c <+63>: sub esp,0x80x5655621f <+66>: push DWORD PTR [ebp+0x8]0x56556222 <+69>: lea eax,[ebp-0x14]0x56556225 <+72>: push eax0x56556226 <+73>: call 0x56556070 <strcpy@plt>
strcpy中最重要的几行如下图红框所示,将字符串分双字节装入edx所指的内存空间中。
回到verify_password函数,查看栈空间,发现buffer中已经被赋值。如下5行回收16字节空间,将ebp-0xc处的四个字节(authentication)作为返回值赋给eax,恢复ebx的值,关闭函数栈帧,回到主函数。
0x5655622b <+78>: add esp,0x100x5655622e <+81>: mov eax,DWORD PTR [ebp-0xc]0x56556231 <+84>: mov ebx,DWORD PTR [ebp-0x4]0x56556234 <+87>: leave0x56556235 <+88>: ret
如下10行,回收16字节空间,将返回值赋给ebp-0x1c对应的空间,将valid_flag与0进行比较,若为0则跳转到<main+160>,不为零则扩展12字节,将[ebx-0x1f9c]即incorrect password!赋给eax并保存,调用puts函数打印,然后回收16字节空间并跳转到<main+70>即继续循环。
0x565562b6 <+128>: add esp,0x100x565562b9 <+131>: mov DWORD PTR [ebp-0x1c],eax0x565562bc <+134>: cmp DWORD PTR [ebp-0x1c],0x00x565562c0 <+138>: je 0x565562d6 <main+160>0x565562c2 <+140>: sub esp,0xc0x565562c5 <+143>: lea eax,[ebx-0x1f9c]0x565562cb <+149>: push eax0x565562cc <+150>: call 0x56556080 <puts@plt>0x565562d1 <+155>: add esp,0x100x565562d4 <+158>: jmp 0x5655627c <main+70>
如下14行,将[ebx-0x1f84]输出,并回收栈空间,与程序开始对应弹出ecx、ebx、edi、ebp,复位esp,程序结束。
0x565562d6 <+160>: sub esp,0xc0x565562d9 <+163>: lea eax,[ebx-0x1f84]0x565562df <+169>: push eax0x565562e0 <+170>: call 0x56556080 <puts@plt>0x565562e5 <+175>: add esp,0x100x565562e8 <+178>: nop0x565562e9 <+179>: nop0x565562ea <+180>: lea esp,[ebp-0xc]0x565562ed <+183>: pop ecx0x565562ee <+184>: pop ebx0x565562ef <+185>: pop edi0x565562f0 <+186>: pop ebp0x565562f1 <+187>: lea esp,[ecx-0x4]0x565562f4 <+190>: ret
观察到栈空间中buffer紧挨着authentication,可以键入12345678,此时返回值将因为大于1234567而是1。
栈空间如下,红框内将被赋予12345678,字符串结尾处的空字符将溢出并覆盖authentication的0x01:
执行ni,再次查看栈空间和authentication的值如下:
authentication被复写为0,溢出攻击成功,继续程序查看最终效果:
进入认证成功分支,实验结束。
上述拙见如有谬误,敬请斧正!转载请注明出处,侵权请联系删除。
壬寅年孟夏月初五
极简栈溢出程序逆向分析相关推荐
- excel像素画教程_像素画新手教程:极简像素画角色分析
摘要:像素画新手教程:极简像素画角色分析 关键词:像素画,新手教程,极简像素画,角色分析 撰文&编辑:三二 教你画像素画首发 | 公众号 pixelart 本文共755个字,阅读大约需要2分钟 ...
- 应用程序逆向分析 有道词典_逆向工程媒体应用程序(并使其中的所有故事免费)...
应用程序逆向分析 有道词典 上周,我意识到距我在这里的最新帖子已经一年了. 我喜欢写故事,也喜欢阅读别人的故事,但前提是我认为这些故事足够有趣,而老实说这是一个很高的标准. 我最近一直在对Androi ...
- 第八章 Android 原生程序开发与逆向分析(五)(原生 C 程序逆向分析)
文章目录 原生 C 程序逆向分析 编译原生 C 程序 for 循环分支结构 for1() for2() while 循环分支结构 dowhile() whiledo() if--else 分支结构 i ...
- WinXP下 扫雷程序逆向分析 --扫雷辅助(一)
逐步走向逆向的坑 , 慢慢的锻炼 也算是一种兴趣爱好吧 突然起兴 , (及时行乐) 就想着尝试分析一下扫雷 这次就用 winxp自带的扫雷试试 查壳 使用peid 直接拖进去 可以看到 是使用VC 编 ...
- 小甲鱼 OllyDbg 教程系列 (七) :VB 程序逆向分析
小甲鱼视频:https://www.bilibili.com/video/av6889190?p=14 VB程序逆向反汇编常见的函数:https://www.cnblogs.com/bbdxf/p/3 ...
- 大润发小程序逆向分析
内容仅供参考学习 欢迎朋友们V一起交流: zcxl7_7 通过对大润发小程序反编译,进行加密参数分析 逆向分析加密参数, 并实现模拟 详情请看这边
- 最新大润发优鲜小程序逆向分析
目标小程序:大润发优鲜小程序 重要说明:文章教程仅供参考学习,请勿用于非法用途,否则后果自负. 目录 1.需要准备的工具 2.工具预先初始化准备
- 微信小程序逆向分析浅析
Android微信小程序本地代码存储路径 通过Hook java.lang.String.toString.java.lang.StringBuilder.toString可以获取到路径,如下所示: ...
- 【深入学习51单片机】二、一个极简RTOS源码分析
目录 一.书接上回 二.初始化过程 三.任务的创建 四.任务的切换 五.任务的等待(系统延时) 一.书接上回 上回写了一个测试程序,可以直观的体会PC指针和堆栈指针的变化和影响.这章写下参考程序的过程 ...
最新文章
- GCC生成的汇编代码
- 教你如何运用python实现简单文件读写函数
- shopeeLazada越南站点“热销品类”推荐
- HDU - 3694 Fermat Point in Quadrangle(三分套三分/凸包)
- 五分钟搞懂后缀数组!
- 【BZOJ3083】遥远的国度,树链剖分练习
- 如何产生1-100之间的100个不重复的随机数
- 中秋佳节共团圆,送3本Python书
- KITTI激光雷达点云解析与图像反投影
- 关于学习数据库基础的一点心得体会
- 旧词新解:项目与产品,项目经理与产品经理
- QEMU虚拟磁盘资料
- ContextCapture数据处理及电脑配置常见问题汇总
- linux ntfs 3g 格式化,linux使用ntfs-3g操作ntfs格式硬盘
- NBIoT接收十六进制数据格式转化
- 有一个已经排好序的数组,要求输入一个数后,按原来排序规律将它插入数组中。
- 微信支付官方揭开刷脸支付神秘面纱
- 第一方数据,第二方数据,第三方数据,都是什么意思?
- 我和ChatGPT聊了聊:它承认自己没有人性
- html5 圆圈扩散,CSS3地图动态实例代码(圆圈向外扩散)
热门文章
- IDEA关闭当前文件改为ctrl + w
- overflow: auto与overflow:hidden区别
- 无人驾驶技术——Radar雷达
- 高防BGP服务器有什么用
- 怎么给自己的html网页加个密码,密码正确才能显示网页
- 多麦克风做拾音的波束_【语音交互】先从麦克风阵列聊起
- 标签平滑深度学习:Google Brain解释了为什么标签平滑有用以及什么时候使用它(SOTA tips)​...
- 大数据第一阶段学习笔记
- 如何安装husky_利用huskylint-staged构建代码检查工作流
- MySQL数据库的进销存