极简栈溢出程序逆向分析

如下所示是本实验的源代码,为方便添加断点和查看栈,特对几个变量进行初始化

#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,溢出攻击成功,继续程序查看最终效果:

进入认证成功分支,实验结束。

上述拙见如有谬误,敬请斧正!转载请注明出处,侵权请联系删除。
壬寅年孟夏月初五

极简栈溢出程序逆向分析相关推荐

  1. excel像素画教程_像素画新手教程:极简像素画角色分析

    摘要:像素画新手教程:极简像素画角色分析 关键词:像素画,新手教程,极简像素画,角色分析 撰文&编辑:三二 教你画像素画首发 | 公众号 pixelart 本文共755个字,阅读大约需要2分钟 ...

  2. 应用程序逆向分析 有道词典_逆向工程媒体应用程序(并使其中的所有故事免费)...

    应用程序逆向分析 有道词典 上周,我意识到距我在这里的最新帖子已经一年了. 我喜欢写故事,也喜欢阅读别人的故事,但前提是我认为这些故事足够有趣,而老实说这是一个很高的标准. 我最近一直在对Androi ...

  3. 第八章 Android 原生程序开发与逆向分析(五)(原生 C 程序逆向分析)

    文章目录 原生 C 程序逆向分析 编译原生 C 程序 for 循环分支结构 for1() for2() while 循环分支结构 dowhile() whiledo() if--else 分支结构 i ...

  4. WinXP下 扫雷程序逆向分析 --扫雷辅助(一)

    逐步走向逆向的坑 , 慢慢的锻炼 也算是一种兴趣爱好吧 突然起兴 , (及时行乐) 就想着尝试分析一下扫雷 这次就用 winxp自带的扫雷试试 查壳 使用peid 直接拖进去 可以看到 是使用VC 编 ...

  5. 小甲鱼 OllyDbg 教程系列 (七) :VB 程序逆向分析

    小甲鱼视频:https://www.bilibili.com/video/av6889190?p=14 VB程序逆向反汇编常见的函数:https://www.cnblogs.com/bbdxf/p/3 ...

  6. 大润发小程序逆向分析

    内容仅供参考学习 欢迎朋友们V一起交流: zcxl7_7 通过对大润发小程序反编译,进行加密参数分析 逆向分析加密参数, 并实现模拟 详情请看这边

  7. 最新大润发优鲜小程序逆向分析

    目标小程序:大润发优鲜小程序 重要说明:文章教程仅供参考学习,请勿用于非法用途,否则后果自负. 目录 1.需要准备的工具 2.工具预先初始化准备

  8. 微信小程序逆向分析浅析

    Android微信小程序本地代码存储路径 通过Hook java.lang.String.toString.java.lang.StringBuilder.toString可以获取到路径,如下所示: ...

  9. 【深入学习51单片机】二、一个极简RTOS源码分析

    目录 一.书接上回 二.初始化过程 三.任务的创建 四.任务的切换 五.任务的等待(系统延时) 一.书接上回 上回写了一个测试程序,可以直观的体会PC指针和堆栈指针的变化和影响.这章写下参考程序的过程 ...

最新文章

  1. GCC生成的汇编代码
  2. 教你如何运用python实现简单文件读写函数
  3. shopeeLazada越南站点“热销品类”推荐
  4. HDU - 3694 Fermat Point in Quadrangle(三分套三分/凸包)
  5. 五分钟搞懂后缀数组!
  6. 【BZOJ3083】遥远的国度,树链剖分练习
  7. 如何产生1-100之间的100个不重复的随机数
  8. 中秋佳节共团圆,送3本Python书
  9. KITTI激光雷达点云解析与图像反投影
  10. 关于学习数据库基础的一点心得体会
  11. 旧词新解:项目与产品,项目经理与产品经理
  12. QEMU虚拟磁盘资料
  13. ContextCapture数据处理及电脑配置常见问题汇总
  14. linux ntfs 3g 格式化,linux使用ntfs-3g操作ntfs格式硬盘
  15. NBIoT接收十六进制数据格式转化
  16. 有一个已经排好序的数组,要求输入一个数后,按原来排序规律将它插入数组中。
  17. 微信支付官方揭开刷脸支付神秘面纱
  18. 第一方数据,第二方数据,第三方数据,都是什么意思?
  19. 我和ChatGPT聊了聊:它承认自己没有人性
  20. html5 圆圈扩散,CSS3地图动态实例代码(圆圈向外扩散)

热门文章

  1. IDEA关闭当前文件改为ctrl + w
  2. overflow: auto与overflow:hidden区别
  3. 无人驾驶技术——Radar雷达
  4. 高防BGP服务器有什么用
  5. 怎么给自己的html网页加个密码,密码正确才能显示网页
  6. 多麦克风做拾音的波束_【语音交互】先从麦克风阵列聊起
  7. 标签平滑深度学习:Google Brain解释了为什么标签平滑有用以及什么时候使用它(SOTA tips)​...
  8. 大数据第一阶段学习笔记
  9. 如何安装husky_利用huskylint-staged构建代码检查工作流
  10. MySQL数据库的进销存