1.8 運(yùn)用C編寫ShellCode代碼
在筆者前幾篇文章中,我們使用匯編語(yǔ)言并通過(guò)自定位的方法實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的MessageBox
彈窗功能,但由于匯編語(yǔ)言過(guò)于繁瑣在編寫效率上不僅要考驗(yàn)開發(fā)者的底層功底,還需要寫出更多的指令集,這對(duì)于普通人來(lái)說(shuō)是非常困難的,當(dāng)然除了通過(guò)匯編來(lái)實(shí)現(xiàn)ShellCode
的編寫以外,使用C同樣可以實(shí)現(xiàn)編寫,在多數(shù)情況下讀者可以直接使用C開發(fā),只有某些環(huán)境下對(duì)ShellCode條件有極為苛刻的長(zhǎng)度限制時(shí)才會(huì)考慮使用匯編。
相較于匯編語(yǔ)言,使用C編寫Shellcode
可以更加方便、高效,特別是對(duì)于需要大量計(jì)算的操作。在編寫Shellcode時(shí),讀者需要注意以下幾點(diǎn):
??1.使用純C語(yǔ)言進(jìn)行編寫:在編寫Shellcode時(shí),需要避免使用C++標(biāo)準(zhǔn)庫(kù)或其他外部依賴庫(kù),因?yàn)檫@些庫(kù)往往會(huì)增加代碼的長(zhǎng)度和復(fù)雜度。
??2.關(guān)閉編譯器優(yōu)化:在編寫Shellcode時(shí),需要關(guān)閉編譯器的優(yōu)化功能,因?yàn)閮?yōu)化可能會(huì)改變代碼的執(zhí)行順序,導(dǎo)致Shellcode無(wú)法正常工作。
??3.避免使用全局變量和靜態(tài)變量:在Shellcode中,全局變量和靜態(tài)變量往往會(huì)導(dǎo)致代碼長(zhǎng)度過(guò)長(zhǎng),并且這些變量的地址也可能與Shellcode中其他代碼的地址產(chǎn)生沖突。
??4.使用裸指針和裸內(nèi)存管理:為了減小Shellcode的長(zhǎng)度和復(fù)雜度,需要使用裸指針和裸內(nèi)存管理,這可以減少代碼中不必要的輔助函數(shù)調(diào)用。
??5.不能使用全局變量,或者用static修飾的變量,在Shellcode中要自定義入口函數(shù),所有的字符串都要用字符串?dāng)?shù)組的方式代替。
首先讀者應(yīng)自行新建一個(gè)開發(fā)項(xiàng)目,并將編譯模式調(diào)整為Release
模式,這是因?yàn)?code>Debug模式下的代碼在轉(zhuǎn)換成匯編后首先都是一個(gè)JMP
指令,然后再跳到我們的功能代碼處,但JMP
指令是地址相關(guān)的 ,所以在轉(zhuǎn)換成ShellCode
時(shí)就會(huì)出錯(cuò)。此外在讀者新建項(xiàng)目文件時(shí)請(qǐng)最好使用*.c
結(jié)尾而不要使用*.cpp
結(jié)尾。
當(dāng)讀者新建文件以后,接下來(lái)請(qǐng)修改配置屬性,將運(yùn)行庫(kù)修改為多線程(MT)
并關(guān)閉安全檢查機(jī)制,如下圖所示;

接著在連接器部分,新增一個(gè)EntryMain
入口點(diǎn),默認(rèn)的Main
入口點(diǎn)顯然時(shí)不能使用的,如下圖所示;

