从大二开始真正接触技术开始,从最早的HTML,PHP,WEB开发。一直以为以后可能会从事开发的工作,碰巧大三上的时候和同专业的郭子,邹豪参加了南京的一个信息安全技能大赛,才真正找到了兴趣的方向,也从懵懵懂懂开始懂了信安怎么学,像海贼王里面一样,感觉到了新世界了(好吧,我又YY了......)

  最近参加了ISCC2013的比赛,历时一个月的过程,虽然过程很辛苦,每天除了吃饭睡觉都在想题目(我不会告诉你我有一题是在睡觉的时候想出来的,做梦梦到单步调试....)。但是感觉收获颇丰,貌似比以前牛逼了一点点。也因此萌生了要开一个技术博客,写一点自己原创的东西,算是总结提高吧。

  在做溢出关第一题的时候,遇到了缓冲区溢出的问题,之前从没接触过这东西,汇编也不懂。于是各种查资料,看小甲鱼的汇编视频,总算是把原理搞明白了,希望在这里分享一些关于缓冲区溢出和汇编的基础知识,小白一个,大神路过不要拍砖啊。

  首先看一段代码:

#include<stdio.h>
void why_here(void) /*这个函数没有任何地方调用过*/
{
  printf("why u here ?!\n");
  _exit(0);
}
int main(int argc,char * argv[])
{
  int buff[1];
  buff[2]=(int)why_here;
  return 0;
}

用GCC或者VC编译运行一下,会发现why_here函数被运行了,地球人都知道这是缓冲区溢出,但是在底层代码到底发生了什么呢?

原因是是因为错误的越界溢出赋值导致了函数返回地址被错误的覆盖了,程序流执行到这里之后不是正常的返回而是执行了被覆盖的地址。这样说有点抽象,我们用VC进行debug一下,看看汇编代码到底做了什么。

发生溢出的C代码:

6:    int main(int argc,char * argv[])
7:    {
00401070   push        ebp
00401071   mov         ebp,esp
00401073   sub         esp,44h
00401076   push        ebx
00401077   push        esi
00401078   push        edi
00401079   lea         edi,[ebp-44h]
0040107C   mov         ecx,11h
00401081   mov         eax,0CCCCCCCCh
00401086   rep stos    dword ptr [edi]
8:        int buff[1];
9:        buff[2]=(int)why_here;
00401088   mov         dword ptr [ebp+4],offset @ILT+5(why_here) (0040100a)
10:       return 0;
0040108F   xor         eax,eax
11:   }
00401091   pop         edi
00401092   pop         esi
00401093   pop         ebx
00401094   mov         esp,ebp
00401096   pop         ebp
00401097   ret

这两行是函数调用的常用语句,EBP是基址寄存器,目的是在进入函数前将EBP保护起来,一遍出来的时候可以恢复现场(计算机组成原理上的知识)。

下面这句  sub   esp,44h 应该就是为新进入的函数预留栈空间。接下来就是经典的三个push为main函数传参。

............

我们重点看这行代码:

00401088   mov     dword ptr [ebp+4],offset @ILT+5(why_here) (0040100a)

将why_here的函数入口基地址放入[ebp+4]中,这样看也许看不出什么不妥,我们对比一下正常的情况。

下面是正规规范编写的C代码:

#include<stdio.h>
void why_here(void) /*这个函数没有任何地方调用过*/
{
  printf("why u here ?!\n");
  _exit(0);
}
int main(int argc,char * argv[])
{
  int buff[1];
  buff[0]=(int)why_here;(因为buff只申请了一个int型,也就是4字节的内存空间,所以只能只能保存一个32位的函数RVA地址)
  return 0;
}

对应的汇编

6:    int main(int argc,char * argv[])
7:    {
00401070   push        ebp
00401071   mov         ebp,esp
00401073   sub         esp,44h
00401076   push        ebx
00401077   push        esi
00401078   push        edi
00401079   lea         edi,[ebp-44h]
0040107C   mov         ecx,11h
00401081   mov         eax,0CCCCCCCCh
00401086   rep stos    dword ptr [edi]
8:        int buff[1];
9:        buff[0]=(int)why_here;
00401088   mov         dword ptr [ebp-4],offset @ILT+5(why_here) (0040100a)
10:       return 0;
0040108F   xor         eax,eax
11:   }
00401091   pop         edi
00401092   pop         esi
00401093   pop         ebx
00401094   mov         esp,ebp
00401096   pop         ebp
00401097   ret

  我看到这里的时候就恍然大悟了,嗖嘎斯内。因为溢出赋值覆盖了EIP(也就是指令寄存器,导致了函数返回后,程序的执行流被改变了)。

这里我们详细讨论一下:

进入main 函数后的栈内容下:
[ eip ][ ebp ][ buff[0] ]
高地址<---- 低地址

  因为在栈空间中,栈是向下生长的,而赋值是向上生长的。所以正常情况下[ebp-4]的赋值是正常的系统允许的,因为这个空间本来就系统为你分配好了。但是如果我们越界赋值[ebp+4]就会怎么?

  对!导致了EIP被覆盖,即发生了缓冲区溢出,可能有朋友要问了,你这么说有什么依据呢?我们还是回到汇编代码:

00401070   push        ebp
00401071   mov         ebp,esp
00401073   sub         esp,44h
00401076   push        ebx
00401077   push        esi
00401078   push        edi

在开头的4个push入栈操作对应结尾了4个pop出栈操作。

00401091   pop         edi
00401092   pop         esi
00401093   pop         ebx
00401094   mov         esp,ebp
00401096   pop         ebp

00401097   ret

EBP被完好的保存了,注意到这里有个汇编指令ret,这是返回的意思,它的原理实质上等于2条汇编指令的组合:

