freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Wx64ST:一款轻松可修改的C语言Shellcode模板
2024-08-22 15:26:45

关于windows_x64_shellcode_template

windows_x64_shellcode_template简称为Wx64ST,它是一款功能强大的Shellcode模板,该模板基于C语言编写,其简单、可扩展和易于修改等特性可以帮助广大安全研究人员轻松开发适用于Windows x64的Shellcode。

值得一提的是,该模板代码可以加载LoadLibraryA和GetProcAddress,并以C语言方式暴露HMODULE给kernel32.dll。

除此之外,模板提供的函数中包含了很多注释内容,以供广大研究人员参考。

工具要求

VS 2015 / VS 2017

工具安装

广大研究人员可以直接使用下列命令将该项目源码克隆至本地:

git clone https://github.com/rainerzufalldererste/windows_x64_shellcode_template.git

cd windows_x64_shellcode_template

git submodule update --init --recursive

create_project.bat

该shellcode_template项目包括开始开发自定义Shellcode的所有内容。

如何检索Shellcode

有很多方法可以检索生成的 shellcode。最简单的方法可能是直接进入Visual Studio 调试器并切换到void shellcode_template(),然后打开反汇编视图 (Ctrl+Alt+D)。请确保打开显示代码字节并关闭显示源代码和地址以简化输出:

48 89 5C 24 08       mov         qword ptr [rsp+8],rbx  

57                   push        rdi  

48 83 EC 30          sub         rsp,30h  

65 48 8B 04 25 60 00 00 00 mov         rax,qword ptr gs:[0000000000000060h]  

33 D2                xor         edx,edx  

49 B8 47 65 74 50 72 6F 63 41 mov         r8,41636F7250746547h  

48 8B 48 18          mov         rcx,qword ptr [rax+18h]  

48 8B 41 20          mov         rax,qword ptr [rcx+20h]  

48 8B 08             mov         rcx,qword ptr [rax]  

48 8B 01             mov         rax,qword ptr [rcx]  

48 8B 78 20          mov         rdi,qword ptr [rax+20h]  

48 63 47 3C          movsxd      rax,dword ptr [rdi+3Ch]  

44 8B 8C 38 88 00 00 00 mov         r9d,dword ptr [rax+rdi+0000000000000088h]  

41 8B 44 39 20       mov         eax,dword ptr [r9+rdi+20h]  

48 03 C7             add         rax,rdi  

48 63 08             movsxd      rcx,dword ptr [rax]  

4C 39 04 39          cmp         qword ptr [rcx+rdi],r8  

 ...

现在您只需移除标签和反汇编代码即可,剩下的就是Shellcode:

48 89 5C 24 08

57

48 83 EC 30

65 48 8B 04 25 60 00 00 00

33 D2

49 B8 47 65 74 50 72 6F 63 41

48 8B 48 18

48 8B 41 20

48 8B 08

48 8B 01

48 8B 78 20

48 63 47 3C

44 8B 8C 38 88 00 00 00

41 8B 44 39 20

48 03 C7

48 63 08

4C 39 04 39

 ...

或者,您可以将代码粘贴到像godbolt.org这样的在线编译器中,然后将生成的 MSVC 程序集(不调用任何内部函数,例如缓冲区安全检查或 memcpy)(即使用x64 msvc v19.latest)复制到像https://defuse.ca/online-x86-assembler.htm/O2 /GS-这样的在线汇编程序中。然后只需复制生成的Shellcode即可。

当使用在线编译器时,你可能需要稍微清理一下汇编代码,如下所示:

x$ = 32

void shellcode_template(void) PROC               ; shellcode_template, COMDAT

