mirror of
https://github.com/mochabyte0x/Morpheus
synced 2026-07-28 10:11:01 +00:00
Add files via upload
This commit is contained in:
+202
@@ -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 );
|
||||
}
|
||||
Reference in New Issue
Block a user