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

21 KiB

StackSentry

English日本語PortugueseTürkçe

      Release



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 を本当に起こしたのは誰か?

Important

このプロジェクトは現在も開発中です。Bug、false positive、false negative が起きる可能性があります。
StackSentry は主に lab 環境で loader や in-memory payload の分析を楽にするために作られました。
Kernel driver はインストールせず、永続的な system change もしません。ただし、結果は必ず補助ツールと比較し、絶対的な判定ではなく triage evidence として扱ってください。

アイデア

StackSentry は、memory 上で実行されている code の高速 triage に焦点を当てています。考え方は実用的です。C2、RAT、fileless loader は disk 上の file を隠せます。sleep 中に自分を暗号化できます。見た目のきれいな call stack も作れます。それでも、どこかの時点で network DLL を load するか、network API を使う必要があります。

Windows では、多くの場合 ws2_32.dllwininet.dllwinhttp.dlldnsapi.dll、またはそれらが export する API が関係します。これらの DLL が不自然な origin から load/use されているなら、そこで止まって確認する価値があります。

この pattern は StackSentry だけの発想ではありません。Elastic の behavioral detection rule とも同じ方向を向いています:

こうした 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_32wininetwinhttpdnsapi、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.rdataRUNTIME_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.jsonlsummary.jsonmemory.jsonnetwork_trace.jsonbyoud_trace.json、origin dump、module dump、hash、entropy、strings などの artifact。

Sample の command と期待される call-stack summary は samples/README.md にあります。
先に言っておくと、sample は local test 用のかなり疑わしい lab artifact です。きれいな showcase binary ではありません。
以下は、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 の modified variant を使い、synthetic stack で network DLL を load します。Visible frame は legitimate module に見えますが、callsite validation と origin tracing が DLL load を準備した code に結び直します。

SilentMoonwalk synthetic stack detection

BYOUD / Unwind Metadata Spoofing

この test は klezVirus/byoud を RDI shellcode 化したものです。Bootstrap は wininet.dllmscoree.dll を load しますが、重要なのは BYOUD を使って ws2_32.dll load を隠す stage です。Return address を変える代わりに Windows unwind metadata を操作します。StackSentry は sensitive load の前に起きる temporal divergence を捕まえます。

BYOUD RDI detection BYOUD RDI detection

Threadpool Callback Chain

この sample は klezvirus/ThreadPoolExecChain の考え方に基づいています。Threadpool/proxy chain によって load が自然に見える context で発生します。StackSentry は chain context を保持し、path 上の modified frame を報告します。

Threadpool callback-chain detection

Image .text Proxy

この test は LibTPLoadLib を元にした実験的な RefinedPool variant から作った PIC shellcode を使います。この technique は paranoidninja が説明した API proxying の流れや 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

Code Cave / Modified Image

この pattern も RefinedPool の小さな実験的 variant から来ています。Sensitive load は image-backed code cave に書き込まれた bytes を通ります。StackSentry は modified module と .tag changed-byte map を保存するため、triage でかなり役に立ちます。

Code cave modified image detection

SilentMoonwalk RDI Synthetic Stack

ここでは SilentMoonwalk variant を Donut/RDI payload として package しています。Bootstrap は wininet.dllmscoree.dll を load することがありますが、重要な stage は synthetic stack での ws2_32.dll load です。2枚目の画像は hidden executable region に戻る probable origin を示します。

SilentMoonwalk RDI synthetic detection SilentMoonwalk RDI synthetic detection

MassDriver 風 Dispatch

Sizeable-Bingus/MassDriver の dispatch pattern から着想を得た sample です。きれいに見える worker thread が LoadLibraryA を実行します。/dispatch-trace は、その load を dispatch structure を送った requester に結びます。

MassDriver-style dispatch detection

C2 Payload の Network Use Trace

この例は /network-use-trace を使います。Network DLL load だけでなく、payload が connectWSAConnectsendrecv、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

Stack 表示モード

Detection そのものだけでなく、現在の StackSentry console は分析を少し楽にするために stack 表示も調整できます。同じ stack でも、見たい内容に応じて表示形式を変えられます:

Compact Stack

現在の default 表示です。連続して繰り返される frame は [module.dll xN] の形でまとめられます。.NET のような大きい runtime を通る payload では、これだけでかなり noise が減ります。

Compact stack

Offset 付き Full Stack

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

Full stack with offsets

Compression なし Inline Stack

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

Inline stack without compression

Verbose で Clean Event も表示

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

Clean event in verbose mode

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

Build

Build には build.ps1 を使います。Microsoft Visual Studio/MSVC が必要です。

.\build.ps1

Script は build\ に以下を出力します:

  • StackSentry64.exe
  • CallstackMonitor.dll

Third-party code は third_party\ にあります。Credit と license note は THIRD_PARTY_NOTICES.md を確認してください。

Test Commands

Recommended commands と exact sample commands は 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

全オプション:

.\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 を戻そうとします:

.\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 を追います:

.\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.jsonmemory.jsonsummary.json に出ます。

Memory Audit

/memory-audit は Moneta-style memory artifact から着想を得た live process memory scan です:

.\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 です:

.\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 を作ります:

out\loader\loader_binary.exe - 24216\

主な artifact:

  • summary.jsonmemory.jsonevents.jsonl
  • origin_regions\
  • dumps\memory_audit\modified_modules\modified_network_modules\
  • network_trace.jsonbyoud_trace.jsonshadow_stack_trace.jsonetw_timeline.json
  • /follow-remote 時の children\

Modified module dump の隣に出る .tag は重要です。Disk 上の file と比較して、どの offset/byte が変わったかを示す diff map です。

Rules

Default rule format は config\rules.json を見てください。Custom config:

.\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 で壊して、直して、比較して、結果を公開してください。
分析時間が少しでも節約できたなら、それだけで嬉しいです。コーヒーも歓迎です。

Third-party code の license と credit は THIRD_PARTY_NOTICES.md にあります。