$LN10:

        mov     QWORD PTR [rsp+8], rbx

        push    rdi

        sub     rsp, 48                             ; 00000030H

        mov     rax, QWORD PTR gs:96

        xor     edx, edx

        mov     r8, 4711732171926431047             ; 41636f7250746547H

        mov     rcx, QWORD PTR [rax+24]

        mov     rax, QWORD PTR [rcx+32]

        mov     rcx, QWORD PTR [rax]

        mov     rax, QWORD PTR [rcx]

        mov     rdi, QWORD PTR [rax+32]

        movsxd  rax, DWORD PTR [rdi+60]

        mov     r9d, DWORD PTR [rax+rdi+136]

        mov     eax, DWORD PTR [r9+rdi+32]

        add     rax, rdi

        movsxd  rcx, DWORD PTR [rax]

        cmp     QWORD PTR [rcx+rdi], r8

        je      SHORT $LN3@shellcode_

        npad    2

$LL2@shellcode_:

        movsxd  rcx, DWORD PTR [rax+4]

        lea     rax, QWORD PTR [rax+4]

        inc     edx

        cmp     QWORD PTR [rcx+rdi], r8

        jne     SHORT $LL2@shellcode_

$LN3@shellcode_:

        mov     ecx, DWORD PTR [r9+rdi+36]

        mov     rax, 8242266044863967052      ; 7262694c64616f4cH

        ...

可以通过一些查找和替换将代码变成这样:

shellcode_template:

        mov     qword ptr [rsp+8], rbx

        push    rdi

        sub     rsp, 48

        mov     rax, qword ptr gs:96

        xor     edx, edx

        mov     r8, 4711732171926431047

        mov     rcx, qword ptr [rax+24]

        mov     rax, qword ptr [rcx+32]

        mov     rcx, qword ptr [rax]

        mov     rax, qword ptr [rcx]

        mov     rdi, qword ptr [rax+32]

        movsxd  rax, DWORD ptr [rdi+60]

        mov     r9d, DWORD ptr [rax+rdi+136]

        mov     eax, DWORD ptr [r9+rdi+32]

        add     rax, rdi

        movsxd  rcx, DWORD ptr [rax]

        cmp     qword ptr [rcx+rdi], r8

        je      SHORT _function_found

        

        ; `npad    2` can be turned into two `nop`s.

 

        nop

        nop

 

 

_find_next_function:

        movsxd  rcx, DWORD ptr [rax+4]

        lea     rax, qword ptr [rax+4]

        inc     edx

        cmp     qword ptr [rcx+rdi], r8

        jne     SHORT _find_next_function

 

 

_function_found:

        mov     ecx, DWORD ptr [r9+rdi+36]

        mov     rax, 8242266044863967052

        ...

生成的Shellcode

下面给出的是该项目提供的Shellcode样例,它将执行下列操作:

1、在kernel32.dll中查找GetProcAddress;

2、从kernel32.dll检索LoadLibraryA;

3、加载useR32.dll;

4、从useR32.dll检索MessageBoxA;

5、显示一个消息提示框;

6、从kernel32.dll检索ExitProcess;

7、调用ExitProcess;

