Shellcode
- 定義
是一段可注入的指令(opcode),可以在被攻擊的程式內運行,
- 特點
短小精悍,靈活多變,獨立存在,無需任何檔案格式的包裝,因為shellcode直接操作暫存器和函式,所以opcode必須是16進制形式,因此也不能用高級語言撰寫shellcode,在記憶體中運行,無需運行在固定的宿主行程上,
- Shellcode的利用原理
將shellcode注入緩沖區,然后欺騙目標程式執行它,而將shellcode注入緩沖區最常用的方法是利用目標系統上的緩沖區溢位漏洞,
Shellcode生成方法
- 編程語言撰寫:匯編語言,C語言
- shellcode生成器
Shellcode撰寫原則
- 杜絕雙引號字串的直接使用
關閉VS自動優化沒有使用到的變數
自定義函式入口
- 動態獲取函式的地址
GetProAddress 從dll中獲取函式的地址
引數1:呼叫dll的句柄,引數2:函式名
Bug:error C2760: 語法錯誤: 意外的令牌“識別符號”,預期的令牌為“型別說明符”
打開專案工程-> 屬性 -> c/c++ --> 語言 -> 符合模式 修改成否即可 如果這樣設定將無法使用c函式,
這個比較關鍵,否則使用printf就直接崩潰或者是編譯報錯
最佳方案是:修改平臺工具集
- 通過獲得Kernel32基址來獲取GetProcAddres基址
- 避免全域變數的使用
因為vs會將全域變數編譯在其他區段中 結果就是一個絕對的地址不能使用static定義變數(變數放到內部函式使用)
- 確保已加載使用API的元件
撰寫shellcode前的準備
- 修改程式入口點:聯結器-高級,作用:去除自動生成的多余的exe代碼
- 關倍訓沖區安全檢查,屬性->C/C++ ->代碼生成->安全檢查禁用
- 設定工程兼容window XP :代碼生成 ->運行庫 選擇 debug MTD release MT
- 清除資源:聯結器->除錯->清單檔案
- 關閉除錯功能
如下:
屬性->常規->平臺工具集 選擇xp版本

C/C++->代碼生成->運行庫選擇MT
安全檢查禁用

聯結器->高級->入口點修改為EntryMain

函式動態鏈接呼叫
在撰寫shellcode時,所有用到的函式都需要動態呼叫,通過LoadLibrary函式加載元件,GetProAddress獲取元件中函式的地址,所以獲取到GetProAddress和LoadLibrary地址時非常重要的,通過GetProAddress和LoadLibrary,可以獲取到已加載的元件的地址,
動態獲取Kernel32.dll基址和GetProAddress 地址
在正常情況下,我們不知道LoadLibraryA的地址,所以不能直接使用該函式,GetProAddress是元件Kernel32.dll中的函式,每個行程內部加載都會加載Kernel32.dll,獲取到加載Kernel32.dll地址就可以獲取到GetProAddress函式的地址,通過匯編內嵌獲取到Kernel32的基址,獲取到GetProAddress地址后,就可以獲取到所有需要的函式的地址,
pE檔案運行時加載的鏈接庫

代碼如下:
#include <windows.h> #include <stdio.h> //內嵌匯編獲取Kernel32的地址 __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 } } //通過kernel32基址獲取GetProcAddress的地址 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; } int main() { //kernel32.dll 基址的動態獲取 HMODULE hLoadLibrary = LoadLibraryA("kernel32.dll"); //使用內嵌匯編來獲取基址 HMODULE _hLoadLibrary = (HMODULE)getKernel32(); //效果是一樣的 printf("LoadLibraryA動態獲取的地址: 0x%x\n", hLoadLibrary); printf("內嵌匯編獲取的地址: 0x%x\n", _hLoadLibrary); //宣告定義,先轉到到原函式定義,然后重新定義 typedef FARPROC(WINAPI *FN_GetProcAddress)( _In_ HMODULE hModule, _In_ LPCSTR lpProcName ); FN_GetProcAddress fn_GetProcAddress; fn_GetProcAddress = (FN_GetProcAddress)_GetProcAddress(_hLoadLibrary); printf("動態獲取GetProcAddress地址: 0x%x\n",fn_GetProcAddress); printf("內置函式獲取: 0x%x\n",GetProcAddress); }
可以看到,動態獲取的地址和系統函式獲取的是一致的

