Write docs and detection gallery
@@ -0,0 +1,444 @@
|
||||

|
||||
|
||||
<div align="center">
|
||||
|
||||
<a href="README.md">`English`</a>
|
||||
•
|
||||
<a href="README_JP.md">`日本語`</a> •
|
||||
<a href="README_PT-BR.md">`Portuguese`</a> •
|
||||
<a href="README_TR.md">`Türkçe`</a>
|
||||
|
||||
<a href="#build"><img src="https://img.shields.io/badge/Documentation-%23000000.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1ib29rLW9wZW4iPjxwYXRoIGQ9Ik0xMiA3djE0Ii8+PHBhdGggZD0iTTMgMThhMSAxIDAgMCAxLTEtMVY0YTEgMSAwIDAgMSAxLTFoNWE0IDQgMCAwIDEgNCA0IDQgNCAwIDAgMSA0LTRoNWExIDEgMCAwIDEgMSAxdjEzYTEgMSAwIDAgMS0xIDFoLTZhMyAzIDAgMCAwLTMgMyAzIDMgMCAwIDAtMy0zeiIvPjwvc3ZnPg==&logoColor=white"></a>
|
||||
|
||||
<img src="https://img.shields.io/badge/Windows-%23000000.svg?style=for-the-badge&logo=Windows">
|
||||
|
||||