pop EIP

jmp

将栈上的最底下的4字节空间弹栈赋值给EIP,并跳转到EIP对应的那条指令执行。而这个所谓的栈底的4个空间的内容就是在进入main函数前入栈的,目的是为了等main函数执行完后可以继续执行main之后的指令,这也就是一般的函数调用的原理(因为这里只有一个main函数,所有有些特殊,不过本质上一样的)。

而所谓的溢出本质上是我们覆盖了这段EIP对应的栈空间,然后操作系统就傻乎乎的以为这就是真实的返回地址,pop并执行了,然后就.........所以说童话里都是骗人的

这个程序很简单,但是第一次分析的时候也花了我不少力气,以后希望能更多的研究一些原理性的东西,也继续和大家分享!

最后,希望这次的ISCC能玩的开心

  

转载于:https://www.cnblogs.com/LittleHann/archive/2013/05/26/3100165.html

一个简单的缓冲区溢出的思考相关推荐

  1. linux畸形文件夹,Linux下简单的缓冲区溢出

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? 科班出身,或者搞过汇编C等的应该知道,当缓冲区边界限制不严格时,由于变量传入畸形数据或进程运行错误,导致缓冲区被&quo ...

  2. 罗生门:一个简单查询实现引发的思考

    站在不同的角度看待同一个问题,会得出不同的结论.对于程序的实现也一样. 一.查询功能实现的罗生门 不久前公司遇到一个场景,也是微服务状态下肯定会遇到的一个场景(以下对比真实情况有所抽象和改编): 某一 ...

  3. 一个简单CI/CD流程的思考

    因为公司有两地研发团队,在统一CI/CD上难度不亚于两家公司合并后的新流程建立,并非不可攻克,简单描述下心得. 首先,代码管理使用gerrit -> 因其强大的 codereview 功能被选中 ...

  4. php crypt函数缓冲区溢出漏洞,简单缓冲区溢出漏洞攻击实验

    缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况.这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段.这一漏洞的出现是由于数据缓冲器和返回地址的暂时关闭,溢出会引起返回 ...

  5. C语言实现缓冲区溢出实例

    参考书目:0day安全:软件漏洞分析技术  相关工具使用:OD,IDA Pro,VC++6.0,UltraEdit 最近需要做课堂演习,就选了缓冲区溢出的实践.主要参考0day安全这本书,一面一句话很 ...

  6. 软件安全实验——lab7(缓冲区溢出3:返回导向编程技术ROP)

    目录标题 1.举例详细解释什么是Return-orientd Programming ROP?(至少两个例子:x86 和arm) (1)ROP在X86指令集上的实例 (2)ROP在ARM上的可行性 2 ...

  7. java 缓冲区溢出_浅析缓冲区溢出

    最近一直在学习缓冲区溢出漏洞的攻击,但是关于这一块的内容还是需要很多相关知识的基础,例如编程语言及反汇编工具使用.所以研究透彻还需要不少的时间,这里简单的做一个学习的总结,通过具体的实验案例对缓冲区溢 ...

  8. 使用Linux进行缓冲区溢出实验的配置记录

    在基础的软件安全实验中,缓冲区溢出是一个基础而又经典的问题.最基本的缓冲区溢出即通过合理的构造输入数据,使得输入数据量超过原始缓冲区的大小,从而覆盖数据输入缓冲区之外的数据,达到诸如修改函数返回地址等 ...

  9. 安全编程: 防止缓冲区溢出

    防止如今最常见的程序缺陷 本文讨论 Linux/UNIX 系统中最常见的缺陷:缓冲区溢出.本文首先解释什么是缓冲区溢出,以及它们为何如此常见和如此危险.然后讨论广泛用于解决缓冲区溢出的新 Linux ...

最新文章

  1. Spring Boot 学习(1)
  2. 文档信息的向量化-NNLM模型和word2vec
  3. rocketmq python 一个进程订阅多个topic_玩转不同业务场景,这些RabbitMQ特性会是得力助攻...
  4. Jquery中使用Validate插件使表单验证更加简单
  5. ytu 2335: 0-1背包问题
  6. 滑动listview隐藏和显示顶部布局
  7. 【Codeforces - 977F】Consecutive Subsequence(STLmap,输出路径,dp)
  8. Android Butterknife
  9. 代码整洁之道-程序员的职业素养
  10. 如何有效预防宕机?你需要掌握这4个方法
  11. 使用gsds绘制基因结构图_使用 GSDS 绘制基因结构图
  12. SPI FLASH 波形测量演示实例
  13. 微信小程序:修改单选radio大小样式
  14. 淘宝CDN架构全解析
  15. 路由器和交换机的工作原理---笔面试
  16. [置顶]《游戏引擎架构》信息总汇
  17. 大厂可能真不像你想象的那样系列之阿里
  18. 3322linux自动更新ip,ubuntu开机自动运行动态域名更新
  19. 从零开始使用 Mac 电脑之入门篇
  20. 第三组 Alpha(3/3)

热门文章

  1. html 右边框变短,HTML / CSS:使边框右侧高度动态化
  2. html5游戏 虚拟主机,基于HTML5的云虚拟主机配置界面
  3. python内置模块重要程度排名_论Python常见的内置模块
  4. return与exit()
  5. 进程创建函数fork()和vfork()
  6. 数字图像处理:(4)二阶微分在数字图像处理中的应用
  7. Yolo模型部署的两种方法
  8. vue element upload 控件用form-data上传方式导入xls文件
  9. 【全网之最】全网最短代码——给名字、电话、账号、昵称加星号*添加隐私保护、身份证号加密、信息脱敏
  10. 3dmax Vray建筑可视化入门学习教程