0x48, 0x89, 0x5C, 0x24, 0x08, 0x57, 0x48, 0x83, 0xEC, 0x30, 0x65, 0x48, 0x8B, 0x04, 0x25, 0x60,

  0x00, 0x00, 0x00, 0x33, 0xD2, 0x49, 0xB8, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6F, 0x63, 0x41, 0x48,

  0x8B, 0x48, 0x18, 0x48, 0x8B, 0x41, 0x20, 0x48, 0x8B, 0x08, 0x48, 0x8B, 0x01, 0x48, 0x8B, 0x78,

  0x20, 0x48, 0x63, 0x47, 0x3C, 0x44, 0x8B, 0x8C, 0x38, 0x88, 0x00, 0x00, 0x00, 0x41, 0x8B, 0x44,

  0x39, 0x20, 0x48, 0x03, 0xC7, 0x48, 0x63, 0x08, 0x4C, 0x39, 0x04, 0x39, 0x74, 0x12, 0x66, 0x90,

  0x48, 0x63, 0x48, 0x04, 0x48, 0x8D, 0x40, 0x04, 0xFF, 0xC2, 0x4C, 0x39, 0x04, 0x39, 0x75, 0xF0,

  0x41, 0x8B, 0x4C, 0x39, 0x24, 0x48, 0xB8, 0x4C, 0x6F, 0x61, 0x64, 0x4C, 0x69, 0x62, 0x72, 0x48,

  0x03, 0xCF, 0x48, 0x63, 0xD2, 0x4C, 0x0F, 0xBF, 0x04, 0x51, 0x48, 0x8D, 0x54, 0x24, 0x20, 0x41,

  0x8B, 0x4C, 0x39, 0x1C, 0x48, 0x03, 0xCF, 0x4A, 0x63, 0x1C, 0x81, 0x48, 0x8B, 0xCF, 0x48, 0x03,

  0xDF, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0xC7, 0x44, 0x24, 0x28, 0x61, 0x72, 0x79, 0x41, 0xFF,

  0xD3, 0x48, 0xB9, 0x75, 0x73, 0x65, 0x72, 0x33, 0x32, 0x2E, 0x64, 0x48, 0xC7, 0x44, 0x24, 0x28,

  0x6C, 0x6C, 0x00, 0x00, 0x48, 0x89, 0x4C, 0x24, 0x20, 0x48, 0x8D, 0x4C, 0x24, 0x20, 0xFF, 0xD0,

  0x48, 0xB9, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x48, 0xC7, 0x44, 0x24, 0x28, 0x6F,

  0x78, 0x41, 0x00, 0x48, 0x89, 0x4C, 0x24, 0x20, 0x48, 0x8D, 0x54, 0x24, 0x20, 0x48, 0x8B, 0xC8,

  0xFF, 0xD3, 0x48, 0xB9, 0x48, 0x61, 0x73, 0x74, 0x61, 0x20, 0x6C, 0x61, 0x4C, 0x8D, 0x44, 0x24,

  0x2F, 0x48, 0x89, 0x4C, 0x24, 0x20, 0x48, 0x8D, 0x54, 0x24, 0x20, 0x48, 0xB9, 0x20, 0x76, 0x69,

  0x73, 0x74, 0x61, 0x21, 0x00, 0x45, 0x33, 0xC9, 0x48, 0x89, 0x4C, 0x24, 0x28, 0x33, 0xC9, 0xFF,

  0xD0, 0x48, 0xB8, 0x45, 0x78, 0x69, 0x74, 0x50, 0x72, 0x6F, 0x63, 0x48, 0xC7, 0x44, 0x24, 0x28,

  0x65, 0x73, 0x73, 0x00, 0x48, 0x8D, 0x54, 0x24, 0x20, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0x8B,

  0xCF, 0xFF, 0xD3, 0xB9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0x48, 0x8B, 0x5C, 0x24, 0x40, 0x48,

  0x83, 0xC4, 0x30, 0x5F, 0xC3

工具使用演示

下面给出的是一个示例应用程序,演示了如何使用Wx64ST在子进程内执行生成的Shellcode:

