diff --git a/src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md b/src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md index b6187a3061c..0fe7c45b7a7 100644 --- a/src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md +++ b/src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md @@ -654,6 +654,80 @@ Clipboard hijackers are easy to miss if the operator never steals wallet files. - Validate copied text against **wallet regexes**. - Replace the victim's address with an attacker-controlled value immediately before paste/exfil. + +## NativeAOT + Mixed-Mode .NET malware triage + +Some modern .NET implants avoid obvious packers and instead split the framework across **three compilation targets** so analysts must switch toolchains: + +- **IL-only .NET Framework** modules: easiest stage. Decompile with **ILSpy** / **dnSpyEx** and recover shared enums, module names, operator verbs, and argument grammar. +- **Mixed-Mode C++/CLI** DLLs: the same PE contains both native stubs and managed logic. Review it twice: native disassembler for exports / trampolines, .NET decompiler for the real code behind them. +- **.NET NativeAOT** modules: expect a large native PE with thousands of stripped runtime functions, a `.managed` executable section, and a `hydrated` section where many strings only become readable after runtime materialization. + +### Quick fingerprinting + +- **IL-only**: CLR metadata is obvious and types/methods keep meaningful names. +- **Mixed-Mode**: `DiE`, `dumpbin`, or PE headers show both native code and CLR metadata; exported functions may just be tiny `jmp` stubs into managed code. +- **NativeAOT**: imports may look sparse compared to the capability set, while section names such as `.managed` / `hydrated` strongly suggest NativeAOT-style output. + +### Mixed-Mode export traps + +Do not assume `DllMain`, ordinal `#1`, or the first export is operational. Some sideloaded implants mimic a legitimate DLL export table and leave most exports inert, with the real loader hidden behind a **specific ordinal**. Enumerate all exports, diff them against the legitimate DLL, and single-step the non-trivial stubs. + +If the initial access chain is a trusted EXE + local DLL, also review the dedicated [DLL hijacking](../../windows-hardening/windows-local-privilege-escalation/dll-hijacking/README.md) page for search-order and export-proxying details. + +### NativeAOT reversing workflow + +1. Run normal PE triage (`strings`, `capa`, imports/exports), but expect major blind spots. +2. Look for **packed UTF-16 immediates** in comparisons instead of plaintext verbs. For example, `"get"` may compile into integer compares over constants derived from `0x0067 0x0065 0x0074` instead of a normal `String.Equals` call. +3. Reconstruct **ReadyToRun / EEType / MethodTable** metadata with NativeAOT-aware tooling so virtual methods and type names become navigable again. +4. Recover **rehydrated strings** from the `hydrated` section; on-disk `strings` output may miss URLs, headers, content-types, and operator verbs entirely. +5. Treat missing imports carefully: security-relevant APIs can be hidden in runtime **P/Invoke descriptor tables** instead of the PE import table. + +Useful tools: + +- [ida-nativeaot](https://github.com/Dump-GUY/ida-nativeaot) +- [ghidra-nativeaot](https://github.com/washi1337/ghidra-nativeaot) + +### Per-module AppDomain isolation + +Some managed loaders avoid `Assembly.Load` in the **default AppDomain** and instead: + +1. Create a fresh AppDomain +2. Marshal a proxy object across the boundary with `MarshalByRefObject` +3. Reflectively load and invoke the module +4. Unload the whole AppDomain after execution + +This matters because assemblies loaded into the default AppDomain cannot be unloaded without killing the process. During live response, dump memory **while the module is running** or break on `AppDomain.CreateDomain`, reflection loads, and `AppDomain.Unload` to capture transient assemblies before they disappear. + +### Universal module entry points + +Modular frameworks often normalize execution by forcing **every** plugin to expose the same export/method name (for example `get_version`) while the loader chooses the path based on the file format or a filename convention (`n-*.dll` as native, everything else managed). When you recover one module, search the working directory for siblings that share the same entry point and delimiter grammar. + +### Numbered module updates and startup cleanup + +Another useful hunting pattern is **self-update by numbered DLL suffixes** (`module0.dll`, `module1.dll`, ...). Instead of replacing files in place, the agent writes a higher-numbered variant, loads the newest copy, and may hot-swap its own loader DLL after decoding a **Base64 + GZip** payload from the C2. In parallel, some builds wipe the working directory on startup and keep only the transport module, config, and logs. + +During triage: + +- Sort dropped DLLs by basename + numeric suffix before assuming duplicates are unrelated. +- Carve Base64 blobs and test **GZip** decompression early when tasking/update messages look text-safe but module-like. +- Prioritize first-run filesystem timelines because previously delivered modules may be deleted before the next beacon loop. + +### Credential access and lateral-movement clues hidden by NativeAOT + +If NativeAOT imports look too clean, search reconstructed metadata and runtime-resolved API tables for high-value pivots such as: + +- `ProtectedData.Unprotect` / `CryptUnprotectData` for **DPAPI** abuse under the compromised user context +- `LDAP://RootDSE`, paged LDAP searches, or certificate-validation bypasses for AD reconnaissance / LDAP credential testing +- `WNetAddConnection2`, `WNetCancelConnection2`, `NetShareEnum`, `NetServerEnum`, `NetLocalGroupGetMembers` for SMB spraying, share enumeration, and domain discovery +- Embedded SOCKS5 / WebSocket tunnel parsers that expose a pivot channel after initial access + +For DPAPI-specific post-exploitation workflows, see [DPAPI - Extracting Passwords](../../windows-hardening/windows-local-privilege-escalation/dpapi-extracting-passwords.md). + +### Traffic decoding shortcuts + +When a transport module exposes generic verbs such as `get`, `send`, `upload`, `ws`, or `sendws`, check for a **small fixed XOR key** and text-safe wrappers such as **Base64** before assuming heavy crypto. Reused static headers, fixed URL paths, and custom tokens are often better detection pivots than domains alone. + ## References - [Unit42 – Evolving Tactics of SLOW#TEMPEST: A Deep Dive Into Advanced Malware Techniques](https://unit42.paloaltonetworks.com/slow-tempest-malware-obfuscation/) @@ -676,5 +750,9 @@ Clipboard hijackers are easy to miss if the operator never steals wallet files. - [Check Point Research – VECT: Ransomware by design, Wiper by accident](https://research.checkpoint.com/2026/vect-ransomware-by-design-wiper-by-accident/) - [Libsodium documentation – ChaCha20 stream cipher APIs](https://doc.libsodium.org/advanced/stream_ciphers/chacha20) - [RFC 8439 – ChaCha20 and Poly1305 for IETF Protocols](https://www.rfc-editor.org/rfc/rfc8439) +- [Check Point Research – Cavern Manticore: Exposing an Iran-Linked Modular C2 Framework](https://research.checkpoint.com/2026/cavern-manticore-exposing-iran-linked-modular-c2-framework/) +- [ida-nativeaot – IDA Pro plugin for .NET NativeAOT](https://github.com/Dump-GUY/ida-nativeaot) +- [ghidra-nativeaot – Ghidra plugin for .NET NativeAOT](https://github.com/washi1337/ghidra-nativeaot) +- [Recovering Metadata from .NET Native AOT Binaries](https://blog.washi.dev/posts/recovering-nativeaot-metadata/) {{#include ../../banners/hacktricks-training.md}}