shellcode,一段短小精幹的代碼,放在任何地方都能執行,不依賴當前所處環境,那麼就有這麼幾點要求:html
針對以上要求,解決的思路:c++
一、動態獲取LoadLibraryA的地址git
windwos 32位下,每一個進程都有PEB結構體,記錄了進程各類信息;在0xc處是PEB_LDR_DATA結構體,該結構體記錄了順序加載的模塊鏈表,說明以下:程序員
核心代碼以下:shell
(1)找到鏈表的頭指針:(注意:不一樣版本windows的PEB結構體可能有細微差異,須要適配)編程
__asm{ mov eax,fs:[0x30] mov eax,[eax+0x0c] add eax,0x0c mov pBeg,eax mov eax,[eax] mov pPLD,eax }
(2)遍歷鏈表,查找kernerl32.dllwindows
while(pPLD!=pBeg) { pLast=(WORD*)pPLD->BaseDllName.Buffer; pFirst=(WORD*)szKernel32; while(*pFirst && (*pFirst-32==*pLast||*pFirst==*pLast)) {pFirst++,pLast++;} if(*pFirst==*pLast) { dwKernelBase=(DWORD)pPLD->DllBase; break; } pPLD=(LDR_DATA_TABLE_ENTRY*)pPLD->InLoadOrderModuleList.Flink; }
二、kernerl32.dll 基址找到後就好辦了:根據PE的導出表能找到GetProcAddr的地址,核心代碼以下:api
MAGE_DOS_HEADER *pIDH=(IMAGE_DOS_HEADER *)dwKernelBase; IMAGE_NT_HEADERS *pINGS=(IMAGE_NT_HEADERS *)((DWORD)dwKernelBase+pIDH->e_lfanew); IMAGE_EXPORT_DIRECTORY *pIED=(IMAGE_EXPORT_DIRECTORY*)((DWORD)dwKernelBase+pINGS ->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); DWORD *pAddOfFun_Raw=(DWORD*)((DWORD)dwKernelBase+pIED->AddressOfFunctions); WORD *pAddOfOrd_Raw=(WORD*)((DWORD)dwKernelBase+pIED->AddressOfNameOrdinals); DWORD *pAddOfNames_Raw=(DWORD*)((DWORD)dwKernelBase+pIED->AddressOfNames); DWORD dwCnt=0; char *pFinded=NULL,*pSrc=szGetProcAddr; for(;dwCnt<pIED->NumberOfNames;dwCnt++) { pFinded=(char *)((DWORD)dwKernelBase+pAddOfNames_Raw[dwCnt]); while(*pFinded &&*pFinded==*pSrc) {pFinded++;pSrc++;} if(*pFinded == *pSrc) { pGetProcAddress=(PGETPROCADDRESS)((DWORD)dwKernelBase+pAddOfFun_Raw[pAddOfOrd_Raw[dwCnt]]); break; } pSrc=szGetProcAddr; }
有了GetProcAddr,又能繼續查找LoadLibrary(也在kernerl32.dll裏面)的地址,以下:數組
pLoadLibrary=(PLOADLIBRARY)pGetProcAddress((HMODULE)dwKernelBase,szLoadLibrary);
這下有了LoadLibrary和GetProcAddr兩大函數地址,任何dll的任何函數入口都能找到了,好比MessageBox,以下:ide
pMessageBox=(PMESSAGEBOX)pGetProcAddress(pLoadLibrary(szUser32),szMessageBox); char szTitle[]={'S','h','e','l','l','C','o','d','e',0}; char szContent[]={0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64,0x20,0x21,0}; pMessageBox(NULL,szContent,szTitle,0);
完整代碼以下:(全部代碼都寫入main,方便下一步提取)
#include<windows.h> int main() { typedef DWORD (WINAPI *PGETPROCADDRESS) (HMODULE hModule,LPCSTR lpProcName); typedef int (WINAPI * PMESSAGEBOX) (HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType); typedef HMODULE (WINAPI * PLOADLIBRARY) (LPCTSTR lpFileName); typedef struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; }UNICODE_STRING; typedef struct PEB_LDR_DATA{ DWORD Length; BYTE initialized; PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; VOID * EntryInProgress; }PEB_LDR_DATA; typedef struct LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; void* DllBase; void* EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; SHORT LoadCount; SHORT TlsIndex; HANDLE SectionHandle; ULONG CheckSum; ULONG TimeDateStamp; }LDR_DATA_TABLE_ENTRY; LDR_DATA_TABLE_ENTRY *pPLD=NULL,*pBeg=NULL; PGETPROCADDRESS pGetProcAddress=NULL; PMESSAGEBOX pMessageBox=NULL; PLOADLIBRARY pLoadLibrary=NULL; WORD *pFirst =NULL,*pLast=NULL; DWORD ret =0,i=0; DWORD dwKernelBase=0; char szKernel32[]={'k',0,'e',0,'r',0,'n',0,'e',0,'l',0,'3',0,'2',0,'.',0,'d',0,'l',0,'l',0,0,0}; char szUser32[]={'U','S','E','R','3','2','.','d','l','l',0}; char szGetProcAddr[]={'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0}; char szLoadLibrary[]={'L','o','a','d','L','i','b','r','a','r','y','A',0}; char szMessageBox[]={'M','e','s','s','a','g','e','B','o','x','A',0}; __asm{ mov eax,fs:[0x30] mov eax,[eax+0x0c] add eax,0x0c mov pBeg,eax mov eax,[eax] mov pPLD,eax } // 遍歷找到kernel32.dll while(pPLD!=pBeg) { pLast=(WORD*)pPLD->BaseDllName.Buffer; pFirst=(WORD*)szKernel32; while(*pFirst && (*pFirst-32==*pLast||*pFirst==*pLast)) { pFirst++,pLast++;} if(*pFirst==*pLast) { dwKernelBase=(DWORD)pPLD->DllBase; break; } pPLD=(LDR_DATA_TABLE_ENTRY*)pPLD->InLoadOrderModuleList.Flink; } // 遍歷kernel32.dll的導出表,找到GetProcAddr函數地址 IMAGE_DOS_HEADER *pIDH=(IMAGE_DOS_HEADER *)dwKernelBase; IMAGE_NT_HEADERS *pINGS=(IMAGE_NT_HEADERS *)((DWORD)dwKernelBase+pIDH->e_lfanew); IMAGE_EXPORT_DIRECTORY *pIED=(IMAGE_EXPORT_DIRECTORY*)((DWORD)dwKernelBase+ pINGS->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); DWORD *pAddOfFun_Raw=(DWORD*)((DWORD)dwKernelBase+pIED->AddressOfFunctions); WORD *pAddOfOrd_Raw=(WORD*)((DWORD)dwKernelBase+pIED->AddressOfNameOrdinals); DWORD *pAddOfNames_Raw=(DWORD*)((DWORD)dwKernelBase+pIED->AddressOfNames); DWORD dwCnt=0; char *pFinded=NULL,*pSrc=szGetProcAddr; for(;dwCnt<pIED->NumberOfNames;dwCnt++) { pFinded=(char *)((DWORD)dwKernelBase+pAddOfNames_Raw[dwCnt]); while(*pFinded &&*pFinded==*pSrc) {pFinded++;pSrc++;} if(*pFinded == *pSrc) { pGetProcAddress=(PGETPROCADDRESS)((DWORD)dwKernelBase+pAddOfFun_Raw[pAddOfOrd_Raw[dwCnt]]); break; } pSrc=szGetProcAddr; } // 有了GetProcAddr 能夠得到任何api pLoadLibrary=(PLOADLIBRARY)pGetProcAddress((HMODULE)dwKernelBase,szLoadLibrary); pMessageBox=(PMESSAGEBOX)pGetProcAddress(pLoadLibrary(szUser32),szMessageBox); // 使用函數 char szTitle[]={'S','h','e','l','l','C','o','d','e',0}; char szContent[]={0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64,0x20,0x21,0}; pMessageBox(NULL,szContent,szTitle,0); return 0; }
二、shellcode提取:從IDA看,main函數從401000開始,
在4012E0結束,總長度2e0;
這裏能夠直接在hexview查看二進制編碼,能夠直接從401000複製到4012E0,這段代碼插入其餘exe代碼的入口點
三、隨變找個exe,好比下面這個變量位置測試的exe,查看發現其入口點是4796:
12bc入口+2e0(shellcode長度)=159c,先刪除這部分代碼:
再把shellcode複製過來:
再次運行exe:能看到彈窗,達到注入代碼的目的:
-----------------------------------------------------------分割線------------------------------------------------------------------------------------------------------------------------------
內存和數據相關區域的分工:
(1)棧(stack):由編譯器進行管理,自動分配和釋放,存放函數調用過程當中的各類參數、局部變量、返回值以及函數返回地址;
(2)堆(heap):用於程序動態申請分配和釋放空間。C語言中的malloc和free,C++中的new和delete均是在堆中進行的,還有windows驅動編程經常使用的ExAllocatePool;正常狀況下,程序員申請的空間在使用結束後應該釋放,若程序員沒有釋放空間,則程序結束時系統自動回收。堆內存的好處:只要程序員不主動釋放,且程序運行不結束,這塊數據會一直存在;這個特性能夠用來隱藏驅動(http://www.javashuo.com/article/p-sqkkmuvy-nd.html);
(3)全局(靜態)存儲區:分爲DATA段和BSS段。DATA段(全局初始化區)存放初始化的全局變量和靜態變量;BSS段(全局未初始化區)存放未初始化的全局變量和靜態變量。程序運行結束時自動釋放。其中BBS段在程序執行以前會被系統自動清0,因此未初始化的全局變量和靜態變量在程序執行以前已經爲0;
(4)文字常量區:存放常量字符串,程序結束後由系統釋放;
其中,棧內存存放的數據僅僅在函數調用過程使用,結束後就沒用了,因此編譯器會增長分配(esp-xxx)和釋放(esp+xxx)的代碼; 但全局變量和靜態變量要求在任何函數都能使用,因此不能存放在棧,只能放在DATA和BSS段,等程序運行結束後由操做系統回收;代碼實驗以下:
第一次的代碼(就是上面用來作注入測試的storPosition.exe):
#include <stdio.h> #include <stdlib.h> int k1 = 1; int k2; static int k3 = 2; static int k4; int main( ) { static int m1=2, m2; int i=1; char*p; char str[10] = "hello"; char* q = "hello"; p= (char *)malloc( 100 ); free(p); printf("棧區-變量地址 i:%p\n", &i); printf(" p:%p\n", &p); printf(" str:%p\n", str); printf(" q:%p\n", &q); printf("堆區地址-動態申請:%p\n", p); printf("全局外部有初值 k1:%p\n", &k1); printf(" 外部無初值 k2:%p\n", &k2); printf("靜態外部有初值 k3:%p\n", &k3); printf(" 外靜無初值 k4:%p\n", &k4); printf(" 內靜態有初值 m1:%p\n", &m1); printf(" 內靜態無初值 m2:%p\n", &m2); printf("文字常量地址 :%p, %s\n",q, q); printf("程序區地址 :%p\n",&main); return 0; }
各類變量地址的分佈:
第二次的代碼:和第一次比增長了3個局部變量:m、j和q1,並打亂了順序:
#include <stdio.h> #include <stdlib.h> int k1 = 1; int k2; static int k3 = 2; static int k4; int main( ) { char*p; int m=3; static int m1=2, m2; int j=2,i=1; char str[10] = "hello"; char* q = "hello"; char q1[] = {'h','e','l','l','o','\0'}; p= (char *)malloc( 100 ); free(p); printf("棧區-變量地址 i:%p\n", &i); printf("棧區-變量地址 j:%p\n", &j); printf("棧區-變量地址 m:%p\n", &m); printf(" p:%p\n", &p); printf(" str:%p\n", str); printf(" q:%p\n", &q); printf(" q1=%s:%p\n", q1, &q1); printf("堆區地址-動態申請:%p\n", p); printf("全局外部有初值 k1:%p\n", &k1); printf(" 外部無初值 k2:%p\n", &k2); printf("靜態外部有初值 k3:%p\n", &k3); printf(" 外靜無初值 k4:%p\n", &k4); printf(" 內靜態有初值 m1:%p\n", &m1); printf(" 內靜態無初值 m2:%p\n", &m2); printf("文字常量地址 :%p, %s\n",q, q); printf("程序區地址 :%p\n",&main); return 0; }
各類變量地址的分佈:
經過對比能夠發現:靜態變量、全局變量只要有初值,地址都是固定的;下面是更詳細的說明:一樣都是字符串,s1存放在棧,函數執行完返回後該區域被收回;s2存放在字符常量,程序結束後纔會被釋放;
最後:參考別人的文章以下:
https://b0ldfrev.gitbook.io/note/windows_operating_system/windows-xia-tong-yong-shellcode-yuan-li
https://www.bilibili.com/video/BV1y4411k7ch?p=6
https://blog.csdn.net/yangquanhui1991/article/details/51786380