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