From b6acdef6f6d764f7b3787cff841a0407d401f78f Mon Sep 17 00:00:00 2001 From: cccs-mog <117194682+cccs-mog@users.noreply.github.com> Date: Wed, 30 Jul 2025 05:16:54 +0000 Subject: [PATCH 01/11] Anti-anti-debug first --- hook_misc.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++- hook_process.c | 3 ++ hook_thread.c | 11 ++++- hook_window.c | 5 +++ hooks.c | 8 ++++ hooks.h | 18 ++++++++ ntapi.h | 5 +++ 7 files changed, 159 insertions(+), 2 deletions(-) diff --git a/hook_misc.c b/hook_misc.c index e532d1c5..f1942d71 100644 --- a/hook_misc.c +++ b/hook_misc.c @@ -424,6 +424,21 @@ HOOKDEF(NTSTATUS, WINAPI, NtClose, LOQ_ntstatus("system", "ps", "Handle", Handle, "Alert", "Tried to close Cuckoo's log handle"); return ret; } + //https://anti-debug.checkpoint.com/techniques/object-handles.html + ULONG Size = 0; + NTSTATUS Status = pNtQueryObject(Handle, 0, &Size, sizeof(Size), &Size); + void* Buff = (void*)calloc(1, Size); + if (!g_config.no_stealth && NTSUCCESS(pNtQueryObject(Handle, 0, Buff, Size))) + __try + { + Old_NtClose(Handle); + ret = STATUS_SUCCESS; + } + __except + { + ret = STATUS_SUCCESS; + } + else ret = Old_NtClose(Handle); LOQ_ntstatus("system", "p", "Handle", Handle); if(NT_SUCCESS(ret)) { @@ -796,6 +811,13 @@ HOOKDEF(NTSTATUS, WINAPI, NtQueryInformationProcess, ) { NTSTATUS ret = Old_NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength); LOQ_ntstatus("process", "ib", "ProcessInformationClass", ProcessInformationClass, "ProcessInformation", ProcessInformationLength, ProcessInformation); + //https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-checkremotedebuggerpresent + if (!g_config.no_stealth && ProcessInformationClass == PROCESSINFOCLASS.ProcessDebugPort) + *ProcessInformation = 0; + else if (!g_config.no_stealth && ProcessInformationClass == PROCESSINFOCLASS.ProcessDebugFlags) + *ProcessInformation = 1; + else if (!g_config.no_stealth && ProcessInformationClass == PROCESSINFOCLASS.ProcessDebugObjectHandle) + *ProcessInformation = 0; return ret; } @@ -832,7 +854,10 @@ HOOKDEF(NTSTATUS, WINAPI, NtQuerySystemInformation, PLARGE_INTEGER perf_info = (PLARGE_INTEGER)SystemInformation; perf_info->HighPart |= 2; } - + //https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-checkremotedebuggerpresent + if (!g_config.no_stealth && SystemInformationClass == SYSTEM_INFORMATION_CLASS.SystemKernelDebuggerInformation) + SYSTEM_KERNEL_DEBUGGER_INFORMATION perf_info = (SYSTEM_KERNEL_DEBUGGER_INFORMATION)SystemInformation; + perf_info->DebuggerNotPresent = 1; return ret; } @@ -1961,3 +1986,87 @@ HOOKDEF(ULONG, __fastcall, vDbgPrintExWithPrefixInternal, return Old_vDbgPrintExWithPrefixInternal(Prefix, ComponentId, Level, Format, arglist, HandleBreakpoint); } + +HOOKDEF(BOOL, WINAPI, NtDebugActiveProcess, + _In_ HANDLE ProcessHandle, + _In_ HANDLE DebugObjectHandle +){ + DWORD pid = pid_from_process_handle(ProcessHandle); + DWORD debug_pid = pid_from_process_handle(DebugObjectHandle); + BOOL ret = Old_NtDebugActiveProcess(ProcessHandle, DebugObjectHandle); + LOQ_bool("misc", "ll", "ProcessID", pid, "DebugObject", debug_pid); + //https://anti-debug.checkpoint.com/techniques/interactive.html + if (!g_config.no_stealth && is_protected_pid(pid) && g_config.debugger) + ret = TRUE; + return ret; +} + +HOOKDEF(NTSTATUS, WINAPI, DbgUiDebugActiveProcess, + _In_ HANDLE ProcessHandle +){ + DWORD pid = pid_from_process_handle(ProcessHandle); + NTSTATUS ret = Old_DbgUiDebugActiveProcess(ProcessHandle); + LOQ_ntstatus("misc", "l", "ProcessID", pid); + //https://anti-debug.checkpoint.com/techniques/interactive.html + if (!g_config.no_stealth && is_protected_pid(pid) && g_config.debugger) + return TRUE; + return ret; +} + +HOOKDEF(NTSTATUS, WINAPI, GenerateConsoleCtrlEvent, + _In_ DWORD dwCtrlEvent, + _In_ DWORD dwProcessGroupId +){ + //https://anti-debug.checkpoint.com/techniques/interactive.html + //Assuming the ProcessGroupID is the pid in this case make sense because it would be a case where it's targeted specifically ? + if (!g_config.no_stealth && g_config.debugger && is_protected_pid(dwProcessGroupId)) { + ret = TRUE; + } + NTSTATUS ret = Old_GenerateConsoleCtrlEvent(dwCtrlEvent,dwProcessGroupId); + LOQ_ntstatus("misc", "ll", "CtrlEvent", dwCtrlEvent, "ProcessGroupID", dwProcessGroupId); + return ret; +} + +//Does this solve more problems than it create ? +//https://anti-debug.checkpoint.com/techniques/interactive.html +HOOKDEF(BOOL, WINAPI, BlockInput, + _In_ BOOL fBlockIt +){ + BOOL ret; + if(!g_config.no_stealth) + { + + int length = sizeof(BlockInputInstances) / sizeof(struct BlockInputThreadInstance); + DWORD ThreadId = GetCurrentThreadId(); + BOOL found = FALSE; + int index = 0; + BOOL CurrentState = FALSE; + //Don't run the real deal since it's an effective way to block a debugger and a way to detect hooking. + for(int i=0;iDr0 = 0; + Context->Dr1 = 0; + Context->Dr2 = 0; + Context->Dr3 = 0 + } LOQ_ntstatus("threading", "pi", "ThreadHandle", ThreadHandle, "ProcessId", pid); return ret; @@ -835,6 +841,9 @@ HOOKDEF(NTSTATUS, WINAPI, NtYieldExecution, NTSTATUS ret = 0; LOQ_void("threading", ""); ret = Old_NtYieldExecution(); + //https://anti-debug.checkpoint.com/techniques/misc.html + if (!g_config.no_stealth && rand() % 2 == 1): + ret = -1; return ret; } diff --git a/hook_window.c b/hook_window.c index 8bf7b296..80a20d58 100644 --- a/hook_window.c +++ b/hook_window.c @@ -453,6 +453,11 @@ HOOKDEF(BOOL, WINAPI, EnumWindows, ) { BOOL ret = Old_EnumWindows(lpEnumFunc, lParam); + if (sizeof(*lpEnumFunc)>5*sizeof(void *)) + { + LOQ_bool("windows","p", "lpEnumFunc", lpEnumFunc); + } + else LOQ_bool("windows", ""); return ret; } diff --git a/hooks.c b/hooks.c index 2f321063..73676c43 100644 --- a/hooks.c +++ b/hooks.c @@ -417,6 +417,10 @@ hook_t full_hooks[] = { HOOK(oleaut32, VarBstrCat), HOOK_NOTAIL(usp10, ScriptIsComplex, 3), HOOK_NOTAIL(inseng,DownloadFile,3), + HOOK(ntdll, DbgUiDebugActiveProcess), + HOOK(ntdll, NtDebugActiveProcess), + HOOK(Kernel32, GenerateConsoleCtrlEvent), + HOOK(User32, BlockInput), #ifndef _WIN64 HOOK(ntdll, RtlDosPathNameToNtPathName_U), HOOK(ntdll, NtQueryLicenseValue), @@ -1333,6 +1337,10 @@ hook_t office_hooks[] = { HOOK(oleaut32, VarBstrCat), HOOK_NOTAIL(usp10, ScriptIsComplex, 3), HOOK_NOTAIL(inseng,DownloadFile,3), + HOOK(ntdll, DbgUiDebugActiveProcess), + HOOK(ntdll, NtDebugActiveProcess), + HOOK(Kernel32, GenerateConsoleCtrlEvent), + HOOK(User32, BlockInput), #ifndef _WIN64 HOOK(ntdll, RtlDosPathNameToNtPathName_U), HOOK(ntdll, NtQueryLicenseValue), diff --git a/hooks.h b/hooks.h index 58502ac0..5c8bd6ac 100644 --- a/hooks.h +++ b/hooks.h @@ -3737,4 +3737,22 @@ HOOKDEF(ULONG, __fastcall, vDbgPrintExWithPrefixInternal, __in BOOLEAN HandleBreakpoint ); +HOOKDEF(BOOL, WINAPI, NtDebugActiveProcess, + _In_ HANDLE ProcessHandle, + _In_ HANDLE DebugObjectHandle +); + +HOOKDEF(NTSTATUS, WINAPI, DbgUiDebugActiveProcess, + _In_ HANDLE ProcessHandle +); + +HOOKDEF(NTSTATUS, WINAPI, GenerateConsoleCtrlEvent, + _In_ DWORD dwCtrlEvent, + _In_ DWORD dwProcessGroupId +); + +HOOKDEF(BOOL, WINAPI, BlockInput, + _In_ BOOL fBlockIt +); + #include "hook_vbscript.h" diff --git a/ntapi.h b/ntapi.h index 69eed2ae..4ec806b7 100644 --- a/ntapi.h +++ b/ntapi.h @@ -271,6 +271,11 @@ typedef struct _SYSTEM_PROCESS_INFORMATION { SYSTEM_THREAD Threads[0]; } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; +typedef struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION { + BOOLEAN KernelDebuggerEnabled; + BOOLEAN KernelDebuggerNotPresent; +} SYSTEM_KERNEL_DEBUGGER_INFORMATION; + typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { LARGE_INTEGER IdleTime; LARGE_INTEGER KernelTime; From fb010faef13c5e2e8c162b4a52bb58ebb30387db Mon Sep 17 00:00:00 2001 From: cccs-mog <117194682+cccs-mog@users.noreply.github.com> Date: Wed, 30 Jul 2025 16:36:35 +0000 Subject: [PATCH 02/11] Fixing anti-anti-debug --- capemon.vcxproj | 8 +++---- hook_misc.c | 61 ++++++++++++++++++++++++++++++++++--------------- hook_process.c | 2 +- hook_thread.c | 4 ++-- hook_window.c | 2 +- misc.c | 2 +- ntapi.h | 9 ++++---- 7 files changed, 57 insertions(+), 31 deletions(-) diff --git a/capemon.vcxproj b/capemon.vcxproj index e01a1f72..9abc9b15 100644 --- a/capemon.vcxproj +++ b/capemon.vcxproj @@ -477,12 +477,12 @@ - true - false + true + false - true - false + true + false diff --git a/hook_misc.c b/hook_misc.c index f1942d71..f5948f70 100644 --- a/hook_misc.c +++ b/hook_misc.c @@ -36,6 +36,7 @@ along with this program. If not, see . extern char *our_process_name; extern void ProcessMessage(DWORD ProcessId, DWORD ThreadId); extern const char* GetLanguageName(LANGID langID); +extern NTSTATUS pNtQueryObject(HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass, PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength); extern BOOL TraceRunning; @@ -44,6 +45,11 @@ extern BOOL Trace(struct _EXCEPTION_POINTERS* ExceptionInfo); LPTOP_LEVEL_EXCEPTION_FILTER TopLevelExceptionFilter; BOOL PlugXConfigDumped, CompressedPE; DWORD ExportAddress; +struct BlockInputThreadInstance { + BOOL CurrentlyBlockedInput; + DWORD BlockInputThreadID; +}; +static struct BlockInputThreadInstance BlockInputInstances[256]; //Should be enough for most situations HOOKDEF(HHOOK, WINAPI, SetWindowsHookExA, __in int idHook, @@ -426,20 +432,23 @@ HOOKDEF(NTSTATUS, WINAPI, NtClose, } //https://anti-debug.checkpoint.com/techniques/object-handles.html ULONG Size = 0; + ULONG Size2 = 0; NTSTATUS Status = pNtQueryObject(Handle, 0, &Size, sizeof(Size), &Size); void* Buff = (void*)calloc(1, Size); - if (!g_config.no_stealth && NTSUCCESS(pNtQueryObject(Handle, 0, Buff, Size))) - __try + if (!g_config.no_stealth && NT_SUCCESS(pNtQueryObject(Handle, 0, Buff, Size, &Size2))) + { + __try { - Old_NtClose(Handle); + Old_NtClose(Handle); ret = STATUS_SUCCESS; } - __except + __except(EXCEPTION_EXECUTE_HANDLER) { ret = STATUS_SUCCESS; } + } else - ret = Old_NtClose(Handle); + ret = Old_NtClose(Handle); LOQ_ntstatus("system", "p", "Handle", Handle); if(NT_SUCCESS(ret)) { remove_file_from_log_tracking(Handle); @@ -812,12 +821,24 @@ HOOKDEF(NTSTATUS, WINAPI, NtQueryInformationProcess, NTSTATUS ret = Old_NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength); LOQ_ntstatus("process", "ib", "ProcessInformationClass", ProcessInformationClass, "ProcessInformation", ProcessInformationLength, ProcessInformation); //https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-checkremotedebuggerpresent - if (!g_config.no_stealth && ProcessInformationClass == PROCESSINFOCLASS.ProcessDebugPort) - *ProcessInformation = 0; - else if (!g_config.no_stealth && ProcessInformationClass == PROCESSINFOCLASS.ProcessDebugFlags) - *ProcessInformation = 1; - else if (!g_config.no_stealth && ProcessInformationClass == PROCESSINFOCLASS.ProcessDebugObjectHandle) - *ProcessInformation = 0; + if (!g_config.no_stealth && ProcessInformationClass == ProcessDebugPort) + { + HANDLE value =0; + void* voidPtr = &value; + ProcessInformation = (HANDLE*)voidPtr; + } + else if (!g_config.no_stealth && ProcessInformationClass == ProcessDebugFlags) + { + ULONG value = 1; + void* voidPtr = &value; + ProcessInformation = (ULONG*)voidPtr; + } + else if (!g_config.no_stealth && ProcessInformationClass == ProcessDebugObjectHandle) + { + HANDLE value = 0; + void* voidPtr = &value; + ProcessInformation = (HANDLE*)voidPtr; + } return ret; } @@ -855,9 +876,11 @@ HOOKDEF(NTSTATUS, WINAPI, NtQuerySystemInformation, perf_info->HighPart |= 2; } //https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-checkremotedebuggerpresent - if (!g_config.no_stealth && SystemInformationClass == SYSTEM_INFORMATION_CLASS.SystemKernelDebuggerInformation) - SYSTEM_KERNEL_DEBUGGER_INFORMATION perf_info = (SYSTEM_KERNEL_DEBUGGER_INFORMATION)SystemInformation; - perf_info->DebuggerNotPresent = 1; + if (!g_config.no_stealth && SystemInformationClass == SystemKernelDebuggerInformation) + { + PSYSTEM_KERNEL_DEBUGGER_INFORMATION perf_info = (PSYSTEM_KERNEL_DEBUGGER_INFORMATION)SystemInformation; + perf_info->DebuggerNotPresent = 1; + } return ret; } @@ -2019,15 +2042,14 @@ HOOKDEF(NTSTATUS, WINAPI, GenerateConsoleCtrlEvent, ){ //https://anti-debug.checkpoint.com/techniques/interactive.html //Assuming the ProcessGroupID is the pid in this case make sense because it would be a case where it's targeted specifically ? + NTSTATUS ret = Old_GenerateConsoleCtrlEvent(dwCtrlEvent,dwProcessGroupId); + LOQ_ntstatus("misc", "ll", "CtrlEvent", dwCtrlEvent, "ProcessGroupID", dwProcessGroupId); if (!g_config.no_stealth && g_config.debugger && is_protected_pid(dwProcessGroupId)) { ret = TRUE; } - NTSTATUS ret = Old_GenerateConsoleCtrlEvent(dwCtrlEvent,dwProcessGroupId); - LOQ_ntstatus("misc", "ll", "CtrlEvent", dwCtrlEvent, "ProcessGroupID", dwProcessGroupId); return ret; } -//Does this solve more problems than it create ? //https://anti-debug.checkpoint.com/techniques/interactive.html HOOKDEF(BOOL, WINAPI, BlockInput, _In_ BOOL fBlockIt @@ -2054,11 +2076,14 @@ HOOKDEF(BOOL, WINAPI, BlockInput, index = length; } if (!CurrentState && fBlockIt) + { BlockInputInstances[index].CurrentlyBlockedInput = TRUE; ret = TRUE; + } else if (CurrentState && fBlockIt) ret = FALSE; - else if(CurrentState && !fBlockIt){ + else if(CurrentState && !fBlockIt) + { BlockInputInstances[index].CurrentlyBlockedInput = FALSE; ret = TRUE; } diff --git a/hook_process.c b/hook_process.c index 5b394718..8c65387f 100644 --- a/hook_process.c +++ b/hook_process.c @@ -528,7 +528,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtOpenProcess, LOQ_ntstatus("process", "Phi", "ProcessHandle", ProcessHandle, "DesiredAccess", DesiredAccess, "ProcessIdentifier", pid); //https://anti-debug.checkpoint.com/techniques/object-handles.html if (!g_config.no_stealth && strcmp(ProcessName, "csrss.exe") == 0) - return NULL + return NULL; return ret; } diff --git a/hook_thread.c b/hook_thread.c index 102a943c..dce96a73 100644 --- a/hook_thread.c +++ b/hook_thread.c @@ -401,7 +401,7 @@ HOOKDEF(NTSTATUS, WINAPI, RtlWow64GetThreadContext, Context->Dr0 = 0; Context->Dr1 = 0; Context->Dr2 = 0; - Context->Dr3 = 0 + Context->Dr3 = 0; } LOQ_ntstatus("threading", "pi", "ThreadHandle", ThreadHandle, "ProcessId", pid); @@ -842,7 +842,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtYieldExecution, LOQ_void("threading", ""); ret = Old_NtYieldExecution(); //https://anti-debug.checkpoint.com/techniques/misc.html - if (!g_config.no_stealth && rand() % 2 == 1): + if (!g_config.no_stealth && rand() % 2 == 1) ret = -1; return ret; } diff --git a/hook_window.c b/hook_window.c index 80a20d58..e81c2f20 100644 --- a/hook_window.c +++ b/hook_window.c @@ -453,7 +453,7 @@ HOOKDEF(BOOL, WINAPI, EnumWindows, ) { BOOL ret = Old_EnumWindows(lpEnumFunc, lParam); - if (sizeof(*lpEnumFunc)>5*sizeof(void *)) + if (sizeof(lpEnumFunc)>5*sizeof(void *)) { LOQ_bool("windows","p", "lpEnumFunc", lpEnumFunc); } diff --git a/misc.c b/misc.c index e224e72a..806ce32b 100644 --- a/misc.c +++ b/misc.c @@ -37,7 +37,7 @@ static _NtQueryInformationProcess pNtQueryInformationProcess; static _NtQueryInformationThread pNtQueryInformationThread; static _RtlGenRandom pRtlGenRandom; static _NtQueryAttributesFile pNtQueryAttributesFile; -static _NtQueryObject pNtQueryObject; +_NtQueryObject pNtQueryObject; static _NtQueryKey pNtQueryKey; static _NtDelayExecution pNtDelayExecution; static _NtQuerySystemInformation pNtQuerySystemInformation; diff --git a/ntapi.h b/ntapi.h index 4ec806b7..81816bac 100644 --- a/ntapi.h +++ b/ntapi.h @@ -272,9 +272,9 @@ typedef struct _SYSTEM_PROCESS_INFORMATION { } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; typedef struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION { - BOOLEAN KernelDebuggerEnabled; - BOOLEAN KernelDebuggerNotPresent; -} SYSTEM_KERNEL_DEBUGGER_INFORMATION; + BOOLEAN DebuggerEnabled; + BOOLEAN DebuggerNotPresent; +} SYSTEM_KERNEL_DEBUGGER_INFORMATION, *PSYSTEM_KERNEL_DEBUGGER_INFORMATION; typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { LARGE_INTEGER IdleTime; @@ -317,7 +317,8 @@ typedef enum _SYSTEM_INFORMATION_CLASS { SystemVdmBopInformation, SystemFileCacheInformation, SystemInterruptInformation = 23, - SystemExceptionInformation = 33 + SystemExceptionInformation = 33, + SystemKernelDebuggerInformation = 35 } SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS; typedef struct _SYSTEM_BASIC_INFORMATION { From e072e143149a933f7d776ed35143f5254989b764 Mon Sep 17 00:00:00 2001 From: cccs-mog <117194682+cccs-mog@users.noreply.github.com> Date: Wed, 30 Jul 2025 16:59:57 +0000 Subject: [PATCH 03/11] Fixing code assist concern and missed --- hook_misc.c | 26 ++++++++++++++------------ hook_process.c | 5 +++-- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/hook_misc.c b/hook_misc.c index f5948f70..b7e27bb5 100644 --- a/hook_misc.c +++ b/hook_misc.c @@ -435,7 +435,12 @@ HOOKDEF(NTSTATUS, WINAPI, NtClose, ULONG Size2 = 0; NTSTATUS Status = pNtQueryObject(Handle, 0, &Size, sizeof(Size), &Size); void* Buff = (void*)calloc(1, Size); - if (!g_config.no_stealth && NT_SUCCESS(pNtQueryObject(Handle, 0, Buff, Size, &Size2))) + BOOLEAN valid_handle; + if (!Buff) + valid_handle = NT_SUCCESS(pNtQueryObject(Handle, 0, Buff, Size, &Size2)); + else + valid_handle = FALSE; + if (!g_config.no_stealth && valid_handle) { __try { @@ -823,21 +828,18 @@ HOOKDEF(NTSTATUS, WINAPI, NtQueryInformationProcess, //https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-checkremotedebuggerpresent if (!g_config.no_stealth && ProcessInformationClass == ProcessDebugPort) { - HANDLE value =0; - void* voidPtr = &value; - ProcessInformation = (HANDLE*)voidPtr; + if (ProcessInformationLength >= sizeof(HANDLE)) + *(HANDLE*)ProcessInformation = 0; } else if (!g_config.no_stealth && ProcessInformationClass == ProcessDebugFlags) { - ULONG value = 1; - void* voidPtr = &value; - ProcessInformation = (ULONG*)voidPtr; + if (ProcessInformationLength >= sizeof(ULONG)) + *(ULONG*)ProcessInformation = 1; } else if (!g_config.no_stealth && ProcessInformationClass == ProcessDebugObjectHandle) { - HANDLE value = 0; - void* voidPtr = &value; - ProcessInformation = (HANDLE*)voidPtr; + if (ProcessInformationLength >= sizeof(HANDLE)) + *(HANDLE*)ProcessInformation = 0; } return ret; } @@ -2072,8 +2074,8 @@ HOOKDEF(BOOL, WINAPI, BlockInput, } } if (!found) { - BlockInputInstances[length].BlockInputThreadID = ThreadId; - index = length; + BlockInputInstances[length-1].BlockInputThreadID = ThreadId; + index = length-1; } if (!CurrentState && fBlockIt) { diff --git a/hook_process.c b/hook_process.c index 8c65387f..c6086231 100644 --- a/hook_process.c +++ b/hook_process.c @@ -527,8 +527,9 @@ HOOKDEF(NTSTATUS, WINAPI, NtOpenProcess, else LOQ_ntstatus("process", "Phi", "ProcessHandle", ProcessHandle, "DesiredAccess", DesiredAccess, "ProcessIdentifier", pid); //https://anti-debug.checkpoint.com/techniques/object-handles.html - if (!g_config.no_stealth && strcmp(ProcessName, "csrss.exe") == 0) - return NULL; + if(!ProcessName) + if (!g_config.no_stealth && strcmp(ProcessName, "csrss.exe") == 0) + return NULL; return ret; } From b1f997ecffa57f27d0b395f75c2fb27fb8b38cc1 Mon Sep 17 00:00:00 2001 From: cccs-mog <117194682+cccs-mog@users.noreply.github.com> Date: Wed, 30 Jul 2025 17:02:20 +0000 Subject: [PATCH 04/11] Freeing buff --- hook_misc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hook_misc.c b/hook_misc.c index b7e27bb5..596df0ff 100644 --- a/hook_misc.c +++ b/hook_misc.c @@ -460,6 +460,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtClose, DumpSectionViewsForHandle(Handle); file_close(Handle); } + free Buff; return ret; } From 4ec2ab119137fb90f2353e1ea4f99341e30d9e26 Mon Sep 17 00:00:00 2001 From: cccs-mog <117194682+cccs-mog@users.noreply.github.com> Date: Wed, 30 Jul 2025 20:14:18 +0000 Subject: [PATCH 05/11] Freeing buff --- hook_misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hook_misc.c b/hook_misc.c index 596df0ff..5286d37a 100644 --- a/hook_misc.c +++ b/hook_misc.c @@ -460,7 +460,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtClose, DumpSectionViewsForHandle(Handle); file_close(Handle); } - free Buff; + free(Buff); return ret; } From 753cfa90b20d108650d2aae96266d4f0f392900c Mon Sep 17 00:00:00 2001 From: cccs-mog <117194682+cccs-mog@users.noreply.github.com> Date: Wed, 30 Jul 2025 20:17:44 +0000 Subject: [PATCH 06/11] Compiler option copy paste error --- capemon.vcxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/capemon.vcxproj b/capemon.vcxproj index 9abc9b15..f98b0c78 100644 --- a/capemon.vcxproj +++ b/capemon.vcxproj @@ -478,11 +478,11 @@ true - false + true true - false + true From 587d7ca7985fb8cf2ae54739c9b6775d5f05d1db Mon Sep 17 00:00:00 2001 From: cccs-mog <117194682+cccs-mog@users.noreply.github.com> Date: Thu, 31 Jul 2025 15:09:45 +0000 Subject: [PATCH 07/11] Using GetThreadContextHandler instead for RtlWow64GetThreadContext --- hook_thread.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hook_thread.c b/hook_thread.c index dce96a73..1b56edeb 100644 --- a/hook_thread.c +++ b/hook_thread.c @@ -397,11 +397,9 @@ HOOKDEF(NTSTATUS, WINAPI, RtlWow64GetThreadContext, NTSTATUS ret = Old_RtlWow64GetThreadContext(ThreadHandle, Context); //https://anti-debug.checkpoint.com/techniques/process-memory.html - if (!g_config.no_stealth && g_config.debugger) { - Context->Dr0 = 0; - Context->Dr1 = 0; - Context->Dr2 = 0; - Context->Dr3 = 0; + if (!g_config.no_stealth) { + // This needs to be __declspec(noinline) to prevent inlining + GetThreadContextHandler(ThreadHandle, Context); } LOQ_ntstatus("threading", "pi", "ThreadHandle", ThreadHandle, "ProcessId", pid); From 5f0ab1a266dd01e5cfe21bc0afc1221178c7e290 Mon Sep 17 00:00:00 2001 From: cccs-mog <117194682+cccs-mog@users.noreply.github.com> Date: Mon, 11 Aug 2025 20:05:58 +0000 Subject: [PATCH 08/11] Removing unneeded hooks aad and testing the remaining --- CAPE/CAPE.h | 1 + CAPE/Debugger.c | 45 ++++++++----- CAPE/Debugger.h | 2 +- CAPE/Injection.c | 6 +- CAPE/Trace.c | 6 +- CAPE/YaraHarness.c | 80 +++++++++++++--------- capemon.vcxproj | 4 +- config.c | 17 +++-- hook_misc.c | 162 +++++++-------------------------------------- hook_process.c | 4 -- hooks.c | 8 --- hooks.h | 18 ----- ntapi.h | 8 +-- tests/PR_test.c | 62 +++++++++++++++++ 14 files changed, 184 insertions(+), 239 deletions(-) create mode 100644 tests/PR_test.c diff --git a/CAPE/CAPE.h b/CAPE/CAPE.h index 61f1d821..3cadd931 100644 --- a/CAPE/CAPE.h +++ b/CAPE/CAPE.h @@ -77,6 +77,7 @@ int DumpImageInCurrentProcess(PVOID ImageBase); void DumpSectionViewsForPid(DWORD Pid); BOOL DumpRange(PVOID Address, SIZE_T Size); BOOL DumpStackRegion(void); +void DumpStrings(void); BOOL ProcessDumped; unsigned int DumpCount, DotNetCacheDumpCount; diff --git a/CAPE/Debugger.c b/CAPE/Debugger.c index 7179c5a6..57c78e11 100644 --- a/CAPE/Debugger.c +++ b/CAPE/Debugger.c @@ -104,32 +104,43 @@ HANDLE GetThreadHandle(DWORD ThreadId) } //************************************************************************************** -BOOL PatchByte(LPVOID Address, BYTE Byte) +BOOL PatchBytes(LPVOID Address, const char* HexBytes) //************************************************************************************** { - DWORD OldProtect; + if (!Address || !HexBytes || !IsAddressAccessible(Address)) + { + DebugOutput("PatchBytes: Invalid address or hex string"); + return FALSE; + } - if (!Address || !IsAddressAccessible(Address)) - return FALSE; + SIZE_T HexLen = strlen(HexBytes); + if (HexLen == 0 || HexLen % 2 != 0) + { + DebugOutput("PatchBytes: Invalid hex string length %d", HexLen); + return FALSE; + } - if (!VirtualProtect(Address, 1, PAGE_EXECUTE_READWRITE, &OldProtect)) + SIZE_T ByteCount = HexLen / 2; + DWORD OldProtect; + if (!VirtualProtect(Address, ByteCount, PAGE_EXECUTE_READWRITE, &OldProtect)) { - DebugOutput("PatchByte: Unable to change memory protection at 0x%p", Address); - return FALSE; - } + DebugOutput("PatchBytes: Failed to change memory protection at 0x%p", Address); + return FALSE; + } -#ifdef DEBUG_COMMENTS - DebugOutput("PatchByte: Changed memory protection at 0x%p", Address); -#endif + PBYTE Dest = (PBYTE)Address; + for (SIZE_T i = 0; i < HexLen; i += 2) + { + char HexByte[3] = { HexBytes[i], HexBytes[i + 1], '\0' }; + BYTE Byte = (BYTE)strtoul(HexByte, NULL, 16); + *Dest++ = Byte; + } - *(PBYTE)Address = Byte; + VirtualProtect(Address, ByteCount, OldProtect, &OldProtect); -#ifdef DEBUG_COMMENTS - DebugOutput("PatchByte: New instruction byte at 0x%p: 0x%x", Address, *(PBYTE)Address); -#endif - VirtualProtect(Address, 1, OldProtect, &OldProtect); + DebugOutput("PatchBytes: Patched %zu bytes at 0x%p: %s", ByteCount, Address, HexBytes); - return TRUE; + return TRUE; } //************************************************************************************** diff --git a/CAPE/Debugger.h b/CAPE/Debugger.h index a0f69dfa..0b990c6f 100644 --- a/CAPE/Debugger.h +++ b/CAPE/Debugger.h @@ -181,7 +181,7 @@ BOOL InitialiseDebugger(void); BOOL ResumeFromBreakpoint(PCONTEXT Context); void OutputThreadBreakpoints(DWORD ThreadId); void DebugOutputThreadBreakpoints(); -BOOL PatchByte(LPVOID Address, BYTE Byte); +BOOL PatchBytes(LPVOID Address, const char* HexBytes); void ShowStack(DWORD_PTR StackPointer, unsigned int NumberOfRecords); diff --git a/CAPE/Injection.c b/CAPE/Injection.c index 96a5a668..89f384e8 100644 --- a/CAPE/Injection.c +++ b/CAPE/Injection.c @@ -46,6 +46,8 @@ extern char *our_process_name; extern void hook_disable(); extern void hook_enable(); +void ProcessMessage(DWORD ProcessId, DWORD ThreadId); + //************************************************************************************** PINJECTIONINFO GetInjectionInfo(DWORD ProcessId) //************************************************************************************** @@ -516,7 +518,6 @@ void DumpSectionViewsForHandle(HANDLE SectionHandle) __declspec(noinline) void GetThreadContextHandler(HANDLE ThreadHandle, LPCONTEXT Context) { - DebugOutput("GetThreadContextHandler"); DWORD Pid = pid_from_thread_handle(ThreadHandle); if (Context && Context->ContextFlags & CONTEXT_CONTROL) @@ -550,7 +551,8 @@ __declspec(noinline) void SetThreadContextHandler(HANDLE ThreadHandle, CONTEXT * DWORD Pid = pid_from_thread_handle(ThreadHandle); DWORD Tid = tid_from_thread_handle(ThreadHandle); - DebugOutput("SetThreadContextHandler: Pid %d", GetCurrentProcessId()); + if (Pid != GetCurrentProcessId()) + ProcessMessage(Pid, 0); if (g_config.debugger && Pid == GetCurrentProcessId()) { diff --git a/CAPE/Trace.c b/CAPE/Trace.c index ada9672a..a767810c 100644 --- a/CAPE/Trace.c +++ b/CAPE/Trace.c @@ -1631,6 +1631,10 @@ void ActionDispatcher(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst De DumpAddress = 0; DumpSize = 0; } + else if (!stricmp(Action, "DumpStrings")) + { + DumpStrings(); + } else if (!stricmp(Action, "Step2OEP")) { SetSingleStepMode(ExceptionInfo->ContextRecord, ProcessOEP); @@ -2021,7 +2025,7 @@ void InstructionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst #endif SkipInstruction(ExceptionInfo->ContextRecord); if (lookup_get(&SoftBPs, (ULONG_PTR)CIP, 0)) - PatchByte(CIP, 0xCC); + PatchBytes(CIP, "CC"); } else if (!strcmp(DecodedInstruction.mnemonic.p, "RET")) { diff --git a/CAPE/YaraHarness.c b/CAPE/YaraHarness.c index 6a78cc89..38630acf 100644 --- a/CAPE/YaraHarness.c +++ b/CAPE/YaraHarness.c @@ -114,21 +114,25 @@ void ScannerError(int Error) } } -BOOL ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match) +void ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match, void* user_data) { char *Value, *Key, *p, *q, *r, c = 0; - unsigned int ValueLength; - int delta=0; + ULONG_PTR delta=0; + SIZE_T ValueLength = 0; + if (!Line || !Identifier) - return FALSE; + return; + p = strchr(Line, '$'); if (!p) - return FALSE; + return; + p = strchr(Line, '='); if (!p) - return FALSE; + return; + r = strchr(p, ':'); - if (r) + if (r && *(r + 1) == '$') Value = r + 1; else Value = p + 1; @@ -151,12 +155,12 @@ BOOL ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match) } if (q) { - ValueLength = (unsigned int)(DWORD_PTR)(q-(DWORD_PTR)Value); + ValueLength = (SIZE_T)(DWORD_PTR)(q-(DWORD_PTR)Value); if (*(q-1) == '*') ValueLength--; } else - ValueLength = (unsigned int)strlen(Value); + ValueLength = (SIZE_T)strlen(Value); if (*(Value+ValueLength-1) == '*') { @@ -164,28 +168,44 @@ BOOL ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match) delta += Match->match_length - 1; } - if (strncmp(Value, Identifier, ValueLength)) - return FALSE; + SIZE_T IdentifierLength = strlen(Identifier); + if (strncmp(Value, Identifier, IdentifierLength < ValueLength ? IdentifierLength : ValueLength)) + return; Key = Line; - if (r) { + if (r && *(r + 1) == '$') + { c = *r; *r = 0; } - else { + else + { c = *p; *p = 0; } + + if (_strnicmp(Line, "bp", 2) && strncmp(Line, "br", 2)) + delta += (ULONG_PTR)user_data; + memset(NewLine, 0, sizeof(NewLine)); - sprintf(NewLine, "%s%c0x%p\0", Key, c, (PUCHAR)Match->offset+delta); if (r) + sprintf(NewLine, "%s%c0x%p%s\0", Key, c, (PUCHAR)Match->offset+delta, r); + else + sprintf(NewLine, "%s%c0x%p\0", Key, c, (PUCHAR)Match->offset+delta); + + if (r && *(r + 1) == '$') *r = c; else *p = c; - p = strchr(NewLine, '$'); - if (p) - return FALSE; - return TRUE; + +#ifdef DEBUG_COMMENTS + DebugOutput("ParseOptionLine: %s", NewLine); +#endif + + if (!strchr(NewLine, '$')) + parse_config_line(NewLine); + + return; } int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data) @@ -219,27 +239,23 @@ int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void char *p = strchr(OptionLine, ','); if (p) *p = 0; - yr_rule_strings_foreach(Rule, String) + if (!strchr(OptionLine, '$')) + parse_config_line(OptionLine); + else { - yr_string_matches_foreach(context, String, Match) + yr_rule_strings_foreach(Rule, String) { -#ifdef DEBUG_COMMENTS - DebugOutput("YaraScan match: %s, %s (0x%x)", OptionLine, String->identifier, Match->offset); -#endif - if (ParseOptionLine(OptionLine, (char*)String->identifier, Match)) + yr_string_matches_foreach(context, String, Match) { #ifdef DEBUG_COMMENTS - DebugOutput("YaraScan: NewLine %s", NewLine); + DebugOutput("YaraScan match: %s, %s (0x%x)", OptionLine, String->identifier, Match->offset); #endif - parse_config_line(NewLine); - SetBreakpoints = TRUE; + ParseOptionLine(OptionLine, (char*)String->identifier, Match, user_data); } - else if (!strchr(OptionLine, '$') && _strnicmp(OptionLine, "bp", 2) || strncmp(OptionLine, "br", 2)) - SetBreakpoints = TRUE; - } } - + if (!_strnicmp(OptionLine, "bp", 2) || !strncmp(OptionLine, "br", 2)) + SetBreakpoints = TRUE; if (!_stricmp("dump", OptionLine)) DoDumpRegion = TRUE; if (!_stricmp("clear", OptionLine)) @@ -258,8 +274,6 @@ int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void memset(Action2, 0, MAX_PATH); memset(Action3, 0, MAX_PATH); } - if (!strchr(OptionLine, '$')) - parse_config_line(OptionLine); if (p) { *p = ','; diff --git a/capemon.vcxproj b/capemon.vcxproj index f98b0c78..e01a1f72 100644 --- a/capemon.vcxproj +++ b/capemon.vcxproj @@ -477,12 +477,12 @@ - true true + false - true true + false diff --git a/config.c b/config.c index c7a96909..a7db0894 100644 --- a/config.c +++ b/config.c @@ -26,6 +26,7 @@ along with this program. If not, see . #include "unhook.h" #include "Shlwapi.h" #include "CAPE\CAPE.h" +#include "CAPE\Debugger.h" #define SINGLE_STEP_LIMIT 0x4000 // default unless specified in web ui #define DROPPED_LIMIT 100 @@ -42,7 +43,6 @@ along with this program. If not, see . extern void DebugOutput(_In_ LPCTSTR lpOutputString, ...); extern char *our_dll_path; extern char *our_process_name; -extern BOOL PatchByte(LPVOID Address, BYTE Byte); extern wchar_t *our_process_path_w; extern int EntryPointRegister; extern unsigned int TraceDepthLimit, StepLimit, Type0, Type1, Type2; @@ -385,32 +385,31 @@ void parse_config_line(char* line) if (p) { *p = '\0'; char *p2 = p+1; - unsigned int byte = strtoul(value, NULL, 0); int delta=0; - p = strchr(p2, '+'); + p = strchr(value, '+'); if (p) { delta = strtoul(p+1, NULL, 0); DebugOutput("Config: Delta 0x%x.\n", delta); *p = '\0'; } else { - p = strchr(p2, '-'); + p = strchr(value, '-'); if (p) { delta = - (int)strtoul(p+1, NULL, 0); DebugOutput("Config: Delta 0x%x.\n", delta); *p = '\0'; } } - PVOID address = (PVOID)(DWORD_PTR)strtoul(p2, NULL, 0); + PVOID address = (PVOID)(DWORD_PTR)strtoull(value, NULL, 0); if (address) { - DebugOutput("Config: patching address 0x%p with byte 0x%x", address, byte); - PatchByte(address, (BYTE)byte); + DebugOutput("Config: patching address 0x%p with bytes %s", address, p2); + PatchBytes(address, p2); } else - DebugOutput("Config: patch address missing invalid: %s", value); + DebugOutput("Config: patch address missing or invalid: %s", value); } else - DebugOutput("Config: patch byte missing"); + DebugOutput("Config: patch bytes missing"); } else if (!stricmp(key, "bp")) { unsigned int x = 0; diff --git a/hook_misc.c b/hook_misc.c index 5286d37a..75b84525 100644 --- a/hook_misc.c +++ b/hook_misc.c @@ -36,7 +36,7 @@ along with this program. If not, see . extern char *our_process_name; extern void ProcessMessage(DWORD ProcessId, DWORD ThreadId); extern const char* GetLanguageName(LANGID langID); -extern NTSTATUS pNtQueryObject(HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass, PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength); +//extern NTSTATUS pNtQueryObject(HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass, PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength); extern BOOL TraceRunning; @@ -45,11 +45,6 @@ extern BOOL Trace(struct _EXCEPTION_POINTERS* ExceptionInfo); LPTOP_LEVEL_EXCEPTION_FILTER TopLevelExceptionFilter; BOOL PlugXConfigDumped, CompressedPE; DWORD ExportAddress; -struct BlockInputThreadInstance { - BOOL CurrentlyBlockedInput; - DWORD BlockInputThreadID; -}; -static struct BlockInputThreadInstance BlockInputInstances[256]; //Should be enough for most situations HOOKDEF(HHOOK, WINAPI, SetWindowsHookExA, __in int idHook, @@ -431,36 +426,36 @@ HOOKDEF(NTSTATUS, WINAPI, NtClose, return ret; } //https://anti-debug.checkpoint.com/techniques/object-handles.html - ULONG Size = 0; - ULONG Size2 = 0; - NTSTATUS Status = pNtQueryObject(Handle, 0, &Size, sizeof(Size), &Size); - void* Buff = (void*)calloc(1, Size); - BOOLEAN valid_handle; - if (!Buff) - valid_handle = NT_SUCCESS(pNtQueryObject(Handle, 0, Buff, Size, &Size2)); - else - valid_handle = FALSE; - if (!g_config.no_stealth && valid_handle) - { - __try - { - Old_NtClose(Handle); - ret = STATUS_SUCCESS; - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - ret = STATUS_SUCCESS; - } - } - else - ret = Old_NtClose(Handle); + //ULONG Size = 0; + //ULONG Size2 = 0; + //NTSTATUS Status = pNtQueryObject(Handle, 0, &Size, sizeof(Size), &Size); + //void* Buff = (void*)calloc(1, Size); + //BOOLEAN valid_handle; + //if (!Buff) + // valid_handle = NT_SUCCESS(pNtQueryObject(Handle, 0, Buff, Size, &Size2)); + //else + // valid_handle = FALSE; + //if (!g_config.no_stealth && valid_handle) + //{ + // __try + // { + // Old_NtClose(Handle); + // ret = STATUS_SUCCESS; + // } + // __except(EXCEPTION_EXECUTE_HANDLER) + // { + // ret = STATUS_SUCCESS; + // } + //} + //else + ret = Old_NtClose(Handle); LOQ_ntstatus("system", "p", "Handle", Handle); if(NT_SUCCESS(ret)) { remove_file_from_log_tracking(Handle); DumpSectionViewsForHandle(Handle); file_close(Handle); } - free(Buff); + //free(Buff); return ret; } @@ -826,22 +821,6 @@ HOOKDEF(NTSTATUS, WINAPI, NtQueryInformationProcess, ) { NTSTATUS ret = Old_NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength); LOQ_ntstatus("process", "ib", "ProcessInformationClass", ProcessInformationClass, "ProcessInformation", ProcessInformationLength, ProcessInformation); - //https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-checkremotedebuggerpresent - if (!g_config.no_stealth && ProcessInformationClass == ProcessDebugPort) - { - if (ProcessInformationLength >= sizeof(HANDLE)) - *(HANDLE*)ProcessInformation = 0; - } - else if (!g_config.no_stealth && ProcessInformationClass == ProcessDebugFlags) - { - if (ProcessInformationLength >= sizeof(ULONG)) - *(ULONG*)ProcessInformation = 1; - } - else if (!g_config.no_stealth && ProcessInformationClass == ProcessDebugObjectHandle) - { - if (ProcessInformationLength >= sizeof(HANDLE)) - *(HANDLE*)ProcessInformation = 0; - } return ret; } @@ -878,12 +857,6 @@ HOOKDEF(NTSTATUS, WINAPI, NtQuerySystemInformation, PLARGE_INTEGER perf_info = (PLARGE_INTEGER)SystemInformation; perf_info->HighPart |= 2; } - //https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-checkremotedebuggerpresent - if (!g_config.no_stealth && SystemInformationClass == SystemKernelDebuggerInformation) - { - PSYSTEM_KERNEL_DEBUGGER_INFORMATION perf_info = (PSYSTEM_KERNEL_DEBUGGER_INFORMATION)SystemInformation; - perf_info->DebuggerNotPresent = 1; - } return ret; } @@ -2013,88 +1986,3 @@ HOOKDEF(ULONG, __fastcall, vDbgPrintExWithPrefixInternal, return Old_vDbgPrintExWithPrefixInternal(Prefix, ComponentId, Level, Format, arglist, HandleBreakpoint); } -HOOKDEF(BOOL, WINAPI, NtDebugActiveProcess, - _In_ HANDLE ProcessHandle, - _In_ HANDLE DebugObjectHandle -){ - DWORD pid = pid_from_process_handle(ProcessHandle); - DWORD debug_pid = pid_from_process_handle(DebugObjectHandle); - BOOL ret = Old_NtDebugActiveProcess(ProcessHandle, DebugObjectHandle); - LOQ_bool("misc", "ll", "ProcessID", pid, "DebugObject", debug_pid); - //https://anti-debug.checkpoint.com/techniques/interactive.html - if (!g_config.no_stealth && is_protected_pid(pid) && g_config.debugger) - ret = TRUE; - return ret; -} - -HOOKDEF(NTSTATUS, WINAPI, DbgUiDebugActiveProcess, - _In_ HANDLE ProcessHandle -){ - DWORD pid = pid_from_process_handle(ProcessHandle); - NTSTATUS ret = Old_DbgUiDebugActiveProcess(ProcessHandle); - LOQ_ntstatus("misc", "l", "ProcessID", pid); - //https://anti-debug.checkpoint.com/techniques/interactive.html - if (!g_config.no_stealth && is_protected_pid(pid) && g_config.debugger) - return TRUE; - return ret; -} - -HOOKDEF(NTSTATUS, WINAPI, GenerateConsoleCtrlEvent, - _In_ DWORD dwCtrlEvent, - _In_ DWORD dwProcessGroupId -){ - //https://anti-debug.checkpoint.com/techniques/interactive.html - //Assuming the ProcessGroupID is the pid in this case make sense because it would be a case where it's targeted specifically ? - NTSTATUS ret = Old_GenerateConsoleCtrlEvent(dwCtrlEvent,dwProcessGroupId); - LOQ_ntstatus("misc", "ll", "CtrlEvent", dwCtrlEvent, "ProcessGroupID", dwProcessGroupId); - if (!g_config.no_stealth && g_config.debugger && is_protected_pid(dwProcessGroupId)) { - ret = TRUE; - } - return ret; -} - -//https://anti-debug.checkpoint.com/techniques/interactive.html -HOOKDEF(BOOL, WINAPI, BlockInput, - _In_ BOOL fBlockIt -){ - BOOL ret; - if(!g_config.no_stealth) - { - - int length = sizeof(BlockInputInstances) / sizeof(struct BlockInputThreadInstance); - DWORD ThreadId = GetCurrentThreadId(); - BOOL found = FALSE; - int index = 0; - BOOL CurrentState = FALSE; - //Don't run the real deal since it's an effective way to block a debugger and a way to detect hooking. - for(int i=0;i +#include +#include + +BOOL check_ntyieldexecution_switchtothread() +{ + BYTE ucCounter = 1; + for (int i = 0; i < 8; i++) + { + Sleep(0x0F); + ucCounter <<= (1 - SwitchToThread()); + } + + return !(ucCounter == 0); +} + +BOOL check_hardwarebreakpoint() +{ + CONTEXT ctx; + ZeroMemory(&ctx, sizeof(CONTEXT)); + ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; + + if(!GetThreadContext(GetCurrentThread(), &ctx)) + return TRUE; + + return !(ctx.Dr0 || ctx.Dr1 || ctx.Dr2 || ctx.Dr3); +} + +BOOL check_closehandle() +{ + __try + { + CloseHandle((HANDLE)0xDEADBEEF); + return TRUE; + } + __except (EXCEPTION_INVALID_HANDLE == GetExceptionCode() + ? EXCEPTION_EXECUTE_HANDLER + : EXCEPTION_CONTINUE_SEARCH) + { + return FALSE; + } +} + +int main(int argc, char **argv) +{ + BOOL close_handle_result; + BOOL hardware_breakpoint_result; + BOOL ntyieldexecution_result; + close_handle_result = check_closehandle(); + hardware_breakpoint_result = check_hardwarebreakpoint(); + ntyieldexecution_result = check_ntyieldexecution_switchtothread(); + FILE *fptr; + fptr = fopen("test_result.txt","w"); + if(fptr) + { + fprintf(fptr," Close handle test: %s\n Hardware breakpoint test: %s\n NTYieldExecution test: %s\n", + close_handle_result ? "true": "false", + hardware_breakpoint_result ? "true": "false", + ntyieldexecution_result ? "true": "false"); + } + fclose(fptr); +} \ No newline at end of file From 7e9fb0ff597d3834862b0297879c9741af8bf0ec Mon Sep 17 00:00:00 2001 From: cccs-mog <117194682+cccs-mog@users.noreply.github.com> Date: Mon, 11 Aug 2025 20:26:17 +0000 Subject: [PATCH 09/11] Fixing diff mismatch --- CAPE/CAPE.h | 1 - CAPE/Debugger.c | 21 ++++++------- CAPE/Debugger.h | 2 +- CAPE/Injection.c | 6 ++-- CAPE/Trace.c | 6 +--- CAPE/YaraHarness.c | 78 +++++++++++++++++++--------------------------- config.c | 17 +++++----- 7 files changed, 54 insertions(+), 77 deletions(-) diff --git a/CAPE/CAPE.h b/CAPE/CAPE.h index 3cadd931..61f1d821 100644 --- a/CAPE/CAPE.h +++ b/CAPE/CAPE.h @@ -77,7 +77,6 @@ int DumpImageInCurrentProcess(PVOID ImageBase); void DumpSectionViewsForPid(DWORD Pid); BOOL DumpRange(PVOID Address, SIZE_T Size); BOOL DumpStackRegion(void); -void DumpStrings(void); BOOL ProcessDumped; unsigned int DumpCount, DotNetCacheDumpCount; diff --git a/CAPE/Debugger.c b/CAPE/Debugger.c index 57c78e11..fb43bc1d 100644 --- a/CAPE/Debugger.c +++ b/CAPE/Debugger.c @@ -104,31 +104,30 @@ HANDLE GetThreadHandle(DWORD ThreadId) } //************************************************************************************** -BOOL PatchBytes(LPVOID Address, const char* HexBytes) +BOOL PatchByte(LPVOID Address, BYTE Byte) //************************************************************************************** { - if (!Address || !HexBytes || !IsAddressAccessible(Address)) + if (!Address || !HexBytes || !IsAddressAccessible(Address)) { DebugOutput("PatchBytes: Invalid address or hex string"); return FALSE; } - SIZE_T HexLen = strlen(HexBytes); + SIZE_T HexLen = strlen(HexBytes); if (HexLen == 0 || HexLen % 2 != 0) { DebugOutput("PatchBytes: Invalid hex string length %d", HexLen); return FALSE; } - SIZE_T ByteCount = HexLen / 2; + SIZE_T ByteCount = HexLen / 2; DWORD OldProtect; if (!VirtualProtect(Address, ByteCount, PAGE_EXECUTE_READWRITE, &OldProtect)) - { - DebugOutput("PatchBytes: Failed to change memory protection at 0x%p", Address); + DebugOutput("PatchBytes: Failed to change memory protection at 0x%p", Address); return FALSE; } - PBYTE Dest = (PBYTE)Address; + PBYTE Dest = (PBYTE)Address; for (SIZE_T i = 0; i < HexLen; i += 2) { char HexByte[3] = { HexBytes[i], HexBytes[i + 1], '\0' }; @@ -136,11 +135,9 @@ BOOL PatchBytes(LPVOID Address, const char* HexBytes) *Dest++ = Byte; } - VirtualProtect(Address, ByteCount, OldProtect, &OldProtect); - - DebugOutput("PatchBytes: Patched %zu bytes at 0x%p: %s", ByteCount, Address, HexBytes); - - return TRUE; + VirtualProtect(Address, ByteCount, OldProtect, &OldProtect); + DebugOutput("PatchBytes: Patched %zu bytes at 0x%p: %s", ByteCount, Address, HexBytes); + return TRUE; } //************************************************************************************** diff --git a/CAPE/Debugger.h b/CAPE/Debugger.h index 0b990c6f..a0f69dfa 100644 --- a/CAPE/Debugger.h +++ b/CAPE/Debugger.h @@ -181,7 +181,7 @@ BOOL InitialiseDebugger(void); BOOL ResumeFromBreakpoint(PCONTEXT Context); void OutputThreadBreakpoints(DWORD ThreadId); void DebugOutputThreadBreakpoints(); -BOOL PatchBytes(LPVOID Address, const char* HexBytes); +BOOL PatchByte(LPVOID Address, BYTE Byte); void ShowStack(DWORD_PTR StackPointer, unsigned int NumberOfRecords); diff --git a/CAPE/Injection.c b/CAPE/Injection.c index 89f384e8..96a5a668 100644 --- a/CAPE/Injection.c +++ b/CAPE/Injection.c @@ -46,8 +46,6 @@ extern char *our_process_name; extern void hook_disable(); extern void hook_enable(); -void ProcessMessage(DWORD ProcessId, DWORD ThreadId); - //************************************************************************************** PINJECTIONINFO GetInjectionInfo(DWORD ProcessId) //************************************************************************************** @@ -518,6 +516,7 @@ void DumpSectionViewsForHandle(HANDLE SectionHandle) __declspec(noinline) void GetThreadContextHandler(HANDLE ThreadHandle, LPCONTEXT Context) { + DebugOutput("GetThreadContextHandler"); DWORD Pid = pid_from_thread_handle(ThreadHandle); if (Context && Context->ContextFlags & CONTEXT_CONTROL) @@ -551,8 +550,7 @@ __declspec(noinline) void SetThreadContextHandler(HANDLE ThreadHandle, CONTEXT * DWORD Pid = pid_from_thread_handle(ThreadHandle); DWORD Tid = tid_from_thread_handle(ThreadHandle); - if (Pid != GetCurrentProcessId()) - ProcessMessage(Pid, 0); + DebugOutput("SetThreadContextHandler: Pid %d", GetCurrentProcessId()); if (g_config.debugger && Pid == GetCurrentProcessId()) { diff --git a/CAPE/Trace.c b/CAPE/Trace.c index a767810c..376b0ce7 100644 --- a/CAPE/Trace.c +++ b/CAPE/Trace.c @@ -1631,10 +1631,6 @@ void ActionDispatcher(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst De DumpAddress = 0; DumpSize = 0; } - else if (!stricmp(Action, "DumpStrings")) - { - DumpStrings(); - } else if (!stricmp(Action, "Step2OEP")) { SetSingleStepMode(ExceptionInfo->ContextRecord, ProcessOEP); @@ -2025,7 +2021,7 @@ void InstructionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst #endif SkipInstruction(ExceptionInfo->ContextRecord); if (lookup_get(&SoftBPs, (ULONG_PTR)CIP, 0)) - PatchBytes(CIP, "CC"); + PatchByte(CIP, "0xCC"); } else if (!strcmp(DecodedInstruction.mnemonic.p, "RET")) { diff --git a/CAPE/YaraHarness.c b/CAPE/YaraHarness.c index 38630acf..090920b9 100644 --- a/CAPE/YaraHarness.c +++ b/CAPE/YaraHarness.c @@ -114,25 +114,21 @@ void ScannerError(int Error) } } -void ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match, void* user_data) +BOOL ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match) { char *Value, *Key, *p, *q, *r, c = 0; - ULONG_PTR delta=0; - SIZE_T ValueLength = 0; - + unsigned int ValueLength; + int delta=0; if (!Line || !Identifier) - return; - + return FALSE; p = strchr(Line, '$'); if (!p) - return; - + return FALSE; p = strchr(Line, '='); if (!p) - return; - + return FALSE; r = strchr(p, ':'); - if (r && *(r + 1) == '$') + if (r) Value = r + 1; else Value = p + 1; @@ -155,12 +151,12 @@ void ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match, void* user_d } if (q) { - ValueLength = (SIZE_T)(DWORD_PTR)(q-(DWORD_PTR)Value); + ValueLength = (unsigned int)(DWORD_PTR)(q-(DWORD_PTR)Value); if (*(q-1) == '*') ValueLength--; } else - ValueLength = (SIZE_T)strlen(Value); + ValueLength = (unsigned int)strlen(Value); if (*(Value+ValueLength-1) == '*') { @@ -168,44 +164,29 @@ void ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match, void* user_d delta += Match->match_length - 1; } - SIZE_T IdentifierLength = strlen(Identifier); - if (strncmp(Value, Identifier, IdentifierLength < ValueLength ? IdentifierLength : ValueLength)) - return; + if (strncmp(Value, Identifier, ValueLength)) + return FALSE; Key = Line; - if (r && *(r + 1) == '$') - { + if (r) { c = *r; *r = 0; } - else - { + else { c = *p; *p = 0; } - if (_strnicmp(Line, "bp", 2) && strncmp(Line, "br", 2)) - delta += (ULONG_PTR)user_data; - memset(NewLine, 0, sizeof(NewLine)); + sprintf(NewLine, "%s%c0x%p\0", Key, c, (PUCHAR)Match->offset+delta); if (r) - sprintf(NewLine, "%s%c0x%p%s\0", Key, c, (PUCHAR)Match->offset+delta, r); - else - sprintf(NewLine, "%s%c0x%p\0", Key, c, (PUCHAR)Match->offset+delta); - - if (r && *(r + 1) == '$') *r = c; else *p = c; - -#ifdef DEBUG_COMMENTS - DebugOutput("ParseOptionLine: %s", NewLine); -#endif - - if (!strchr(NewLine, '$')) - parse_config_line(NewLine); - - return; + p = strchr(NewLine, '$'); + if (p) + return FALSE; + return TRUE; } int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data) @@ -239,23 +220,26 @@ int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void char *p = strchr(OptionLine, ','); if (p) *p = 0; - if (!strchr(OptionLine, '$')) - parse_config_line(OptionLine); - else + yr_rule_strings_foreach(Rule, String) { - yr_rule_strings_foreach(Rule, String) + yr_string_matches_foreach(context, String, Match) { - yr_string_matches_foreach(context, String, Match) +#ifdef DEBUG_COMMENTS + DebugOutput("YaraScan match: %s, %s (0x%x)", OptionLine, String->identifier, Match->offset); +#endif + if (ParseOptionLine(OptionLine, (char*)String->identifier, Match)) { #ifdef DEBUG_COMMENTS - DebugOutput("YaraScan match: %s, %s (0x%x)", OptionLine, String->identifier, Match->offset); + DebugOutput("YaraScan: NewLine %s", NewLine); #endif - ParseOptionLine(OptionLine, (char*)String->identifier, Match, user_data); + parse_config_line(NewLine); + SetBreakpoints = TRUE; } + else if (!strchr(OptionLine, '$') && _strnicmp(OptionLine, "bp", 2) || strncmp(OptionLine, "br", 2)) + SetBreakpoints = TRUE; } } - if (!_strnicmp(OptionLine, "bp", 2) || !strncmp(OptionLine, "br", 2)) - SetBreakpoints = TRUE; + if (!_stricmp("dump", OptionLine)) DoDumpRegion = TRUE; if (!_stricmp("clear", OptionLine)) @@ -274,6 +258,8 @@ int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void memset(Action2, 0, MAX_PATH); memset(Action3, 0, MAX_PATH); } + if (!strchr(OptionLine, '$')) + parse_config_line(OptionLine); if (p) { *p = ','; diff --git a/config.c b/config.c index a7db0894..38ba5f78 100644 --- a/config.c +++ b/config.c @@ -26,7 +26,6 @@ along with this program. If not, see . #include "unhook.h" #include "Shlwapi.h" #include "CAPE\CAPE.h" -#include "CAPE\Debugger.h" #define SINGLE_STEP_LIMIT 0x4000 // default unless specified in web ui #define DROPPED_LIMIT 100 @@ -43,6 +42,7 @@ along with this program. If not, see . extern void DebugOutput(_In_ LPCTSTR lpOutputString, ...); extern char *our_dll_path; extern char *our_process_name; +extern BOOL PatchByte(LPVOID Address, BYTE Byte); extern wchar_t *our_process_path_w; extern int EntryPointRegister; extern unsigned int TraceDepthLimit, StepLimit, Type0, Type1, Type2; @@ -385,6 +385,7 @@ void parse_config_line(char* line) if (p) { *p = '\0'; char *p2 = p+1; + unsigned int byte = strtoul(value, NULL, 0); int delta=0; p = strchr(value, '+'); if (p) { @@ -393,23 +394,23 @@ void parse_config_line(char* line) *p = '\0'; } else { - p = strchr(value, '-'); + p = strchr(p2, '-'); if (p) { delta = - (int)strtoul(p+1, NULL, 0); DebugOutput("Config: Delta 0x%x.\n", delta); *p = '\0'; } } - PVOID address = (PVOID)(DWORD_PTR)strtoull(value, NULL, 0); + PVOID address = (PVOID)(DWORD_PTR)strtoul(p2, NULL, 0); if (address) { - DebugOutput("Config: patching address 0x%p with bytes %s", address, p2); - PatchBytes(address, p2); + DebugOutput("Config: patching address 0x%p with byte 0x%x", address, byte); + PatchByte(address, (BYTE)byte); } else - DebugOutput("Config: patch address missing or invalid: %s", value); + DebugOutput("Config: patch address missing invalid: %s", value); } else - DebugOutput("Config: patch bytes missing"); + DebugOutput("Config: patch byte missing"); } else if (!stricmp(key, "bp")) { unsigned int x = 0; @@ -1248,7 +1249,7 @@ void parse_config_line(char* line) else if (g_config.unpacker == 2) DebugOutput("Active unpacking of payloads enabled\n"); } - else if (!stricmp(key, "injection")) { //When set to 1 this will enable CAPE’s capture of injected payloads between processes + else if (!stricmp(key, "injection")) { //When set to 1 this will enable CAPE�s capture of injected payloads between processes g_config.injection = value[0] == '1'; if (g_config.injection) DebugOutput("Capture of injected payloads enabled.\n"); From 36d618a1649676092eb763a523782cbc05aa620c Mon Sep 17 00:00:00 2001 From: cccs-mog <117194682+cccs-mog@users.noreply.github.com> Date: Mon, 11 Aug 2025 20:32:00 +0000 Subject: [PATCH 10/11] Editor mismatch fix --- CAPE/Debugger.c | 40 ++++++++++++++++------------------------ CAPE/Trace.c | 2 +- CAPE/YaraHarness.c | 2 +- config.c | 2 +- 4 files changed, 19 insertions(+), 27 deletions(-) diff --git a/CAPE/Debugger.c b/CAPE/Debugger.c index fb43bc1d..f3d5200a 100644 --- a/CAPE/Debugger.c +++ b/CAPE/Debugger.c @@ -107,36 +107,28 @@ HANDLE GetThreadHandle(DWORD ThreadId) BOOL PatchByte(LPVOID Address, BYTE Byte) //************************************************************************************** { - if (!Address || !HexBytes || !IsAddressAccessible(Address)) - { - DebugOutput("PatchBytes: Invalid address or hex string"); - return FALSE; - } + DWORD OldProtect; + + if (!Address || !IsAddressAccessible(Address)) + return FALSE; - SIZE_T HexLen = strlen(HexBytes); - if (HexLen == 0 || HexLen % 2 != 0) + if (!VirtualProtect(Address, 1, PAGE_EXECUTE_READWRITE, &OldProtect)) { - DebugOutput("PatchBytes: Invalid hex string length %d", HexLen); - return FALSE; + DebugOutput("PatchByte: Unable to change memory protection at 0x%p", Address); + return FALSE; } - SIZE_T ByteCount = HexLen / 2; - DWORD OldProtect; - if (!VirtualProtect(Address, ByteCount, PAGE_EXECUTE_READWRITE, &OldProtect)) - DebugOutput("PatchBytes: Failed to change memory protection at 0x%p", Address); - return FALSE; - } +#ifdef DEBUG_COMMENTS + DebugOutput("PatchByte: Changed memory protection at 0x%p", Address); +#endif - PBYTE Dest = (PBYTE)Address; - for (SIZE_T i = 0; i < HexLen; i += 2) - { - char HexByte[3] = { HexBytes[i], HexBytes[i + 1], '\0' }; - BYTE Byte = (BYTE)strtoul(HexByte, NULL, 16); - *Dest++ = Byte; - } + *(PBYTE)Address = Byte; + +#ifdef DEBUG_COMMENTS + DebugOutput("PatchByte: New instruction byte at 0x%p: 0x%x", Address, *(PBYTE)Address); +#endif + VirtualProtect(Address, 1, OldProtect, &OldProtect); - VirtualProtect(Address, ByteCount, OldProtect, &OldProtect); - DebugOutput("PatchBytes: Patched %zu bytes at 0x%p: %s", ByteCount, Address, HexBytes); return TRUE; } diff --git a/CAPE/Trace.c b/CAPE/Trace.c index 376b0ce7..ada9672a 100644 --- a/CAPE/Trace.c +++ b/CAPE/Trace.c @@ -2021,7 +2021,7 @@ void InstructionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst #endif SkipInstruction(ExceptionInfo->ContextRecord); if (lookup_get(&SoftBPs, (ULONG_PTR)CIP, 0)) - PatchByte(CIP, "0xCC"); + PatchByte(CIP, 0xCC); } else if (!strcmp(DecodedInstruction.mnemonic.p, "RET")) { diff --git a/CAPE/YaraHarness.c b/CAPE/YaraHarness.c index 090920b9..2b211b86 100644 --- a/CAPE/YaraHarness.c +++ b/CAPE/YaraHarness.c @@ -176,7 +176,6 @@ BOOL ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match) c = *p; *p = 0; } - memset(NewLine, 0, sizeof(NewLine)); sprintf(NewLine, "%s%c0x%p\0", Key, c, (PUCHAR)Match->offset+delta); if (r) @@ -237,6 +236,7 @@ int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void } else if (!strchr(OptionLine, '$') && _strnicmp(OptionLine, "bp", 2) || strncmp(OptionLine, "br", 2)) SetBreakpoints = TRUE; + } } diff --git a/config.c b/config.c index 38ba5f78..bf3baae7 100644 --- a/config.c +++ b/config.c @@ -387,7 +387,7 @@ void parse_config_line(char* line) char *p2 = p+1; unsigned int byte = strtoul(value, NULL, 0); int delta=0; - p = strchr(value, '+'); + p = strchr(p2, '+'); if (p) { delta = strtoul(p+1, NULL, 0); DebugOutput("Config: Delta 0x%x.\n", delta); From 94240a9c066794eda202edfc90c5bf92ee826dab Mon Sep 17 00:00:00 2001 From: cccs-mog <117194682+cccs-mog@users.noreply.github.com> Date: Mon, 11 Aug 2025 20:33:29 +0000 Subject: [PATCH 11/11] Spacing --- CAPE/Debugger.c | 4 ++-- CAPE/YaraHarness.c | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CAPE/Debugger.c b/CAPE/Debugger.c index f3d5200a..097ee1aa 100644 --- a/CAPE/Debugger.c +++ b/CAPE/Debugger.c @@ -114,8 +114,8 @@ BOOL PatchByte(LPVOID Address, BYTE Byte) if (!VirtualProtect(Address, 1, PAGE_EXECUTE_READWRITE, &OldProtect)) { - DebugOutput("PatchByte: Unable to change memory protection at 0x%p", Address); - return FALSE; + DebugOutput("PatchByte: Unable to change memory protection at 0x%p", Address); + return FALSE; } #ifdef DEBUG_COMMENTS diff --git a/CAPE/YaraHarness.c b/CAPE/YaraHarness.c index 2b211b86..fac8d2d2 100644 --- a/CAPE/YaraHarness.c +++ b/CAPE/YaraHarness.c @@ -235,8 +235,7 @@ int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void SetBreakpoints = TRUE; } else if (!strchr(OptionLine, '$') && _strnicmp(OptionLine, "bp", 2) || strncmp(OptionLine, "br", 2)) - SetBreakpoints = TRUE; - + SetBreakpoints = TRUE; } }