作 者: moonflow
百年一遇的高级光棍节,我得写篇详细完整的文章才行。初学者,菜文,老鸟请飘过
分析段简单的shellcode代码
1.shellcode.c
代码:
#include <stdio.h>
static char shellcode[]=
"\xeb\x17\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89"
"\xf3\x8d\x4e\x08\x31\xd2\xcd\x80\xe8\xe4\xff\xff\xff\x2f\x62\x69\x6e"
"\x2f\x73\x68\x58";
int main() {
(*(void(*)())shellcode)();
return 0;
}
gcc -fno-stack-protector -z execstack -g -o shell shell.c //记住一定要加-fno-stack-protector -z execstack
2.shellcode动态调试
记住编译的时候一定要加-fno-stack-protector -z execstack
gdb动态调试shellcode(凑合看,文档里会清晰点。)
代码:
[root@localhost ceshi]# gdb shellcode
GNU gdb Fedora (6.8-27.el5)
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...
(gdb) l
1 #include <stdio.h>
2
3 static char shellcode[]=
4 "\xeb\x17\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89"
5 "\xf3\x8d\x4e\x08\x31\xd2\xcd\x80\xe8\xe4\xff\xff\xff\x2f\x62\x69\x6e"
6 "\x2f\x73\x68\x58";
7
8 int main() {
9 (*(void(*)())shellcode)();
10 return 0;
(gdb)
加断点在shellcode部分,并使用汇编方式来调试,即display /i $pc
代码:
(gdb) b 9
Breakpoint 1 at 0x8048365: file shellcode.c, line 9.
(gdb) display /i $pc
(gdb) run
Starting program: /root/ceshi/shellcode
Breakpoint 1, main () at shellcode.c:9
9 (*(void(*)())shellcode)();
1: x/i $pc
0x8048365 <main+17>: mov $0x8049580,%eax
(gdb)
单步调试si,使用x/8xb 134518174观察esi中的字符串,可以发现这串ascii码这个时候是/bin/sh,但是末尾58
代码:
0x08049582 in shellcode ()
1: x/i $pc
0x8049582 <shellcode+2>: pop %esi
(gdb)
0x08049583 in shellcode ()
1: x/i $pc
0x8049583 <shellcode+3>: mov %esi,0x8(%esi)
(gdb) i r esi
esi 0x804959e 134518174
(gdb) x/8xb 134518174
0x804959e <shellcode+30>: 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 0x58
(gdb)
继续单步,,跟踪到mov %al,0x7(%esi)过后,会发现末尾的58变成了00,这个时候就是完整的"/bin/sh"了。
代码:
0x8049586 <shellcode+6>: xor %eax,%eax
(gdb)
0x08049588 in shellcode ()
1: x/i $pc
0x8049588 <shellcode+8>: mov %al,0x7(%esi)
(gdb)
0x0804958b in shellcode ()
1: x/i $pc
0x804958b <shellcode+11>: mov %eax,0xc(%esi)
(gdb) i r esi
esi 0x804959e 134518174
(gdb) x/8xb 134518174
0x804959e <shellcode+30>: 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 0x00
继续si,会发现mov $0xb,%al,将0xb(11)赋给eax , 这是execve()在系统中的索引号.然后调用80号中断
代码:
(gdb) si
0x0804958e in shellcode ()
1: x/i $pc
0x804958e <shellcode+14>: mov $0xb,%al
(gdb)
0x08049590 in shellcode ()
1: x/i $pc
0x8049590 <shellcode+16>: mov %esi,%ebx
(gdb)
0x08049592 in shellcode ()
1: x/i $pc
0x8049592 <shellcode+18>: lea 0x8(%esi),%ecx
(gdb)
0x08049595 in shellcode ()
1: x/i $pc
0x8049595 <shellcode+21>: xor %edx,%edx
(gdb)
0x08049597 in shellcode ()
1: x/i $pc
0x8049597 <shellcode+23>: int $0x80
通过这段简单地跟踪可以了解到这段简单的shellcode的用处。即打开一个shell窗口。
3.详细汇编解释
这里采用nasm来汇编一下,即ndisasm -u,使用的intel风格的汇编,效果都一样,添加了详细注释。
代码:
echo -ne
"\xeb\x17\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x31\xd2\xcd\x80
\xe8\xe4\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x58" | ndisasm -u -
代码:
00000000 EB17 jmp short 0x19 //注意17+2等于19,这是相对偏移,此地方跳转到call dword 0x2
00000002 5E pop esi //弹出到esi寄存器中,即是call dword 0x2后面的返回地址0000001E,也就是存储着/bin/sh
00000003 897608 mov [esi+0x8],esi
00000006 31C0 xor eax,eax
00000008 884607 mov [esi+0x7],al //使58为00,这样就是完整的字符串"/bin/sh"
0000000B 89460C mov [esi+0xc],eax
0000000E B00B mov al,0xb //将0xb(11)赋给eax , 这是execve()在系统中的索引号.
00000010 89F3 mov ebx,esi //赋值esi字符串
00000012 8D4E08 lea ecx,[esi+0x8]
00000015 31D2 xor edx,edx
00000017 CD80 int 0x80
00000019 E8E4FFFFFF call dword 0x2 //这里跳转到pop esi执行,但是call的时候会压入返回地址,即0000001E
0000001E 2F das
0000001F 62696E bound ebp,[ecx+0x6e]
00000022 2F das
00000023 7368 jnc 0x8d
00000025 58 pop eax
末尾从2F开始,这里面2F 62 69 6E 2F 73 68 对应着/ b i n / sh,应该末尾还有个00,但是这边放的是58,不是00,那要怎么进行呢?shellcode里不能直接放0x00,通过动态调试可以发现这里就是通过mov [esi+0x7],al这条指令来是58置为00的。这样子/bin/sh就完整了。
分析结束,初次分析shellcode,很多不会,需要进一步学习。
好好学习,天天向上。