这是对shellcode学习的一个初步实践,采用经典的winexec shellcode函数来做实践,采用的工具是VC6.0作为获取工具。其过程如下:
1.书写正常的执行代码:
<span style="font-family: Arial, Helvetica, sans-serif;">#include "stdafx.h"</span>
#include <windows.h>
int main(int argc, char* argv[])
{
char buff[9];
buff[0]='c';
buff[1]='a';
buff[2]='l';
buff[3]='c';
buff[4]='.';
buff[5]='e';
buff[6]='x';
buff[7]='e';
buff[8]='\0';
WinExec(buff,SW_HIDE);return 0;
}
2.对程序进行编译后,F10来debug一下,然后按Alt+8查看汇编代码,该程序的汇编代码如下:
55 push ebp
8B EC mov ebp,esp
83 EC 4C sub esp,4Ch
53 push ebx
56 push esi
57 push edi
8D 7D B4 lea edi,[ebp-4Ch]
B9 13 00 00 00 mov ecx,13h
B8 CC CC CC CC mov eax,0CCCCCCCCh
F3 AB rep stos dword ptr [edi]
19: char buff[9];
20: buff[0]='c';
C6 45 F4 63 mov byte ptr [ebp-0Ch],63h
21: buff[1]='a';
C6 45 F5 61 mov byte ptr [ebp-0Bh],61h
22: buff[2]='l';
C6 45 F6 6C mov byte ptr [ebp-0Ah],6Ch
23: buff[3]='c';
C6 45 F7 63 mov byte ptr [ebp-9],63h
24: buff[4]='.';
C6 45 F8 2E mov byte ptr [ebp-8],2Eh
25: buff[5]='e';
C6 45 F9 65 mov byte ptr [ebp-7],65h
26: buff[6]='x';
C6 45 FA 78 mov byte ptr [ebp-6],78h
27: buff[7]='e';
C6 45 FB 65 mov byte ptr [ebp-5],65h
28: buff[8]='\0';
C6 45 FC 00 mov byte ptr [ebp-4],0
29: WinExec(buff,SW_HIDE);
8B F4 mov esi,esp
6A 00 push 0
8D 45 F4 lea eax,[ebp-0Ch]
50 push eax
FF 15 14 A2 42 00 call dword ptr [KERNEL32_NULL_THUNK_DATA (0042a214)]
3B F4 cmp esi,esp
E8 2F 00 00 00 call __chkesp (00401090)
其中
0042a214
是 WinExec的执行地址的存放地址,到0x0042a214下去查看函数地址:

可以看出,该函数地址为0x7605f22e,然后对汇编代码进行整理后获得的汇编程序如下:
</pre><pre code_snippet_id="394634" snippet_file_name="blog_20140616_7_9412625" name="code" class="cpp">#include "stdafx.h"
#include <windows.h>
int main(int argc, char* argv[]){
__asm{
//保存现场
push ebp
mov ebp,esp
push ebx
push esi
push edi
//设置第一个参数
mov byte ptr [ebp-0Ch],63h
mov byte ptr [ebp-0Bh],61h
mov byte ptr [ebp-0Ah],6Ch
mov byte ptr [ebp-9],63h
mov byte ptr [ebp-8],2Eh
mov byte ptr [ebp-7],65h
mov byte ptr [ebp-6],78h
mov byte ptr [ebp-5],65h
mov byte ptr [ebp-4],0
//设置第二个参数
push 3
//压栈
lea eax,[ebp-0Ch]
push eax
//call winexec
mov eax,0x7605F22E
call eax
//恢复现场
mov esp,ebp
pop ebp
}
}
3.保存后对其进行编译并debug,同样按Alt+8来查看:
__asm{
<span style="color:#ff0000;">55 </span>push ebp
<span style="color:#ff0000;">8B EC</span> mov ebp,esp
<span style="color:#ff0000;">53 </span> push ebx
<span style="color:#ff0000;">56 </span> push esi
<span style="color:#ff0000;">57</span> push edi
<span style="color:#ff0000;">C6 45 F4 63</span> mov byte ptr [ebp-0Ch],63h
<span style="color:#ff0000;">C6 45 F5 61</span> mov byte ptr [ebp-0Bh],61h
<span style="color:#ff0000;">C6 45 F6 6C</span> mov byte ptr [ebp-0Ah],6Ch
<span style="color:#ff0000;">C6 45 F7 63</span> mov byte ptr [ebp-9],63h
<span style="color:#ff0000;">C6 45 F8 2E </span> mov byte ptr [ebp-8],2Eh
<span style="color:#ff0000;">C6 45 F9 65</span> mov byte ptr [ebp-7],65h
<span style="color:#ff0000;">C6 45 FA 78 </span> mov byte ptr [ebp-6],78h
<span style="color:#ff0000;">C6 45 FB 65</span> mov byte ptr [ebp-5],65h
<span style="color:#ff0000;">C6 45 FC 00</span> mov byte ptr [ebp-4],0
<span style="color:#ff0000;">6A 03</span> push 3
<span style="color:#ff0000;">8D 45 F4 </span> lea eax,[ebp-0Ch]
<span style="color:#ff0000;">50 </span> push eax
<span style="color:#ff0000;">B8 2E F2 05 76</span> mov eax,7605F22Eh
<span style="color:#ff0000;">FF D0</span> call eax
<span style="color:#ff0000;">8B E5 </span> mov esp,ebp
<span style="color:#ff0000;">5D</span> pop ebp
}
头部红色的部分就是要获取的shellcode,在内存中的情况如下:
提取后的shellcode为:
char buff[] =
"\x55\x8B\xEC\x53\x56\x57\xC6"
"\x45\xF4\x63\xC6\x45\xF5\x61"
"\xC6\x45\xF6\x6C\xC6\x45\xF7"
"\x63\xC6\x45\xF8\x2E\xC6\x45"
"\xF9\x65\xC6\x45\xFA\x78\xC6"
"\x45\xFB\x65\xC6\x45\xFC\x00"
"\x6A\x03\x8D\x45\xF4\x50\xB8"
"\x2E\xF2\x05\x76\xFF\xD0\x8B"
"\xE5\x5D";
int main(){
__asm{
lea eax,buff;
call eax;
}
return 0;
}
最后成功弹出了calc.exe
当然,查询函数入口地址时,也可以用depends对编译好的程序进行查看,查看方式是:
dll的基地址(base)+函数偏移(entry point)地址
如Winexec的入口地址为,kernal.dll的地址+winexec的偏移地址就是函数的入口地址。