Shellcode框架(一)
參考前面kernel32.dll和GetProcaddress的獲取地址,對于每一個要參考的函式,通過查看定義來宣告定義函式,通過GetProcaddress動態獲取地址,
具體代碼如下:
原C代碼:
#include <windows.h> #include <stdio.h> #include <windows.h> int EntryMain() { CreateFileA(“1.txt”, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); MessageBoxA(NULL, “hello world”, tip, MB_OK); return 0; }
Shellcode撰寫:
#include <windows.h> #include <stdio.h> FARPROC getProcAddress(HMODULE hModuleBase); DWORD getKernel32(); int EntryMain() { //宣告定義GetProcAddress typedef FARPROC(WINAPI *FN_GetProcAddress)( _In_ HMODULE hModule, _In_ LPCSTR lpProcName ); //獲取GetProcAddress真實地址 FN_GetProcAddress fn_GetProcAddress = (FN_GetProcAddress)getProcAddress((HMODULE)getKernel32()); //宣告定義CreateFileA typedef HANDLE(WINAPI *FN_CreateFileA)( __in LPCSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile ); //將來的替換,地址全部動態獲取 //FN_CreateFileA fn_CreateFileA = (FN_CreateFileA)GetProcAddress(LoadLibrary("kernel32.dll"), "CreateFileA"); //帶引號的字串打散處理 char xyCreateFile[] = { 'C','r','e','a','t','e','F','i','l','e','A',0 }; //動態獲取CreateFile的地址 FN_CreateFileA fn_CreateFileA = (FN_CreateFileA)fn_GetProcAddress((HMODULE)getKernel32(), xyCreateFile); char xyNewFile[] = { '1','.','t','x','t','\0'}; fn_CreateFileA(xyNewFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); //定義LoadLibraryA typedef HMODULE(WINAPI *FN_LoadLibraryA)( __in LPCSTR lpLibFileName ); char xyLoadLibraryA[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',0}; //動態獲取LoadLibraryA的地址 FN_LoadLibraryA fn_LoadLibraryA = (FN_LoadLibraryA)fn_GetProcAddress((HMODULE)getKernel32(), xyLoadLibraryA); //定義MessageBoxA typedef int (WINAPI *FN_MessageBoxA)( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType); //原來的:MessageBoxA(NULL, "Hello world", "tip", MB_OK); char xy_user32[] = { 'u','s','e','r','3','2','.','d','l','l',0 }; char xy_MessageBoxA[] = { 'M','e','s','s','a','g','e','B','o','x','A',0 }; FN_MessageBoxA fn_MessageBoxA = (FN_MessageBoxA)fn_GetProcAddress(fn_LoadLibraryA(xy_user32), xy_MessageBoxA); char xy_Hello[] = { 'H','e','l','l','o',' ','w','o','r','l','d',0 }; char xy_tip[] = { 't','i','p' }; fn_MessageBoxA(NULL, xy_Hello, xy_tip, MB_OK); return 0; } //內嵌匯編獲取Kernel32的地址 __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 } } //獲取GetProcAddress的地址 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; }
結果:運行exe檔案創建了1.txt檔案,并出現彈框,

函式生成的位置規律
- 單檔案函式生成的位置規律
規律:單檔案函式的生成規律,與函式實作的先后順序有關,而與函式的定義順序無關,
例如:
#include<windows.h> #include<stdio.h> int FuncA(int a, int b) { puts("AAAA"); return a + b; } int FuncB(int a, int b) { puts("BBB"); return a + b; } int main() { FuncA(1, 2); FuncB(2, 3); return 0; }
結果:在IDA中看到生成的exe檔案中的函式順序為FuncA,FuncB,main,與函式實作的先后順序有關,通過函式的位置,可以得到兩個函式之間的空間大小,
2.多檔案函式生成的位置規律
規律:與包含檔案的位置無關,與實際呼叫的順序有關
//A.h #include<stdio.h> void A() { puts("AAA"); } //B.h #include<stdio.h> void B() { puts("BBB"); } //main #include"A.h" #include"B.h" int main() {
A(); B(); return 0; }
結果:

工程下的后綴名為vcxproj檔案

修改順序

可以看到生成的順序發生了改變

Shellcode框架(二)
Shellcode代碼執行程序
ShellcodeStart-> ShellcodeEntry-> ShellcodeEnd
工程檔案:
api.h檔案:存放定義的的函式
header.h檔案:存放定義的功能函式
0.entry.cpp檔案:shellcode的入口點
a.start.cpp檔案:shellcode執行(實作邏輯功能)
b.work.cpp檔案:存放具體功能實作
z.end.cpp檔案:shellcode結束
a.start.cpp檔案和b.work.cpp檔案分別管理邏輯功能和具體實作,更方便管理,
生成的檔案順序:

