diff --git a/BackstageCapture/src/ReflectiveLoader.c b/BackstageCapture/src/ReflectiveLoader.c index 01f9fbf..bc2ac76 100644 --- a/BackstageCapture/src/ReflectiveLoader.c +++ b/BackstageCapture/src/ReflectiveLoader.c @@ -26,6 +26,7 @@ // POSSIBILITY OF SUCH DAMAGE. //===============================================================================================// #include "ReflectiveLoader.h" +#include "obfstr.h" //===============================================================================================// // Our loader will set this to a pseudo correct HINSTANCE/HMODULE value HINSTANCE hAppInstance = NULL; @@ -284,9 +285,9 @@ DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader(VOID) #ifdef WIN_X64 { - HMODULE hNtdll = (HMODULE)pLoadLibraryA("ntdll.dll"); + HMODULE hNtdll = (HMODULE)pLoadLibraryA(OBFS(_enc_ntdll_dll)); if (hNtdll) - pRtlAddFunctionTable = pGetProcAddress(hNtdll, "RtlAddFunctionTable"); + pRtlAddFunctionTable = pGetProcAddress(hNtdll, OBFS(_enc_RtlAddFunctionTable)); } #endif diff --git a/BackstageCapture/src/obfstr.h b/BackstageCapture/src/obfstr.h new file mode 100644 index 0000000..25869c4 --- /dev/null +++ b/BackstageCapture/src/obfstr.h @@ -0,0 +1,70 @@ +//===============================================================================================// +// Compile-time string obfuscation via ROT16 cipher. +// Each character is individually ROT16'd in the source so the plaintext never +// appears contiguously in the binary. Obfuscated arrays are decrypted at call +// site through the OBFS() macro. +// +// To add new strings: +// 1. Define an encrypted array using _OBF_CHR('c') for each character. +// 2. Use OBFS(your_array) at the point of use. +// +// The companion script scripts/obfuscate-strings.py automates generating +// the _OBF_CHR sequences from a plaintext input. +//===============================================================================================// +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OBFS_KEY 16 + +#define _OBF_CHR(c) ((unsigned char)(c) + OBFS_KEY) + +static inline char* obfs_dec(char* buf, int len) +{ + for (int i = 0; i < len; i++) + buf[i] = (char)((unsigned char)buf[i] - OBFS_KEY); + return buf; +} + +#ifdef _MSC_VER +#define OBFS(arr) \ + obfs_dec((char*)memcpy((char*)_alloca(sizeof(arr)), (const char*)(arr), sizeof(arr)), \ + (int)(sizeof(arr) - 1)) +#else +#define OBFS(arr) \ + obfs_dec((char*)memcpy((char[sizeof(arr)]){0}, (const char*)(arr), sizeof(arr)), \ + (int)(sizeof(arr) - 1)) +#endif + +// ---- Encrypted string arrays ---- +// Use _OBF_CHR() for each character so the compiler folds to constants. + +static const unsigned char _enc_ReflectiveLoader[] = { + _OBF_CHR('R'),_OBF_CHR('e'),_OBF_CHR('f'),_OBF_CHR('l'), + _OBF_CHR('e'),_OBF_CHR('c'),_OBF_CHR('t'),_OBF_CHR('i'), + _OBF_CHR('v'),_OBF_CHR('e'),_OBF_CHR('L'),_OBF_CHR('o'), + _OBF_CHR('a'),_OBF_CHR('d'),_OBF_CHR('e'),_OBF_CHR('r'), 0 +}; + +static const unsigned char _enc_ntdll_dll[] = { + _OBF_CHR('n'),_OBF_CHR('t'),_OBF_CHR('d'),_OBF_CHR('l'), + _OBF_CHR('l'),_OBF_CHR('.'),_OBF_CHR('d'),_OBF_CHR('l'), + _OBF_CHR('l'), 0 +}; + +static const unsigned char _enc_RtlAddFunctionTable[] = { + _OBF_CHR('R'),_OBF_CHR('t'),_OBF_CHR('l'),_OBF_CHR('A'), + _OBF_CHR('d'),_OBF_CHR('d'),_OBF_CHR('F'),_OBF_CHR('u'), + _OBF_CHR('n'),_OBF_CHR('c'),_OBF_CHR('t'),_OBF_CHR('i'), + _OBF_CHR('o'),_OBF_CHR('n'),_OBF_CHR('T'),_OBF_CHR('a'), + _OBF_CHR('b'),_OBF_CHR('l'),_OBF_CHR('e'), 0 +}; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/BackstageInjection/src/NtApiHooks.c b/BackstageInjection/src/NtApiHooks.c index 92693ee..1a8b7de 100644 --- a/BackstageInjection/src/NtApiHooks.c +++ b/BackstageInjection/src/NtApiHooks.c @@ -7,6 +7,7 @@ extern "C" { #include "NtApiHooks.h" #include "MinHook.h" +#include "obfstr.h" #include #include @@ -845,7 +846,7 @@ static inline void _hvnc_wcsncpy_s(wchar_t *dst, size_t dstSize, const wchar_t * BOOL found = FALSE; for (DWORD k = 0; nameFO + k < peSize && name[k] != 0; k++) { if (name[k] == 'R' && nameFO + k + 16 <= peSize) { - if (memcmp(&name[k], "ReflectiveLoader", 16) == 0) { + if (memcmp(&name[k], OBFS(_enc_ReflectiveLoader), 16) == 0) { found = TRUE; break; } diff --git a/BackstageInjection/src/ReflectiveLoader.c b/BackstageInjection/src/ReflectiveLoader.c index d015817..e2ede62 100644 --- a/BackstageInjection/src/ReflectiveLoader.c +++ b/BackstageInjection/src/ReflectiveLoader.c @@ -26,6 +26,7 @@ // POSSIBILITY OF SUCH DAMAGE. //===============================================================================================// #include "ReflectiveLoader.h" +#include "obfstr.h" //===============================================================================================// // Our loader will set this to a pseudo correct HINSTANCE/HMODULE value HINSTANCE hAppInstance = NULL; @@ -286,9 +287,9 @@ DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader(VOID) // Resolve RtlAddFunctionTable so we can register exception unwind data. // Without this, stack unwinding through our hook functions crashes browsers. { - HMODULE hNtdll = (HMODULE)pLoadLibraryA("ntdll.dll"); + HMODULE hNtdll = (HMODULE)pLoadLibraryA(OBFS(_enc_ntdll_dll)); if (hNtdll) - pRtlAddFunctionTable = pGetProcAddress(hNtdll, "RtlAddFunctionTable"); + pRtlAddFunctionTable = pGetProcAddress(hNtdll, OBFS(_enc_RtlAddFunctionTable)); } #endif diff --git a/BackstageInjection/src/obfstr.h b/BackstageInjection/src/obfstr.h new file mode 100644 index 0000000..1f14dd5 --- /dev/null +++ b/BackstageInjection/src/obfstr.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OBFS_KEY 16 + +#define _OBF_CHR(c) ((unsigned char)(c) + OBFS_KEY) + +static inline char* obfs_dec(char* buf, int len) +{ + for (int i = 0; i < len; i++) + buf[i] = (char)((unsigned char)buf[i] - OBFS_KEY); + return buf; +} + +#ifdef _MSC_VER +#define OBFS(arr) \ + obfs_dec((char*)memcpy((char*)_alloca(sizeof(arr)), (const char*)(arr), sizeof(arr)), \ + (int)(sizeof(arr) - 1)) +#else +#define OBFS(arr) \ + obfs_dec((char*)memcpy((char[sizeof(arr)]){0}, (const char*)(arr), sizeof(arr)), \ + (int)(sizeof(arr) - 1)) +#endif + +// ---- Encrypted string arrays ---- +// Use _OBF_CHR() for each character so the compiler folds to constants. + +static const unsigned char _enc_ReflectiveLoader[] = { + _OBF_CHR('R'),_OBF_CHR('e'),_OBF_CHR('f'),_OBF_CHR('l'), + _OBF_CHR('e'),_OBF_CHR('c'),_OBF_CHR('t'),_OBF_CHR('i'), + _OBF_CHR('v'),_OBF_CHR('e'),_OBF_CHR('L'),_OBF_CHR('o'), + _OBF_CHR('a'),_OBF_CHR('d'),_OBF_CHR('e'),_OBF_CHR('r'), 0 +}; + +static const unsigned char _enc_ntdll_dll[] = { + _OBF_CHR('n'),_OBF_CHR('t'),_OBF_CHR('d'),_OBF_CHR('l'), + _OBF_CHR('l'),_OBF_CHR('.'),_OBF_CHR('d'),_OBF_CHR('l'), + _OBF_CHR('l'), 0 +}; + +static const unsigned char _enc_RtlAddFunctionTable[] = { + _OBF_CHR('R'),_OBF_CHR('t'),_OBF_CHR('l'),_OBF_CHR('A'), + _OBF_CHR('d'),_OBF_CHR('d'),_OBF_CHR('F'),_OBF_CHR('u'), + _OBF_CHR('n'),_OBF_CHR('c'),_OBF_CHR('t'),_OBF_CHR('i'), + _OBF_CHR('o'),_OBF_CHR('n'),_OBF_CHR('T'),_OBF_CHR('a'), + _OBF_CHR('b'),_OBF_CHR('l'),_OBF_CHR('e'), 0 +}; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/scripts/obfuscate-strings.py b/scripts/obfuscate-strings.py new file mode 100644 index 0000000..9ec12cc --- /dev/null +++ b/scripts/obfuscate-strings.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +""" +Generate _OBF_CHR sequences for compile-time string obfuscation (ROT16). + +Usage: + python scripts/obfuscate-strings.py ReflectiveLoader + python scripts/obfuscate-strings.py "ntdll.dll" "RtlAddFunctionTable" + +Outputs a C array initializer using _OBF_CHR() for each character. +""" +import sys + +KEY = 16 +INDENT = "\t" + +def obfuscate(s): + chars = [] + for c in s: + chars.append(f"_OBF_CHR('{c}')") + chars.append("0") + return ",\n".join(INDENT + ch for ch in chars) + +def main(): + if len(sys.argv) < 2: + print("Usage: python obfuscate-strings.py STRING [STRING...]", file=sys.stderr) + print("Example: python obfuscate-strings.py ReflectiveLoader", file=sys.stderr) + sys.exit(1) + + for arg in sys.argv[1:]: + body = obfuscate(arg) + var = arg.replace(".", "_").replace(" ", "_") + print(f"static const unsigned char _enc_{var}[] = {{\n{body}\n}};\n") + +if __name__ == "__main__": + main() \ No newline at end of file