04_CC++漏洞_栈溢出利用_第1页
04_CC++漏洞_栈溢出利用_第2页
04_CC++漏洞_栈溢出利用_第3页
04_CC++漏洞_栈溢出利用_第4页
04_CC++漏洞_栈溢出利用_第5页
已阅读5页,还剩45页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

安全性编程方法,天津农学院计算机科学与信息工程系软件工程教研室许晓华xuxiaohua,第4讲栈溢出利用,4.1系统栈的工作原理,栈与系统栈的区别:栈:数据结构角度的栈,是一种先进后出的数据表.常见操作PUSH进栈POP出栈栈的属性TOP栈顶BASE栈底系统栈:内存的栈区,透明性概念,对于C语言这样的高级语言,系统栈的PUSH,POP等堆栈平衡细节是透明的。一般说来,只有在使用汇编语言开发程序的时候,才需要和它直接打交道。计算机中透明性概念:计算机中存在,但对于开发人员不需要了解的东西.,高级语言写出的程序经过编译链接,最终会变成PE文件。当PE文件被装载运行后,就成了所谓的进程。如下图所示:,系统栈与函数调用,#includestdafx.hintfunc_B(intarg_B1,intarg_B2)intvar_B1,var_B2;var_B1=arg_B1+arg_B2;var_B2=arg_B1-arg_B2;returnvar_B1*var_B2;intfunc_A(intarg_A1,intarg_A2)intvar_A;var_A=func_B(arg_A1,arg_A2)+arg_A1;returnvar_A;intmain(intargc,char*argv,char*envp)intvar_main;var_main=func_A(4,3);printf(var_mainis%dn,var_main);returnvar_main;,栈帧,当函数被调用时,系统栈会为这个函数开辟一个新的栈帧,并把它压入栈中。当函数返回时,系统栈会弹出该函数所对应的栈帧。,调用时栈中的操作,在main函数调用func_A的时候,首先在自己的栈帧中压入函数返回地址,然后为func_A创建新栈帧并压入系统栈在func_A调用func_B的时候,同样先在自己的栈帧中压入函数返回地址,然后为func_B创建新栈帧并压入系统栈在func_B返回时,func_B的栈帧被弹出系统栈,func_A栈帧中的返回地址被“露”在栈顶,此时处理器按照这个返回地址重新跳到func_A代码区中执行在func_A返回时,func_A的栈帧被弹出系统栈,main函数栈帧中的返回地址被“露”在栈顶,此时处理器按照这个返回地址跳到main函数代码区中执行,寄存器与函数栈帧,WIN32系统提供两个特殊的寄存器用于标识位于系统栈栈顶的栈帧:ESP:(extendedstackpointer)栈指针寄存器,指向系统栈最上面一个栈帧的栈顶EBP:(extendedbasepointer)基址指针寄存器,指向系统栈最上面一个栈帧的底部函数栈帧:ESP和EBP之间的内存空间为当前栈帧。另一个重要的寄存器EIP:(extendedinstructionpointer)指令寄存器,指向下一条待执行的指令地址,函数调用约定与相关指令,VisualC+默认的参数入栈顺序是:右-左,函数调用的步骤,参数入栈:将参数从右向左依次压入系统栈中返回地址入栈:将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行代码区跳转:处理器从当前代码区跳转到被调用函数的入口处栈帧调整:具体包括:保存当前栈帧状态值,以备后面恢复本栈帧时使用(EBP入栈)将当前栈帧切换到新栈帧(将ESP值装入EBP,更新栈帧底部)给新栈帧分配空间(把ESP减去所需空间的大小,抬高栈顶),函数调用时的指令序列,;调用前push参数3/假设函数有3个参数,将从右向左依次入栈push参数2push参数1call函数地址/call指令将同时完成两项工作:a)向栈中压入当前指令在内存中的位置,即保存返回地址;b)跳转到所调用函数的入口地址函数入口处pushebp;保存旧栈帧的底部movebp,esp;设置新栈帧的底部(栈帧切换)subesp,xxx;设置新栈帧的顶部(抬高栈顶,为新栈帧开辟空间),例题,#includestdafx.hvoidfun(inta,intb)intc=a+b;voidmain()fun(1,2);,反汇编,VC+里如何反汇编?1.先在程序中设置断点(F9)2.调试(F5)3.用以下三种方法进入反汇编右键gotodisassemblyAlt+8Windows菜单下选择“disassembly”,函数返回的步骤,保存返回值:通常将函数的返回值保存在寄存器EAX中弹出当前栈帧,恢复上一个栈帧,具体包括:在堆栈平衡的基础上,给ESP加上栈帧的大小,降低栈顶,回收当前栈帧的空间将当前栈帧底部保存的前栈帧EBP值弹入EBP寄存器,恢复出上一个栈帧将函数返回地址弹给EIP寄存器跳转:按照函数返回地址跳回母函数中继续执行,函数返回时的指令序列,addxxx,esp/降低栈顶,回收当前的栈帧popebp/将上一个栈帧底部位置恢复到ebp,retn/这条指令有两个功能:a)弹出当前栈顶元素,即弹出栈帧中的返回地址。至此,栈帧恢复工作完成。b)让处理器跳转到弹出的返回地址,恢复调用前的代码区,4.2修改邻接变量,#includestdafx.h#includestring.h#includestdio.h#definePASSWORD1234567intverify_password(char*password)intauthenticated;charbuffer8;authenticated=strcmp(password,PASSWORD);strcpy(buffer,password);/在此处溢出!returnauthenticated;,main()intvalid_flag=0;charpassword1024;while(1)printf(请输入密码:);scanf(%s,password);valid_flag=verify_password(password);if(valid_flag)printf(密码不正确!nn);elseprintf(密码验证通过n);break;,运行,密码输入qqqqqqq(7个q),不通过输入qqqqqqqq(8个q),居然通过了密码验证为什么?,先来了解一下大端机与小端机的概念。,大端机与小端机,大端机(bigendian)低地址存放最高有效位(MSB:MostSignificantBit)例如:JavaVirtualMachine小端机(littleendian)低地址存放最低有效位(LSB:LeastSignificantBit)例如:Intelx86,栈帧布局图,形参:password,返回地址,前栈帧EBP,authenticated(0 x00000001),buffer47(ASCII:qqqnull),buffer03(ASCII:qqqq),再来看一下,函数调用时的栈帧布局图,栈从高到低扩展数组,按人类的书写习惯,低位在前,高位在后整数,按人类的书写习惯,高位在前,低位在后,当输入7个q,7个q大于1234567,所以authenticated的值为0 x00000001,不能通过验证。当输入8个q,“qqqqqqqq”大于1234567,authenticated的值还是0 x00000001,但“qqqqqqqq”后面的字符串截断符0 x00会溢出。这溢出数组的一个字节0 x00恰好把authenticated变量改为0 x00000000。函数返回时,main函数一看authenticated是0,就会通过密码验证.,为什么01234567不行?因为字符串大小的比较是按字典序来的,所以这个串小于“1234567”authenticated的值是-1,在内存里将按照补码存负数,所以实际存的是0 xffffffff。那么字符串截断符0 x00淹没后,变成0 xffffff00,还是非0,所以没有进入正确分支。,4.3修改函数返回地址,如果在刚才溢出的基础上,多溢出几个字节,将下面的返回地址覆盖。就可以做到劫持进程。要将返回地址覆盖为什么地址呢?当然是通过验证的地址了,见下图。,然后输入密码.出于字节对齐、容易辨认的目的,将“4321”作为一个输入单元。buffer8共需要2个这样的单元第3个输入单元将authenticated覆盖第4个输入单元将前栈帧EBP值覆盖第5个输入单元将返回地址覆盖由于地址00401122无法在控制台输入,所以,我们改造一下代码,将密码获取方式改为从.txt文件中读取。,从.txt文件中读取密码,#includestdafx.h#includestring.h#include#definePASSWORD1234567intverify_password(char*password)intauthenticated;charbuffer8;authenticated=strcmp(password,PASSWORD);strcpy(buffer,password);/overflowedhere!returnauthenticated;,main()intvalid_flag=0;charpassword1024;FILE*fp;if(!(fp=fopen(password.txt,rw+)exit(0);fscanf(fp,%s,password);valid_flag=verify_password(password);if(valid_flag)printf(incorrectpassword!n);elseprintf(Congratulations!Youhavepassedtheverification!n);fclose(fp);,将上述代码,编译链接生成PE文件在PE文件同目录的位置创建一个password.txt文件。内容为43214321432143214321用UltraEdit打开该文件,点击“切换十六进制模式”,将最后一个4321,改成22114011,将PE文件拖入OD,F9运行,4.4代码植入,#include#include#definePASSWORD1234567intverify_password(char*password)intauthenticated;charbuffer44;authenticated=strcmp(password,PASSWORD);strcpy(buffer,password);/overflowedhere!returnauthenticated;,main()intvalid_flag=0;charpassword1024;FILE*fp;LoadLibrary(user32.dll);/prepareformessageboxif(!(fp=fopen(password.txt,rw+)exit(0);fscanf(fp,%s,password);valid_flag=verify_password(password);if(valid_flag)printf(incorrectpassword!n);elseprintf(Congratulation!Youhavepassedtheverification!n);fclose(fp);,步骤,1.分析并调试漏洞程序,获得淹没返回地址的偏移在password.txt的第几个字节填伪造的返回地址2.获得buffer的起始地址,并将其写入password.txt的相应偏移处,用来冲刷返回地址填什么值3.向password.txt中写入可执行的机器代码,用来调用API弹出一个消息框编写能够成功运行的机器代码(二进制级别的)这三个步骤也是漏洞利用过程中最基本的三个问题淹到哪里淹成什么开发shellcode,淹到哪里,password.txt文件中的第53-56个字符.,淹成什么,buffer的起始地址.,开发shellcode,给password.txt中植入机器代码装载动态链接库user32.dll。(MessageBoxA是动态链接库user32.dll的导出函数)求MessageBoxA函数的入口地址在调用前需要向栈中按从右向左的顺序压入MessageBoxA的四个参数,求MessageBoxA函数的入口地址,入口地址=基址+偏移地址user32.dll在系统中加载的基址MessageBoxA在库中的偏移地址,使用VC6.0自带的小工具“DependencyWalker”,运行Depends后,随便拖一个有图形界面的PE文件进去,就可以看到它所使用的库文件了。在左栏中找到并选中user32.dll后,右栏中会列出这个库文件的所有导出函数及偏移地址;下栏中则列出了PE文件用到的所有的库的基地址。,要植

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论