// Load Process Environment Block.

  PEB *pProcessEnvironmentBlock = (PEB *)__readgsqword(0x60);

 

  // `pProcessEnvironmentBlock->Ldr->InMemoryOrderModuleList` contains a double linked list.

  // `Flink` and `Blink` are pointers to the next and previous element.

  //

  // All Windows executables should have the following module order.

  //  1. The module of the current executable.

  //  2. `ntdll.dll` (`%windir%\System32\ntdll.dll`)

  //  3. `kernel32.dll` (`%windir%\System32\kernel32.dll`)

  //

  //  ... followed by other modules.

  //

  // In order to get the `GetProcAddress` function we need to therefore get the third item (`Flink->Flink->Flink`).

  // We use the `CONTAINING_RECORD` macro to retrieve the associated table entry.

  LDR_DATA_TABLE_ENTRY *pKernel32TableEntry = CONTAINING_RECORD(pProcessEnvironmentBlock->Ldr->InMemoryOrderModuleList.Flink->Flink->Flink, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

 

  // We've ended up at the base address of `kernel32.dll`.

  IMAGE_DOS_HEADER *pDosHeader = (IMAGE_DOS_HEADER *)pKernel32TableEntry->DllBase;

 

  // In order to get the exported functions we need to go to the NT PE header.

  IMAGE_NT_HEADERS *pNtHeader = (IMAGE_NT_HEADERS *)((size_t)pDosHeader + pDosHeader->e_lfanew);

 

  // From the NtHeader we can extract the virtual address of the export directory of this module.

  IMAGE_EXPORT_DIRECTORY *pExports = (IMAGE_EXPORT_DIRECTORY *)((size_t)pDosHeader + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

 

  // The exports directory contains both a list of function _names_ of this module and the associated _addresses_ of the functions.

  const int32_t *pNameOffsets = (const int32_t *)((size_t)pDosHeader + pExports->AddressOfNames);

  

  // We will use this struct to store strings.

  // We are using a struct to make sure strings don't end up in another section of the executable where we wouldn't be able to address them in a different process.

  struct

  {

    uint64_t text0, text1;

  } x;

 

  // We're now looking for the `GetProcAddress` function. Since there's no other function starting with `GetProcA` we'll just find that instead.

  x.text0 = 0x41636F7250746547; // `GetProcA`

 

  int32_t i = 0;

 

  // We're just extracting the first 8 bytes of the strings and compare them to `GetProcA`. We'll find it eventually.

  while (*(uint64_t *)((size_t)pDosHeader + pNameOffsets[i]) != x.text0)

    ++i;

 

  // We have found the index of `GetProcAddress`.

 

  // The entry at an index in `AddressOfNames` corresponds to an entry at the same index in `AddressOfNameOrdinals`, which resolves the index of a given name to it's corresponding entry in `AddressOfFunctions`. (DLLs can export unnamed functions, which will not be listed in `AddressOfNames`.)

  // Let's get the function name ordinal offsets and function offsets in order to retrieve the location of `GetProcAddress` in memory.

  const int16_t *pFunctionNameOrdinalOffsets = (const int16_t *)((size_t)pDosHeader + pExports->AddressOfNameOrdinals);

  const int32_t *pFunctionOffsets = (const int32_t *)((size_t)pDosHeader + pExports->AddressOfFunctions);

 

  // Now resolve the index in `pFunctionOffsets` from `pFunctionNameOrdinalOffsets` to get the address of the desired function in memory.

  typedef FARPROC(*GetProcAddressFunc)(HMODULE, const char *);

  GetProcAddressFunc pGetProcAddress = (GetProcAddressFunc)(const void *)((size_t)pDosHeader + pFunctionOffsets[pFunctionNameOrdinalOffsets[i]]);

 

  // Now that we've got `GetProcAddress`, let's use it to get `LoadLibraryA`.

 

  // A HMODULE is just a pointer to the base address of a module.

  HMODULE kernel32Dll = (HMODULE)pDosHeader;

 

  // Get `LoadLibraryA`.

  x.text0 = 0x7262694C64616F4C; // `LoadLibr`

  x.text1 = 0x0000000041797261; // `aryA\0\0\0\0`

 

  typedef HMODULE(*LoadLibraryAFunc)(const char *);

  LoadLibraryAFunc pLoadLibraryA = (LoadLibraryAFunc)pGetProcAddress(kernel32Dll, (const char *)&x.text0);

项目地址

windows_x64_shellcode_template:【GitHub传送门

参考资料

https://godbolt.org/

https://defuse.ca/online-x86-assembler.htm

# shellcode # windows # 代码安全 # 模板 # Windows系统安全
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录