From ae2f58c3caf1287885bbe237ebb18bf134b68b42 Mon Sep 17 00:00:00 2001 From: MochaByte <115954804+mochabyte0x@users.noreply.github.com> Date: Mon, 27 Jul 2026 22:50:29 +0200 Subject: [PATCH] Add files via upload --- src/sleep.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 src/sleep.c diff --git a/src/sleep.c b/src/sleep.c new file mode 100644 index 0000000..63e9b3b --- /dev/null +++ b/src/sleep.c @@ -0,0 +1,202 @@ +// sleep obfuscation via CreateThreadpoolTimer ( NtSetTimer2 path ) +// same idea as Ekko but different fingerprint +// build: gcc sleep.c -o sleep.exe -static + +#include +#include +#include + +typedef struct { ULONG Length, MaximumLength; PVOID Buffer; } USTRING; + +typedef NTSTATUS ( NTAPI *fnNtContinue )( PCONTEXT, BOOLEAN ); +typedef NTSTATUS ( NTAPI *fnSysFunc032 )( USTRING *, USTRING * ); + +static fnNtContinue g_NtContinue = NULL; +static fnSysFunc032 g_SysFunc032 = NULL; +static PTP_POOL g_Pool = NULL; + +static TP_CALLBACK_ENVIRON g_Env; + +// shims live outside the encrypted image so they survive sleep +// TpTimer callbacks: ( Instance, Context, Timer ) -> rcx/rdx/r8 +// NtContinue wants ( PCONTEXT, BOOLEAN ) -> rcx/rdx +// so we need: mov rcx, rdx; [xor edx,edx]; jmp target +typedef struct { + BYTE ntc[20]; // 17 bytes: mov rcx,rdx / xor edx,edx / mov rax,addr / jmp rax + BYTE cap[16]; // 15 bytes: mov rcx,rdx / mov rax,addr / jmp rax + } SHIMS; + +_Static_assert( sizeof( ( ( SHIMS* )0 )->ntc ) >= 17, "ntc shim too small" ); +_Static_assert( sizeof( ( ( SHIMS* )0 )->cap ) >= 15, "cap shim too small" ); + +static SHIMS *g_Shims = NULL; + +static BOOL setup_shims( void ) +{ + PVOID rtlcc = GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "RtlCaptureContext" ); + if ( !rtlcc ) return FALSE; + + g_Shims = VirtualAlloc( NULL, sizeof( SHIMS ), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE ); + if ( !g_Shims ) return FALSE; + + // NtContinue shim + BYTE s1[] = { 0x48,0x89,0xD1, 0x33,0xD2, 0x48,0xB8,0,0,0,0,0,0,0,0, 0xFF,0xE0 } ; + *( void ** )( s1+7 ) = g_NtContinue; + memcpy( g_Shims->ntc, s1, sizeof( s1 ) ); + + // RtlCaptureContext shim + BYTE s2[] = { 0x48,0x89,0xD1, 0x48,0xB8,0,0,0,0,0,0,0,0, 0xFF,0xE0 } ; + *( void ** )( s2+5 ) = rtlcc; + memcpy( g_Shims->cap, s2, sizeof( s2 ) ); + + return TRUE; + } + +static BOOL init( void ) +{ + HMODULE ntdll = GetModuleHandleA( "ntdll.dll" ); + HMODULE cryptsp = LoadLibraryA( "cryptsp.dll" ); + if ( !ntdll || !cryptsp ) return FALSE; + + g_NtContinue = ( fnNtContinue )GetProcAddress( ntdll, "NtContinue" ); + g_SysFunc032 = ( fnSysFunc032 )GetProcAddress( cryptsp, "SystemFunction032" ); + if ( !g_NtContinue || !g_SysFunc032 ) return FALSE; + + if ( !setup_shims( ) ) return FALSE; + + g_Pool = CreateThreadpool( NULL ); + if ( !g_Pool ) return FALSE; + + SetThreadpoolThreadMaximum( g_Pool, 1 ); + SetThreadpoolThreadMinimum( g_Pool, 1 ); + InitializeThreadpoolEnvironment( &g_Env ); + SetThreadpoolCallbackPool( &g_Env, g_Pool ); + + printf( "[+] init ok NtContinue=%p shims=%p\n", g_NtContinue, g_Shims ); + return TRUE; + } + +void obf_sleep( DWORD ms ) +{ + CONTEXT ctx = { 0 } , rProtRW = { 0 } , rEncrypt = { 0 } , rDelay = { 0 } , rDecrypt = { 0 } , rProtRX = { 0 } , rSetEvt = { 0 } ; + ctx.ContextFlags = CONTEXT_FULL; + + PTP_TIMER tCap=NULL, tProtRW=NULL, tEnc=NULL, tDelay=NULL, tDec=NULL, tProtRX=NULL, tSetEvt=NULL; + + HANDLE hEvt = CreateEventW( NULL, FALSE, FALSE, NULL ); + PVOID base = GetModuleHandleA( NULL ); + DWORD size = ( ( PIMAGE_NT_HEADERS )( ( PBYTE )base + ( ( PIMAGE_DOS_HEADER )base )->e_lfanew ) )->OptionalHeader.SizeOfImage; + DWORD oldprot = 0; + + printf( "[*] base= @ 0x%p size=0x%lX\n", base, size ); + + BYTE key[16] = { 0xDE,0xAD,0xBE,0xEF,0xDE,0xAD,0xBE,0xEF, + 0xDE,0xAD,0xBE,0xEF,0xDE,0xAD,0xBE,0xEF } ; + USTRING uKey = { 16, 16, key } ; + USTRING uImg = { ( ULONG )size, ( ULONG )size, base } ; + + // capture pool thread context with retries, the thread gets killed by + // NtContinue each cycle ( stack abandoned mid-frame ), so MinThreads=1 + // replaces it but needs ~200ms. retry loop absorbs that delay. + tCap = CreateThreadpoolTimer( ( PTP_TIMER_CALLBACK )g_Shims->cap, &ctx, &g_Env ); + if ( !tCap ) goto done; + + for ( int i = 0; i < 8; i++ ) { + ctx.Rsp = 0xDEADBEEFDEADBEEFULL; + FILETIME ft = { 0 } ; + SetThreadpoolTimer( tCap, &ft, 0, 0 ); + Sleep( 100 ); + WaitForThreadpoolTimerCallbacks( tCap, FALSE ); + if ( ctx.Rsp != 0xDEADBEEFDEADBEEFULL && ctx.Rsp ) break; + SetThreadpoolTimer( tCap, NULL, 0, 0 ); // disarm before retry + Sleep( 300 ); + } + + WaitForThreadpoolTimerCallbacks( tCap, TRUE ); + CloseThreadpoolTimer( tCap ); tCap = NULL; + + if ( !ctx.Rsp || ctx.Rsp == 0xDEADBEEFDEADBEEFULL ) { + printf( "[-] capture failed\n" ); + goto done; + } + + printf( "[*] captured rsp=%016llx rip=%016llx\n", ctx.Rsp, ctx.Rip ); + + // clone base context into each ROP step, then patch + // rsp-=8 corrects RtlCaptureContext's +0x10 overshoot so ret + // lands on the ntdll return address rather than 8 bytes past it + memcpy( &rProtRW, &ctx, sizeof ctx ); + memcpy( &rEncrypt, &ctx, sizeof ctx ); + memcpy( &rDelay, &ctx, sizeof ctx ); + memcpy( &rDecrypt, &ctx, sizeof ctx ); + memcpy( &rProtRX, &ctx, sizeof ctx ); + memcpy( &rSetEvt, &ctx, sizeof ctx ); + + rProtRW.Rsp -= 8; rProtRW.Rip = ( DWORD64 )VirtualProtect; + rProtRW.Rcx = ( DWORD64 )base; rProtRW.Rdx = size; + rProtRW.R8 = PAGE_READWRITE; rProtRW.R9 = ( DWORD64 )&oldprot; + + rEncrypt.Rsp -= 8; rEncrypt.Rip = ( DWORD64 )g_SysFunc032; + rEncrypt.Rcx = ( DWORD64 )&uImg; rEncrypt.Rdx = ( DWORD64 )&uKey; + + rDelay.Rsp -= 8; rDelay.Rip = ( DWORD64 )Sleep; + rDelay.Rcx = ms; + + rDecrypt.Rsp -= 8; rDecrypt.Rip = ( DWORD64 )g_SysFunc032; + rDecrypt.Rcx = ( DWORD64 )&uImg; rDecrypt.Rdx = ( DWORD64 )&uKey; + + rProtRX.Rsp -= 8; rProtRX.Rip = ( DWORD64 )VirtualProtect; + rProtRX.Rcx = ( DWORD64 )base; rProtRX.Rdx = size; + rProtRX.R8 = PAGE_EXECUTE_READ; rProtRX.R9 = ( DWORD64 )&oldprot; + + rSetEvt.Rsp -= 8; rSetEvt.Rip = ( DWORD64 )SetEvent; + rSetEvt.Rcx = ( DWORD64 )hEvt; + + // stagger in 500ms slots. Post-sleep steps offset by ms so they + // fire after the delay callback's Sleep() returns + const LONGLONG t = 5000000LL, sN = ( LONGLONG )ms * 10000LL; + LARGE_INTEGER d[6]; + d[0].QuadPart = -( t ); d[1].QuadPart = -( 2*t ); + d[2].QuadPart = -( 3*t ); d[3].QuadPart = -( 3*t + sN + t ); + d[4].QuadPart = -( 3*t + sN + 2*t ); d[5].QuadPart = -( 3*t + sN + 3*t ); + + PTP_TIMER_CALLBACK shim = ( PTP_TIMER_CALLBACK )g_Shims->ntc; + tProtRW = CreateThreadpoolTimer( shim, &rProtRW, &g_Env ); + tEnc = CreateThreadpoolTimer( shim, &rEncrypt, &g_Env ); + tDelay = CreateThreadpoolTimer( shim, &rDelay, &g_Env ); + tDec = CreateThreadpoolTimer( shim, &rDecrypt, &g_Env ); + tProtRX = CreateThreadpoolTimer( shim, &rProtRX, &g_Env ); + tSetEvt = CreateThreadpoolTimer( shim, &rSetEvt, &g_Env ); + + if ( !tProtRW || !tEnc || !tDelay || !tDec || !tProtRX || !tSetEvt ) { + printf( "[-] timer alloc failed\n" ); + goto done; + } + + SetThreadpoolTimer( tProtRW, ( PFILETIME )&d[0], 0, 0 ); + SetThreadpoolTimer( tEnc, ( PFILETIME )&d[1], 0, 0 ); + SetThreadpoolTimer( tDelay, ( PFILETIME )&d[2], 0, 0 ); + SetThreadpoolTimer( tDec, ( PFILETIME )&d[3], 0, 0 ); + SetThreadpoolTimer( tProtRX, ( PFILETIME )&d[4], 0, 0 ); + SetThreadpoolTimer( tSetEvt, ( PFILETIME )&d[5], 0, 0 ); + + printf( "[*] sleeping %lu ms ( image encrypted )\n", ms ); + WaitForSingleObject( hEvt, INFINITE ); + printf( "[*] awake\n" ); + +done: + if ( tCap ) { WaitForThreadpoolTimerCallbacks( tCap, TRUE ); CloseThreadpoolTimer( tCap ); } + if ( tProtRW ) { WaitForThreadpoolTimerCallbacks( tProtRW, TRUE ); CloseThreadpoolTimer( tProtRW ); } + if ( tEnc ) { WaitForThreadpoolTimerCallbacks( tEnc, TRUE ); CloseThreadpoolTimer( tEnc ); } + if ( tDelay ) { WaitForThreadpoolTimerCallbacks( tDelay, TRUE ); CloseThreadpoolTimer( tDelay ); } + if ( tDec ) { WaitForThreadpoolTimerCallbacks( tDec, TRUE ); CloseThreadpoolTimer( tDec ); } + if ( tProtRX ) { WaitForThreadpoolTimerCallbacks( tProtRX, TRUE ); CloseThreadpoolTimer( tProtRX ); } + if ( tSetEvt ) { WaitForThreadpoolTimerCallbacks( tSetEvt, TRUE ); CloseThreadpoolTimer( tSetEvt ); } + if ( hEvt ) CloseHandle( hEvt ); + } + +int main( void ) +{ + if ( !init( ) ) { printf( "[-] init failed\n" ); return 1; } + for ( ;; ) obf_sleep( 3000 ); + } \ No newline at end of file