From b7b1cabf9122c5285f4202c4919768c4408cfbfd Mon Sep 17 00:00:00 2001 From: doomedraven Date: Tue, 18 Aug 2026 12:17:46 +0200 Subject: [PATCH 1/3] Implement .NET Core (CoreCLR) JIT and active AMSI buffer monitoring (PR-1) Surgically implements the first PR of our .NET modernization roadmap: 1. Adds full .NET Core / .NET 5+ JIT compileMethod monitoring support by registering hook_special(coreclr, compileMethod). This completely activates JIT MSIL scans/dumps for self-contained, unmanaged modern .NET runtimes. 2. Implements active Windows Antimalware Scan Interface (AMSI) intercepting by creating a dedicated hook_amsi.c module and hooking amsi.dll!AmsiScanBuffer and amsi.dll!AmsiScanString. 3. If g_config.amsidump is enabled, actively dumps plain-text scripts and buffers before execution using DumpMemoryRaw and SetCapeMetaData. --- capemon.vcxproj | 1 + capemon.vcxproj.filters | 3 +++ hook_amsi.c | 50 +++++++++++++++++++++++++++++++++++++++++ hooks.c | 6 +++++ hooks.h | 20 +++++++++++++++++ 5 files changed, 80 insertions(+) create mode 100644 hook_amsi.c diff --git a/capemon.vcxproj b/capemon.vcxproj index cc45d7a1..2f9ba1f1 100644 --- a/capemon.vcxproj +++ b/capemon.vcxproj @@ -260,6 +260,7 @@ + diff --git a/capemon.vcxproj.filters b/capemon.vcxproj.filters index 21d30e1d..88739d42 100644 --- a/capemon.vcxproj.filters +++ b/capemon.vcxproj.filters @@ -60,6 +60,9 @@ Source Files + + Source Files + Source Files diff --git a/hook_amsi.c b/hook_amsi.c new file mode 100644 index 00000000..42b5f33a --- /dev/null +++ b/hook_amsi.c @@ -0,0 +1,50 @@ +#include +#include "hooking.h" +#include "log.h" +#include "misc.h" +#include "CAPE\CAPE.h" + +#define AMSIBUFFER 0x6a +#define AMSISTREAM 0x6b + +HOOKDEF(HRESULT, WINAPI, AmsiScanBuffer, + _In_ PVOID amsiContext, + _In_ PVOID buffer, + _In_ ULONG length, + _In_opt_ LPCWSTR contentName, + _In_opt_ PVOID amsiSession, + _Out_ PVOID result +) { + HRESULT ret = Old_AmsiScanBuffer(amsiContext, buffer, length, contentName, amsiSession, result); + + LOQ_hresult("amsi", "up", "ContentName", contentName, "Length", length); + + if (g_config.amsidump && buffer != NULL && length > 0) { + SetCapeMetaData(AMSIBUFFER, NULL, NULL, NULL); + DumpMemoryRaw(buffer, (SIZE_T)length); + DebugOutput("AmsiScanBuffer: Actively dumped AMSI buffer of size %d at 0x%p.\n", length, buffer); + } + + return ret; +} + +HOOKDEF(HRESULT, WINAPI, AmsiScanString, + _In_ PVOID amsiContext, + _In_ LPCWSTR string, + _In_opt_ LPCWSTR contentName, + _In_opt_ PVOID amsiSession, + _Out_ PVOID result +) { + HRESULT ret = Old_AmsiScanString(amsiContext, string, contentName, amsiSession, result); + + LOQ_hresult("amsi", "uu", "ContentName", contentName, "String", string); + + if (g_config.amsidump && string != NULL) { + SIZE_T len = (wcslen(string) + 1) * sizeof(wchar_t); + SetCapeMetaData(AMSIBUFFER, NULL, NULL, NULL); + DumpMemoryRaw((PVOID)string, len); + DebugOutput("AmsiScanString: Actively dumped AMSI string at 0x%p.\n", string); + } + + return ret; +} diff --git a/hooks.c b/hooks.c index 072a8065..7407f96b 100644 --- a/hooks.c +++ b/hooks.c @@ -189,6 +189,7 @@ hook_t full_hooks[] = { // Script hooks HOOK_SPECIAL(clrjit, compileMethod), + HOOK_SPECIAL(coreclr, compileMethod), HOOK_SPECIAL(urlmon, IsValidURL), HOOK_SPECIAL(jscript, COleScript_ParseScriptText), HOOK_NOTAIL(jscript, JsEval, 5), @@ -516,6 +517,8 @@ hook_t full_hooks[] = { HOOK(urlmon, URLDownloadToFileW), HOOK(urlmon, URLDownloadToCacheFileW), HOOK(urlmon, ObtainUserAgentString), + HOOK(amsi, AmsiScanBuffer), + HOOK(amsi, AmsiScanString), HOOK(wininet, InternetGetConnectedState), HOOK(wininet, InternetOpenA), HOOK(wininet, InternetOpenW), @@ -1046,6 +1049,7 @@ hook_t min_hooks[] = { HOOK(kernel32, CreateRemoteThreadEx), HOOK_SPECIAL(clrjit, compileMethod), + HOOK_SPECIAL(coreclr, compileMethod), HOOK_SPECIAL(ole32, CoCreateInstance), HOOK_SPECIAL(ole32, CoCreateInstanceEx), HOOK_SPECIAL(ole32, CoGetClassObject), @@ -1477,6 +1481,8 @@ hook_t office_hooks[] = { HOOK(urlmon, URLDownloadToFileW), HOOK(urlmon, URLDownloadToCacheFileW), HOOK(urlmon, ObtainUserAgentString), + HOOK(amsi, AmsiScanBuffer), + HOOK(amsi, AmsiScanString), HOOK(wininet, InternetGetConnectedState), HOOK(wininet, InternetOpenA), HOOK(wininet, InternetOpenW), diff --git a/hooks.h b/hooks.h index a09c440c..14d4f325 100644 --- a/hooks.h +++ b/hooks.h @@ -4005,4 +4005,24 @@ HOOKDEF(DWORD, WINAPI, MapFileAndCheckSumA, _Out_ PDWORD CheckSum ); +HOOKDEF(HRESULT, WINAPI, AmsiScanBuffer, + _In_ PVOID amsiContext, + _In_ PVOID buffer, + _In_ ULONG length, + _In_opt_ LPCWSTR contentName, + _In_opt_ PVOID amsiSession, + _Out_ PVOID result +); + +HOOKDEF(HRESULT, WINAPI, AmsiScanString, + _In_ PVOID amsiContext, + _In_ LPCWSTR string, + _In_opt_ LPCWSTR contentName, + _In_opt_ PVOID amsiSession, + _Out_ PVOID result +); + +#define New_coreclr_compileMethod New_clrjit_compileMethod +#define Old_coreclr_compileMethod Old_clrjit_compileMethod + #include "hook_vbscript.h" From db55953da0398d75c2e19f85013fe7bb85e5fb8f Mon Sep 17 00:00:00 2001 From: doomedraven Date: Thu, 20 Aug 2026 11:54:44 +0200 Subject: [PATCH 2/3] Fix header collision and parameter warnings in hook_amsi.c --- hook_amsi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hook_amsi.c b/hook_amsi.c index 42b5f33a..55e57f09 100644 --- a/hook_amsi.c +++ b/hook_amsi.c @@ -1,4 +1,6 @@ +#define WIN32_LEAN_AND_MEAN #include +#include #include "hooking.h" #include "log.h" #include "misc.h" @@ -20,7 +22,7 @@ HOOKDEF(HRESULT, WINAPI, AmsiScanBuffer, LOQ_hresult("amsi", "up", "ContentName", contentName, "Length", length); if (g_config.amsidump && buffer != NULL && length > 0) { - SetCapeMetaData(AMSIBUFFER, NULL, NULL, NULL); + SetCapeMetaData(AMSIBUFFER, 0, NULL, NULL); DumpMemoryRaw(buffer, (SIZE_T)length); DebugOutput("AmsiScanBuffer: Actively dumped AMSI buffer of size %d at 0x%p.\n", length, buffer); } @@ -41,7 +43,7 @@ HOOKDEF(HRESULT, WINAPI, AmsiScanString, if (g_config.amsidump && string != NULL) { SIZE_T len = (wcslen(string) + 1) * sizeof(wchar_t); - SetCapeMetaData(AMSIBUFFER, NULL, NULL, NULL); + SetCapeMetaData(AMSIBUFFER, 0, NULL, NULL); DumpMemoryRaw((PVOID)string, len); DebugOutput("AmsiScanString: Actively dumped AMSI string at 0x%p.\n", string); } From 8e143af6f56bd448fc60af6031fba36bbd569c35 Mon Sep 17 00:00:00 2001 From: doomedraven Date: Fri, 21 Aug 2026 17:01:16 +0200 Subject: [PATCH 3/3] Avoid AMSI dump recursion during scan Add a thread-local AMSI guard to prevent re-entrant dumping while an AMSI hook is actively processing a scan. The dumper now exits early when the same thread is in a live AMSI scan, avoiding recursive dump attempts and duplicate processing while still reporting a non-detected result. --- CAPE/AmsiDumper.cpp | 9 +++++++++ hook_amsi.c | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/CAPE/AmsiDumper.cpp b/CAPE/AmsiDumper.cpp index 8c202868..e9e34b98 100644 --- a/CAPE/AmsiDumper.cpp +++ b/CAPE/AmsiDumper.cpp @@ -46,6 +46,8 @@ extern "C" void ErrorOutput(_In_ LPCTSTR lpOutputString, ...); extern "C" int DumpMemoryRaw(PVOID Buffer, SIZE_T Size); extern "C" BOOL SetCapeMetaData(DWORD DumpType, DWORD TargetPid, HANDLE hTargetProcess, PVOID Address); +extern "C" __declspec(thread) BOOL t_amsi_active; + HMODULE g_currentModule; STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv) @@ -75,6 +77,13 @@ T GetFixedSizeAttribute(_In_ IAmsiStream* stream, _In_ AMSI_ATTRIBUTE attribute) HRESULT AmsiDumper::Scan(_In_ IAmsiStream* stream, _Out_ AMSI_RESULT* result) { + if (t_amsi_active) + { + DebugOutput("AmsiDumper: Skipping dump because active AMSI hook is handling it.\n"); + *result = AMSI_RESULT_NOT_DETECTED; + return S_OK; + } + auto session = GetFixedSizeAttribute(stream, AMSI_ATTRIBUTE_SESSION); auto contentSize = GetFixedSizeAttribute(stream, AMSI_ATTRIBUTE_CONTENT_SIZE); auto contentAddress = GetFixedSizeAttribute(stream, AMSI_ATTRIBUTE_CONTENT_ADDRESS); diff --git a/hook_amsi.c b/hook_amsi.c index 55e57f09..9761fa9f 100644 --- a/hook_amsi.c +++ b/hook_amsi.c @@ -9,6 +9,8 @@ #define AMSIBUFFER 0x6a #define AMSISTREAM 0x6b +__declspec(thread) BOOL t_amsi_active = FALSE; + HOOKDEF(HRESULT, WINAPI, AmsiScanBuffer, _In_ PVOID amsiContext, _In_ PVOID buffer, @@ -17,7 +19,9 @@ HOOKDEF(HRESULT, WINAPI, AmsiScanBuffer, _In_opt_ PVOID amsiSession, _Out_ PVOID result ) { + t_amsi_active = TRUE; HRESULT ret = Old_AmsiScanBuffer(amsiContext, buffer, length, contentName, amsiSession, result); + t_amsi_active = FALSE; LOQ_hresult("amsi", "up", "ContentName", contentName, "Length", length); @@ -37,7 +41,9 @@ HOOKDEF(HRESULT, WINAPI, AmsiScanString, _In_opt_ PVOID amsiSession, _Out_ PVOID result ) { + t_amsi_active = TRUE; HRESULT ret = Old_AmsiScanString(amsiContext, string, contentName, amsiSession, result); + t_amsi_active = FALSE; LOQ_hresult("amsi", "uu", "ContentName", contentName, "String", string);