代碼:
api.h
#pragma once #include<windows.h> //宣告定義GetProcAddress typedef FARPROC(WINAPI *FN_GetProcAddress)( _In_ HMODULE hModule, _In_ LPCSTR lpProcName ); //定義LoadLibraryA typedef HMODULE(WINAPI *FN_LoadLibraryA)( __in LPCSTR lpLibFileName ); //定義MessageBoxA typedef int (WINAPI *FN_MessageBoxA)( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType); //定義CreateFileA typedef HANDLE(WINAPI *FN_CreateFileA)( __in LPCSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile ); //定義一個指標 typedef struct _FUNCTIONS { FN_GetProcAddress fn_GetProcAddress; FN_LoadLibraryA fn_LoadLibraryA; FN_MessageBoxA fn_MessageBoxA; FN_CreateFileA fn_CreateFileA; }FUNCTIONS,*PFUNCTIONS;
header.h
#pragma once #include<windows.h> #include<stdio.h> #include"api.h" void ShellcodeStart(); void ShellcodeEntry(); void ShellcodeEnd(); void CreateShellcode(); void InitFunctions(PFUNCTIONS pFn); void CreateConfigFile(PFUNCTIONS pFn);
0.entry.cpp
#include "header.h" int EntryMain() { CreateShellcode(); return 0; } void CreateShellcode() { HMODULE hMsvcrt = LoadLibraryA("msvcrt.dll"); //定義printf typedef int (__CRTDECL *FN_printf)( _In_z_ _Printf_format_string_ char const* const _Format, ...); FN_printf fn_printf = (FN_printf)GetProcAddress(hMsvcrt,"printf"); HANDLE hBin = CreateFileA("sh.bin", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, 0, NULL); if (hBin == INVALID_HANDLE_VALUE) { //這里不能使用printf函式,因為修改了函式入口,找不到printf的地址,所有需要動態呼叫printf函式 //printf("create file error:%d\n", GetLastError()); fn_printf("create file error:%d\n", GetLastError()); return ; } DWORD dwSize = (DWORD)ShellcodeEnd - (DWORD)ShellcodeStart; DWORD dwWrite; WriteFile(hBin, ShellcodeStart, dwSize, &dwWrite, NULL); CloseHandle(hBin); }
a.start.cpp檔案
#include "header.h" #include "api.h" __declspec(naked) void ShellcodeStart() { __asm { jmp ShellcodeEntry } } //內嵌匯編獲取Kernel32的地址 __declspec(naked) DWORD getKernel32() { __asm { mov eax, fs:[30h]; test eax, eax; js finished; mov eax, [eax + 0ch]; mov eax, [eax + 14h]; mov eax, [eax]; mov eax, [eax] mov eax, [eax + 10h] finished: ret } } //獲取GetProcAddress的地址 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; } //動態呼叫地址 void InitFunctions(PFUNCTIONS pFn) { //獲取GetProcAddress真實地址 pFn->fn_GetProcAddress = (FN_GetProcAddress)getProcAddress((HMODULE)getKernel32()); //動態獲取LoadLibraryA的地址 char xyLoadLibraryA[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',0 }; pFn->fn_LoadLibraryA = (FN_LoadLibraryA)pFn->fn_GetProcAddress((HMODULE)getKernel32(), xyLoadLibraryA); //動態獲取MessageBoxA的地址 char xy_user32[] = { 'u','s','e','r','3','2','.','d','l','l',0 }; char xy_MessageBoxA[] = { 'M','e','s','s','a','g','e','B','o','x','A',0 }; pFn->fn_MessageBoxA = (FN_MessageBoxA)pFn->fn_GetProcAddress(pFn->fn_LoadLibraryA(xy_user32), xy_MessageBoxA); //動態獲取CreateFile的地址 char xyCreateFile[] = { 'C','r','e','a','t','F','i','l','e','A',0 }; pFn->fn_CreateFileA = (FN_CreateFileA)pFn->fn_GetProcAddress((HMODULE)getKernel32(), xyCreateFile); } //入口點 void ShellcodeEntry() { char xy_Hello[] = { 'H','e','l','l','o',' ','w','o','r','l','d',0 }; char xy_tip[] = { 't','i','p' }; FUNCTIONS fn; InitFunctions(&fn); CreateConfigFile(&fn); }
b.work.cpp
#include"api.h" #include<stdio.h> //存放功能 void CreateConfigFile(PFUNCTIONS pFn) { char xyNewFile[] = { '1','.','t','x','t','\0' }; pFn->fn_CreateFileA(xyNewFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); }
z.end.cpp
#include "header.h" void ShellcodeEnd() { }
Shellcode加載器
用來運行提取出來的shellcode代碼,保存在sh.bin檔案,將sb.bin檔案拖入加載器,即可執行,
#include<stdio.h> #include<windows.h> int main(int argc,char* argv[]) { //打開檔案 HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("Oen file error:%d\n", GetLastError); return -1; } DWORD dwSize; dwSize = GetFileSize(hFile, NULL); LPVOID lpAddress = VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); //記憶體分配是否成功 if (lpAddress ==NULL) { printf("VirtualAlloc error:%d\n", GetLastError); CloseHandle(hFile); return -1; } DWORD dwRead; ReadFile(hFile,lpAddress,dwSize,&dwRead, 0); //內嵌匯編 __asm { call lpAddress } _flushall(); system("pause"); return 0; }
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/44987.html
標籤:C++
上一篇:C++抓圖服務
下一篇:演算法分析作業(C|C++版本)
