commit 581e189e2a14baffef66ca5a5abda67a015a5504
Author: Ido Veltzman <62358580+Idov31@users.noreply.github.com>
Date: Sun Sep 25 10:48:32 2022 +0300
Init
Co-Authored-By: Yxel <47823818+janoglezcampos@users.noreply.github.com>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..796f0d2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.vscode
+bin/
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b1d6652
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+BSD 2-Clause License
+
+Copyright (c) 2022, Ido Veltzman, Yxel
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..19d47ff
--- /dev/null
+++ b/README.md
@@ -0,0 +1,56 @@
+# Cronos
+
+  
+
+## This project was co-authored by
+
+ 
+
+## Description
+
+PoC for a new sleep obfuscation technique (based on [Ekko](https://github.com/Cracked5pider/Ekko)) leveraging waitable timers to RC4 encrypt the current process and change the permissions from RW to RX to evade memory scanners.
+
+A more detailed explanation will be available in the blog post (COMING SOON).
+
+
+
+## Usage
+
+To use it, all you have to do is to include Cronos in your project and use it like so:
+
+```c
+#include "Cronos.h"
+
+int main() {
+ int timesToExecute = 1337;
+ int seconds = 10;
+
+ for (int i = 0; i < timesToExecute; i++) {
+ CronosSleep(seconds);
+
+ // YOUR CODE HERE!
+ }
+}
+```
+
+## Setup
+
+To compile it you will need:
+
+- [NASM](https://www.nasm.us/)
+- [make](https://stackoverflow.com/questions/32127524/how-to-install-and-use-make-in-windows)
+- [VisualStudio Compiler](https://developer.microsoft.com/en-US/windows/downloads/windows-sdk/)
+
+After you have all of the above, navigate to the project's directory and build it with the makefile, the EXE will be in the bin directory.
+
+## Contributors
+
+Thanks a lot to those people that contributed to this project:
+
+- [Orca](https://github.com/ORCx41)
+
+## Resources
+
+- [Ekko](https://github.com/Cracked5pider/Ekko)
+- [DeathSleep](https://github.com/janoglezcampos/DeathSleep)
+- [Waitable Timers](https://learn.microsoft.com/en-us/windows/win32/sync/using-a-waitable-timer-with-an-asynchronous-procedure-call)
diff --git a/headers/Cronos.h b/headers/Cronos.h
new file mode 100644
index 0000000..0cb0c84
--- /dev/null
+++ b/headers/Cronos.h
@@ -0,0 +1,29 @@
+#ifndef CRONUS_H
+#define CRONUS_H
+
+#include
+#include
+
+// Macros
+#define TIMER_SLEEP 2000
+#define InitializeTimerMs(ft, sec) \
+ { \
+ (ft)->HighPart = (DWORD)(((ULONGLONG) - ((sec) * 1000 * 10 * 1000)) >> 32); \
+ (ft)->LowPart = (DWORD)(((ULONGLONG) - ((sec) * 1000 * 10 * 1000)) & 0xffffffff); \
+ }
+
+// Struct definitions.
+typedef struct _CRYPT_BUFFER {
+ DWORD Length;
+ DWORD MaximumLength;
+ PVOID Buffer;
+} CRYPT_BUFFER, *PCRYPT_BUFFER, DATA_KEY, *PDATA_KEY, CLEAR_DATA, * PCLEAR_DATA, CYPHER_DATA, * PCYPHER_DATA;
+
+
+// Functions.
+typedef NTSTATUS(WINAPI* tSystemFunction032)(PCRYPT_BUFFER pData, PDATA_KEY pKey);
+
+extern void QuadSleep(PVOID, PVOID, PVOID, PVOID);
+void CronosSleep(int ticks);
+
+#endif
\ No newline at end of file
diff --git a/headers/Utils.h b/headers/Utils.h
new file mode 100644
index 0000000..ee9b7cd
--- /dev/null
+++ b/headers/Utils.h
@@ -0,0 +1,15 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include "Cronos.h"
+#include
+
+#define SIZE_MODULE_LIST 2
+#define MAX_MODULE_NAME 100
+
+BOOL bCompare (const BYTE *pData, const BYTE *bMask, const char *szMask);
+DWORD_PTR findPattern (DWORD_PTR dwAddress, DWORD dwLen, PBYTE bMask, PCHAR szMask);
+DWORD_PTR findInModule (LPCSTR moduleName, PBYTE bMask, PCHAR szMask);
+PVOID findGadget (PBYTE hdrParserFuncB, PCHAR hdrParserFunctMask);
+
+#endif
\ No newline at end of file
diff --git a/img/poc.png b/img/poc.png
new file mode 100644
index 0000000..fe9836a
Binary files /dev/null and b/img/poc.png differ
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..d08628c
--- /dev/null
+++ b/makefile
@@ -0,0 +1,21 @@
+CC = cl
+
+ASM_IN = src/asm/rop.asm
+ASM_OUT = bin/rop.obj
+HEADERS = headers
+BIN_FOLDER = bin
+BIN_NAME = Cronos.exe
+
+all: clean compile_asm compile clean_obj
+
+compile_asm:
+ nasm -f win64 $(ASM_IN) -o bin/rop.obj
+
+compile:
+ $(CC) src/*.c $(ASM_OUT) /I $(HEADERS) /Fo:$(BIN_FOLDER)\ /Fe: $(BIN_FOLDER)\$(BIN_NAME)
+
+clean_obj:
+ del /Q $(BIN_FOLDER)\*.obj
+
+clean:
+ del /Q $(BIN_FOLDER)\*
diff --git a/src/Cronos.c b/src/Cronos.c
new file mode 100644
index 0000000..7921317
--- /dev/null
+++ b/src/Cronos.c
@@ -0,0 +1,148 @@
+#include "Cronos.h"
+#include "Utils.h"
+
+void CronosSleep(int sleepTime) {
+ HANDLE hProtectionRWTimer;
+ HANDLE hProtectionRWXTimer;
+ HANDLE hEncryptionTimer;
+ HANDLE hDecryptionTimer;
+ HANDLE hDummyThreadTimer;
+
+ LARGE_INTEGER protectionRWDueTime;
+ LARGE_INTEGER protectionRWXDueTime;
+ LARGE_INTEGER encryptionDueTime;
+ LARGE_INTEGER decryptionDueTime;
+ LARGE_INTEGER dummyDueTime;
+
+ PVOID rcxGadget;
+ PVOID rdxGadget;
+ PVOID shadowFixerGadget;
+
+ CONTEXT ctxDummyThread = { 0 };
+ CONTEXT ctxProtectionRW = { 0 };
+ CONTEXT ctxProtectionRWX = { 0 };
+ CONTEXT ctxEncryption = { 0 };
+ CONTEXT ctxDecryption = { 0 };
+
+ PVOID NtContinue = NULL;
+ tSystemFunction032 SystemFunction032 = NULL;
+
+ PVOID ImageBase = NULL;
+ DWORD ImageSize = 0;
+ DWORD oldProtect = 0;
+ CRYPT_BUFFER Image = { 0 };
+ DATA_KEY Key = { 0 };
+ CHAR keyBuffer[16] = { 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 };
+
+ // Load systemfunction032.
+ HMODULE hAdvapi32 = LoadLibraryA("Advapi32.dll");
+ HMODULE hNtdll = GetModuleHandleA("Ntdll.dll");
+
+ if (hAdvapi32 == 0 || hNtdll == 0)
+ return;
+
+ SystemFunction032 = (tSystemFunction032) GetProcAddress(hAdvapi32, "SystemFunction032");
+ NtContinue = GetProcAddress(hNtdll, "NtContinue");
+
+ // Getting the image base.
+ ImageBase = GetModuleHandleA(NULL);
+ ImageSize = ((PIMAGE_NT_HEADERS)((DWORD_PTR)ImageBase + ((PIMAGE_DOS_HEADER)ImageBase)->e_lfanew))->OptionalHeader.SizeOfImage;
+
+ // Initializing the image and key for SystemFunction032.
+ Key.Buffer = keyBuffer;
+ Key.Length = Key.MaximumLength = 16;
+
+ Image.Buffer = ImageBase;
+ Image.Length = Image.MaximumLength = ImageSize;
+
+ // Creating the waitable timers.
+ hProtectionRWTimer = CreateWaitableTimerW(NULL, TRUE, L"ProtectionRWTimer");
+ hProtectionRWXTimer = CreateWaitableTimerW(NULL, TRUE, L"ProtectionRWXTimer");
+ hEncryptionTimer = CreateWaitableTimerW(NULL, TRUE, L"EncryptionTimer");
+ hDecryptionTimer = CreateWaitableTimerW(NULL, TRUE, L"DecryptionTimer");
+ hDummyThreadTimer = CreateWaitableTimerW(NULL, TRUE, L"DummyTimer");
+
+ if (hProtectionRWTimer == 0 || hProtectionRWXTimer == 0 ||
+ hEncryptionTimer == 0 || hDecryptionTimer == 0 || hDummyThreadTimer == 0) {
+
+ printf("[ - ] Failed to create waitable timers: %d", GetLastError());
+ FreeLibrary(hAdvapi32);
+ return;
+ }
+
+ InitializeTimerMs(&dummyDueTime, 0);
+
+ // Capture apc context.
+ if (!SetWaitableTimer(hDummyThreadTimer, &dummyDueTime, 0, (PTIMERAPCROUTINE) RtlCaptureContext, &ctxDummyThread, FALSE)) {
+ printf("[ - ] Failed to capture context: %d", GetLastError());
+ goto CleanUp;
+ }
+ SleepEx(INFINITE, TRUE);
+
+ // Creating the contexts.
+ memcpy(&ctxProtectionRW, &ctxDummyThread, sizeof(CONTEXT));
+ memcpy(&ctxEncryption, &ctxDummyThread, sizeof(CONTEXT));
+ memcpy(&ctxDecryption, &ctxDummyThread, sizeof(CONTEXT));
+ memcpy(&ctxProtectionRWX, &ctxDummyThread, sizeof(CONTEXT));
+
+ // VirtualProtect( ImageBase, ImageSize, PAGE_READWRITE, &OldProtect );
+ ctxProtectionRW.Rsp -= (8 + 0x150);
+ ctxProtectionRW.Rip = (DWORD_PTR) VirtualProtect;
+ ctxProtectionRW.Rcx = (DWORD_PTR) ImageBase;
+ ctxProtectionRW.Rdx = ImageSize;
+ ctxProtectionRW.R8 = PAGE_READWRITE;
+ ctxProtectionRW.R9 = (DWORD_PTR) &oldProtect;
+
+ ctxEncryption.Rsp -= (8 + 0xF0);
+ ctxEncryption.Rip = (DWORD_PTR) SystemFunction032;
+ ctxEncryption.Rcx = (DWORD_PTR) &Image;
+ ctxEncryption.Rdx = (DWORD_PTR) &Key;
+
+ ctxDecryption.Rsp -= (8 + 0x90);
+ ctxDecryption.Rip = (DWORD_PTR) SystemFunction032;
+ ctxDecryption.Rcx = (DWORD_PTR) &Image;
+ ctxDecryption.Rdx = (DWORD_PTR) &Key;
+
+ ctxProtectionRWX.Rsp -= (8 + 0x30);
+ ctxProtectionRWX.Rip = (DWORD_PTR) VirtualProtect;
+ ctxProtectionRWX.Rcx = (DWORD_PTR) ImageBase;
+ ctxProtectionRWX.Rdx = ImageSize;
+ ctxProtectionRWX.R8 = PAGE_EXECUTE_READWRITE;
+ ctxProtectionRWX.R9 = (DWORD_PTR) &oldProtect;
+
+ InitializeTimerMs(&protectionRWDueTime, 0 );
+ InitializeTimerMs(&encryptionDueTime, 1 );
+ InitializeTimerMs(&decryptionDueTime, sleepTime-1 );
+ InitializeTimerMs(&protectionRWXDueTime, sleepTime);
+
+ // Getting the gadgets for the sleepex rop.
+ rcxGadget = findGadget((PBYTE) "\x59\xC3", "xx");
+ rdxGadget = findGadget((PBYTE) "\x5A\xC3", "xx");
+ shadowFixerGadget = findGadget((PBYTE) "\x48\x83\xC4\x20\x5F\xC3", "xxxxxx");
+
+ if(rcxGadget == 0 || rdxGadget == 0 || shadowFixerGadget == 0) {
+ printf("[!] Error finding gadget\n");
+ goto CleanUp;
+ }
+
+ // Setting the timers.
+ if (!SetWaitableTimer(hDecryptionTimer, &decryptionDueTime, 0, NtContinue, &ctxDecryption, FALSE) ||
+ !SetWaitableTimer(hProtectionRWXTimer, &protectionRWXDueTime, 0, NtContinue, &ctxProtectionRWX, FALSE) ||
+ !SetWaitableTimer(hProtectionRWTimer, &protectionRWDueTime, 0, NtContinue, &ctxProtectionRW, FALSE) ||
+ !SetWaitableTimer(hEncryptionTimer, &encryptionDueTime, 0, NtContinue, &ctxEncryption, FALSE))
+ {
+ printf("[ - ] Failed to SetWaitableTimer: %d", GetLastError());
+ goto CleanUp;
+ }
+
+ // Executing the code.
+ QuadSleep(rcxGadget, rdxGadget, shadowFixerGadget, (PVOID) SleepEx);
+
+CleanUp:
+ CloseHandle(hDummyThreadTimer);
+ CloseHandle(hDecryptionTimer);
+ CloseHandle(hProtectionRWXTimer);
+ CloseHandle(hProtectionRWTimer);
+ CloseHandle(hEncryptionTimer);
+ FreeLibrary(hAdvapi32);
+}
\ No newline at end of file
diff --git a/src/Main.c b/src/Main.c
new file mode 100644
index 0000000..36ddf0d
--- /dev/null
+++ b/src/Main.c
@@ -0,0 +1,9 @@
+#include "Cronos.h"
+
+int main() {
+ for (int i = 0; i < 4; i++) {
+ printf("\n[ + ] Sleeping\n");
+ CronosSleep(10);
+ printf("[ + ] Code executed!\n");
+ }
+}
\ No newline at end of file
diff --git a/src/Utils.c b/src/Utils.c
new file mode 100644
index 0000000..7c40805
--- /dev/null
+++ b/src/Utils.c
@@ -0,0 +1,74 @@
+#include "Utils.h"
+
+BOOL bCompare(const BYTE *pData, const BYTE *bMask, const char *szMask)
+{
+ for (; *szMask; ++szMask, ++pData, ++bMask)
+ if (*szMask == 'x' && *pData != *bMask)
+ return FALSE;
+
+ return TRUE;
+}
+
+DWORD_PTR findPattern(DWORD_PTR dwAddress, DWORD dwLen, PBYTE bMask, PCHAR szMask)
+{
+ for (DWORD i = 0; i < dwLen; i++)
+ if (bCompare((PBYTE)(dwAddress + i), bMask, szMask))
+ return (DWORD_PTR)(dwAddress + i);
+
+ return 0;
+}
+
+DWORD_PTR findInModule(LPCSTR moduleName, PBYTE bMask, PCHAR szMask)
+{
+ PIMAGE_DOS_HEADER ImageBase = (PIMAGE_DOS_HEADER)GetModuleHandleA(moduleName);
+ PIMAGE_NT_HEADERS imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)ImageBase + ImageBase->e_lfanew);
+ DWORD_PTR section_offset = (DWORD_PTR)ImageBase + ImageBase->e_lfanew + sizeof(IMAGE_NT_HEADERS);
+ PIMAGE_SECTION_HEADER text_section = (PIMAGE_SECTION_HEADER)(section_offset);
+ DWORD_PTR dwAddress = findPattern((DWORD_PTR)ImageBase + text_section->VirtualAddress, text_section->SizeOfRawData, bMask, szMask);
+ return dwAddress;
+}
+
+PVOID findGadget(PBYTE hdrParserFuncB, PCHAR hdrParserFunctMask)
+{
+ HANDLE hProcess;
+ BOOL result;
+ HMODULE *moduleList;
+ DWORD bytesNeeded;
+ DWORD nModules = 0;
+ LPSTR moduleName = NULL;
+ DWORD_PTR ptr = 0;
+ // PBYTE hdrParserFuncB = (PBYTE)"\x48\x89\x22\xc3";
+
+ hProcess = GetCurrentProcess();
+
+ moduleList = malloc(SIZE_MODULE_LIST * sizeof(HMODULE));
+ result = EnumProcessModules(hProcess, moduleList, SIZE_MODULE_LIST * sizeof(HMODULE), &bytesNeeded);
+ if (bytesNeeded > SIZE_MODULE_LIST * sizeof(HMODULE))
+ {
+ moduleList = realloc(moduleList, bytesNeeded);
+ result = EnumProcessModules(hProcess, moduleList, bytesNeeded, &bytesNeeded);
+ }
+
+ if (!result)
+ goto end;
+ for (int iModule = 1; iModule < (bytesNeeded / sizeof(HMODULE)); iModule++)
+ {
+ moduleName = malloc(MAX_MODULE_NAME * sizeof(CHAR));
+ if (GetModuleFileNameExA(hProcess, moduleList[iModule], moduleName, MAX_MODULE_NAME * sizeof(CHAR)) == 0)
+ goto end;
+ ptr = findInModule(moduleName, hdrParserFuncB, hdrParserFunctMask);
+ if (ptr)
+ {
+ break;
+ }
+ }
+end:
+ if (moduleList)
+ free(moduleList);
+
+ if (moduleName)
+ free(moduleName);
+ if (hProcess)
+ CloseHandle(hProcess);
+ return (PVOID)ptr;
+}
diff --git a/src/asm/rop.asm b/src/asm/rop.asm
new file mode 100644
index 0000000..6aff8fb
--- /dev/null
+++ b/src/asm/rop.asm
@@ -0,0 +1,59 @@
+[BITS 64]
+
+GLOBAL QuadSleep
+EXTERN SleepEx
+
+[SECTION .text]
+
+QuadSleep:
+ sub rsp, 0x28 ;0x28
+ mov r10, end
+ push r10 ;0x30
+ push r9 ;0x38
+
+ mov r10, 0xFFFFFFFF
+ push r10 ;first arg ;0x40
+ push rcx ;0x48
+ mov r10, 1
+ push r10 ;second arg ;0x50
+ push rdx ;0x58
+
+ lea r10, [rdx + 1]
+ push r10 ;0x60
+
+ sub rsp, 0x28 ;0x88
+ push r8 ;0x90
+ push r9 ;0x98
+ mov r10, 0xFFFFFFFF
+ push r10 ;first arg ;0xA0
+ push rcx ;0xA8
+ mov r10, 1
+ push r10 ;second arg ;0xB0
+ push rdx ;0xB8
+
+ lea r10, [rdx + 1]
+ push r10 ;0xC0
+
+ sub rsp, 0x28 ;0xE8
+ push r8 ;0xF0
+ push r9 ;0xF8
+ mov r10, 0xFFFFFFFF
+ push r10 ;first arg ;0x100
+ push rcx ;0x108
+ mov r10, 1
+ push r10 ;second arg ;0x110
+ push rdx ;0x118
+
+ lea r10, [rdx + 1]
+ push r10 ;0x120
+
+ sub rsp, 0x28 ;0x148
+ push r8 ;0x150
+ mov rcx, 0xFFFFFFFF ;first arg
+ mov rdx, 1 ;second arg
+
+ jmp r9
+
+end:
+ add rsp, 0x28
+ ret