Files
2026-05-22 14:54:55 -03:00

310 lines
21 KiB
Markdown

![StackSentry](/static/StackSentry.png)
<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>
&nbsp;&nbsp;
<img src="https://img.shields.io/badge/Windows-%23000000.svg?style=for-the-badge&logo=Windows">
&nbsp;&nbsp;
![Release](https://img.shields.io/badge/release-v0.8-000000?style=for-the-badge)
<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 に結び直します。
![SilentMoonwalk synthetic stack detection](static/stack-03-silentmoon-synthetic.png)
### 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 を捕まえます。
![BYOUD RDI detection](static/stack-05-byoud-rdi-1.png)
![BYOUD RDI detection](static/stack-05-byoud-rdi-2.png)
### Threadpool Callback Chain
この sample は [klezvirus/ThreadPoolExecChain](https://github.com/klezvirus/ThreadPoolExecChain) の考え方に基づいています。Threadpool/proxy chain によって load が自然に見える context で発生します。StackSentry は chain context を保持し、path 上の modified frame を報告します。
![Threadpool callback-chain detection](static/stack-07-threadpool-chain.png)
### 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 に戻します。
![Image .text proxy detection](static/stack-01-text-section-proxy.png)
### 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 でかなり役に立ちます。
![Code cave modified image detection](static/stack-02-code-cave-modified-image.png)
### 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 を示します。
![SilentMoonwalk RDI synthetic detection](static/stack-04-silentmoon-rdi-synthetic-1.png)
![SilentMoonwalk RDI synthetic detection](static/stack-04-silentmoon-rdi-synthetic-2.png)
### MassDriver 風 Dispatch
[Sizeable-Bingus/MassDriver](https://github.com/Sizeable-Bingus/MassDriver) の dispatch pattern から着想を得た sample です。きれいに見える worker thread が `LoadLibraryA` を実行します。`/dispatch-trace` は、その load を dispatch structure を送った requester に結びます。
![MassDriver-style dispatch detection](static/stack-06-massdriver-dispatch.png)
### 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` を使ってください。
![Network-use trace detection in C2 payload](static/stack-08-network-use-trace-c2.png)
### Stack 表示モード
Detection そのものだけでなく、現在の StackSentry console は分析を少し楽にするために stack 表示も調整できます。同じ stack でも、見たい内容に応じて表示形式を変えられます:
#### Compact Stack
現在の default 表示です。連続して繰り返される frame は `[module.dll xN]` の形でまとめられます。.NET のような大きい runtime を通る payload では、これだけでかなり noise が減ります。
![Compact stack](static/stack-09-compressed-stack.png)
#### Offset 付き Full Stack
`/full-stack` を使うと、各 frame が module offset 付きで1行ずつ表示されます。どの return frame がどこに落ちたのかを細かく確認したい時に向いています。
![Full stack with offsets](static/stack-10-full-stack.png)
#### Compression なし Inline Stack
`/inline-stack` を使うと、stack は1行の形式に戻り、繰り返し frame の compression も行われません。
![Inline stack without compression](static/stack-11-inline-stack.png)
#### Verbose で Clean Event も表示
Default の console は terminal が埋まらないよう alert を優先します。`/verbose` を使うと、`score=0` の clean load も表示されます。この event は青色で出るため、DLL load が観測され記録されたことをすぐ確認できます。`/verbose` を使わなくても、`score=0` event は `events.jsonl``memory.json` に保存されます。
![Clean event in verbose mode](static/stack-12-verbose-clean-load.png)
#### BackedModified と Memory Audit
Return frame が本物の DLL 内に落ちても、その region の bytes が disk 上の file と一致しない場合、StackSentry はそれを clean frame として扱いません。Stack は `BackedModified` / `captured-modified` として表示され、Memory Audit は module、region、そして temporal な変更 detail を記録します。
![BackedModified and memory audit](static/stack-13-backed-modified-memory-audit.png)
## 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) にあります。