fix(dotnet): Resolve JIT vtable slot issues and add comprehensive V2-V5 ABI translation support (1.1 to 10.0) - #188
fix(dotnet): Resolve JIT vtable slot issues and add comprehensive V2-V5 ABI translation support (1.1 to 10.0)#188doomedraven wants to merge 14 commits into
Conversation
Introduce g_dotnet_jit_lock CRITICAL_SECTION and initialize it in DllMain to serialize access to the JIT-related shared state. Protect concurrent access to the lock-free g_dotnet_jit lookup and DotNetCacheDumpCount in compileMethod with Enter/LeaveCriticalSection. Add basic .NET runtime detection scaffolding (runtime enum, version string, GetMethodName slot handling) and ResolveDotNetRuntime() to identify CoreCLR/Framework and derive a version token. Harden SafeGetMethodName to consult the resolved vtable slot. Add <string.h> include and explanatory comments. GetMethodNameSlot currently contains TODO placeholders for verified slot values.
Add runtime-specific ICorJitInfo method-name ABI detection and validation for .NET Framework and Core, including .NET 9 vtable layout handling and safer name filtering. This prevents invalid vtable calls and distinguishes namespace/class/method names in compileMethod logging. Also serialize the .NET JIT dump teardown path with a critical section so DumpInterestingRegions cannot race the compileMethod hook while both touch the shared JIT dump metadata and DotNetCacheDumpCount.
|
note for myself to get the slot id The only missing piece is one integer: the vtable slot of Getting the number (pick one)Static, reproducible, no malware needed — load Live, WinDbg: Do it on the Once you have the slot(s), it's: case DOTNET_RT_FRAMEWORK:
*abi = METHOD_NAME_ABI_FRAMEWORK_V2;
return <slot>; // clr.dll 4.8.x, verified via dps <date> |
… __fastcall with dummy parameters
…IT vtable slots (1.1 to 10.0 and Framework 4.8)
Rename Get-DotNetVTableSlots.ps1 → Extract-DotNetJitLayout.ps1 and add method-signature extraction (captures argument types). Remove old dotnet-vtable-extraction.md and add vtables.md with expanded ABI, offsets and signatures. Update hook_clr.c to introduce METHOD_NAME_ABI_CORE_V3, add v3 function pointer typedefs, return CORE_V3 in GetMethodNameSlot for CoreCLR 2.1/2.2, and invoke the new v3 slot path in SafeGetMethodName. Enables correct handling of the 3-arg getMethodNameFromMetadata shape.
|
@kevoreilly the docs structure is fine for you? is for future versions handling semi-automatically |
…NET 11.0 - Update hook_clr.c with robust memory-based PE resource parsing (GetDllVersion) to extract file versions dynamically from clr.dll and mscorwks.dll. - Map exact JIT compile vtable slots for all .NET Framework versions: .NET 1.1 (slot 105), .NET 2.0-3.5 (slots 16/110), .NET 4.0-4.5.2 (slot 101), .NET 4.6-4.6.2 (slot 102), .NET 4.7-4.7.2 (slot 106), and .NET 4.8-4.8.1 (slot 113). - Implement support for .NET 11.0 Preview (slot 123) under ABI V5. - Consolidate .NET documentation and tools: merge vtables.md into README.md, rename get_all.py to extract_dotnet_runtimes.py, and remove legacy/obsolete scripts.
Document x86 vs x64 calling convention differences for COM interfaces (ICorJitInfo, etc): - x86 methods use __thiscall (this in ECX register), not __stdcall - Wrong convention causes stack imbalance and ESP corruption crashes - Must validate vtable/parameter pointers with IsBadReadPtr before CLR calls - Addresses issue from PR kevoreilly#188 (dotnet JIT vtable hooking) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CYuhA1ZnEMA7waWWJKBgZy
1. Common Crash Patterns & Prevention: - Static TLS Corruption (wrong declspec(thread) usage in post-loaded DLLs) - Calling Convention Mismatch (__thiscall vs __stdcall on x86) - Heap Allocation Under Lock (re-entrancy deadlock, process freeze) Each with symptoms, root cause, bad example, and fix 2. Synchronization & Lock Safety: - Critical section usage rules - Lock ordering discipline - Nested lock prevention - Shared state vs code region protection 3. Stack-Based Allocation (SBO) Pattern: - Why: prevent re-entrancy deadlock - Pattern examples: stack buffers, pre-allocated TLS - Verification: grep for malloc/calloc in hook callbacks Based on real crash fixes in recent PRs: - PR decoupled-logging-v2: static TLS crashes - PR kevoreilly#188: calling convention crashes - PR kevoreilly#162: re-entrancy deadlock from heap allocation Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CYuhA1ZnEMA7waWWJKBgZy
CAPE: Add local flags and tighten g_dotnet_jit access with Enter/LeaveCriticalSection to avoid races when detecting/dumping .NET images and JIT native caches; move debug output and protect increment of DotNetCacheDumpCount with the lock. hook_clr: Make version parsing accept single-number versions (major only); fix IsPlausibleName loop to correctly detect NUL within 256 bytes and avoid off-by-one; and require g_dotnet_runtime_resolved before reading pointers in SafeGetMethodName to prevent invalid reads.
Will be testing this fixes tomorrow
The crash in
SafeGetMethodNameduring detonation (especially on samples like Formbook that hook, inject, or tamper with .NET runtimes) typically happens due to three core issues in x86 CLR interaction:1. Calling Convention Mismatch (
__thiscallvs__stdcall) on x86In 32-bit MSVC, methods in
ICorJitInfo/ICorMethodInfouse the__thiscallconvention (wherethisis passed in registerECX), unless explicitly declared as COMSTDMETHODCALLTYPE.When defined as:
compHndis pushed onto the stack as the first argument instead of being loaded intoECX.clr.dll: The method reads whatever garbage was inECXas itsthispointer (CEEJitInfo*), and readscompHndfrom the stack thinking it isftn(CORINFO_METHOD_HANDLE).ret 8), but your caller pushed 12 bytes. This corruptsESP, leading to an immediate crash or an uncatchable stack corruption.Fix for x86 MSVC: Use
__thiscallwithcompHndpassed as thethisargument:2. CLR FailFast Bypasses
__try / __exceptSEH (
__try / __except) cannot catch Windows Fast Fail exceptions (STATUS_FAIL_FAST_EXCEPTION/__fastfail).Inside
clr.dll,CEEJitInfo::getMethodNameperforms sanity checks on theftnpointer (casting it internally toMethodDesc*). Formbook frequently performs process hollowing, runtime injection, or dynamic method invoke with synthetic/obfuscated tokens:ftnis not a validMethodDescor points to a dynamic stub without metadata, CLR's internal runtime contracts/assertions trigger an immediate **EEPolicy::HandleFatalError/FailFast**.__excepthandler.3. Vtable Index Instability Across CLR Versions
vtable[0]is not guaranteed to begetMethodNameacross every .NET CLR release:ICorMethodInfo(defined incorinfo.h),getMethodNameis preceded bygetMethodDescFromMethodor debugging methods depending on the CLR build and architecture.clr.dllbuilds, index 0 isgetMethodDescFromMethod(virtual MethodDesc* getMethodDescFromMethod(CORINFO_METHOD_HANDLE ftn)), which expects 1 parameter, not 2.Recommended Defensive Implementation
To prevent detonation termination, verify
compHndmemory readability, ensure__thiscallon x86, and validate thatftnresides in valid readable memory before invoking: