├── .gitignore ├── Include ├── Common.h └── Ekko.h ├── README.md ├── Src ├── Main.c └── PhaseDive.c └── makefile /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.exe 3 | -------------------------------------------------------------------------------- /Include/Common.h: -------------------------------------------------------------------------------- 1 | #ifndef EKKO_COMMON_H 2 | #define EKKO_COMMON_H 3 | 4 | #include 5 | #include 6 | 7 | #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) 8 | #define NtCurrentThread() ( ( HANDLE ) ( LONG_PTR ) -2 ) 9 | #define NtCurrentProcess() ( ( HANDLE ) ( LONG_PTR ) -1 ) 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /Include/Ekko.h: -------------------------------------------------------------------------------- 1 | #ifndef EKKO_EKKO_H 2 | #define EKKO_EKKO_H 3 | 4 | #include 5 | 6 | typedef struct 7 | { 8 | DWORD Length; 9 | DWORD MaximumLength; 10 | PVOID Buffer; 11 | } USTRING ; 12 | 13 | VOID EkkoObf( DWORD SleepTime ); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |
4 | PhaseDive (Ekko fork) 5 |

6 | better explained at https://tishina.in/execution/phase-dive-sleep-obfuscation 7 | 8 | This is a PoC for a change to Ekko to use trampoline calls to ZwContinue and a `jmp rax` gadget to call functions from the `CONTEXT` struct. The `ntdll.dll` gadget is static, you need to find your own `call ` to test this
9 | 10 | ### Credit 11 | - Ekko implementation by C5pider (original repository) 12 | - [Austin Hudson (@SecIdiot)](https://twitter.com/ilove2pwn_) https://suspicious.actor/2022/05/05/mdsec-nighthawk-study.html 13 | - Originally discovered by [Peter Winter-Smith](peterwintrsmith) and used in MDSec’s Nighthawk 14 | -------------------------------------------------------------------------------- /Src/Main.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | int main( ) 6 | { 7 | puts( "[*] PhaseDive fork of Ekko Sleep Obfuscation by C5pider" ); 8 | 9 | do 10 | // Start Sleep Obfuscation 11 | EkkoObf( 4 * 1000 ); 12 | while ( TRUE ); 13 | 14 | return 0; 15 | } -------------------------------------------------------------------------------- /Src/PhaseDive.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | VOID EkkoObf( DWORD SleepTime ) 9 | { 10 | CONTEXT CtxThread = { 0 }; 11 | 12 | CONTEXT RopProtRW = { 0 }; 13 | CONTEXT RopMemEnc = { 0 }; 14 | CONTEXT RopDelay = { 0 }; 15 | CONTEXT RopMemDec = { 0 }; 16 | CONTEXT RopProtRX = { 0 }; 17 | CONTEXT RopSetEvt = { 0 }; 18 | 19 | HANDLE hTimerQueue = NULL; 20 | HANDLE hNewTimer = NULL; 21 | HANDLE hEvent = NULL; 22 | PVOID ImageBase = NULL; 23 | DWORD ImageSize = 0; 24 | DWORD OldProtect = 0; 25 | 26 | // Can be randomly generated 27 | CHAR KeyBuf[ 16 ]= { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }; 28 | USTRING Key = { 0 }; 29 | USTRING Img = { 0 }; 30 | 31 | PVOID NtContinue = NULL; 32 | PVOID SysFunc032 = NULL; 33 | 34 | PVOID ntdll_jmprax = NULL; 35 | 36 | HANDLE process = GetCurrentProcess(); 37 | MODULEINFO mi = {}; 38 | HMODULE ntdllModule = GetModuleHandleA("ntdll.dll"); 39 | 40 | GetModuleInformation(process, ntdllModule, &mi, sizeof(mi)); 41 | LPVOID ntdllBase = (LPVOID)mi.lpBaseOfDll + 4096; 42 | 43 | CHAR jmprax_bytecode[2] = {0xFF, 0xE0}; 44 | 45 | 46 | for (LPVOID ntdll_cursor=ntdllBase;;ntdll_cursor++) { 47 | if (strncmp(ntdll_cursor, jmprax_bytecode, 2) == 0) { 48 | ntdll_jmprax = ntdll_cursor; 49 | break; 50 | } 51 | } 52 | 53 | 54 | PVOID ntdll_callzw = (PVOID)0x00007FFB300FDDD0; 55 | printf("(ntdll.dll) JMP RAX -> %p\n", ntdll_jmprax); 56 | printf("(ntdll.dll) CALL ZwContinue -> %p FIND YOUR OWN WITH x64dbg\n", ntdll_callzw); 57 | 58 | hEvent = CreateEventW( 0, 0, 0, 0 ); 59 | hTimerQueue = CreateTimerQueue(); 60 | 61 | SysFunc032 = GetProcAddress( LoadLibraryA( "Advapi32" ), "SystemFunction032" ); 62 | 63 | ImageBase = GetModuleHandleA( NULL ); 64 | ImageSize = ( ( PIMAGE_NT_HEADERS ) ( ImageBase + ( ( PIMAGE_DOS_HEADER ) ImageBase )->e_lfanew ) )->OptionalHeader.SizeOfImage; 65 | 66 | Key.Buffer = KeyBuf; 67 | Key.Length = Key.MaximumLength = 16; 68 | 69 | Img.Buffer = ImageBase; 70 | Img.Length = Img.MaximumLength = ImageSize; 71 | 72 | if ( CreateTimerQueueTimer( &hNewTimer, hTimerQueue, RtlCaptureContext, &CtxThread, 0, 0, WT_EXECUTEINTIMERTHREAD ) ) 73 | { 74 | WaitForSingleObject( hEvent, 0x32 ); 75 | 76 | memcpy( &RopProtRW, &CtxThread, sizeof( CONTEXT ) ); 77 | memcpy( &RopMemEnc, &CtxThread, sizeof( CONTEXT ) ); 78 | memcpy( &RopDelay, &CtxThread, sizeof( CONTEXT ) ); 79 | memcpy( &RopMemDec, &CtxThread, sizeof( CONTEXT ) ); 80 | memcpy( &RopProtRX, &CtxThread, sizeof( CONTEXT ) ); 81 | memcpy( &RopSetEvt, &CtxThread, sizeof( CONTEXT ) ); 82 | 83 | // VirtualProtect( ImageBase, ImageSize, PAGE_READWRITE, &OldProtect ); 84 | RopProtRW.Rsp -= 8; 85 | RopProtRW.Rax = VirtualProtect; 86 | RopProtRW.Rip = ntdll_jmprax; 87 | RopProtRW.Rcx = ImageBase; 88 | RopProtRW.Rdx = ImageSize; 89 | RopProtRW.R8 = PAGE_READWRITE; 90 | RopProtRW.R9 = &OldProtect; 91 | 92 | // SystemFunction032( &Key, &Img ); 93 | RopMemEnc.Rsp -= 8; 94 | RopMemEnc.Rax = SysFunc032; 95 | RopMemEnc.Rip = ntdll_jmprax; 96 | RopMemEnc.Rcx = &Img; 97 | RopMemEnc.Rdx = &Key; 98 | 99 | // WaitForSingleObject( hTargetHdl, SleepTime ); 100 | RopDelay.Rsp -= 8; 101 | RopDelay.Rax = WaitForSingleObject; 102 | RopDelay.Rip = ntdll_jmprax; 103 | RopDelay.Rcx = NtCurrentProcess(); 104 | RopDelay.Rdx = SleepTime; 105 | 106 | // SystemFunction032( &Key, &Img ); 107 | RopMemDec.Rsp -= 8; 108 | RopMemDec.Rax = SysFunc032; 109 | RopMemDec.Rip = ntdll_jmprax; 110 | RopMemDec.Rcx = &Img; 111 | RopMemDec.Rdx = &Key; 112 | 113 | // VirtualProtect( ImageBase, ImageSize, PAGE_EXECUTE_READWRITE, &OldProtect ); 114 | RopProtRX.Rsp -= 8; 115 | RopProtRX.Rax = VirtualProtect; 116 | RopProtRX.Rip = ntdll_jmprax; 117 | RopProtRX.Rcx = ImageBase; 118 | RopProtRX.Rdx = ImageSize; 119 | RopProtRX.R8 = PAGE_EXECUTE_READWRITE; 120 | RopProtRX.R9 = &OldProtect; 121 | 122 | // SetEvent( hEvent ); 123 | RopSetEvt.Rsp -= 8; 124 | RopSetEvt.Rax = SetEvent; 125 | RopSetEvt.Rip = ntdll_jmprax; 126 | RopSetEvt.Rcx = hEvent; 127 | 128 | puts( "[INFO] Queue timers" ); 129 | 130 | CreateTimerQueueTimer( &hNewTimer, hTimerQueue, ntdll_callzw, &RopProtRW, 100, 0, WT_EXECUTEINTIMERTHREAD ); 131 | CreateTimerQueueTimer( &hNewTimer, hTimerQueue, ntdll_callzw, &RopMemEnc, 200, 0, WT_EXECUTEINTIMERTHREAD ); 132 | CreateTimerQueueTimer( &hNewTimer, hTimerQueue, ntdll_callzw, &RopDelay, 300, 0, WT_EXECUTEINTIMERTHREAD ); 133 | CreateTimerQueueTimer( &hNewTimer, hTimerQueue, ntdll_callzw, &RopMemDec, 400, 0, WT_EXECUTEINTIMERTHREAD ); 134 | CreateTimerQueueTimer( &hNewTimer, hTimerQueue, ntdll_callzw, &RopProtRX, 500, 0, WT_EXECUTEINTIMERTHREAD ); 135 | CreateTimerQueueTimer( &hNewTimer, hTimerQueue, ntdll_callzw, &RopSetEvt, 600, 0, WT_EXECUTEINTIMERTHREAD ); 136 | 137 | puts( "[INFO] Wait for hEvent" ); 138 | 139 | WaitForSingleObject( hEvent, INFINITE ); 140 | 141 | puts( "[INFO] Finished waiting for event" ); 142 | } 143 | 144 | DeleteTimerQueue( hTimerQueue ); 145 | } 146 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | 2 | CCX64 := x86_64-w64-mingw32-gcc 3 | CCX86 := i686-w64-mingw32-gcc 4 | 5 | CFLAGS := -Os -fno-asynchronous-unwind-tables 6 | CFLAGS += -fno-ident -fpack-struct=8 -falign-functions=1 7 | CFLAGS += -s -ffunction-sections -falign-jumps=1 -w 8 | CFLAGS += -falign-labels=1 -fPIC # -Wl,-Tscripts/Linker.ld 9 | CFLAGS += -Wl,-s,--no-seh,--enable-stdcall-fixup -lPsapi 10 | 11 | OUTX64 := PhaseDive.x64.exe 12 | 13 | all: x64 14 | 15 | x64: 16 | @ echo Compile executable... 17 | @ $(CCX64) Src/*.c -o $(OUTX64) $(CFLAGS) $(LFLAGS) -IInclude 18 | 19 | clean: 20 | @ rm -rf bin/*.exe --------------------------------------------------------------------------------