|
||||
|
||||
<hr>
|
||||
|
||||
<br>
|
||||
|
||||
StackSentry started from a simple idea that kept getting more stubborn: if a loader tries to hide where a `LoadLibrary` came from, the call stack probably still leaves a clue somewhere.
|
||||
|
||||
This project is an x64 user-mode research tool for memory triage, loader analysis, and sensitive DLL-load detection. It starts a target process, injects a lightweight monitor DLL, and watches important events as they happen, trying to answer one direct question: **who actually caused this DLL load or network use?**
|
||||
|
||||
</div>
|
||||
|
||||
> [!Important]
|
||||
> |**This project is still under development**. Bugs, false positives, and false negatives may happen. |
|
||||
> |:--------------------------------|
|
||||
> | StackSentry was built mainly to make loader and in-memory payload analysis easier in a lab. <br> The tool itself does not install a driver and does not make persistent system changes, but you should still compare results with auxiliary tools and treat the output as triage evidence, not an absolute verdict. |
|
||||
|
||||
<br>
|
||||
|
||||
## The Idea
|
||||
|
||||
StackSentry is primarily focused on fast triage of code running in memory. The reasoning behind it is practical: a C2, RAT, or fileless loader can hide the file on disk, encrypt itself while sleeping, and build a nice-looking call stack, but at some point it still needs to load or talk through a network DLL.
|
||||
|
||||
On Windows, that usually means DLLs like `ws2_32.dll`, `wininet.dll`, `winhttp.dll`, `dnsapi.dll`, or APIs exported by them. If those DLLs are loaded or used from a strange origin, it is worth stopping and looking closer.
|
||||
|
||||
This pattern did not come out of nowhere. It lines up directly with ideas already used in behavioral detection, such as these Elastic rules:
|
||||
|
||||
- [`defense_evasion_library_loaded_via_a_callback_function.toml`](https://github.com/elastic/protections-artifacts/blob/6e9ee22c5a7f57b85b0cb063adba9a3c72eca348/behavior/rules/windows/defense_evasion_library_loaded_via_a_callback_function.toml): identifies a library load through a callback, possibly to hide the real origin of the `LoadLibrary` call from the call stack.
|
||||
- [`defense_evasion_network_module_loaded_from_suspicious_unbacked_memory.toml`](https://github.com/elastic/protections-artifacts/blob/6e9ee22c5a7f57b85b0cb063adba9a3c72eca348/behavior/rules/windows/defense_evasion_network_module_loaded_from_suspicious_unbacked_memory.toml): identifies a network module load when the thread stack contains frames outside known executable images, a common pattern in in-memory execution.
|
||||
- [`defense_evasion_library_loaded_from_a_spoofed_call_stack.toml`](https://github.com/elastic/protections-artifacts/blob/6e9ee22c5a7f57b85b0cb063adba9a3c72eca348/behavior/rules/windows/defense_evasion_library_loaded_from_a_spoofed_call_stack.toml): detects a library load from a potentially altered call stack used to conceal the real source of the call.
|
||||
|
||||
Because detections like these exist, some loaders now avoid the obvious path. Instead of calling `LoadLibrary` directly from private memory, they try to hide the origin with callbacks, gadgets inside legitimate modules, threadpool chains, cross-thread dispatch, execution from modified images, or even manipulated unwind metadata.
|
||||
|
||||
StackSentry takes that idea and pushes it further in a lab setting: not only saying that a sensitive DLL was loaded, but also showing the probable origin, the memory involved, the stack state, useful dumps, and, when possible, the path the loader tried to hide.
|
||||
|
||||
## What It Looks For
|
||||
|
||||
- Sensitive DLL loads (`ws2_32`, `wininet`, `winhttp`, `dnsapi`, CLR/.NET, and related modules) from suspicious stacks.
|
||||
- `Unbacked` frames: execution in `MEM_PRIVATE` or `MEM_MAPPED` memory that does not belong to a known image.
|
||||
- `BackedModified` frames: execution inside a `MEM_IMAGE` region whose live bytes differ from the file on disk.
|
||||
- Origins hidden through proxy/gadget flows, callbacks, thread starts, APC, VEH, threadpool, cross-thread dispatch, or prior execution inside the target `.text`.
|
||||
- Stack spoofing: return frames without a plausible callsite, truncated stacks, synthetic stacks, or visible callers that look too clean for the event.
|
||||
- BYOUD/unwind spoofing: temporal changes in `.pdata`, `.xdata`, `.rdata`, `RUNTIME_FUNCTION`, and dynamic unwind tables.
|
||||
- Real network API use, when `/network-use-trace` is enabled, even when the DLL was already loaded and there is no fresh `LoadLibrary` to observe.
|
||||
- Suspicious LDR/loader metadata, including entrypoint hijacks and module inconsistencies.
|
||||
- Memory audit: modified images, orphan executable mappings, private executable regions, and memory artifacts with focused dumps.
|
||||
- Correlation and artifacts: `events.jsonl`, `summary.json`, `memory.json`, `network_trace.json`, `byoud_trace.json`, origin dumps, module dumps, hashes, entropy, and useful strings.
|
||||
- Smaller telemetry, integrity, optional ETW, compact console output, and research/lab modes.
|
||||
|
||||
## Detection Gallery
|
||||
|
||||
The sample commands and expected call-stack summaries are documented in [samples/README.md](samples/README.md). <br> Quick warning: the samples are rough lab builds I made for local testing, so do not expect polished showcase binaries. <br>
|
||||
Below are a few patterns StackSentry can show in the terminal without needing a kernel driver.
|
||||
|
||||
Some images still show output captured around the `v0.8` phase. Since then I improved console rendering, stack compaction, and noise reduction, but I did not think it was honest to call that `v0.9` just because the output got cleaner. If a formatting detail differs slightly from the current version, that is why.
|
||||
|
||||
> [!Important]
|
||||
> Most screenshots below show only a slice of the detection result. Run the samples to see the full output.
|
||||
|
||||
### SilentMoonwalk With Synthetic Stack
|
||||
|
||||
This test uses a modified variant of [klezVirus/SilentMoonwalk](https://github.com/klezVirus/SilentMoonwalk) to load a network DLL with a synthetic stack. All visible frames look like legitimate modules, but callsite validation and origin tracing still tie the DLL load back to the code that prepared the call.
|
||||
|
||||

|
||||
|
||||
### BYOUD / Unwind Metadata Spoofing
|
||||
|
||||
This test uses [klezVirus/byoud](https://github.com/klezVirus/byoud) converted into RDI shellcode. The bootstrap loads DLLs such as `wininet.dll` and `mscoree.dll`, but the stage that matters here uses BYOUD to hide the `ws2_32.dll` load: instead of changing the return address, it manipulates Windows unwind metadata. StackSentry catches the temporal divergence before the sensitive load and reports the table/structure that went out of line.
|
||||
|
||||

|
||||

|
||||
|
||||
### Threadpool Callback Chain
|
||||
|
||||
This sample is based on the idea behind [klezvirus/ThreadPoolExecChain](https://github.com/klezvirus/ThreadPoolExecChain): a threadpool/proxy chain makes the load happen in a context that looks more natural. The report preserves the chain context and marks the modified frames that appear along the path.
|
||||
|
||||

|
||||
|
||||
### Image `.text` Proxy
|
||||
|
||||
This test uses a PIC shellcode I built from the experimental [RefinedPool](https://github.com/Vith0r/RefinedPool/tree/main/RefinedPool) variant, based on [LibTPLoadLib](https://github.com/AlmondOffSec/LibTPLoadLib). The technique follows the API proxying line presented by [paranoidninja](https://0xdarkvortex.dev/hiding-in-plainsight/) and also demonstrated in [paranoidninja/Proxy-DLL-Loads](https://github.com/paranoidninja/Proxy-DLL-Loads).
|
||||
|
||||
Because it is PIC, the code can live inside the loader `.text` and call `LoadLibrary` through a proxy, adding another stack element through an existing gadget in `nvwgf2umx.dll`. The final stack points to a seemingly clean place, but register/origin tracing connects the DLL load back to the `.text` region that started the flow. This proves the detection can hold up even in very specific patterns like this one.
|
||||
|
||||

|
||||
|
||||
### Code Cave / Modified Image
|
||||
|
||||
This pattern is also inspired by a simple experimental variant of my [RefinedPool](https://github.com/Vith0r/RefinedPool/tree/main/RefinedPool) project, converted into PIC shellcode. The sensitive load passes through *bytes written into an image-backed code cave*. The useful part is that StackSentry preserves both the modified module and the changed-byte map in the dump output, which I think is genuinely valuable during triage.
|
||||
|
||||

|
||||
|
||||
### SilentMoonwalk RDI With Synthetic Stack
|
||||
|
||||
Here, the [klezVirus/SilentMoonwalk](https://github.com/klezVirus/SilentMoonwalk) variant is packed as a [Donut](https://github.com/TheWover/donut)/RDI payload. The bootstrap may load DLLs such as `wininet.dll` and `mscoree.dll`, but the relevant stage is the `ws2_32.dll` load with a synthetic stack. The first image shows the DLL-load alerts; the second shows the probable origin leading back to the "hidden" executable region.
|
||||
|
||||

|
||||

|
||||
|
||||
### MassDriver-Style Dispatch
|
||||
|
||||
Inspired by the dispatch pattern from [Sizeable-Bingus/MassDriver](https://github.com/Sizeable-Bingus/MassDriver), a seemingly clean worker thread executes `LoadLibraryA`. `/dispatch-trace` ties the load back to the requester that posted the dispatch structure. It is a very specific detection, but I thought it was interesting enough to include.
|
||||
|
||||

|
||||
|
||||
### Network Use Trace In A C2 Payload
|
||||
|
||||
This example uses `/network-use-trace` to show the case where loading the network DLL is not the only interesting part. The payload also needs to use network APIs, and StackSentry tries to attribute who called `connect`, `WSAConnect`, `send`, `recv`, WinHTTP/WinINet, and related APIs. This can be useful because the output may reveal the real destination, such as domain, IP/port, and even third-party services used in the flow, like `pastebin.com` in this test.
|
||||
|
||||
The stack in the image is compacted on purpose so it does not become a giant block of repeated `system.ni.dll` frames. <br> If you prefer the full stack in one line, like in the older screenshots, use `/inline-stack`; if you want frame-by-frame output with offsets, use `/full-stack`.
|
||||
|
||||

|
||||
|
||||
### Stack View Modes
|
||||
|
||||
Beyond the detections themselves, the current StackSentry console tries to make analysis less exhausting. The same stack can be shown in different ways depending on what you want to inspect:
|
||||
|
||||
#### Compact Stack
|
||||
|
||||
This is the current default format. Repeated frames are grouped as `[module.dll xN]`, which cuts a lot of noise in payloads that pass through large runtimes such as .NET.
|
||||
|
||||

|
||||
|
||||
#### Full Stack With Offsets
|
||||
|
||||
With `/full-stack`, each frame is printed on its own line with the module offset. This is useful when you want to audit exactly where every return frame landed.
|
||||
|
||||

|
||||
|
||||
#### Inline Stack Without Compression
|
||||
|
||||
With `/inline-stack`, the stack goes back to a single-line format without repeated-frame compression.
|
||||
|
||||

|
||||
|
||||
#### Clean Events With Verbose
|
||||
|
||||
The default console output prioritizes alerts so the terminal does not flood. With `/verbose`, clean loads with `score=0` are also shown; those events appear in blue. This helps confirm quickly that a DLL load was observed and recorded, even when it is not suspicious by itself. Even without `/verbose`, `score=0` events are still written to `events.jsonl` and `memory.json`.
|
||||
|
||||

|
||||
|
||||
#### BackedModified And Memory Audit
|
||||
|
||||
When a return frame lands inside a real DLL, but the bytes in that region no longer match the file on disk, StackSentry does not treat it as a clean frame. It marks the stack as `BackedModified`/`captured-modified`, and Memory Audit records the module, region, and temporal detail of the change.
|
||||
|
||||

|
||||
|
||||
## Build
|
||||
|
||||
To keep the build simple, I included `build.ps1`. It uses Microsoft Visual Studio/MSVC to compile. If your environment is not exactly the same, it should not be hard to adapt: the script is short, and reading it makes the required compiler calls pretty clear.
|
||||
|
||||
```powershell
|
||||
.\build.ps1
|
||||
```
|
||||
|
||||
When using the script, build output is written to `build\`:
|
||||
|
||||
- `StackSentry64.exe`
|
||||
- `CallstackMonitor.dll`
|
||||
|
||||
Third-party code lives under `third_party\`. See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for credits and license notes.
|
||||
|
||||
## Test Commands
|
||||
|
||||
Recommended commands and exact sample commands live in [samples/README.md](samples/README.md). <br> If you are unsure where to start, begin there; it includes the first-pass command, the stronger profile, stack output modes, and the examples I use to validate the gallery screenshots.
|
||||
|
||||
## Master Profiles
|
||||
|
||||
- `/quick`: stable DLL-load triage profile. Good for first runs, benign baselines, and lower-noise output.
|
||||
- `/deep`: DLL-load hunting profile. Enables stable callback/thread-start hooks, unwind table hooks, LDR integrity checks, dumps, carving, and a longer correlation window. Memory/API telemetry stays off unless `/mem` is passed.
|
||||
- `/max`: strongest practical DLL-load profile. Enables deep telemetry, stack audit, LDR checks, and `/auto-enter` by default, but leaves memory/API, wait, and threadpool hooks disabled unless explicitly requested.
|
||||
- `/profile <quick|deep|max>`: selects a profile by name.
|
||||
|
||||
If you just want to test a loader and do not want to think too much about flags, I would start with `/max`. After that, add `/hunt`, `/network-use-trace`, or specific flags depending on the result.
|
||||
|
||||
Explicit hook flags are additive, so `/quick /mem`, `/deep /mem`, and `/max /mem` are valid.
|
||||
Use `/max /tp /wait` only when you want the most aggressive experimental hook set.
|
||||
|
||||
## Command Groups
|
||||
|
||||
Use `/features` if you want to see all available arguments. <br>
|
||||
The full `/features` output is grouped by intent so new users do not have to treat every flag as equally important:
|
||||
|
||||
- `Common options`: output directory, timeout, keep-alive, stdin automation, and verbosity.
|
||||
- `Output style`: quiet/plain/live/color controls, target output suppression, `/inline-stack`, and `/full-stack`.
|
||||
- `Origin / proxy analysis`: `/regtrace`, `/dispatch-trace`, and `/threadpool-chain-trace` for hidden-caller and proxy-loading cases.
|
||||
- `Network use analysis`: `/network-use-trace` and `/net-use-trace` for already-loaded network DLL reuse.
|
||||
- `Remote / multi-process`: `/follow-remote` and `/net-reset` for loaders that move execution into another process.
|
||||
- `Extra telemetry / integrity`: `/etw`, `/ldr-integrity`, `/unwind`, `/stack-audit`, `/memory-audit`, `/byoud-trace`, and `/shadow-stack`.
|
||||
- `Aggressive / low-level hooks`: `/mem`, `/tp`, `/wait`, and direct `/xhooks`.
|
||||
- `Advanced config`: custom rules/configuration.
|
||||
|
||||
## Origin Tracing
|
||||
|
||||
Proxy DLL-load techniques can make the final call stack look clean, including by placing an existing image gadget between the real loader code and `LoadLibrary`. StackSentry keeps the default profiles focused and low-noise, but adds opt-in modes for those cases:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run .\samples\sample_03_text_section_proxy.exe /max /origin-trace /no-target-output /timeout 9000
|
||||
.\build\StackSentry64.exe /run .\samples\sample_03_text_section_proxy.exe /max /regtrace /no-target-output /timeout 9000
|
||||
```
|
||||
|
||||
- `/origin-trace`: correlates later DLL loads with recent callback/thread/APC context.
|
||||
- `/regtrace`: enables origin tracing and wraps target `.text` thread starts so a DLL load hidden behind a proxy can still be tied back to the original code when that origin is absent from the final stack.
|
||||
- `/proxy-trace`: alias for `/regtrace`.
|
||||
|
||||
Technical note: `/regtrace` intentionally avoids full register tracing over non-main image modules larger than 32 MB. Private executable memory, thread starts, dynamic executable transitions, and origin correlation are still traced; this only avoids expensive full instrumentation of huge gadget-carrier images. I had to do this because tracing those modules was hurting normal analysis more than it helped. If you really need a higher ceiling for a lab case, it is just a small constant in the source.
|
||||
|
||||
Threadpool hooks remain explicit. Add `/tp` only when you want `TpAllocWork/TpPostWork` telemetry.
|
||||
|
||||
When `/regtrace` correlates a clean gadget load back to executable `MEM_IMAGE` code, StackSentry writes an artifact under `origin_regions\`. This is separate from `dumps\`: it is not an unbacked allocation, but a focused memory window around the traced `.text` origin, with a sidecar JSON containing origin VA/RVA, visible gadget caller, SHA256, entropy, and selected strings.
|
||||
|
||||
The `LdrLoadDll` hook also records origin evidence from live `UNICODE_STRING` arguments and proxy parameter blocks. This covers simple bypasses that enter `LoadLibrary*` after the instrumented prologue but still reach `ntdll!LdrLoadDll`.
|
||||
|
||||
## LDR Integrity
|
||||
|
||||
StackSentry also detects loader entrypoint hijacking. This covers techniques such as LdrShuffle/EPI, where a module still looks legitimate in the PEB loader lists, but its `LDR_DATA_TABLE_ENTRY.EntryPoint` is changed to attacker-controlled code.
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /ldr-integrity /timeout 10000
|
||||
```
|
||||
|
||||
`/deep` and `/max` enable this check. `/quick` keeps it disabled. The analyzer reports the hijacked module, current entrypoint, expected PE entrypoint, memory type/protection for the current entrypoint, and context such as suspicious `OriginalBase` when an entrypoint anomaly already exists.
|
||||
|
||||
## Remote Follow And Net Reset
|
||||
|
||||
These are opt-in lab modes and are not part of `/quick`, `/deep`, or `/max` by default:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run loader.exe /max /follow-remote /timeout 15000
|
||||
.\build\StackSentry64.exe /run loader.exe /max /follow-remote /net-reset /timeout 15000
|
||||
```
|
||||
|
||||
- `/follow-remote`: watches remote-process handles and injects `CallstackMonitor.dll` into the remote process when remote execution is about to happen.
|
||||
- `/net-reset`: tries to unload already-loaded watched network DLLs so a payload has to reload its own network module, making the caller visible to StackSentry. It may or may not work, so expect possible failures.
|
||||
|
||||
Remote-process artifacts are written under `children\<process.exe - pid>\` inside the main run directory. `events.jsonl` remains aggregated, and every event includes the PID that produced it.
|
||||
|
||||
## Network Use Trace
|
||||
|
||||
`/network-use-trace` covers the case where a payload does not need to call `LoadLibrary` for a watched network DLL because the host, loader, or runtime already loaded it. Instead of asking only who loaded `wininet.dll`, `ws2_32.dll`, `winhttp.dll`, or `dnsapi.dll`, StackSentry also asks who is using the network APIs from those modules. It can also be used when the loader/shellcode loads the network DLL by itself:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /network-use-trace /timeout 10000
|
||||
.\build\StackSentry64.exe /run target.exe /max /hunt /network-use-trace /timeout 15000
|
||||
```
|
||||
|
||||
The monitor installs hooks on a focused set of APIs such as `connect`, `WSAConnect`, `send`, `recv`, `getaddrinfo`, `DnsQuery_*`, `InternetOpenUrl*`, `InternetConnect*`, `HttpOpenRequest*`, `HttpSendRequest*`, and common WinHTTP request/read/write calls. The analyzer then classifies the caller address and stack like a DLL-load event. High-signal cases include network APIs called from executable `MEM_PRIVATE`, modified image-backed code, tampered unwind metadata, or spoofed/unusual stacks.
|
||||
|
||||
Honest note: depending on the target, this mode may produce errors, a high event volume, and false positives. It was built for loaders and payloads in a lab, not to promise a perfect experience on large programs that may already be heavily instrumented.
|
||||
|
||||
Findings appear under `== Network Use Details ==` and are written to `network_trace.json`, `memory.json`, and `summary.json`. This mode is not part of `/hunt` by default: it is strong, but can be verbose, so I prefer making the analyst enable it explicitly when they want to prove real network API use.
|
||||
|
||||
## Memory Audit
|
||||
|
||||
`/memory-audit` is an opt-in scan of live process memory inspired by Forrest Orr's excellent Moneta research on malicious memory artifacts. It complements StackSentry's event/call-stack model by asking what the process looks like in memory before StackSentry terminates or detaches from it:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /memory-audit /timeout 10000
|
||||
.\build\StackSentry64.exe /run target.exe /max /regtrace /memory-audit /timeout 10000
|
||||
```
|
||||
|
||||
The audit checks for `MEM_IMAGE` regions missing from loader/module lists, image mappings with no mapped filename available, executable private pages inside images, wiped or heavily altered PE headers, executable permissions on non-executable sections, and private or mapped executable regions that look like payloads.
|
||||
|
||||
High-confidence findings appear under `== Memory Audit ==` and may create focused dumps under `memory_audit\`. Lower-confidence hunting context stays in `memory_audit.json` without raising an alert by default, because modern Windows components and resident tools can legitimately create private or modified pages. This mode is intentionally not enabled by `/quick`, `/deep`, or `/max`.
|
||||
|
||||
## BYOUD And Shadow Stack Research
|
||||
|
||||
`/byoud-trace` is a lab mode for DLL-load cases that manipulate Windows x64 unwind metadata instead of obvious return addresses. It observes unwind table APIs, memory protection changes around `.pdata`/`.xdata`/`.rdata`, and temporal metadata divergence before sensitive loader calls:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /byoud-trace /regtrace /timeout 12000
|
||||
```
|
||||
|
||||
`/hunt` includes `/byoud-trace` because the current BYOUD test corpus gives repeatable proof through temporal unwind metadata divergence, and I found that too useful to hide behind a separate flag.
|
||||
|
||||
`/shadow-stack` is different. It is exposed only as a research/testing switch for systems where Windows exposes user-mode CET/HSP shadow-stack state. It captures CET return frames, compares them against the classic stack as an ordered sequence, and reports hidden, missing, or out-of-order frames. It is not included in `/hunt`, is not counted as mature coverage, and may produce no findings when `XSTATE_CET_U` is unavailable. To be fully honest, I only had limited testing time with this on a friend's laptop, so expect possible rough edges:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /shadow-stack /stack-audit /regtrace /timeout 12000
|
||||
```
|
||||
|
||||
When it works, findings appear under `== Shadow Stack Trace ==` and are written to `shadow_stack_trace.json`. When the platform does not expose the required CET state, the normal `/regtrace`, `/stack-audit`, `/memory-audit`, and `/byoud-trace` layers still carry the detection work.
|
||||
|
||||
## ETW Timeline
|
||||
|
||||
`/etw` is an opt-in lab mode that starts a krabsetw kernel trace before the target's main thread resumes. It records process, thread, and image-load events for the primary PID and child PIDs whose parent is already being tracked. Realistically, it is not the most useful feature in the project, but I thought it was interesting to add:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /etw /timeout 10000
|
||||
.\build\StackSentry64.exe /run loader.exe /max /follow-remote /etw /timeout 15000
|
||||
```
|
||||
|
||||
This does not replace the monitor DLL detections. It provides a kernel-backed timeline to answer questions like which child process appeared, which DLL was mapped at that moment, and whether remote payload execution lines up with a suspicious loader stage. The timeline is written to `etw_timeline.json` and summarized in the final console output. Kernel ETW collection may require elevation; if Windows refuses the trace, StackSentry reports `/etw` as unavailable and continues normal analysis.
|
||||
|
||||
## Individual Hooks
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /xhooks
|
||||
.\build\StackSentry64.exe /run target.exe /mem /unwind
|
||||
```
|
||||
|
||||
`/xhooks` enables the most stable callback/thread-start hooks (`CreateThread` and `QueueUserAPC`). `/origin-trace` adds origin correlation, and `/regtrace` adds the heavier target `.text` thread-start tracing path. `/mem` enables noisy memory/API stack telemetry (`NtAllocateVirtualMemory`, `NtProtectVirtualMemory`, `NtMapViewOfSection`, writes, thread creation, and APC queueing) and is intentionally not enabled by any main profile. `/etw` adds kernel process/thread/image-load timeline telemetry through krabsetw. `/tp` and `/wait` are separated because `Tp*` and `WaitFor*` hooks can destabilize some targets. And yes, they really can destabilize things, so use them according to the target.
|
||||
|
||||
Legacy arguments (`-e`, `--out`, `--rules`, `--timeout-ms`, and `--experimental-hooks`) still work.
|
||||
|
||||
## Console Output
|
||||
|
||||
Console output is grouped by analysis block (`DLL LOAD ANALYSIS`, `MEMORY API TELEMETRY`, `CALLBACK/THREAD ANALYSIS`, and related sections). By default, the console shows only alerts so DLL-load findings do not get buried under routine telemetry. Use `/verbose` when you also want non-alert events. `events.jsonl` still receives the complete event stream.
|
||||
|
||||
In practice, the console tries not to become a novel. The summary stays readable, and raw detail remains in JSON files for anyone who wants to dig later.
|
||||
|
||||
Useful output flags:
|
||||
|
||||
- `/no-target-output`: does not mix target stdout/stderr into the StackSentry console.
|
||||
- `/inline-stack`: prints the full stack in one line, without compacting repeated frames.
|
||||
- `/full-stack`: prints frames one per line with module offsets and disables `[module xN]` compaction.
|
||||
- `/quiet`: writes artifacts and reduces console UI.
|
||||
- `/plain`, `/live`, and `/no-color`: tune animation/color because terminals have opinions.
|
||||
|
||||
At the end, the `Memory` block lists each watched DLL load with the loaded module base address and the selected caller/origin address from the stack. Those addresses are also written to `memory.json` and `summary.json`.
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe
|
||||
.\build\StackSentry64.exe /help
|
||||
.\build\StackSentry64.exe /version
|
||||
```
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /live /verbose
|
||||
.\build\StackSentry64.exe /run target.exe /plain /no-color
|
||||
.\build\StackSentry64.exe /run target.exe /quiet
|
||||
```
|
||||
|
||||
## Outputs
|
||||
|
||||
Each run creates a per-process directory inside the selected `/out` path:
|
||||
|
||||
```text
|
||||
out\loader\loader_binary.exe - 24216\
|
||||
```
|
||||
|
||||
The console tries to show the important parts first, so you do not need to open every JSON file to understand a simple run. But when you want to validate a detection or compare StackSentry with another tool, the artifacts are worth checking:
|
||||
|
||||
- `summary.json`, `memory.json`, and `events.jsonl` store the summary, events, and decisions that supported the alert.
|
||||
- `origin_regions\` stores focused memory windows around regions origin tracing linked to a hidden load.
|
||||
- `dumps\`, `memory_audit\`, `modified_modules\`, and `modified_network_modules\` store preserved bytes for later analysis.
|
||||
- `network_trace.json`, `byoud_trace.json`, `shadow_stack_trace.json`, and `etw_timeline.json` appear when their corresponding modes are used.
|
||||
- `children\` stores per-PID artifacts when `/follow-remote` follows execution into another process.
|
||||
|
||||
One detail worth calling out: when StackSentry preserves a modified module, it also writes a `.tag` file next to the dump. <br> That `.tag` is a simple diff map with offsets/bytes changed relative to the file on disk. For code caves, module stomping, or temporarily modified images, this is often more useful than only having the full module dump.
|
||||
|
||||
Exit codes:
|
||||
|
||||
- `0`: no alerts.
|
||||
- `10`: at least one alert was generated.
|
||||
- `1`/`2`: runtime error, target crash, argument error, or configuration error.
|
||||
|
||||
## Rules
|
||||
|
||||
`config\rules.json` shows the supported format:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": 3,
|
||||
"network_modules": ["ws2_32.dll", "wininet.dll", "winhttp.dll", "dnsapi.dll", "iphlpapi.dll"],
|
||||
"dotnet_modules": ["clr.dll", "coreclr.dll", "mscoree.dll", "System.Management.Automation.dll"],
|
||||
"alert_on_unbacked_executable": true,
|
||||
"alert_on_backed_modified": true,
|
||||
"dump_suspicious_regions": true,
|
||||
"analyze_dumps": true,
|
||||
"carve_embedded_pe": true,
|
||||
"module_integrity_enabled": true,
|
||||
"enable_sleep_hooks": true,
|
||||
"enable_msgwait_hook": true,
|
||||
"enable_wait_object_hooks": false,
|
||||
"enable_thread_start_hooks": false,
|
||||
"enable_threadpool_hooks": false,
|
||||
"experimental_hooks": false,
|
||||
"memory_api_hooks": false,
|
||||
"unwind_integrity": true,
|
||||
"unwind_table_hooks": false,
|
||||
"origin_trace": false,
|
||||
"register_trace": false,
|
||||
"follow_remote": false,
|
||||
"net_reset": false,
|
||||
"etw_telemetry": false,
|
||||
"ldr_integrity": false,
|
||||
"dispatch_trace": false,
|
||||
"threadpool_chain_trace": false,
|
||||
"stack_audit": false,
|
||||
"memory_audit": false,
|
||||
"byoud_trace": false,
|
||||
"shadow_stack": false,
|
||||
"network_use_trace": false,
|
||||
"callsite_validation": true,
|
||||
"alert_on_unwind_tamper": true,
|
||||
"correlation_window_ms": 5000,
|
||||
"max_dump_bytes": 16777216,
|
||||
"long_sleep_ms": 1000
|
||||
}
|
||||
```
|
||||
|
||||
Use another file with `--rules path\to\rules.json`.
|
||||
|
||||
The old `watch_dlls` field is still accepted as a compatibility alias. You probably will not see it in use, because basically only I tested that path, and why not remove it? I am tired.
|
||||
|
||||
## Current Limits
|
||||
|
||||
I could try to sell this project as if it solved everything, but that would be dishonest. After hundreds of tests, it is clearly strong at what it was built to do, but it still depends on the target. Like every tool in this area, it has real limitations; some obvious, some less so:
|
||||
|
||||
- This is still user-mode instrumentation. A strong target can obviously detect/remove hooks or use execution paths the monitor does not observe.
|
||||
- `WaitForSingleObject/WaitForMultipleObjects` and `Tp*` hooks exist, but stay outside `/deep` and `/max` because they destabilized some test targets.
|
||||
- Memory API hooks can be noisy in environments with benign injection/hooking engines, so they are enabled only when `/mem` is passed.
|
||||
- `Register tracing` is opt-in lab telemetry. It was designed to reveal origins hidden by proxy/gadget loaders, not to produce a complete instruction trace.
|
||||
- `/follow-remote` depends on user-mode hooks observing remote-execution setup. Complete direct/indirect syscall chains can still bypass those hooks.
|
||||
- `/net-reset` can fail when a DLL is statically imported, held by refcount, or actively in use. When that happens, StackSentry reports the reset failure and continues normally.
|
||||
- `/etw` is context telemetry, not detection proof by itself. It may require elevation and can miss events that happened before the trace started. There is only so much to do there.
|
||||
- `/memory-audit` runs while the target is still alive, usually at timeout/keep-alive. If the target exits before that, there may be no live address space left to scan.
|
||||
- LDR integrity treats an `EntryPoint` outside the module image or outside `MEM_IMAGE` as the primary proof. `OriginalBase` is reported as context because that field is layout-sensitive across Windows builds and should not be trusted alone. I noticed that while testing on another laptop, so I kept this conservative.
|
||||
- Zydis improves instruction decoding, but callsite validation is still heuristic because a return address alone does not prove real control-flow history.
|
||||
- Module comparison is "PE-sieve-inspired", but clearly simplified: it compares executable and unwind sections against disk and does not try to model every legitimate relocation/hook case.
|
||||
- The memory audit is inspired by Moneta-style artifact classes, but conservative in the console: weak private-page evidence is written as hunting context unless tied to a stronger anomaly.
|
||||
- `/shadow-stack` is a research switch for CET/HSP experiments and may stay silent when the target or platform does not expose user-mode shadow-stack state.
|
||||
- Advanced stack spoofing is not impossible to bypass in ring3. The tool raises the cost by correlating callbacks, callsites, module integrity, captured callsite bytes, unwind metadata, and optional memory/API telemetry when `/mem` is enabled.
|
||||
|
||||
## License
|
||||
|
||||
This project is distributed under the MIT License (Modify It Tonight). Use it, modify it, break it in a lab, fix it, compare it, publish results, do what you need. <br> If it saves you a few hours of analysis, I am already happy. Coffee is accepted, though.
|
||||
|
||||
Third-party code keeps its own licenses and credits in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
||||
@@ -0,0 +1,309 @@
|
||||

|
||||
|
||||
<div align="center">
|
||||
|
||||
<a href="README.md">`English`</a>
|
||||
•
|
||||
<a href="README_JP.md">`日本語`</a> •
|
||||
<a href="README_PT-BR.md">`Portuguese`</a> •
|
||||
<a href="README_TR.md">`Türkçe`</a>
|
||||
|
||||
<a href="#build"><img src="https://img.shields.io/badge/Documentation-%23000000.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1ib29rLW9wZW4iPjxwYXRoIGQ9Ik0xMiA3djE0Ii8+PHBhdGggZD0iTTMgMThhMSAxIDAgMCAxLTEtMVY0YTEgMSAwIDAgMSAxLTFoNWE0IDQgMCAwIDEgNCA0IDQgNCAwIDAgMSA0LTRoNWExIDEgMCAwIDEgMSAxdjEzYTEgMSAwIDAgMS0xIDFoLTZhMyAzIDAgMCAwLTMgMyAzIDMgMCAwIDAtMy0zeiIvPjwvc3ZnPg==&logoColor=white"></a>
|
||||
|
||||
<img src="https://img.shields.io/badge/Windows-%23000000.svg?style=for-the-badge&logo=Windows">
|
||||
|
||||

|
||||
|
||||
<hr>
|
||||
|
||||
<br>
|
||||
|
||||
StackSentry は、かなり単純な発想から始まりました。もし loader が `LoadLibrary` の origin を隠そうとしているなら、call stack のどこかにまだ痕跡が残っているはず、という発想です。
|
||||
|
||||
このプロジェクトは x64 user-mode の研究用ツールです。目的は memory triage、loader analysis、そして sensitive DLL-load detection です。対象 process を起動し、軽量な monitor DLL を inject して、重要なイベントをリアルタイムに観測します。見たいのはかなり直接的な問いです: **この DLL load、または network use を本当に起こしたのは誰か?**
|
||||
|
||||
</div>
|
||||
|
||||
> [!Important]
|
||||
> |**このプロジェクトは現在も開発中です**。Bug、false positive、false negative が起きる可能性があります。 |
|
||||
> |:--------------------------------|
|
||||
> | StackSentry は主に lab 環境で loader や in-memory payload の分析を楽にするために作られました。<br> Kernel driver はインストールせず、永続的な system change もしません。ただし、結果は必ず補助ツールと比較し、絶対的な判定ではなく triage evidence として扱ってください。 |
|
||||
|
||||
<br>
|
||||
|
||||
## アイデア
|
||||
|
||||
StackSentry は、memory 上で実行されている code の高速 triage に焦点を当てています。考え方は実用的です。C2、RAT、fileless loader は disk 上の file を隠せます。sleep 中に自分を暗号化できます。見た目のきれいな call stack も作れます。それでも、どこかの時点で network DLL を load するか、network API を使う必要があります。
|
||||
|
||||
Windows では、多くの場合 `ws2_32.dll`、`wininet.dll`、`winhttp.dll`、`dnsapi.dll`、またはそれらが export する API が関係します。これらの DLL が不自然な origin から load/use されているなら、そこで止まって確認する価値があります。
|
||||
|
||||
この pattern は StackSentry だけの発想ではありません。Elastic の behavioral detection rule とも同じ方向を向いています:
|
||||
|
||||
- [`defense_evasion_library_loaded_via_a_callback_function.toml`](https://github.com/elastic/protections-artifacts/blob/6e9ee22c5a7f57b85b0cb063adba9a3c72eca348/behavior/rules/windows/defense_evasion_library_loaded_via_a_callback_function.toml): callback 経由の library load を識別します。`LoadLibrary` の真の origin を call stack から隠す試みかもしれません。
|
||||
- [`defense_evasion_network_module_loaded_from_suspicious_unbacked_memory.toml`](https://github.com/elastic/protections-artifacts/blob/6e9ee22c5a7f57b85b0cb063adba9a3c72eca348/behavior/rules/windows/defense_evasion_network_module_loaded_from_suspicious_unbacked_memory.toml): thread stack に known executable image 以外の frame がある状態で network module が load されるケースを検出します。
|
||||
- [`defense_evasion_library_loaded_from_a_spoofed_call_stack.toml`](https://github.com/elastic/protections-artifacts/blob/6e9ee22c5a7f57b85b0cb063adba9a3c72eca348/behavior/rules/windows/defense_evasion_library_loaded_from_a_spoofed_call_stack.toml): call source を隠すために改変/spoofed された call stack からの library load を検出します。
|
||||
|
||||
こうした detection が存在するため、一部の loader は単純な `LoadLibrary` 呼び出しを避けます。Callback、legitimate module 内の gadget、threadpool chain、cross-thread dispatch、modified image execution、unwind metadata manipulation などを使って origin を隠そうとします。
|
||||
|
||||
StackSentry はその考えを lab 向けにもう少し押し進めています。単に「sensitive DLL が load された」と言うだけでなく、probable origin、memory state、stack pattern、有用な dump、そして loader が隠そうとした path を可能な範囲で表示します。
|
||||
|
||||
## 何を見るか
|
||||
|
||||
- `ws2_32`、`wininet`、`winhttp`、`dnsapi`、CLR/.NET などの sensitive DLL load。
|
||||
- `Unbacked` frame: known image に属さない executable `MEM_PRIVATE` / `MEM_MAPPED` memory。
|
||||
- `BackedModified` frame: `MEM_IMAGE` だが disk 上の file と live bytes が一致しない code。
|
||||
- Proxy/gadget、callback、thread start、APC、VEH、threadpool、dispatch、または target `.text` から隠された origin。
|
||||
- Stack spoofing: plausible callsite のない return frame、truncated stack、synthetic stack、不自然にきれいな visible caller。
|
||||
- BYOUD/unwind spoofing: `.pdata`、`.xdata`、`.rdata`、`RUNTIME_FUNCTION`、dynamic unwind table の temporal change。
|
||||
- `/network-use-trace` 有効時の real network API use。DLL がすでに load 済みでも観測します。
|
||||
- LDR metadata inconsistency、EntryPoint hijack、module inconsistency。
|
||||
- Memory audit: modified image、orphan executable mapping、private executable region、focused dump。
|
||||
- `events.jsonl`、`summary.json`、`memory.json`、`network_trace.json`、`byoud_trace.json`、origin dump、module dump、hash、entropy、strings などの artifact。
|
||||
|
||||
## Detection Gallery
|
||||
|
||||
Sample の command と期待される call-stack summary は [samples/README.md](samples/README.md) にあります。<br>先に言っておくと、sample は local test 用のかなり疑わしい lab artifact です。きれいな showcase binary ではありません。<br>
|
||||
以下は、kernel driver なしで StackSentry が terminal に表示できる pattern の一部です。
|
||||
|
||||
いくつかの画像は `v0.8` 付近の output です。その後 console rendering、stack compaction、noise reduction を改善しましたが、output が少しきれいになっただけで `v0.9` と呼ぶのは正直ではないと思ったので、そのままにしています。
|
||||
|
||||
> [!Important]
|
||||
> 下の画像の多くは detection result の一部だけを切り出したものです。完全な output は sample を実行して確認してください。
|
||||
|
||||
### Synthetic Stack の SilentMoonwalk
|
||||
|
||||
この test は [klezVirus/SilentMoonwalk](https://github.com/klezVirus/SilentMoonwalk) の modified variant を使い、synthetic stack で network DLL を load します。Visible frame は legitimate module に見えますが、callsite validation と origin tracing が DLL load を準備した code に結び直します。
|
||||
|
||||

|
||||
|
||||
### BYOUD / Unwind Metadata Spoofing
|
||||
|
||||
この test は [klezVirus/byoud](https://github.com/klezVirus/byoud) を RDI shellcode 化したものです。Bootstrap は `wininet.dll` や `mscoree.dll` を load しますが、重要なのは BYOUD を使って `ws2_32.dll` load を隠す stage です。Return address を変える代わりに Windows unwind metadata を操作します。StackSentry は sensitive load の前に起きる temporal divergence を捕まえます。
|
||||
|
||||

|
||||

|
||||
|
||||
### Threadpool Callback Chain
|
||||
|
||||
この sample は [klezvirus/ThreadPoolExecChain](https://github.com/klezvirus/ThreadPoolExecChain) の考え方に基づいています。Threadpool/proxy chain によって load が自然に見える context で発生します。StackSentry は chain context を保持し、path 上の modified frame を報告します。
|
||||
|
||||

|
||||
|
||||
### Image `.text` Proxy
|
||||
|
||||
この test は [LibTPLoadLib](https://github.com/AlmondOffSec/LibTPLoadLib) を元にした実験的な [RefinedPool](https://github.com/Vith0r/RefinedPool/tree/main/RefinedPool) variant から作った PIC shellcode を使います。この technique は [paranoidninja](https://0xdarkvortex.dev/hiding-in-plainsight/) が説明した API proxying の流れや [Proxy-DLL-Loads](https://github.com/paranoidninja/Proxy-DLL-Loads) と近いものです。
|
||||
|
||||
PIC なので code は loader の `.text` に置けます。そして `nvwgf2umx.dll` 内の existing gadget を経由して proxy `LoadLibrary` flow を作れます。Final stack はきれいに見えますが、register/origin tracing は DLL load を開始した `.text` region に戻します。
|
||||
|
||||

|
||||
|
||||
### Code Cave / Modified Image
|
||||
|
||||
この pattern も [RefinedPool](https://github.com/Vith0r/RefinedPool/tree/main/RefinedPool) の小さな実験的 variant から来ています。Sensitive load は image-backed code cave に書き込まれた bytes を通ります。StackSentry は modified module と `.tag` changed-byte map を保存するため、triage でかなり役に立ちます。
|
||||
|
||||

|
||||
|
||||
### SilentMoonwalk RDI Synthetic Stack
|
||||
|
||||
ここでは [SilentMoonwalk](https://github.com/klezVirus/SilentMoonwalk) variant を [Donut](https://github.com/TheWover/donut)/RDI payload として package しています。Bootstrap は `wininet.dll` や `mscoree.dll` を load することがありますが、重要な stage は synthetic stack での `ws2_32.dll` load です。2枚目の画像は hidden executable region に戻る probable origin を示します。
|
||||
|
||||

|
||||

|
||||
|
||||
### MassDriver 風 Dispatch
|
||||
|
||||
[Sizeable-Bingus/MassDriver](https://github.com/Sizeable-Bingus/MassDriver) の dispatch pattern から着想を得た sample です。きれいに見える worker thread が `LoadLibraryA` を実行します。`/dispatch-trace` は、その load を dispatch structure を送った requester に結びます。
|
||||
|
||||

|
||||
|
||||
### C2 Payload の Network Use Trace
|
||||
|
||||
この例は `/network-use-trace` を使います。Network DLL load だけでなく、payload が `connect`、`WSAConnect`、`send`、`recv`、WinHTTP/WinINet などを実際に使う場面を追います。Output には domain、IP/port、さらにこの test では `pastebin.com` のような third-party service も出ます。
|
||||
|
||||
画像の stack は意図的に compact しています。古い screenshot のように full stack を1行で見たい場合は `/inline-stack`、offset 付きで frame ごとに見たい場合は `/full-stack` を使ってください。
|
||||
|
||||

|
||||
|
||||
### Stack 表示モード
|
||||
|
||||
Detection そのものだけでなく、現在の StackSentry console は分析を少し楽にするために stack 表示も調整できます。同じ stack でも、見たい内容に応じて表示形式を変えられます:
|
||||
|
||||
#### Compact Stack
|
||||
|
||||
現在の default 表示です。連続して繰り返される frame は `[module.dll xN]` の形でまとめられます。.NET のような大きい runtime を通る payload では、これだけでかなり noise が減ります。
|
||||
|
||||

|
||||
|
||||
#### Offset 付き Full Stack
|
||||
|
||||
`/full-stack` を使うと、各 frame が module offset 付きで1行ずつ表示されます。どの return frame がどこに落ちたのかを細かく確認したい時に向いています。
|
||||
|
||||

|
||||
|
||||
#### Compression なし Inline Stack
|
||||
|
||||
`/inline-stack` を使うと、stack は1行の形式に戻り、繰り返し frame の compression も行われません。
|
||||
|
||||

|
||||
|
||||
#### Verbose で Clean Event も表示
|
||||
|
||||
Default の console は terminal が埋まらないよう alert を優先します。`/verbose` を使うと、`score=0` の clean load も表示されます。この event は青色で出るため、DLL load が観測され記録されたことをすぐ確認できます。`/verbose` を使わなくても、`score=0` event は `events.jsonl` と `memory.json` に保存されます。
|
||||
|
||||

|
||||
|
||||
#### BackedModified と Memory Audit
|
||||
|
||||
Return frame が本物の DLL 内に落ちても、その region の bytes が disk 上の file と一致しない場合、StackSentry はそれを clean frame として扱いません。Stack は `BackedModified` / `captured-modified` として表示され、Memory Audit は module、region、そして temporal な変更 detail を記録します。
|
||||
|
||||

|
||||
|
||||
## Build
|
||||
|
||||
Build には `build.ps1` を使います。Microsoft Visual Studio/MSVC が必要です。
|
||||
|
||||
```powershell
|
||||
.\build.ps1
|
||||
```
|
||||
|
||||
Script は `build\` に以下を出力します:
|
||||
|
||||
- `StackSentry64.exe`
|
||||
- `CallstackMonitor.dll`
|
||||
|
||||
Third-party code は `third_party\` にあります。Credit と license note は [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) を確認してください。
|
||||
|
||||
## Test Commands
|
||||
|
||||
Recommended commands と exact sample commands は [samples/README.md](samples/README.md) にあります。どこから始めるか迷ったらそこから始めてください。First-pass command、strong profile、stack output mode、gallery screenshot の検証用例があります。
|
||||
|
||||
## Main Profiles
|
||||
|
||||
- `/quick`: low-noise DLL-load triage profile。
|
||||
- `/deep`: callback/thread-start hooks、unwind hooks、LDR integrity、dump/correlation を有効化する hunting profile。Memory API hooks は opt-in のままです。
|
||||
- `/max`: 実用上いちばん強い DLL-load profile。Deep telemetry、stack audit、LDR checks、default `/auto-enter` を含みますが、`/mem`、`/tp`、`/wait` は別途有効化します。
|
||||
- `/profile <quick|deep|max>`: profile を選択します。
|
||||
|
||||
疑わしい loader をまず見るなら、基本は `/max` からでよいと思います。その後、結果に応じて `/hunt`、`/network-use-trace`、または個別 flag を追加します。
|
||||
|
||||
## Command Groups
|
||||
|
||||
全オプション:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /features
|
||||
```
|
||||
|
||||
主要グループ:
|
||||
|
||||
- `Output style`: `/no-target-output`、`/inline-stack`、`/full-stack`、`/quiet`、`/plain`、`/live`、`/no-color`。
|
||||
- `Origin / proxy analysis`: `/regtrace`、`/dispatch-trace`、`/threadpool-chain-trace`。
|
||||
- `Network use analysis`: `/network-use-trace`、`/net-use-trace`。
|
||||
- `Remote / multi-process`: `/follow-remote`、`/net-reset`。
|
||||
- `Extra telemetry / integrity`: `/etw`、`/ldr-integrity`、`/unwind`、`/stack-audit`、`/memory-audit`、`/byoud-trace`、`/shadow-stack`。
|
||||
- `Aggressive / low-level hooks`: `/mem`、`/tp`、`/wait`、`/xhooks`。
|
||||
|
||||
## 重要なモード
|
||||
|
||||
### Origin Tracing
|
||||
|
||||
Proxy DLL-load technique は final stack をきれいに見せることがあります。`/regtrace` はそのような case で origin を戻そうとします:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run .\samples\sample_03_text_section_proxy.exe /max /regtrace /no-target-output /timeout 9000
|
||||
```
|
||||
|
||||
`/origin-trace` は callback/thread/APC context と correlation します。`/regtrace` はそれを広げ、gadget/proxy の裏にある real origin を追います。Artifact は `origin_regions\` に出力されます。
|
||||
|
||||
Technical note: 現在の `/regtrace` は、main image ではない 32 MB 超の image module に対して full register tracing を意図的に避けます。Private executable memory、thread start、dynamic executable transition、origin correlation は引き続き追跡されます。この制限は、巨大な gadget-carrier image への高コストな full instrumentation を避けるためのものです。Lab case で本当に上限を上げたい場合は、source 内の小さな constant を変更できます。
|
||||
|
||||
### Network Use Trace
|
||||
|
||||
Payload が network DLL をすでに load 済みとして使う場合、新しい `LoadLibrary` は観測できないかもしれません。`/network-use-trace` は network API use を追います:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /network-use-trace /timeout 10000
|
||||
.\build\StackSentry64.exe /run target.exe /max /hunt /network-use-trace /timeout 15000
|
||||
```
|
||||
|
||||
この mode は `/hunt` に default では含まれません。強力ですが verbose になる可能性があります。Findings は `== Network Use Details ==`、`network_trace.json`、`memory.json`、`summary.json` に出ます。
|
||||
|
||||
### Memory Audit
|
||||
|
||||
`/memory-audit` は Moneta-style memory artifact から着想を得た live process memory scan です:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /memory-audit /timeout 10000
|
||||
```
|
||||
|
||||
Modified image、orphan executable mapping、private executable region、suspicious section permission などを報告します。弱い finding は alert ではなく JSON の hunting context に残すようにしています。
|
||||
|
||||
### BYOUD と Shadow Stack Research
|
||||
|
||||
`/byoud-trace` は return address ではなく Windows x64 unwind metadata を操作する case の lab mode です:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /byoud-trace /regtrace /timeout 12000
|
||||
```
|
||||
|
||||
`/hunt` は `/byoud-trace` を含みます。`/shadow-stack` は CET/HSP research switch で、platform が user-mode shadow-stack state を公開しない場合は何も出ないことがあります。`/hunt` には含めていません。
|
||||
|
||||
## Console Output
|
||||
|
||||
Console output は alert を優先します。生の詳細は JSON artifact に残ります。
|
||||
|
||||
- `/no-target-output`: target の stdout/stderr を StackSentry console に混ぜません。
|
||||
- `/inline-stack`: repeated-frame compression なしで full stack を1行に出します。
|
||||
- `/full-stack`: 各 frame を offset 付きで1行ずつ出します。
|
||||
- `/verbose`: non-alert event も表示します。
|
||||
- `/quiet`: artifact を書き、console UI を減らします。
|
||||
|
||||
Exit code:
|
||||
|
||||
- `0`: alert なし。
|
||||
- `10`: 1つ以上の alert。
|
||||
- `1`/`2`: runtime、argument、target/config error。
|
||||
|
||||
## Outputs
|
||||
|
||||
各 run は選択した `/out` path に per-process directory を作ります:
|
||||
|
||||
```text
|
||||
out\loader\loader_binary.exe - 24216\
|
||||
```
|
||||
|
||||
主な artifact:
|
||||
|
||||
- `summary.json`、`memory.json`、`events.jsonl`
|
||||
- `origin_regions\`
|
||||
- `dumps\`、`memory_audit\`、`modified_modules\`、`modified_network_modules\`
|
||||
- `network_trace.json`、`byoud_trace.json`、`shadow_stack_trace.json`、`etw_timeline.json`
|
||||
- `/follow-remote` 時の `children\`
|
||||
|
||||
Modified module dump の隣に出る `.tag` は重要です。Disk 上の file と比較して、どの offset/byte が変わったかを示す diff map です。
|
||||
|
||||
## Rules
|
||||
|
||||
Default rule format は `config\rules.json` を見てください。Custom config:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe --rules path\to\rules.json
|
||||
```
|
||||
|
||||
古い `watch_dlls` field は compatibility alias としてまだ受け付けます。
|
||||
|
||||
## 現在の制限
|
||||
|
||||
- これは user-mode instrumentation です。強い target は hook を検出/削除できます。
|
||||
- `/mem`、`/tp`、`/wait` は noisy または destabilizing になることがあるため、明示的に有効化します。
|
||||
- `/follow-remote` は user-mode remote-execution setup の観測に依存します。
|
||||
- `/net-reset` は network DLL を常に unload できるわけではありません。
|
||||
- `/memory-audit` は target process がまだ生きているときに意味があります。
|
||||
- `/shadow-stack` は experimental CET/HSP mode で、多くの system では output が出ない可能性があります。
|
||||
- Advanced stack spoofing を ring3 で完全に不可能にするものではありません。StackSentry は callsite、origin、memory、unwind、network-use の correlation でコストを上げるための tool です。
|
||||
|
||||
## License
|
||||
|
||||
このプロジェクトは MIT License (Modify It Tonight) で配布されます。使って、変えて、lab で壊して、直して、比較して、結果を公開してください。<br>分析時間が少しでも節約できたなら、それだけで嬉しいです。コーヒーも歓迎です。
|
||||
|
||||
Third-party code の license と credit は [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) にあります。
|
||||
@@ -0,0 +1,443 @@
|
||||

|
||||
|
||||
<div align="center">
|
||||
|
||||
<a href="README.md">`English`</a>
|
||||
•
|
||||
<a href="README_JP.md">`日本語`</a> •
|
||||
<a href="README_PT-BR.md">`Portuguese`</a> •
|
||||
<a href="README_TR.md">`Türkçe`</a>
|
||||
|
||||
<a href="#build"><img src="https://img.shields.io/badge/Documentation-%23000000.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1ib29rLW9wZW4iPjxwYXRoIGQ9Ik0xMiA3djE0Ii8+PHBhdGggZD0iTTMgMThhMSAxIDAgMCAxLTEtMVY0YTEgMSAwIDAgMSAxLTFoNWE0IDQgMCAwIDEgNCA0IDQgNCAwIDAgMSA0LTRoNWExIDEgMCAwIDEgMSAxdjEzYTEgMSAwIDAgMS0xIDFoLTZhMyAzIDAgMCAwLTMgMyAzIDMgMCAwIDAtMy0zeiIvPjwvc3ZnPg==&logoColor=white"></a>
|
||||
|
||||
<img src="https://img.shields.io/badge/Windows-%23000000.svg?style=for-the-badge&logo=Windows">
|
||||
|
||||

|
||||
|
||||
<hr>
|
||||
|
||||
<br>
|
||||
|
||||
StackSentry nasceu de uma ideia simples que foi ficando cada vez mais teimosa: se um loader tenta esconder a origem de um `LoadLibrary`, a call stack provavelmente ainda deixa alguma pista.
|
||||
|
||||
Este projeto é uma ferramenta de pesquisa em user-mode x64 para triagem de memória, análise de loaders e detecção de carregamento sensível de DLLs. Ela inicia um processo alvo, injeta uma DLL de monitoramento leve e observa eventos importantes no momento em que acontecem, tentando responder uma pergunta direta: <br> **quem realmente provocou esse carregamento ou uso de rede?**
|
||||
|
||||
</div>
|
||||
|
||||
> [!Important]
|
||||
> |**Este projeto ainda está em desenvolvimento**. Portanto, bugs, falsos positivos ou falsos negativos podem ocorrer. |
|
||||
> |:--------------------------------|
|
||||
> | O StackSentry foi desenvolvido principalmente para facilitar a análise de loaders e payloads em laboratório. <br> A ferramenta em si não instala driver e não faz alteração persistente no sistema, compare resultados com ferramentas auxiliares e considere o resultado como evidência de triagem, não como veredito absoluto. |
|
||||
|
||||
<br>
|
||||
|
||||
## A Ideia
|
||||
|
||||
O foco principal do StackSentry é triagem rápida de código executando em memória. A lógica por trás do projeto é bem prática: um C2, RAT ou loader fileless pode esconder o arquivo em disco, criptografar o próprio corpo enquanto dorme e montar uma call stack bonita, mas uma hora ele precisa carregar ou falar com uma dll de rede.
|
||||
|
||||
No Windows, isso normalmente passa por DLLs como `ws2_32.dll`, `wininet.dll`, `winhttp.dll`, `dnsapi.dll` ou por APIs que vivem nelas. Se essas DLLs são carregadas ou usadas a partir de uma origem estranha, vale parar e olhar.
|
||||
|
||||
Esse padrão não saiu do nada. Ele vem diretamente de ideias já usadas em detecção comportamental, como estas regras da Elastic:
|
||||
|
||||
- [`defense_evasion_library_loaded_via_a_callback_function.toml`](https://github.com/elastic/protections-artifacts/blob/6e9ee22c5a7f57b85b0cb063adba9a3c72eca348/behavior/rules/windows/defense_evasion_library_loaded_via_a_callback_function.toml): identifica carregamento de biblioteca via callback, possivelmente para esconder a origem real da chamada `LoadLibrary` na call stack.
|
||||
- [`defense_evasion_network_module_loaded_from_suspicious_unbacked_memory.toml`](https://github.com/elastic/protections-artifacts/blob/6e9ee22c5a7f57b85b0cb063adba9a3c72eca348/behavior/rules/windows/defense_evasion_network_module_loaded_from_suspicious_unbacked_memory.toml): identifica carregamento de módulo de rede quando a stack da thread contém frames fora de imagens executáveis conhecidas, um padrão comum em execução em memória.
|
||||
- [`defense_evasion_library_loaded_from_a_spoofed_call_stack.toml`](https://github.com/elastic/protections-artifacts/blob/6e9ee22c5a7f57b85b0cb063adba9a3c72eca348/behavior/rules/windows/defense_evasion_library_loaded_from_a_spoofed_call_stack.toml): detecta carregamento de biblioteca a partir de uma call stack possivelmente alterada para esconder a fonte real da chamada.
|
||||
|
||||
Justamente por existirem regras desse tipo, alguns loaders começaram a evitar o caminho óbvio. Em vez de chamar `LoadLibrary` direto a partir de memória privada, tentam esconder a origem usando callbacks, gadgets em módulos legítimos, threadpool, dispatch entre threads, execução em imagem modificada ou até metadados de unwind.
|
||||
|
||||
O StackSentry pega essa ideia e tenta aprofundar em modo de laboratório: não só dizer que uma DLL sensível foi carregada, mas também mostrar a origem provável, a memória envolvida, o estado da stack, os dumps úteis e, quando possível, o caminho que o loader tentou esconder.
|
||||
|
||||
## O Que Ele Procura
|
||||
|
||||
- Carregamento de DLLs sensíveis (`ws2_32`, `wininet`, `winhttp`, `dnsapi`, CLR/.NET e afins) a partir de stacks suspeitas.
|
||||
- Frames `Unbacked`: execução em `MEM_PRIVATE` ou `MEM_MAPPED` que não pertence a uma imagem conhecida.
|
||||
- Frames `BackedModified`: execução em `MEM_IMAGE` onde bytes vivos divergem do arquivo em disco.
|
||||
- Origem escondida por proxy/gadget, callback, thread start, APC, VEH, threadpool, dispatch entre threads ou execução anterior na `.text` do alvo.
|
||||
- Stack spoofing: return frame sem callsite plausível, stack truncada, stack sintética ou caller visível “limpo” demais para o evento.
|
||||
- BYOUD/unwind spoofing: mudanças temporais em `.pdata`, `.xdata`, `.rdata`, `RUNTIME_FUNCTION` e tabelas dinâmicas de unwind.
|
||||
- Uso real de APIs de rede, quando `/network-use-trace` é habilitado, mesmo se a DLL já estava carregada e não existe um novo `LoadLibrary` para observar.
|
||||
- Metadados LDR/loader fora do esperado, incluindo entrypoint hijack e inconsistências de módulo.
|
||||
- Memory audit: imagens modificadas, mappings executáveis órfãos, regiões privadas executáveis e artefatos em memória com dumps focados.
|
||||
- Correlação e artefatos: `events.jsonl`, `summary.json`, `memory.json`, `network_trace.json`, `byoud_trace.json`, origin dumps, module dumps, hashes, entropia e strings úteis.
|
||||
- E outras checagens menores de telemetria, integridade, ETW opcional, saída compacta e modos de laboratório.
|
||||
|
||||
## Galeria de Detecção
|
||||
|
||||
Os comandos dos samples e os resumos esperados de call stack estão documentados em [samples/README.md](samples/README.md). <br> Já vou adiantando: todos os samples são uma "bomba russa", eu fiz eles apenas para testes locais, então não espere grande coisa. <br>
|
||||
Abaixo estão alguns padrões que o StackSentry consegue mostrar no terminal sem precisar de kernel driver.
|
||||
|
||||
Algumas imagens ainda mostram saídas tiradas na fase `v0.8`. Depois disso eu fiz melhorias de console, compactação de stack e redução de ruído, mas não achei honesto chamar isso de `v0.9` só por causa de polimento visual. Então, se algum detalhe de formatação estiver um pouco diferente da versão atual, é por isso.
|
||||
|
||||
> [!Important]
|
||||
> A maioria das imagens abaixo mostra apenas um recorte do resultado de detecção. Teste para ver o resultado completo.
|
||||
|
||||
### SilentMoonwalk Com Stack Sintética
|
||||
|
||||
Foi usada uma variação do projeto [klezVirus/SilentMoonwalk](https://github.com/klezVirus/SilentMoonwalk) para carregar uma DLL de rede com uma stack sintética. Todos os frames visíveis parecem módulos legítimos, mas a validação de callsite e o origin tracing ainda ligam o DLL load ao código que preparou a chamada.
|
||||
|
||||

|
||||
|
||||
### BYOUD / Spoofing de Metadados de Unwind
|
||||
|
||||
O teste usa [klezVirus/byoud](https://github.com/klezVirus/byoud) transformado em shellcode RDI. O bootstrap carrega DLLs como `wininet.dll` e `mscoree.dll`, mas o estágio que importa aqui usa BYOUD para esconder o carregamento de `ws2_32.dll`: em vez de mexer no endereço de retorno, manipula metadados de unwind do Windows. O StackSentry captura a divergência temporal antes do carregamento sensível e reporta a tabela/estrutura que saiu do esperado.
|
||||
|
||||

|
||||

|
||||
|
||||
### Cadeia de Callback Threadpool
|
||||
|
||||
Este sample é baseado na ideia do [klezvirus/ThreadPoolExecChain](https://github.com/klezvirus/ThreadPoolExecChain): uma cadeia threadpool/proxy faz o carregamento acontecer em um contexto que parece mais natural. O relatório preserva o contexto da cadeia e marca os frames modificados que aparecem no caminho.
|
||||
|
||||

|
||||
|
||||
### Proxy Em `.text` de Imagem
|
||||
|
||||
Este teste usa uma shellcode PIC que eu fiz a partir da variação experimental [RefinedPool](https://github.com/Vith0r/RefinedPool/tree/main/RefinedPool), baseada no projeto [LibTPLoadLib](https://github.com/AlmondOffSec/LibTPLoadLib). A técnica segue a linha de API proxying apresentada por [paranoidninja](https://0xdarkvortex.dev/hiding-in-plainsight/) e também demonstrada em [paranoidninja/Proxy-DLL-Loads](https://github.com/paranoidninja/Proxy-DLL-Loads).
|
||||
|
||||
Por ser PIC, ela pode ficar na `.text` do loader e chamar `LoadLibrary` via proxy, adicionando mais um elemento à stack por meio de um gadget existente em `nvwgf2umx.dll`. A stack final aponta para um trecho aparentemente limpo, mas o register/origin tracing liga o DLL load de volta para a região `.text` que iniciou o fluxo. Isso prova que a detecção consegue acontecer mesmo em padrões bem específicos como este exemplo.
|
||||
|
||||

|
||||
|
||||
### Code Cave / Imagem Modificada
|
||||
|
||||
Esse padrão também é inspirado na variação experimental simples do meu projeto [RefinedPool](https://github.com/Vith0r/RefinedPool/tree/main/RefinedPool), transformado em shellcode PIC. O carregamento sensível passa por *bytes escritos em um code cave de imagem*. O interessante aqui é que o StackSentry consegue preservar o módulo modificado e o mapa dos bytes alterados, entregando isso no dump, o que eu acho ser realmente útil.
|
||||
|
||||

|
||||
|
||||
### SilentMoonwalk RDI Com Stack Sintética
|
||||
|
||||
Aqui a variação de [klezVirus/SilentMoonwalk](https://github.com/klezVirus/SilentMoonwalk) é empacotada como payload via [Donut](https://github.com/TheWover/donut)/RDI. O bootstrap pode carregar DLLs como `wininet.dll` e `mscoree.dll`, mas o estágio relevante da técnica é o carregamento de `ws2_32.dll` com stack sintética. A primeira imagem mostra os alertas de carregamento de DLL, a segunda mostra a origem provável voltando para a região executável "escondida".
|
||||
|
||||

|
||||

|
||||
|
||||
### Dispatch Estilo MassDriver
|
||||
|
||||
Inspirado no padrão de dispatch do [Sizeable-Bingus/MassDriver](https://github.com/Sizeable-Bingus/MassDriver), uma worker thread aparentemente limpa executa `LoadLibraryA`. O `/dispatch-trace` liga o carregamento ao requester que postou a estrutura de dispatch. É uma detecção de um padrão bem específico, mas achei interessante de realizar.
|
||||
|
||||

|
||||
|
||||
### Network Use Trace em Payload C2
|
||||
|
||||
Este exemplo usa `/network-use-trace` para mostrar o caso em que o carregamento da DLL de rede não é a única coisa importante. O payload também precisa usar APIs de rede, e o StackSentry tenta atribuir quem chamou `connect`, `WSAConnect`, `send`, `recv`, WinHTTP/WinINet e afins. Isso acaba sendo útil porque o output pode revelar o destino real, como domínio, IP/porta, e até serviços de terceiros usados no fluxo, como o `pastebin.com` nesse teste.
|
||||
|
||||
A stack da imagem está compactada de propósito para não virar um bloco gigante de `system.ni.dll` repetido. <br> Se preferir ver a stack completa em uma linha, como nas prints antigas, use `/inline-stack`, mas se quiser os frames linha por linha com offsets, use `/full-stack`.
|
||||
|
||||

|
||||
|
||||
### Modos de Visualização da Stack
|
||||
|
||||
Além das detecções em si, a saída atual do StackSentry tenta deixar a análise menos cansativa. A mesma stack pode ser mostrada de formas diferentes dependendo do que você quer fazer:
|
||||
|
||||
#### Stack Compactada
|
||||
|
||||
Esse é o formato padrão atual. Frames repetidos são agrupados como `[module.dll xN]`, o que reduz muito o ruído em payloads que passam por runtimes grandes como .NET.
|
||||
|
||||

|
||||
|
||||
#### Stack Completa com Offsets
|
||||
|
||||
Com `/full-stack`, cada frame é mostrado em uma linha separada, com offset do módulo. É um modo interessante quando você quer auditar exatamente onde cada retorno caiu.
|
||||
|
||||

|
||||
|
||||
#### Stack Inline sem Compressão
|
||||
|
||||
Com `/inline-stack`, a stack volta para o formato em uma única linha, sem compactar repetições.
|
||||
|
||||

|
||||
|
||||
#### Eventos Limpos com Verbose
|
||||
|
||||
O modo padrão prioriza alertas para não lotar o terminal. Com `/verbose`, também aparecem carregamentos limpos com `score=0`, esses eventos ficam em azul. Isso ajuda a confirmar rapidamente que um DLL load foi visto e registrado, mesmo quando ele não é suspeito por si só. Mesmo sem `/verbose`, eventos de `score=0` continuam salvos em `events.jsonl` e `memory.json`.
|
||||
|
||||

|
||||
|
||||
#### BackedModified e Memory Audit
|
||||
|
||||
Quando o retorno cai dentro de uma DLL real, mas os bytes daquela região não batem com o arquivo em disco, o StackSentry não trata isso como um frame limpo. Ele marca a stack como `BackedModified`/`captured-modified` e o Memory Audit registra o módulo, a região e o detalhe temporal da alteração.
|
||||
|
||||

|
||||
|
||||
## Build
|
||||
Para facilitar a compilação do projeto, deixei o arquivo `build.ps1`. Ele usa o Microsoft Visual Studio/MSVC para compilar. Caso você não use exatamente o mesmo ambiente, não deve ser difícil adaptar, o script é curto, e olhando ele você já vai entender o que precisa ser chamado.
|
||||
|
||||
```powershell
|
||||
.\build.ps1
|
||||
```
|
||||
|
||||
Se utilizar o script, a saída do build é escrita em `build\`:
|
||||
|
||||
- `StackSentry64.exe`
|
||||
- `CallstackMonitor.dll`
|
||||
|
||||
Código de terceiros fica em `third_party\`. Veja [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) para créditos e notas de licença.
|
||||
|
||||
## Comandos de Teste
|
||||
|
||||
Os comandos recomendados e os comandos exatos dos samples estão em [samples/README.md](samples/README.md). <br> Se estiver em dúvida, comece por lá, deixei o básico para uma primeira passada, o perfil forte, os modos de stack e os exemplos que eu uso para validar as prints da galeria.
|
||||
|
||||
## Perfis Principais
|
||||
|
||||
- `/quick`: perfil estável de triagem de DLL load. Bom para primeiras execuções, baseline benigno e baixa quantidade de ruído.
|
||||
- `/deep`: perfil de hunting para DLL load. Habilita hooks estáveis de callback/thread-start, hooks de tabela unwind, checagens de integridade LDR, dumps, carving e uma janela de correlação maior. Telemetria de memória/API continua desligada, a menos que `/mem` seja passado.
|
||||
- `/max`: perfil mais forte e prático para DLL load. Habilita telemetria deep, stack audit, checagens LDR e `/auto-enter` por padrão, mas deixa hooks de memória/API, wait e threadpool desligados a menos que sejam pedidos explicitamente.
|
||||
- `/profile <quick|deep|max>`: seleciona um perfil por nome.
|
||||
|
||||
Se você só quer testar um loader e não quer pensar muito, eu começaria com `/max`. Depois disso, adicionaria `/hunt`, `/network-use-trace` ou flags específicas conforme o resultado.
|
||||
|
||||
Flags explícitas de hook continuam sendo aditivas, então `/quick /mem`, `/deep /mem` e `/max /mem` são válidos.
|
||||
Use `/max /tp /wait` somente quando quiser o conjunto experimental mais agressivo de hooks.
|
||||
|
||||
## Grupos de Comandos
|
||||
|
||||
Caso queira saber todos os possíveis argumentos, utilize `/features`. <br>
|
||||
A saída completa de `/features` é agrupada por intenção para usuários novos não precisarem tratar toda flag como igualmente importante:
|
||||
|
||||
- `Common options`: pasta de saída, timeout, keep-alive, automação de stdin e verbosidade.
|
||||
- `Output style`: controles quiet/plain/live/color, supressão de output do alvo, `/inline-stack` e `/full-stack`.
|
||||
- `Origin / proxy analysis`: `/regtrace`, `/dispatch-trace` e `/threadpool-chain-trace` para casos de caller escondido e proxy loading.
|
||||
- `Network use analysis`: `/network-use-trace` e `/net-use-trace` para reutilização de DLL de rede já carregada.
|
||||
- `Remote / multi-process`: `/follow-remote` e `/net-reset` para loaders que movem execução para outro processo.
|
||||
- `Extra telemetry / integrity`: `/etw`, `/ldr-integrity`, `/unwind`, `/stack-audit`, `/memory-audit`, `/byoud-trace` e `/shadow-stack`.
|
||||
- `Aggressive / low-level hooks`: `/mem`, `/tp`, `/wait` e `/xhooks` direto.
|
||||
- `Advanced config`: regras/configurações customizadas.
|
||||
|
||||
## Origin Tracing
|
||||
|
||||
Técnicas de DLL load por proxy podem fazer a call stack final parecer limpa, inclusive colocando um gadget de imagem existente entre o código real do loader e `LoadLibrary`. O StackSentry mantém os perfis padrão focados e com pouco ruído, mas adiciona modos opt-in para esses casos:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run .\samples\sample_03_text_section_proxy.exe /max /origin-trace /no-target-output /timeout 9000
|
||||
.\build\StackSentry64.exe /run .\samples\sample_03_text_section_proxy.exe /max /regtrace /no-target-output /timeout 9000
|
||||
```
|
||||
|
||||
- `/origin-trace`: correlaciona DLL loads posteriores com contexto recente de callback/thread/APC.
|
||||
- `/regtrace`: habilita o caminho de origin-trace e envolve thread starts na `.text` do alvo para ligar um DLL load escondido por proxy de volta ao código original quando essa origem está ausente na stack final.
|
||||
- `/proxy-trace`: alias para `/regtrace`.
|
||||
|
||||
Nota técnica: atualmente `/regtrace` evita intencionalmente o rastreamento completo de registradores em módulos de imagem não principais com mais de 32 MB. Memória executável privada, inicializações de thread, transições executáveis dinâmicas e correlação de origem continuam sendo rastreadas; essa omissão só evita a instrumentação completa e cara de imagens enormes usadas como gadget-carrier. Eu precisei fazer isso porque esse tracing estava atrapalhando mais análises normais do que ajudando. Se você realmente precisar aumentar esse teto em um caso de laboratório, é só uma constante pequena no código.
|
||||
|
||||
Hooks de threadpool continuam explícitos. Adicione `/tp` somente quando quiser telemetria de `TpAllocWork/TpPostWork`.
|
||||
|
||||
Quando `/regtrace` correlaciona um load limpo via gadget de volta para código executável `MEM_IMAGE`, o StackSentry escreve um artefato em `origin_regions\`. Isso é separado de `dumps\`: não é uma alocação unbacked, e sim uma janela focada de memória ao redor da origem `.text` rastreada, com um JSON lateral contendo VA/RVA da origem, caller gadget visível, SHA256, entropia e strings selecionadas.
|
||||
|
||||
O hook de `LdrLoadDll` também registra evidências de origem vindas de argumentos `UNICODE_STRING` vivos e blocos de parâmetros proxy. Isso cobre bypasses simples que entram em `LoadLibrary*` depois do prologue instrumentado, mas ainda chegam em `ntdll!LdrLoadDll`.
|
||||
|
||||
## Integridade LDR
|
||||
|
||||
O StackSentry também detecta hijack de entrypoint do loader. Isso cobre técnicas como LdrShuffle/EPI, onde um módulo ainda parece legítimo nas listas do PEB loader, mas seu `LDR_DATA_TABLE_ENTRY.EntryPoint` é alterado para código controlado pelo atacante.
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /ldr-integrity /timeout 10000
|
||||
```
|
||||
|
||||
`/deep` e `/max` habilitam essa checagem. `/quick` a mantém desligada. O analyzer reporta o módulo hijacked, entrypoint atual, entrypoint esperado do PE, tipo/proteção de memória do entrypoint atual e contexto como `OriginalBase` suspeito quando já existe uma anomalia de entrypoint.
|
||||
|
||||
## Remote Follow e Net Reset
|
||||
|
||||
Esses são modos de laboratório opt-in e não fazem parte de `/quick`, `/deep` ou `/max` por padrão:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run loader.exe /max /follow-remote /timeout 15000
|
||||
.\build\StackSentry64.exe /run loader.exe /max /follow-remote /net-reset /timeout 15000
|
||||
```
|
||||
|
||||
- `/follow-remote`: observa handles de processo remoto e injeta `CallstackMonitor.dll` no processo remoto quando execução remota está prestes a acontecer.
|
||||
- `/net-reset`: tenta descarregar DLLs de rede monitoradas já carregadas para que um payload precise recarregar seu próprio módulo de rede, tornando o caller visível para o StackSentry. Isso pode funcionar ou não, então espere possíveis erros.
|
||||
|
||||
Artefatos de processos remotos são escritos em `children\<process.exe - pid>\` dentro da pasta principal da execução. `events.jsonl` continua agregado, e cada evento inclui o PID que o produziu.
|
||||
|
||||
## Network Use Trace
|
||||
|
||||
`/network-use-trace` cobre o caso em que um payload não precisa chamar `LoadLibrary` para uma DLL de rede monitorada porque o host, loader ou runtime já carregou esse módulo. Em vez de perguntar apenas quem carregou `wininet.dll`, `ws2_32.dll`, `winhttp.dll` ou `dnsapi.dll`, o StackSentry também pergunta quem está usando as APIs de rede desses módulos. Claro, ele também pode ser usado mesmo quando o loader/shellcode carrega a DLL de rede por conta própria:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /network-use-trace /timeout 10000
|
||||
.\build\StackSentry64.exe /run target.exe /max /hunt /network-use-trace /timeout 15000
|
||||
```
|
||||
|
||||
O monitor instala hooks em um conjunto focado de APIs como `connect`, `WSAConnect`, `send`, `recv`, `getaddrinfo`, `DnsQuery_*`, `InternetOpenUrl*`, `InternetConnect*`, `HttpOpenRequest*`, `HttpSendRequest*` e chamadas comuns de request/read/write do WinHTTP. O analyzer então classifica o endereço caller e a stack como em um evento de DLL load. Casos de alto sinal incluem APIs de rede chamadas a partir de `MEM_PRIVATE` executável, código image-backed modificado, metadados de unwind adulterados ou stack spoofing/incomum.
|
||||
|
||||
Aqui vai uma observação honesta: dependendo do programa analisado, você pode enfrentar erros, volume alto de eventos e falsos positivos. Esse modo foi pensado para loaders e payloads em laboratório, não para prometer uma experiência perfeita em programas grandes já previamente alterados.
|
||||
|
||||
Findings aparecem em `== Network Use Details ==` e são escritos em `network_trace.json`, `memory.json` e `summary.json`. Esse modo não faz parte de `/hunt` por padrão: ele é forte, mas pode ser verboso, então prefiro que o analista habilite explicitamente quando quiser provar uso real de APIs de rede.
|
||||
|
||||
## Memory Audit
|
||||
|
||||
`/memory-audit` é um scan opt-in do estado vivo de memória inspirado na maravilhosa pesquisa Moneta, de Forrest Orr, sobre artefatos maliciosos em memória. Ele tenta complementar o modelo de evento/call-stack do StackSentry perguntando como o processo se parece em memória antes de o StackSentry terminar ou desanexar dele:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /memory-audit /timeout 10000
|
||||
.\build\StackSentry64.exe /run target.exe /max /regtrace /memory-audit /timeout 10000
|
||||
```
|
||||
|
||||
O audit verifica regiões `MEM_IMAGE` ausentes das listas de loader/módulos, mappings de imagem sem nome de arquivo mapeado disponível, páginas privadas executáveis dentro de imagens, headers PE apagados ou fortemente alterados, permissões executáveis em seções não executáveis e regiões privadas ou mapeadas executáveis que parecem payloads.
|
||||
|
||||
Findings de alta confiança aparecem em `== Memory Audit ==` e podem criar dumps focados em `memory_audit\`. O contexto de hunting de menor confiança vai ficar em `memory_audit.json` sem gerar alerta por padrão, porque componentes modernos do Windows e ferramentas residentes podem criar páginas privadas ou modificadas de forma legítima. Esse modo intencionalmente não é habilitado por `/quick`, `/deep` ou `/max`.
|
||||
|
||||
## BYOUD e Pesquisa de Shadow Stack
|
||||
|
||||
`/byoud-trace` é um modo de laboratório para casos de DLL load que manipulam metadados de unwind x64 do Windows em vez de endereços de retorno óbvios. Ele observa APIs de tabela unwind, mudanças de proteção de memória ao redor de `.pdata`/`.xdata`/`.rdata` e divergência temporal de metadados antes de chamadas sensíveis do loader:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /byoud-trace /regtrace /timeout 12000
|
||||
```
|
||||
|
||||
`/hunt` inclui `/byoud-trace` porque o corpus atual de testes BYOUD fornece prova repetível por divergência temporal de metadados de unwind, o que eu achei útil demais para deixar escondido atrás de uma flag separada.
|
||||
|
||||
`/shadow-stack` é diferente. Ele é exposto apenas como switch de pesquisa/teste para sistemas onde o Windows expõe *estado de shadow stack CET/HSP em user-mode*. Ele captura frames de retorno CET, compara esses frames contra a stack clássica como uma sequência ordenada e reporta frames escondidos, ausentes ou fora de ordem. Ele não está incluído em `/hunt`, não é contado como cobertura madura e pode não produzir findings em sistemas onde `XSTATE_CET_U` não está disponível. Para ser bem honesto eu precisei testar isso no notebook de um amigo, e não pude realizar muitos testes, então espere possíveis problemas:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /shadow-stack /stack-audit /regtrace /timeout 12000
|
||||
```
|
||||
|
||||
Quando funciona, findings aparecem em `== Shadow Stack Trace ==` e são escritos em `shadow_stack_trace.json`. Quando a plataforma não expõe o estado CET necessário, as camadas normais `/regtrace`, `/stack-audit`, `/memory-audit` e `/byoud-trace` continuam carregando a detecção.
|
||||
|
||||
## Timeline ETW
|
||||
|
||||
`/etw` é um modo de laboratório opt-in que inicia um trace kernel com krabsetw antes de a thread principal do alvo ser retomada. Ele registra eventos de processo, thread e image-load para o PID primário e PIDs filhos cujo processo pai já está sendo rastreado. Para ser realista, não é o recurso mais útil do projeto, mas achei interessante de adicionar:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /etw /timeout 10000
|
||||
.\build\StackSentry64.exe /run loader.exe /max /follow-remote /etw /timeout 15000
|
||||
```
|
||||
|
||||
Isso não substitui as detecções da DLL de monitoramento. Ele fornece uma timeline com respaldo do kernel para responder perguntas como qual processo filho apareceu, qual DLL foi mapeada naquele momento e se a execução remota do payload bate com um estágio suspeito do loader. A timeline é escrita em `etw_timeline.json` e resumida na saída final do console. Coleta kernel ETW pode exigir privilégios elevados, se o Windows recusar o trace, o StackSentry reporta `/etw` como indisponível e continua a análise normal.
|
||||
|
||||
## Hooks Individuais
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /xhooks
|
||||
.\build\StackSentry64.exe /run target.exe /mem /unwind
|
||||
```
|
||||
|
||||
`/xhooks` habilita os hooks mais estáveis de callback/thread-start (`CreateThread` e `QueueUserAPC`). `/origin-trace` adiciona correlação de origem, e `/regtrace` adiciona o caminho mais pesado de tracing de thread-start na `.text` do alvo. `/mem` habilita telemetria ruidosa de stack para APIs de memória (`NtAllocateVirtualMemory`, `NtProtectVirtualMemory`, `NtMapViewOfSection`, writes, criação de thread e queue de APC) e intencionalmente não é habilitado por nenhum perfil principal. `/etw` adiciona telemetria kernel de timeline processo/thread/image-load via krabsetw. `/tp` e `/wait` são separados porque hooks `Tp*` e `WaitFor*` podem desestabilizar alguns alvos. E sim, eles realmente podem desestabilizar, então use conforme o alvo.
|
||||
|
||||
Argumentos legados (`-e`, `--out`, `--rules`, `--timeout-ms` e `--experimental-hooks`) ainda funcionam.
|
||||
|
||||
## Saída de Console
|
||||
|
||||
A saída de console é agrupada por bloco de análise (`DLL LOAD ANALYSIS`, `MEMORY API TELEMETRY`, `CALLBACK/THREAD ANALYSIS` e seções relacionadas). Por padrão, o console mostra apenas alertas para que findings de DLL load não fiquem enterrados em telemetria rotineira. Use `/verbose` quando quiser ver eventos sem alerta também. `events.jsonl` continua recebendo o stream completo de eventos.
|
||||
|
||||
Na prática, o console tenta não virar um romance. O resumo fica legível, e o detalhe bruto continua nos arquivos JSON para quem quiser cavar depois.
|
||||
|
||||
Algumas flags úteis para controlar a saída:
|
||||
|
||||
- `/no-target-output`: não mistura stdout/stderr do alvo no console do StackSentry.
|
||||
- `/inline-stack`: imprime a stack completa em uma linha, sem compactar frames repetidos.
|
||||
- `/full-stack`: imprime frames um por linha com offset de módulo e desativa a compactação tipo `[module xN]`.
|
||||
- `/quiet`: escreve artefatos e reduz a UI do console.
|
||||
- `/plain`, `/live` e `/no-color`: ajustam animação/cor porque sim.
|
||||
|
||||
No final, o bloco `Memory` lista cada DLL monitorada carregada com o endereço base do módulo carregado e o endereço selecionado de caller/origem na stack. Esses endereços também são escritos em `memory.json` e `summary.json`.
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe
|
||||
.\build\StackSentry64.exe /help
|
||||
.\build\StackSentry64.exe /version
|
||||
```
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /live /verbose
|
||||
.\build\StackSentry64.exe /run target.exe /plain /no-color
|
||||
.\build\StackSentry64.exe /run target.exe /quiet
|
||||
```
|
||||
|
||||
## Saídas
|
||||
|
||||
Cada execução cria uma pasta por processo dentro do diretório escolhido em `/out`:
|
||||
|
||||
```text
|
||||
out\loader\loader_binary.exe - 24216\
|
||||
```
|
||||
|
||||
O console já tenta entregar o que importa primeiro, então você não precisa abrir todo JSON para entender uma execução simples. Mas, quando quiser validar uma detecção ou comparar com outra ferramenta, vale olhar os artefatos:
|
||||
|
||||
- `summary.json`, `memory.json` e `events.jsonl` guardam o resumo, os eventos e as decisões que sustentaram o alerta.
|
||||
- `origin_regions\` guarda janelas focadas da região que o origin tracing associou ao carregamento escondido.
|
||||
- `dumps\`, `memory_audit\`, `modified_modules\` e `modified_network_modules\` guardam bytes preservados para análise posterior.
|
||||
- `network_trace.json`, `byoud_trace.json`, `shadow_stack_trace.json` e `etw_timeline.json` aparecem quando os modos correspondentes são usados.
|
||||
- `children\` guarda artefatos por PID quando `/follow-remote` acompanha execução em outro processo.
|
||||
|
||||
Um detalhe que vale atenção: quando o StackSentry preserva um módulo modificado, ele também escreve um arquivo `.tag` ao lado do dump. <br> Esse `.tag` é um mapa de diferença simples, com offsets/bytes alterados em relação ao arquivo no disco. Para casos de code cave, module stomping ou imagem temporariamente modificada, isso costuma ser mais útil do que só ter o módulo inteiro dumpado.
|
||||
|
||||
Exit codes:
|
||||
|
||||
- `0`: nenhum alerta.
|
||||
- `10`: pelo menos um alerta foi gerado.
|
||||
- `1`/`2`: erro de runtime, crash do alvo, erro de argumento ou erro de configuração.
|
||||
|
||||
## Regras
|
||||
|
||||
`config\rules.json` mostra o formato suportado:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": 3,
|
||||
"network_modules": ["ws2_32.dll", "wininet.dll", "winhttp.dll", "dnsapi.dll", "iphlpapi.dll"],
|
||||
"dotnet_modules": ["clr.dll", "coreclr.dll", "mscoree.dll", "System.Management.Automation.dll"],
|
||||
"alert_on_unbacked_executable": true,
|
||||
"alert_on_backed_modified": true,
|
||||
"dump_suspicious_regions": true,
|
||||
"analyze_dumps": true,
|
||||
"carve_embedded_pe": true,
|
||||
"module_integrity_enabled": true,
|
||||
"enable_sleep_hooks": true,
|
||||
"enable_msgwait_hook": true,
|
||||
"enable_wait_object_hooks": false,
|
||||
"enable_thread_start_hooks": false,
|
||||
"enable_threadpool_hooks": false,
|
||||
"experimental_hooks": false,
|
||||
"memory_api_hooks": false,
|
||||
"unwind_integrity": true,
|
||||
"unwind_table_hooks": false,
|
||||
"origin_trace": false,
|
||||
"register_trace": false,
|
||||
"follow_remote": false,
|
||||
"net_reset": false,
|
||||
"etw_telemetry": false,
|
||||
"ldr_integrity": false,
|
||||
"dispatch_trace": false,
|
||||
"threadpool_chain_trace": false,
|
||||
"stack_audit": false,
|
||||
"memory_audit": false,
|
||||
"byoud_trace": false,
|
||||
"shadow_stack": false,
|
||||
"network_use_trace": false,
|
||||
"callsite_validation": true,
|
||||
"alert_on_unwind_tamper": true,
|
||||
"correlation_window_ms": 5000,
|
||||
"max_dump_bytes": 16777216,
|
||||
"long_sleep_ms": 1000
|
||||
}
|
||||
```
|
||||
|
||||
Use outro arquivo com `--rules path\to\rules.json`.
|
||||
|
||||
O campo antigo `watch_dlls` ainda é aceito como alias de compatibilidade. Provavelmente você nem vai ver isso em uso, porque basicamente só eu testei esse caminho, e porque não remover? tô cansado.
|
||||
|
||||
## Limitações Atuais
|
||||
|
||||
Eu poderia tentar vender esse projeto como se ele resolvesse tudo, mas seria desonesto. Depois de centenas de testes, ficou claro que ele é forte no que se propõe, mas ainda enfrenta problemas dependendo do alvo. Como todo projeto desse tipo, existem limitações reais, algumas óbvias, outras nem tanto:
|
||||
|
||||
- Isto ainda é instrumentação em user-mode. Um alvo forte pode obviamente detectar/remover hooks ou usar caminhos de execução que não são observados.
|
||||
- Hooks de `WaitForSingleObject/WaitForMultipleObjects` e `Tp*` existem, mas ficam fora de `/deep` e `/max` porque causaram instabilidade em alguns alvos de teste.
|
||||
- Hooks de API de memória podem ser ruidosos em ambientes com engines benignos de injeção/hook, então são habilitados apenas quando `/mem` é passado.
|
||||
- `Register tracing` é telemetria de laboratório opt-in. Ele foi desenhado para revelar origens escondidas por proxy/gadget em loaders, não para produzir um trace completo de instruções.
|
||||
- `/follow-remote` depende de hooks user-mode observando a preparação de execução remota. Cadeias completas de direct/indirect syscalls ainda podem contornar esses hooks.
|
||||
- `/net-reset` pode falhar quando uma DLL é importada estaticamente, segurada por refcount ou em uso ativo. Caso isso ocorra, uma falha de reset é reportada e a análise continua normalmente.
|
||||
- `/etw` é telemetria de contexto, não prova de detecção por si só. Pode exigir elevação e perder eventos que ocorreram antes do início do trace. Não tem muito o que fazer aqui.
|
||||
- `/memory-audit` roda enquanto o alvo ainda está vivo, normalmente no timeout/keep-alive. Se o alvo sair antes disso, tem grande chance de não existir espaço de endereçamento vivo para escanear.
|
||||
- Integridade LDR trata `EntryPoint` fora da imagem do módulo ou fora de `MEM_IMAGE` como "prova principal". `OriginalBase` é reportado como contexto porque esse campo é sensível ao layout em diferentes builds do Windows e não deve ser confiado sozinho. Eu percebi isso testando em outro notebook, então deixei esse ponto mais conservador.
|
||||
- Zydis melhora o decoding de instruções, mas validação de callsite ainda é heurística porque um endereço de retorno sozinho "não prova o histórico real de control-flow".
|
||||
- Comparação de módulos é "inspirada no PE-sieve", mas claramente simplificada: compara seções executáveis e de unwind contra disco e não tenta modelar todo caso legítimo de relocação/hook.
|
||||
- O memory audit é inspirado em classes de artefatos estilo Moneta, mas é conservador no console: evidência fraca de página privada é escrita como contexto de hunting a menos que esteja ligada a uma anomalia mais forte.
|
||||
- `/shadow-stack` é um switch de pesquisa para experimentos CET/HSP e pode ficar silencioso quando o alvo ou a plataforma não expõe estado de shadow stack em user-mode.
|
||||
- Stack spoofing avançado não é impossível de contornar em ring3. A ferramenta aumenta o custo correlacionando callbacks, callsites, integridade de módulo, bytes capturados de callsite, metadados de unwind e telemetria opcional de memória/API quando `/mem` está habilitado.
|
||||
|
||||
## Licença
|
||||
|
||||
Este projeto é distribuído sob a licença MIT (Modify It Tonight). Use, modifique, quebre em laboratório, arrume, compare, publique resultado, faça o que precisar. <br> Se ele economizar algumas horas de análise, já fico feliz! (Mas aceito um café, se quiser).
|
||||
|
||||
Código de terceiros mantém suas próprias licenças e créditos em [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
||||
@@ -0,0 +1,309 @@
|
||||

|
||||
|
||||
<div align="center">
|
||||
|
||||
<a href="README.md">`English`</a>
|
||||
•
|
||||
<a href="README_JP.md">`日本語`</a> •
|
||||
<a href="README_PT-BR.md">`Portuguese`</a> •
|
||||
<a href="README_TR.md">`Türkçe`</a>
|
||||
|
||||
<a href="#build"><img src="https://img.shields.io/badge/Documentation-%23000000.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1ib29rLW9wZW4iPjxwYXRoIGQ9Ik0xMiA3djE0Ii8+PHBhdGggZD0iTTMgMThhMSAxIDAgMCAxLTEtMVY0YTEgMSAwIDAgMSAxLTFoNWE0IDQgMCAwIDEgNCA0IDQgNCAwIDAgMSA0LTRoNWExIDEgMCAwIDEgMSAxdjEzYTEgMSAwIDAgMS0xIDFoLTZhMyAzIDAgMCAwLTMgMyAzIDMgMCAwIDAtMy0zeiIvPjwvc3ZnPg==&logoColor=white"></a>
|
||||
|
||||
<img src="https://img.shields.io/badge/Windows-%23000000.svg?style=for-the-badge&logo=Windows">
|
||||
|
||||

|
||||
|
||||
<hr>
|
||||
|
||||
<br>
|
||||
|
||||
StackSentry basit bir fikirden çıktı: bir loader `LoadLibrary` çağrısının nereden geldiğini saklamaya çalışıyorsa, call stack büyük ihtimalle bir yerlerde iz bırakır.
|
||||
|
||||
Bu proje x64 user-mode çalışan bir araştırma aracıdır. Amacı memory triage, loader analizi ve hassas DLL-load tespitidir. Hedef process başlatılır, hafif bir monitor DLL enjekte edilir ve önemli olaylar canlı izlenir. Araç temelde tek soruya cevap arar: **bu DLL load veya network kullanımını gerçekten kim tetikledi?**
|
||||
|
||||
</div>
|
||||
|
||||
> [!Important]
|
||||
> |**Bu proje hâlâ geliştirme aşamasında**. Bug, false positive ve false negative olabilir. |
|
||||
> |:--------------------------------|
|
||||
> | StackSentry esas olarak lab ortamında loader ve in-memory payload analizini kolaylaştırmak için geliştirildi. <br> Araç kernel driver kurmaz ve sistemde kalıcı değişiklik yapmaz; yine de sonuçları yardımcı araçlarla karşılaştırın ve output'u kesin hüküm değil, triage evidence olarak değerlendirin. |
|
||||
|
||||
<br>
|
||||
|
||||
## Fikir
|
||||
|
||||
StackSentry bellekte çalışan kodu hızlı şekilde triage etmeye odaklanır. Pratik mantık şu: bir C2, RAT veya fileless loader diskte dosya bırakmayabilir, uyurken kendini şifreleyebilir ve temiz görünen bir stack kurabilir; ama bir noktada yine network DLL yüklemek veya network API kullanmak zorundadır.
|
||||
|
||||
Windows tarafında bu genellikle `ws2_32.dll`, `wininet.dll`, `winhttp.dll`, `dnsapi.dll` veya bu modüllerin export ettiği API'ler anlamına gelir. Bu DLL'ler garip bir origin üzerinden yükleniyor veya kullanılıyorsa durup bakmaya değer.
|
||||
|
||||
Bu yaklaşım sıfırdan uydurulmuş bir fikir değil. Elastic'in davranışsal detection kurallarıyla da aynı eksene oturuyor:
|
||||
|
||||
- [`defense_evasion_library_loaded_via_a_callback_function.toml`](https://github.com/elastic/protections-artifacts/blob/6e9ee22c5a7f57b85b0cb063adba9a3c72eca348/behavior/rules/windows/defense_evasion_library_loaded_via_a_callback_function.toml): bir kütüphanenin callback üzerinden yüklendiğini yakalar; bu, `LoadLibrary` çağrısının gerçek origin'ini call stack'ten saklama girişimi olabilir.
|
||||
- [`defense_evasion_network_module_loaded_from_suspicious_unbacked_memory.toml`](https://github.com/elastic/protections-artifacts/blob/6e9ee22c5a7f57b85b0cb063adba9a3c72eca348/behavior/rules/windows/defense_evasion_network_module_loaded_from_suspicious_unbacked_memory.toml): thread stack içinde bilinen executable image dışında frame varken network module load edildiğini tespit eder.
|
||||
- [`defense_evasion_library_loaded_from_a_spoofed_call_stack.toml`](https://github.com/elastic/protections-artifacts/blob/6e9ee22c5a7f57b85b0cb063adba9a3c72eca348/behavior/rules/windows/defense_evasion_library_loaded_from_a_spoofed_call_stack.toml): gerçek call source'u gizlemek için değiştirilmiş/spoofed call stack üzerinden yapılan library load durumunu yakalar.
|
||||
|
||||
Bu tarz detection'lar olduğu için bazı loader'lar artık düz `LoadLibrary` çağrısından kaçıyor. Callback, legitimate module içindeki gadget, threadpool chain, cross-thread dispatch, modified image execution veya unwind metadata manipulation gibi yöntemlerle origin'i saklamaya çalışıyorlar.
|
||||
|
||||
StackSentry bunu lab seviyesinde daha ileri taşımaya çalışır: yalnızca "hassas DLL yüklendi" demek yerine probable origin, memory state, stack pattern, dump ve loader'ın saklamaya çalıştığı yolu göstermeye çalışır.
|
||||
|
||||
## Ne Arar?
|
||||
|
||||
- Hassas DLL load olayları: `ws2_32`, `wininet`, `winhttp`, `dnsapi`, CLR/.NET ve ilişkili modüller.
|
||||
- `Unbacked` frame'ler: bilinen bir image'a ait olmayan executable `MEM_PRIVATE` veya `MEM_MAPPED` bellek.
|
||||
- `BackedModified` frame'ler: `MEM_IMAGE` içinde çalışan ama diskteki dosyayla byte seviyesinde uyuşmayan kod.
|
||||
- Proxy/gadget, callback, thread start, APC, VEH, threadpool, dispatch veya hedef `.text` içinden saklanan origin.
|
||||
- Stack spoofing: plausible callsite olmayan return frame, kesilmiş stack, synthetic stack veya olay için fazla temiz görünen visible caller.
|
||||
- BYOUD/unwind spoofing: `.pdata`, `.xdata`, `.rdata`, `RUNTIME_FUNCTION` ve dynamic unwind table değişimleri.
|
||||
- `/network-use-trace` açıkken gerçek network API kullanımını izleme; DLL zaten yüklü olsa bile.
|
||||
- Suspicious LDR metadata, EntryPoint hijack ve module inconsistency.
|
||||
- Memory audit: modified image, orphan executable mapping, private executable region ve odaklı dump.
|
||||
- `events.jsonl`, `summary.json`, `memory.json`, `network_trace.json`, `byoud_trace.json`, origin dump, module dump, hash, entropy ve strings gibi artifact'ler.
|
||||
|
||||
## Detection Gallery
|
||||
|
||||
Sample komutları ve beklenen call-stack özetleri [samples/README.md](samples/README.md) içinde. <br> Küçük uyarı: sample'lar lab için hazırlanmış şüpheli test dosyalarıdır; polished showcase binary beklemeyin. <br>
|
||||
Aşağıda StackSentry'nin kernel driver olmadan terminalde gösterebildiği birkaç pattern var.
|
||||
|
||||
Bazı görseller `v0.8` döneminde alınmış output'u gösterir. Sonrasında console rendering, stack compaction ve noise reduction iyileştirildi; ama sırf output temizlendi diye buna `v0.9` demek istemedim.
|
||||
|
||||
> [!Important]
|
||||
> Aşağıdaki görsellerin çoğu detection sonucunun sadece bir bölümünü gösterir. Tam sonucu görmek için sample'ları çalıştırın.
|
||||
|
||||
### Synthetic Stack ile SilentMoonwalk
|
||||
|
||||
Bu test [klezVirus/SilentMoonwalk](https://github.com/klezVirus/SilentMoonwalk) varyantını kullanır. Network DLL synthetic stack ile yüklenir. Visible frame'ler legitimate module gibi görünür; ama callsite validation ve origin tracing hâlâ DLL load'u çağrıyı hazırlayan koda bağlar.
|
||||
|
||||

|
||||
|
||||
### BYOUD / Unwind Metadata Spoofing
|
||||
|
||||
Bu test [klezVirus/byoud](https://github.com/klezVirus/byoud) projesinin RDI shellcode haline getirilmiş varyantını kullanır. Bootstrap aşaması `wininet.dll` ve `mscoree.dll` gibi DLL'leri yükleyebilir; önemli kısım ise `ws2_32.dll` load'unun BYOUD ile gizlenmesidir. Return address değiştirmek yerine Windows unwind metadata manipüle edilir. StackSentry sensitive load öncesindeki temporal divergence'ı yakalar.
|
||||
|
||||

|
||||

|
||||
|
||||
### Threadpool Callback Chain
|
||||
|
||||
Bu sample [klezvirus/ThreadPoolExecChain](https://github.com/klezvirus/ThreadPoolExecChain) fikrine dayanır. Threadpool/proxy chain sayesinde DLL load daha doğal görünen bir context içinde gerçekleşir. StackSentry chain context'i korur ve yol üzerindeki modified frame'leri işaretler.
|
||||
|
||||

|
||||
|
||||
### Image `.text` Proxy
|
||||
|
||||
Bu test, [LibTPLoadLib](https://github.com/AlmondOffSec/LibTPLoadLib) temeline dayanan deneysel [RefinedPool](https://github.com/Vith0r/RefinedPool/tree/main/RefinedPool) varyantından üretilmiş PIC shellcode kullanır. Teknik, [paranoidninja](https://0xdarkvortex.dev/hiding-in-plainsight/) tarafından anlatılan API proxying çizgisine ve [Proxy-DLL-Loads](https://github.com/paranoidninja/Proxy-DLL-Loads) projesine yakındır.
|
||||
|
||||
PIC olduğu için kod loader'ın `.text` bölümünde yaşayabilir ve `nvwgf2umx.dll` içindeki mevcut gadget üzerinden proxy `LoadLibrary` akışı oluşturabilir. Final stack temiz görünebilir; ama register/origin tracing load'u başlatan `.text` region'a geri bağlar.
|
||||
|
||||

|
||||
|
||||
### Code Cave / Modified Image
|
||||
|
||||
Bu pattern de [RefinedPool](https://github.com/Vith0r/RefinedPool/tree/main/RefinedPool) üzerinde yapılan küçük deneysel değişikliklerden gelir. Sensitive load, image-backed code cave içine yazılmış byte'lardan geçer. StackSentry modified module dump'ını ve `.tag` changed-byte map dosyasını korur; triage sırasında bu çoğu zaman tek başına çok değerlidir.
|
||||
|
||||

|
||||
|
||||
### SilentMoonwalk RDI Synthetic Stack
|
||||
|
||||
Burada [SilentMoonwalk](https://github.com/klezVirus/SilentMoonwalk) varyantı [Donut](https://github.com/TheWover/donut)/RDI payload olarak paketlenmiştir. Bootstrap `wininet.dll` ve `mscoree.dll` yükleyebilir; asıl stage synthetic stack ile `ws2_32.dll` yükler. İkinci görsel hidden executable region'a geri giden probable origin'i gösterir.
|
||||
|
||||

|
||||

|
||||
|
||||
### MassDriver Tarzı Dispatch
|
||||
|
||||
[Sizeable-Bingus/MassDriver](https://github.com/Sizeable-Bingus/MassDriver) dispatch pattern'inden esinlenen bu sample'da temiz görünen worker thread `LoadLibraryA` çalıştırır. `/dispatch-trace`, load'u dispatch structure'ı gönderen requester'a bağlar.
|
||||
|
||||

|
||||
|
||||
### C2 Payload içinde Network Use Trace
|
||||
|
||||
Bu örnek `/network-use-trace` kullanır. Burada ilginç olan sadece network DLL'in yüklenmesi değil; payload'un `connect`, `WSAConnect`, `send`, `recv`, WinHTTP/WinINet gibi API'leri gerçekten kullanmasıdır. Output domain, IP/port ve hatta `pastebin.com` gibi üçüncü taraf servisleri gösterebilir.
|
||||
|
||||
Görselde stack bilinçli olarak compact edilmiştir. Eski ekran görüntülerindeki gibi full stack'i tek satırda görmek isterseniz `/inline-stack`; frame frame offset'li çıktı için `/full-stack` kullanın.
|
||||
|
||||

|
||||
|
||||
### Stack Görüntüleme Modları
|
||||
|
||||
Detection dışında, mevcut StackSentry console çıktısı analizi daha az yorucu hale getirmeye çalışır. Aynı stack, neyi incelemek istediğinize göre farklı biçimlerde gösterilebilir:
|
||||
|
||||
#### Compact Stack
|
||||
|
||||
Bu mevcut default formattır. Tekrarlanan frame'ler `[module.dll xN]` şeklinde gruplanır. .NET gibi büyük runtime'lardan geçen payload'larda bu, gürültüyü ciddi biçimde azaltır.
|
||||
|
||||

|
||||
|
||||
#### Offset'li Full Stack
|
||||
|
||||
`/full-stack` ile her frame ayrı satırda ve module offset'iyle yazdırılır. Her return frame'in tam olarak nereye düştüğünü görmek istediğinizde kullanışlıdır.
|
||||
|
||||

|
||||
|
||||
#### Compression Olmadan Inline Stack
|
||||
|
||||
`/inline-stack` ile stack tekrar tek satırlık formata döner ve tekrarlanan frame compression uygulanmaz.
|
||||
|
||||

|
||||
|
||||
#### Verbose ile Clean Event'ler
|
||||
|
||||
Default console çıktısı terminali doldurmamak için alert'leri öne çıkarır. `/verbose` ile `score=0` clean load event'leri de görünür; bu event'ler mavi renkte çıkar. Böylece bir DLL load'ın görüldüğünü ve kaydedildiğini, şüpheli olmasa bile hızlıca doğrulayabilirsiniz. `/verbose` kullanmasanız bile `score=0` event'leri `events.jsonl` ve `memory.json` içine yazılır.
|
||||
|
||||

|
||||
|
||||
#### BackedModified ve Memory Audit
|
||||
|
||||
Return frame gerçek bir DLL içinde görünse bile, o region'daki bytes diskteki file ile uyuşmuyorsa StackSentry bunu clean frame olarak ele almaz. Stack `BackedModified` / `captured-modified` olarak işaretlenir; Memory Audit de module, region ve değişikliğin temporal detayını kaydeder.
|
||||
|
||||

|
||||
|
||||
## Build
|
||||
|
||||
Build işlemi için `build.ps1` kullanılır. Microsoft Visual Studio/MSVC gerekir.
|
||||
|
||||
```powershell
|
||||
.\build.ps1
|
||||
```
|
||||
|
||||
Script çıktıları `build\` altına yazar:
|
||||
|
||||
- `StackSentry64.exe`
|
||||
- `CallstackMonitor.dll`
|
||||
|
||||
Third-party kodlar `third_party\` altında tutulur. Lisans ve credit notları için [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) dosyasına bakın.
|
||||
|
||||
## Test Komutları
|
||||
|
||||
Önerilen komutlar ve exact sample komutları [samples/README.md](samples/README.md) içinde. Nereden başlayacağınızı bilmiyorsanız oradan başlayın; quick pass, strong profile, stack output mode'ları ve gallery screenshot'larını doğrulamak için kullandığım örnekler orada.
|
||||
|
||||
## Ana Profiller
|
||||
|
||||
- `/quick`: düşük noise DLL-load triage profili.
|
||||
- `/deep`: callback/thread-start hook, unwind hook, LDR integrity ve dump/correlation içeren hunting profili. Memory API hook'ları hâlâ opt-in kalır.
|
||||
- `/max`: pratikte en güçlü DLL-load profili. Deep telemetry, stack audit, LDR checks ve default `/auto-enter` içerir; `/mem`, `/tp`, `/wait` ayrı açılır.
|
||||
- `/profile <quick|deep|max>`: profil seçer.
|
||||
|
||||
Şüpheli bir loader için düşünmeden başlanacak yer genelde `/max`. Sonrasında sonuca göre `/hunt`, `/network-use-trace` veya spesifik flag eklemek daha temizdir.
|
||||
|
||||
## Komut Grupları
|
||||
|
||||
Tüm argümanlar için:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /features
|
||||
```
|
||||
|
||||
Öne çıkan gruplar:
|
||||
|
||||
- `Output style`: `/no-target-output`, `/inline-stack`, `/full-stack`, `/quiet`, `/plain`, `/live`, `/no-color`.
|
||||
- `Origin / proxy analysis`: `/regtrace`, `/dispatch-trace`, `/threadpool-chain-trace`.
|
||||
- `Network use analysis`: `/network-use-trace` ve `/net-use-trace`.
|
||||
- `Remote / multi-process`: `/follow-remote`, `/net-reset`.
|
||||
- `Extra telemetry / integrity`: `/etw`, `/ldr-integrity`, `/unwind`, `/stack-audit`, `/memory-audit`, `/byoud-trace`, `/shadow-stack`.
|
||||
- `Aggressive / low-level hooks`: `/mem`, `/tp`, `/wait`, `/xhooks`.
|
||||
|
||||
## Önemli Modlar
|
||||
|
||||
### Origin Tracing
|
||||
|
||||
Proxy DLL-load teknikleri final stack'i temiz gösterebilir. `/regtrace` bu durumlarda origin'i yakalamaya çalışır:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run .\samples\sample_03_text_section_proxy.exe /max /regtrace /no-target-output /timeout 9000
|
||||
```
|
||||
|
||||
`/origin-trace` callback/thread/APC context ile korelasyon yapar. `/regtrace`, bunu genişletip gadget/proxy arkasındaki gerçek origin'i yakalamaya çalışır. Artifact'ler `origin_regions\` altına yazılır.
|
||||
|
||||
Technical note: Mevcut `/regtrace`, main image olmayan ve 32 MB'tan büyük image module'ler üzerinde full register tracing yapmaktan bilinçli olarak kaçınır. Private executable memory, thread start, dynamic executable transition ve origin correlation hâlâ izlenir. Bu sınırlama sadece büyük gadget-carrier image'lara pahalı full instrumentation uygulamamak içindir. Bir lab case için gerçekten daha yüksek limit gerekiyorsa, source içindeki küçük bir constant değiştirilebilir.
|
||||
|
||||
### Network Use Trace
|
||||
|
||||
Payload network DLL'i zaten yüklü bulursa yeni `LoadLibrary` olmayabilir. `/network-use-trace` bu durumda network API kullanımını izler:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /network-use-trace /timeout 10000
|
||||
.\build\StackSentry64.exe /run target.exe /max /hunt /network-use-trace /timeout 15000
|
||||
```
|
||||
|
||||
Bu mod `/hunt` içinde default değildir, çünkü güçlü ama verbose olabilir. Findings `== Network Use Details ==`, `network_trace.json`, `memory.json` ve `summary.json` içinde görünür.
|
||||
|
||||
### Memory Audit
|
||||
|
||||
`/memory-audit`, Moneta tarzı memory artifact sınıflarından ilham alan canlı process memory scan'dir:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /memory-audit /timeout 10000
|
||||
```
|
||||
|
||||
Modified image, orphan executable mapping, private executable region ve suspicious section permission gibi bulguları raporlar. Zayıf bulgular genelde console alert değil, hunting context olarak JSON'da kalır.
|
||||
|
||||
### BYOUD ve Shadow Stack Research
|
||||
|
||||
`/byoud-trace`, return address yerine Windows x64 unwind metadata manipülasyonunu gözlemlemek için lab modudur:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe /max /byoud-trace /regtrace /timeout 12000
|
||||
```
|
||||
|
||||
`/hunt` bunu içerir. `/shadow-stack` ise CET/HSP araştırma switch'idir; platform user-mode shadow-stack state sağlamıyorsa sessiz kalabilir ve `/hunt` içinde değildir.
|
||||
|
||||
## Console Output
|
||||
|
||||
Console output alert odaklıdır. Ham ayrıntı JSON artifact'lerinde kalır.
|
||||
|
||||
- `/no-target-output`: hedefin stdout/stderr çıktısını StackSentry console'una karıştırmaz.
|
||||
- `/inline-stack`: repeated-frame compression olmadan full stack'i tek satırda basar.
|
||||
- `/full-stack`: her frame'i offset ile ayrı satırda basar.
|
||||
- `/verbose`: non-alert event'leri de gösterir.
|
||||
- `/quiet`: artifact yazar, console UI'ı azaltır.
|
||||
|
||||
Exit code:
|
||||
|
||||
- `0`: alert yok.
|
||||
- `10`: en az bir alert üretildi.
|
||||
- `1`/`2`: runtime, argüman veya target/config hatası.
|
||||
|
||||
## Outputs
|
||||
|
||||
Her run seçilen `/out` dizini içinde per-process klasörü oluşturur:
|
||||
|
||||
```text
|
||||
out\loader\loader_binary.exe - 24216\
|
||||
```
|
||||
|
||||
Önemli artifact'ler:
|
||||
|
||||
- `summary.json`, `memory.json`, `events.jsonl`
|
||||
- `origin_regions\`
|
||||
- `dumps\`, `memory_audit\`, `modified_modules\`, `modified_network_modules\`
|
||||
- `network_trace.json`, `byoud_trace.json`, `shadow_stack_trace.json`, `etw_timeline.json`
|
||||
- `children\` (`/follow-remote` için)
|
||||
|
||||
Modified module dump yanında yazılan `.tag` dosyası özellikle değerlidir: diskteki dosyaya göre değişen offset/byte haritasını tutar.
|
||||
|
||||
## Kurallar
|
||||
|
||||
Default rule formatı için `config\rules.json` dosyasına bakın. Özel config:
|
||||
|
||||
```powershell
|
||||
.\build\StackSentry64.exe /run target.exe --rules path\to\rules.json
|
||||
```
|
||||
|
||||
Eski `watch_dlls` alanı compatibility alias olarak hâlâ kabul edilir.
|
||||
|
||||
## Mevcut Limitler
|
||||
|
||||
- Bu hâlâ user-mode instrumentation. Güçlü target hook'ları tespit edebilir veya kaldırabilir.
|
||||
- `/mem`, `/tp` ve `/wait` gürültülü veya destabilizing olabilir, bu yüzden ayrı açılır.
|
||||
- `/follow-remote` user-mode remote-execution setup'ını görmeye bağlıdır.
|
||||
- `/net-reset` her zaman network DLL'i unload edemez.
|
||||
- `/memory-audit`, hedef process hâlâ canlıyken anlamlıdır.
|
||||
- `/shadow-stack` deneysel CET/HSP modudur ve birçok sistemde output üretmeyebilir.
|
||||
- Advanced stack spoofing ring3 içinde tamamen imkânsız değildir; StackSentry maliyeti artırmak için callsite, origin, memory, unwind ve network-use korelasyonu yapar.
|
||||
|
||||
## Lisans
|
||||
|
||||
Bu proje MIT License altında dağıtılır (Modify It Tonight). Kullan, değiştir, lab'da boz, düzelt, karşılaştır, sonuç yayınla; ne gerekiyorsa yap. <br> Eğer birkaç saatlik analiz süresi kazandırırsa ben zaten mutluyum. Kahve kabul edilir.
|
||||
|
||||
Third-party kodlar kendi lisans ve credit notlarını [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) içinde korur.
|
||||
@@ -0,0 +1,121 @@
|
||||
# Third-Party Notices
|
||||
|
||||
First off, a huge shoutout to all the projects and articles listed below. Full disclosure: I left out a ton of sources simply because I was too lazy to dig them up from my bookmarks. My bad!
|
||||
|
||||
StackSentry vendors small portions of third-party projects that are built into the analyzer and monitor binaries.
|
||||
|
||||
The sample corpus under `samples/` also uses or adapts ideas from public offensive-security research projects. Those projects are credited separately below because they are part of the validation corpus, not analyzer/monitor runtime dependencies.
|
||||
|
||||
## MinHook
|
||||
|
||||
- Project: https://github.com/TsudaKageyu/minhook
|
||||
- Purpose: user-mode API hooking
|
||||
- License: BSD 2-Clause style license
|
||||
- Copyright: Copyright (C) 2009-2017 Tsuda Kageyu
|
||||
|
||||
The vendored MinHook source files in `third_party/minhook` retain their original copyright and license headers.
|
||||
|
||||
## Hacker Disassembler Engine 64
|
||||
|
||||
- Project/component: HDE64, vendored as part of MinHook
|
||||
- Purpose: instruction length decoding used by MinHook trampoline creation
|
||||
- Copyright: Copyright (c) 2008-2009, Vyacheslav Patkov
|
||||
|
||||
The vendored HDE64 files in `third_party/minhook/src/hde` retain their original copyright headers.
|
||||
|
||||
## Zydis
|
||||
|
||||
- Project: https://github.com/zyantific/zydis
|
||||
- Vendored version: v4.1.1
|
||||
- Purpose: x86/x64 instruction decoding for StackSentry callsite and proxy-gadget analysis
|
||||
- License: MIT
|
||||
- Copyright: Copyright (c) 2014-2024 Florian Bernd; Copyright (c) 2014-2024 Joel Höner
|
||||
|
||||
The full Zydis license is preserved at `third_party/zydis/LICENSE`.
|
||||
|
||||
## krabsetw
|
||||
|
||||
- Project: https://github.com/microsoft/krabsetw
|
||||
- Purpose: optional ETW process/thread/image-load timeline collection in `/etw` mode
|
||||
- License: MIT
|
||||
- Copyright: Copyright (c) Microsoft
|
||||
|
||||
The full krabsetw license is preserved at `third_party/krabsetw/LICENSE`.
|
||||
|
||||
## Zycore
|
||||
|
||||
- Project: https://github.com/zyantific/zycore-c
|
||||
- Vendored revision: dependency revision used by Zydis v4.1.1
|
||||
- Purpose: support library required by Zydis
|
||||
- License: MIT
|
||||
- Copyright: Copyright (c) 2018-2024 Florian Bernd; Copyright (c) 2018-2024 Joel Höner
|
||||
|
||||
The full Zycore license is preserved at `third_party/zydis/dependencies/zycore/LICENSE`.
|
||||
|
||||
## Research Credits
|
||||
|
||||
The `/memory-audit` mode is an original StackSentry implementation inspired by Forrest Orr's public Moneta research series on malicious memory artifacts:
|
||||
|
||||
- https://www.forrest-orr.net/post/malicious-memory-artifacts-part-i-dll-hollowing
|
||||
- https://www.forrest-orr.net/post/masking-malicious-memory-artifacts-part-ii-insights-from-moneta
|
||||
- https://www.forrest-orr.net/post/masking-malicious-memory-artifacts-part-iii-bypassing-defensive-scanners
|
||||
|
||||
No Moneta source code is vendored in StackSentry; these links are credited as research inspiration for the artifact classes and defensive framing.
|
||||
|
||||
The `/byoud-trace` lab mode and the `BYOUD_LoadNetworkDll.exe` test harness are based on public BYOUD research by klezVirus. StackSentry uses that work as an adversarial test case for unwind metadata tampering; the detection and reporting code is original to StackSentry.
|
||||
|
||||
- https://github.com/klezVirus/BYOUD
|
||||
|
||||
The `/shadow-stack` research switch is inspired by Gabriel Landau and Elastic Security Labs' public ShadowStackWalk research. It explores the same defensive idea of comparing a classic stack walk with CET/HSP shadow-stack returns, but it is explicitly experimental and not part of `/hunt` until it can be validated on CET-capable hardware. StackSentry does not vendor ShadowStackWalk source into the shipped analyzer or monitor.
|
||||
|
||||
- Article: https://www.elastic.co/security-labs/finding-truth-in-the-shadows
|
||||
- Reference project: https://github.com/gabriellandau/ShadowStackWalk
|
||||
|
||||
## Sample Corpus / Test References
|
||||
|
||||
The files under `samples/` are intentionally suspicious lab artifacts used to validate StackSentry behavior. Some are original small harnesses; others are inspired by, adapted from, or generated around the public projects listed here.
|
||||
|
||||
### SilentMoonwalk
|
||||
|
||||
- Project: https://github.com/klezVirus/SilentMoonwalk
|
||||
- Used for: synthetic/spoofed call-stack test cases, including direct and RDI-style network DLL load samples.
|
||||
|
||||
StackSentry uses SilentMoonwalk-style samples to validate callsite checks, origin tracing, register tracing, and stack-spoofing reporting.
|
||||
|
||||
### BYOUD
|
||||
|
||||
- Project: https://github.com/klezVirus/BYOUD
|
||||
- Used for: unwind metadata spoofing and BYOUD RDI payload tests.
|
||||
|
||||
The sample corpus uses BYOUD as an adversarial input for `.pdata`/`.xdata`/`RUNTIME_FUNCTION` tampering tests. StackSentry's detection and reporting logic is separate from the BYOUD project code.
|
||||
|
||||
### Donut
|
||||
|
||||
- Project: https://github.com/TheWover/donut
|
||||
- Used for: shellcode/RDI packaging around managed and native test payloads.
|
||||
|
||||
Donut-generated payloads are used to exercise realistic in-memory bootstrap behavior, including CLR/.NET loading and network-module staging.
|
||||
|
||||
### ThreadPoolExecChain
|
||||
|
||||
- Project: https://github.com/klezvirus/ThreadPoolExecChain
|
||||
- Used for: threadpool callback-chain proxy loading tests.
|
||||
|
||||
These samples help validate StackSentry's ability to preserve threadpool-chain context and attribute sensitive DLL loads that happen through callback chains.
|
||||
|
||||
### MassDriver
|
||||
|
||||
- Project: https://github.com/Sizeable-Bingus/MassDriver
|
||||
- Used for: cross-thread function-dispatch style tests.
|
||||
|
||||
MassDriver-style samples validate `/dispatch-trace`, especially cases where a clean worker thread performs `LoadLibraryA` on behalf of another requester.
|
||||
|
||||
### LibTPLoadLib / API Proxying / RefinedPool
|
||||
|
||||
- LibTPLoadLib: https://github.com/AlmondOffSec/LibTPLoadLib
|
||||
- API proxying article: https://0xdarkvortex.dev/hiding-in-plainsight/
|
||||
- Proxy-DLL-Loads: https://github.com/paranoidninja/Proxy-DLL-Loads
|
||||
- RefinedPool variant: https://github.com/Vith0r/RefinedPool/tree/main/RefinedPool
|
||||
|
||||
These references were used for the image `.text` proxy and code-cave/modified-image samples. The StackSentry samples exercise proxy DLL-load behavior, existing-module gadget use, and modified image-backed execution so the analyzer can validate `BackedModified`, origin-region, and `.tag` diff-map output.
|
||||
|
||||
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 32 KiB |