與前幾章中的內(nèi)容原理一致,首先我們需要得到kernel32.dll
模塊的基址,這段代碼我們依然采用匯編實(shí)現(xiàn),這里需要注意__declspec(naked)
的含義,該聲明是微軟編譯器提供的一個(gè)擴(kuò)展,它用于指示編譯器不要為函數(shù)自動(dòng)生成函數(shù)頭和尾,并將函數(shù)轉(zhuǎn)化為裸函數(shù)。這種函數(shù)不會(huì)自動(dòng)生成函數(shù)前綴和后綴的代碼,也不會(huì)創(chuàng)建任何本地變量或保護(hù)寄存器。
在使用__declspec(naked)
聲明的函數(shù)中,開發(fā)者需要自己手動(dòng)管理堆棧和調(diào)用函數(shù)的傳遞參數(shù),然后在函數(shù)體中使用匯編指令實(shí)現(xiàn)所需的功能。使用__declspec(naked)
聲明的函數(shù)可以有效地減小生成的代碼大小,因?yàn)椴恍枰诤瘮?shù)前后添加額外的代碼,而且可以精確控制函數(shù)內(nèi)部的代碼。
注意:使用
__declspec(naked)
聲明的函數(shù)需要開發(fā)者對(duì)匯編語(yǔ)言有一定的了解,否則容易出現(xiàn)錯(cuò)誤。在使用時(shí),需要非常小心,確保在函數(shù)內(nèi)部正確地管理堆棧和傳遞參數(shù),以確保函數(shù)能夠正常工作。
//?----------------------------------------------
//?32位獲取模塊基址
//?----------------------------------------------
__declspec(naked)?DWORD?getKernel32()
{
????__asm
????{
????????mov?eax,?fs:?[30h]
????????mov?eax,?[eax?+?0ch]
????????mov?eax,?[eax?+?14h]
????????mov?eax,?[eax]
????????mov?eax,?[eax]
????????mov?eax,?[eax?+?10h]
????????ret
????}
}
//?----------------------------------------------
//?64位獲取模塊基址
//?----------------------------------------------
/*
.code
getKernel32?proc
????mov?rax,gs:[60h]
????mov?rax,[rax+18h]
????mov?rax,[rax+30h]
????mov?rax,[rax]
????mov?rax,[rax]
????mov?rax,[rax+10h]
????ret
getKernel32?endp
end
*/
當(dāng)我們能夠拿到kernel32.dll
的模塊基址時(shí),則接下來(lái)就是通過(guò)該基址得到Kernel32的模塊導(dǎo)出表,并獲取該導(dǎo)出表內(nèi)的GetProcessAddress
函數(shù)的基址,至于為什么需要這么做,在讀者前面的文章中有詳細(xì)的分析,這里就不再重復(fù)敘述。
//?----------------------------------------------
//?32位取函數(shù)地址
//?----------------------------------------------
FARPROC?getProcAddress(HMODULE?hModuleBase)
{
????PIMAGE_DOS_HEADER?lpDosHeader?=?(PIMAGE_DOS_HEADER)hModuleBase;
????PIMAGE_NT_HEADERS32?lpNtHeader?=?(PIMAGE_NT_HEADERS)((DWORD)hModuleBase?+?lpDosHeader->e_lfanew);
????if?(!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)
????{
????????return?NULL;
????}
????if?(!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)
????{
????????return?NULL;
????}
????PIMAGE_EXPORT_DIRECTORY?lpExports?=?(PIMAGE_EXPORT_DIRECTORY)((DWORD)hModuleBase?+?(DWORD)lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
????PDWORD?lpdwFunName?=?(PDWORD)((DWORD)hModuleBase?+?(DWORD)lpExports->AddressOfNames);
????PWORD?lpword?=?(PWORD)((DWORD)hModuleBase?+?(DWORD)lpExports->AddressOfNameOrdinals);
????PDWORD?lpdwFunAddr?=?(PDWORD)((DWORD)hModuleBase?+?(DWORD)lpExports->AddressOfFunctions);
????DWORD?dwLoop?=?0;
????FARPROC?pRet?=?NULL;
????for?(;?dwLoop?<=?lpExports->NumberOfNames?-?1;?dwLoop++)
????{
????????char*?pFunName?=?(char*)(lpdwFunName[dwLoop]?+?(DWORD)hModuleBase);
????????if?(pFunName[0]?==?'G'?&&
????????????pFunName[1]?==?'e'?&&
????????????pFunName[2]?==?'t'?&&
????????????pFunName[3]?==?'P'?&&
????????????pFunName[4]?==?'r'?&&
????????????pFunName[5]?==?'o'?&&
????????????pFunName[6]?==?'c'?&&
????????????pFunName[7]?==?'A'?&&
????????????pFunName[8]?==?'d'?&&
????????????pFunName[9]?==?'d'?&&
????????????pFunName[10]?==?'r'?&&
????????????pFunName[11]?==?'e'?&&
????????????pFunName[12]?==?'s'?&&
????????????pFunName[13]?==?'s')
????????{
????????????pRet?=?(FARPROC)(lpdwFunAddr[lpword[dwLoop]]?+?(DWORD)hModuleBase);
????????????break;
????????}
????}
????return?pRet;
}
//?----------------------------------------------
//?64位取函數(shù)地址
//?----------------------------------------------
/*
FARPROC?getProcAddress(HMODULE?hModuleBase)
{
????PIMAGE_DOS_HEADER?lpDosHeader?=?(PIMAGE_DOS_HEADER)hModuleBase;
????PIMAGE_NT_HEADERS64?lpNtHeader?=?(PIMAGE_NT_HEADERS64)((ULONG64)hModuleBase?+?lpDosHeader->e_lfanew);
????if?(!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)
????{
????????return?NULL;
????}
????if?(!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)
????{
????????return?NULL;
????}
????PIMAGE_EXPORT_DIRECTORY?lpExports?=?(PIMAGE_EXPORT_DIRECTORY)((ULONG64)hModuleBase?+?(ULONG64)lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
????PDWORD?lpdwFunName?=?(PDWORD)((ULONG64)hModuleBase?+?(ULONG64)lpExports->AddressOfNames);
????PWORD?lpword?=?(PWORD)((ULONG64)hModuleBase?+?(ULONG64)lpExports->AddressOfNameOrdinals);
????PDWORD??lpdwFunAddr?=?(PDWORD)((ULONG64)hModuleBase?+?(ULONG64)lpExports->AddressOfFunctions);
????DWORD?dwLoop?=?0;
????FARPROC?pRet?=?NULL;
????for?(;?dwLoop?<=?lpExports->NumberOfNames?-?1;?dwLoop++)
????{
????????char*?pFunName?=?(char*)(lpdwFunName[dwLoop]?+?(ULONG64)hModuleBase);
????????if?(pFunName[0]?==?'G'?&&
????????????pFunName[1]?==?'e'?&&
????????????pFunName[2]?==?'t'?&&
????????????pFunName[3]?==?'P'?&&
????????????pFunName[4]?==?'r'?&&
????????????pFunName[5]?==?'o'?&&
????????????pFunName[6]?==?'c'?&&
????????????pFunName[7]?==?'A'?&&
????????????pFunName[8]?==?'d'?&&
????????????pFunName[9]?==?'d'?&&
????????????pFunName[10]?==?'r'?&&
????????????pFunName[11]?==?'e'?&&
????????????pFunName[12]?==?'s'?&&
????????????pFunName[13]?==?'s')
????????{
????????????pRet?=?(FARPROC)(lpdwFunAddr[lpword[dwLoop]]?+?(ULONG64)hModuleBase);
????????????break;
????????}
????}
????return?pRet;
}
*/
接著我們需要編寫主代碼邏輯,主代碼邏輯中使用GetProcAddress
和LoadLibraryW
來(lái)加載user32.dll
并調(diào)用其中的MessageBoxW
函數(shù)彈出一個(gè)消息框的示例。
下面是代碼的詳細(xì)實(shí)現(xiàn)流程:
??1.定義函數(shù)指針類型FN_GetProcAddress,用于存儲(chǔ)GetProcAddress函數(shù)的地址,該函數(shù)用于在加載的DLL中查找導(dǎo)出函數(shù)的地址。
??2.通過(guò)getProcAddress函數(shù)獲取kernel32.dll中的GetProcAddress函數(shù)地址,并將其轉(zhuǎn)換為FN_GetProcAddress類型的函數(shù)指針fn_GetProcAddress。
??3.定義函數(shù)指針類型FN_LoadLibraryW,用于存儲(chǔ)LoadLibraryW函數(shù)的地址,該函數(shù)用于加載指定的DLL文件。
??4.定義名為xyLoadLibraryW的字符數(shù)組,存儲(chǔ)字符串"LoadLibraryW"。
??5.使用fn_GetProcAddress函數(shù)指針獲取kernel32.dll中的LoadLibraryW函數(shù)的地址,并將其轉(zhuǎn)換為FN_LoadLibraryW類型的函數(shù)指針fn_LoadLibraryW。
??6.定義函數(shù)指針類型FN_MessageBoxW,用于存儲(chǔ)MessageBoxW函數(shù)的地址,該函數(shù)用于彈出消息框。
??7.定義名為xy_MessageBoxW的字符數(shù)組,存儲(chǔ)字符串"MessageBoxW"。
??8.定義名為xy_user32的字符數(shù)組,存儲(chǔ)字符串"user32.dll"。
??9.使用fn_LoadLibraryW函數(shù)指針加載user32.dll,并使用fn_GetProcAddress函數(shù)指針獲取其中的MessageBoxW函數(shù)地址,并將其轉(zhuǎn)換為FN_MessageBoxW類型的函數(shù)指針fn_MessageBoxW。
??10.定義名為MsgBox和Title的wchar_t數(shù)組,用于存儲(chǔ)消息框的文本內(nèi)容和標(biāo)題。
??11.使用fn_MessageBoxW函數(shù)指針彈出一個(gè)消息框,顯示MsgBox中的文本內(nèi)容,并使用Title中的文本作為標(biāo)題。
FARPROC?getProcAddress(HMODULE?hModuleBase);
DWORD?getKernel32();
//?extern?"C"?PVOID64??getKernel32();
//?----------------------------------------------
//?32位主函數(shù)
//?----------------------------------------------
int?EntryMain()
{
????//?定義指針,用于存儲(chǔ)GetProcAddress入口地址
????typedef?FARPROC(WINAPI*?FN_GetProcAddress)(
????????_In_?HMODULE?hModule,
????????_In_?LPCSTR?lpProcName
????????);
????FN_GetProcAddress?fn_GetProcAddress?=?(FN_GetProcAddress)getProcAddress((HMODULE)getKernel32());
????//?定義指針,用于存儲(chǔ)LoadLibraryW入口地址
????typedef?HMODULE(WINAPI*?FN_LoadLibraryW)(
????????_In_?LPCWSTR?lpLibFileName
????????);
????char?xyLoadLibraryW[]?=?{?'L',?'o',?'a',?'d',?'L',?'i',?'b',?'r',?'a',?'r',?'y',?'W',?0?};
????FN_LoadLibraryW?fn_LoadLibraryW?=?(FN_LoadLibraryW)fn_GetProcAddress((HMODULE)getKernel32(),?xyLoadLibraryW);
????//?定義指針,用于存儲(chǔ)MessageBoxW入口地址
????typedef?int?(WINAPI*?FN_MessageBoxW)(
????????_In_opt_?HWND?hWnd,
????????_In_opt_?LPCWSTR?lpText,
????????_In_opt_?LPCWSTR?lpCaption,
????????_In_?UINT?uType);
????wchar_t?xy_user32[]?=?{?'u',?'s',?'e',?'r',?'3',?'2',?'.',?'d',?'l',?'l',?0?};
????char?xy_MessageBoxW[]?=?{?'M',?'e',?'s',?'s',?'a',?'g',?'e',?'B',?'o',?'x',?'W',?0?};
????FN_MessageBoxW?fn_MessageBoxW?=?(FN_MessageBoxW)fn_GetProcAddress(fn_LoadLibraryW(xy_user32),?xy_MessageBoxW);
????//?此處用于設(shè)置MessageBoxW彈窗的文本內(nèi)容
????wchar_t?MsgBox[]?=?{?'H',?'e',?'l',?'l',?'o',?'L',?'y',?'S',?'h','a','r','k',?0?};
????wchar_t?Title[]?=?{?'T',?'E',?'S',?'T',?0?};
????fn_MessageBoxW(NULL,?MsgBox,?Title,?NULL);
????return?0;
}
//?----------------------------------------------
//?64位主函數(shù)
//?----------------------------------------------
/*
int?EntryMain()
{
????typedef?FARPROC(WINAPI*?FN_GetProcAddress)(
????????_In_?HMODULE?hModule,
????????_In_?LPCSTR?lpProcName
????????);
????FN_GetProcAddress?fn_GetProcAddress?=?(FN_GetProcAddress)getProcAddress((HMODULE)getKernel32());
????typedef?HMODULE(WINAPI*?FN_LoadLibraryW)(
????????_In_?LPCWSTR?lpLibFileName
????????);
????char?xyLoadLibraryW[]?=?{?'L',?'o',?'a',?'d',?'L',?'i',?'b',?'r',?'a',?'r',?'y',?'W',?0?};
????FN_LoadLibraryW?fn_LoadLibraryW?=?(FN_LoadLibraryW)fn_GetProcAddress((HMODULE)getKernel32(),?xyLoadLibraryW);
????typedef?int?(WINAPI*?FN_MessageBoxW)(
????????_In_opt_?HWND?hWnd,
????????_In_opt_?LPCWSTR?lpText,
????????_In_opt_?LPCWSTR?lpCaption,
????????_In_?UINT?uType);
????wchar_t?xy_user32[]?=?{?'u',?'s',?'e',?'r',?'3',?'2',?'.',?'d',?'l',?'l',?0?};
????char?xy_MessageBoxW[]?=?{?'M',?'e',?'s',?'s',?'a',?'g',?'e',?'B',?'o',?'x',?'W',?0?};
????FN_MessageBoxW?fn_MessageBoxW?=?(FN_MessageBoxW)fn_GetProcAddress(fn_LoadLibraryW(xy_user32),?xy_MessageBoxW);
????wchar_t?xy_Hello[]?=?{?'H',?'e',?'l',?'l',?'o',?'L',?'y',?'S',?'h',?'a',?'r',?'k',?0?};
????wchar_t?xy_tip[]?=?{?'T',?'E',?'S',?'T',?0?};
????fn_MessageBoxW(NULL,?xy_Hello,?xy_tip,?NULL);
????return?0;
}
*/
至此讀者需要手動(dòng)編譯上述代碼,當(dāng)編譯通過(guò)之后,請(qǐng)打開WinHex
工具,并定位到ShellCode
的開頭位置,如下圖所示則是我們需要提取的指令集;

選中這片區(qū)域,并右鍵點(diǎn)擊編輯按鈕,找到復(fù)制,C源碼格式,此時(shí)讀者即可得到一個(gè)完整的源代碼格式;

至此讀者只需要一個(gè)注入器用于測(cè)試代碼的完善性,此處是簡(jiǎn)單實(shí)現(xiàn)的一個(gè)注入器,代碼中shellcode
是我們上圖中提取出來(lái)的片段,讀者需要修改targetPid
為任意一個(gè)32位應(yīng)用程序,并運(yùn)行注入即可;
using?namespace?std;
unsigned?char?shellcode[450]?=
{
????0x55,?0x8B,?0xEC,?0x83,?0xEC,?0x5C,?0x53,?0x56,?0x57,?0xE8,?0xD2,?0x00,?0x00,?0x00,?0x8B,?0xC8,
????0xE8,?0xEB,?0x00,?0x00,?0x00,?0x8B,?0xF0,?0xC7,?0x45,?0xD8,?0x4C,?0x6F,?0x61,?0x64,?0x8D,?0x45,
????0xD8,?0xC7,?0x45,?0xDC,?0x4C,?0x69,?0x62,?0x72,?0x50,?0xC7,?0x45,?0xE0,?0x61,?0x72,?0x79,?0x57,
????0xC6,?0x45,?0xE4,?0x00,?0xE8,?0xA7,?0x00,?0x00,?0x00,?0x50,?0xFF,?0xD6,?0x33,?0xC9,?0xC7,?0x45,
????0xC0,?0x75,?0x00,?0x73,?0x00,?0x66,?0x89,?0x4D,?0xD4,?0x8D,?0x4D,?0xE8,?0x51,?0x8D,?0x4D,?0xC0,
????0x5D,?0xC3
};
int?main(int?argc,?char?*argv[])
{
????DWORD?targetPid?=?2816;
????HANDLE?h_target?=?NULL;
????LPVOID?p_base?=?NULL;
????HANDLE?h_thread?=?NULL;
????h_target?=?OpenProcess(PROCESS_ALL_ACCESS,?FALSE,?targetPid);
????if?(h_target?==?NULL)
????{
????????goto?main_end;
????}
????p_base?=?VirtualAllocEx(h_target,?NULL,?sizeof(shellcode),?MEM_COMMIT,?PAGE_EXECUTE_READWRITE);
????if?(p_base?==?NULL)
????{
????????goto?main_end;
????}
????if?(!WriteProcessMemory(h_target,?p_base,?(LPVOID)shellcode,?sizeof(shellcode),?NULL))?{
????????goto?main_end;
????}
????h_thread?=?CreateRemoteThread(h_target,?0,?0,?(LPTHREAD_START_ROUTINE)p_base,?NULL,?0,?NULL);
????if?(h_thread?==?NULL)
????{
????????goto?main_end;
????}
main_end:
????if?(h_target)
????????CloseHandle(h_target);
????if?(h_thread)
????????CloseHandle(h_thread);
????getchar();
????return?0;
}
如果一切順利,則讀者可看到這段ShellCode已經(jīng)在特定進(jìn)程內(nèi)實(shí)現(xiàn)運(yùn)行了,并輸出了如下圖所示的彈窗提示;

本文作者: 王瑞 本文鏈接: https://www.lyshark.com/post/9cc4ded5.html 版權(quán)聲明: 本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協(xié)議。轉(zhuǎn)載請(qǐng)注明出處!