1 ポイント 投稿者 GN⁺ 2025-05-13 | 1件のコメント | WhatsAppで共有
  • ソウル旅行中に作った defendnot は、Windows Security Center(WSC)サービス API を直接呼び出して Windows Defender を無効化しようとするツールで、数日間にわたるリバースエンジニアリングへとつながった
  • 以前のツール no-defender は、既存のアンチウイルスコードを使って WSC に別のアンチウイルスが存在すると登録する方式で、約 1.5k GitHub stars を獲得した後、アンチウイルス開発元の DMCA 要請で削除された
  • 当初は WSC の COM API 呼び出しを約 1 時間で再現したが、WSC が呼び出しプロセスを検証していたため、通常のプロセスではアクセスが拒否された
  • 分析の結果、WSC は WinDefend SID、管理者権限、署名、PE の DllCharacteristics 内の ForceIntegrity フラグを確認しており、最終実装では Taskmgr.exe が使われた
  • arm64 MacBook、米国のリモート PC、Parsec の 210ms 遅延、Shadow.tech VM まで絡む環境の中で、ctx.bin のパスエラーと autorun の電源条件の問題を修正して実装を完了した

defendnot を作ることになった背景

  • defendnot は、Windows Security Center サービス API を直接使って Windows Defender を無効化するツールである
  • 実装過程の核心は WSC 呼び出しそのものよりも、それを防ぐ検証条件や不便な作業環境をひとつずつ回避していくことにあった
  • より詳しい WSC の技術文書は後日別の人が公開する予定である

1 年前の no-defender と DMCA

  • 約 1 年前に公開した no-defender は、Windows Defender を止めるためにアンチウイルス向けの Windows API を使っていた
  • この API は、システムに「別のアンチウイルスがあるため、Defender のスキャンを実行する必要はない」と伝える用途だった
  • この領域を管理しているシステムコンポーネントが Windows Security Center(WSC) である
  • no-defender は既存アンチウイルスのサードパーティコードを利用し、そのアンチウイルスが WSC に登録されるよう強制する方式で動作していた
  • 公開後数週間で約 1.5k stars を獲得し、使用されたアンチウイルス開発元が DMCA takedown 要請を提出すると、リポジトリの内容を削除して終わらせた

ソウル旅行中に再開した WSC 分析

  • ソウルの Airbnb に滞在していたところ、MrBruh が no-defender を調べていて、アンチウイルスのバイナリなしで「クリーン」な実装が可能か調査しているとメッセージを送ってきた
  • 普段は CTF や x86 リバース用途に別の x86 ノート PC を持っていくが、今回の旅行では M4Pro MacBook しか持っていなかった
  • x86 リバース用の機材がない不利な条件でも、友人たちが起きる前の時間を使って WSC の分析を始めた

Day 1: WSC COM API の再現と最初のアクセス拒否

  • MrBruh が最新の WSC バイナリを提供し、以前使っていたアンチウイルスの WSC 登録実装を参考実装とした
  • WSC にはアンチウイルスが使う COM API があり、既存アンチウイルスが行っていた呼び出しを約 1 時間で再現した
  • arm64 Windows を Parallels で起動してテストしたが、結果は access denied だった
  • 以前の経験から WSC が API 呼び出し元プロセスを検証することは分かっており、最初は署名検証の可能性を疑った
  • 既存アンチウイルスが WSC 作業に使っていたプロセスへコードを注入すると、新しいアンチウイルス登録と状態更新の COM 呼び出しは正常に動作した

アンチウイルスバイナリを排除しようとした試み

  • 新しいプロジェクトが既存アンチウイルス企業から再び問題提起されないよう、アンチウイルスのバイナリではなくシステム提供バイナリを使おうとした
  • 最初の対象プロセスとして cmd.exe を選んだが、WSC API 呼び出しは拒否された
  • wscsvc.dll を調べると、呼び出し元プロセスが PPL かどうかを確認するコードがあった
  • 単純に CreateProcessA で作成したプロセスは PPL 保護プロセスではなく、その日の未明にはそれ以上進めなかった

Day 2: 不便なリモートデバッグ環境の構築

  • arm64 MacBook では x86 Windows をまともに扱いづらく、arm64 環境で作業したり x64dbg を使えなかったりする状況を避けたかった
  • 友人の pindos が PC へのアクセスを許可してくれ、Parsec でリモート接続した
  • 韓国から米国にある PC へ接続していたため、平均遅延は約 210ms だった
  • 当時の作業フローは非常に煩雑だった
    • Parallels 上の Windows arm64 で MSVC によりモジュールをビルド
    • 共有フォルダでビルド成果物をホストと共有
    • AnyDesk で pindos の PC 上の VM に成果物をコピー
    • Parsec で 210ms の遅延がある環境から WSC サービスをデバッグ
  • 開発と分析の速度が大きく落ちるほど環境が不便だった

WSC サービスのデバッグと WinDefend SID

  • WSC サービスは svchost が実行する DLL であり、デバッガ接続を阻む主な要因は PPL 保護 だった
  • VM でテストモードを有効にし、カーネルモードで数行のコードにより対象プロセスの PPL を外すドライバを使った
  • cmd.exe が WSC にアンチウイルス登録を要求した際に失敗する箇所は WscServiceUtils::CreateExternalBaseFromCaller だった
  • この関数は RPC クライアントを装った後、呼び出し元プロセストークンに WinDefend SID があるかどうかを確認していた
  • 当時は Windows トークンの挙動をよく理解していなかったため、WinDefend を装えば検査を通過できると判断した
  • 睡眠不足の状態で、既存アンチウイルスのバイナリもこの IsMember 検査を通過すると誤って解釈していた

WinDefend の偽装の試みと失敗

  • Windows トークンを数時間学んだ後、WinDefend SID を含むトークンで実行すれば WSC の検査を通過できるという方向で実装した
  • cmdWinDefend SID を付与した状態でコードを実行してみたが、COM 呼び出しが STATUS_SUCCESS を返しても実際には新しいアンチウイルス登録は行われなかった
  • このアプローチは結果的に役に立たず、翌日に再検証する必要があった

Day 3: 実際の検証アルゴリズムの再構成

  • 再確認すると、既存アンチウイルスのバイナリは WinDefend SID 検査 を通過していなかった
  • その検査を通過すると Windows Defender の WSC オブジェクトを扱うことになるが、WSC 呼び出しだけで Defender を直接無効化することはできず、役に立たなかった
  • WinDefend 偽装コードを削除し、条件分岐の別のブランチを分析した
  • そのブランチでは、呼び出し元バイナリについて次の点を確認していた
    • プロセスが elevated 状態か
    • バイナリ署名が有効か
    • PE の DllCharacteristics に特定のフラグがあるか
  • CSecurityVerificationManager::CreateExternalBaseFromPESettings で確認していた DllCharacteristics フラグは ForceIntegrity だった
  • WSC が行うバイナリ検査を再現したコードを defendnot リポジトリの wsc-binary-check フォルダに作成し、System32 バイナリを対象にテストした

Taskmgr.exe の使用と ctx.bin バグ

  • 友人が再び PC を使う必要が出たため、VM に直接 Parsec で接続したが、ソフトウェアエンコードまで重なって環境はさらに遅くなった
  • cmd.exe の代わりに Taskmgr.exe を使ったが、それでも RPC エラーが発生した
  • 遅延と入力の問題で同じ VM 上でデバッグするのが難しくなり、勧められるまま shadow.tech の契約に $30 を支払った
  • Shadow.tech の VM は Windows のバージョンがより古く、wscsvc のコードが最新バージョンのように 1 つの関数へ大量にインライン化されておらず、逆コンパイル結果も読みやすかった
  • 実際のエラー原因は、WSC に渡した AV 名が間違っていたこと だった
    • defendnot-loader から defendnot.dll へデータを渡すため、シリアライズ済みパラメータを格納した ctx.bin を使っていた
    • 状態追跡用の IPC は別途実装していたが、設定の受け渡しには古い no-defender コードの名残である ctx.bin 方式が残っていた
    • ctx.bin のパスを探す関数が nodefend.dll ではなく Taskmgr.exe モジュールのベースフォルダを基準にしていた
    • その結果、ヌルバイトを AV 名として渡してしまい、WSC がそのバッファを拒否した
  • パスの問題を修正した後、テストは成功した

コード整理と autorun の問題

  • その日のうちに終わらせるため、午前 8 時までコード整理と追加機能の実装を進めた
  • 追加機能には、自身を autorun に追加する機能が含まれていた
  • 午前 8 時の時点では autorun だけが正しく動作せず、いくつもの方法を試して失敗した後に眠った
  • 翌日あらためて確認すると、タスクスケジューラのタスク作成時にある電源関連のチェックボックス 2 つが原因だった
  • ノート PC が AC 電源に接続されていなかったためタスクが実行されず、そのフラグを解除すると autorun は動作した
  • その後さらに数時間コードを整理して作業を終えた

まとめ

  • 作業そのものは楽しかったが、数日間繰り返した環境トラブルとリモートデバッグの流れは二度と経験したくないものだった
  • 特に arm64 MacBook、リモート x86 VM、高遅延、遅いエンコード、そして後になって判明したファイルパスのバグが、実装の難易度を大きく高めていた
  • より技術的な WSC 文書は今後別途公開される予定である

1件のコメント

 
GN⁺ 2025-05-13
Hacker News のコメント
  • Defender を無効化する方法の中で、最も侵襲的だが効果があったのは、ライブ Linux USB で起動して C:\ProgramData\Microsoft\Windows Defender をリネームし、その場所に空のファイルを作っておくやり方だった

    • グループポリシーはいまだに非常によく効くので、ホームラボにローカルのドメインコントローラーを置き、全ユーザーに対して Defender のポリシーだけを自動で変更するようにしていた
    • ある人気製品もほぼ同じように動作していて、結果的に**インターネット全体の 25%**くらいを一緒に落としてしまったようなもの
    • Windows が署名付きマニフェストでそうした変更を検知しないのは不思議
  • 参考までに、WSC は Windows Security Center の略

  • the antivirus I was using という表現のせいでかなり混乱した。そのウイルス対策ベンダーが、なぜ筆者に DMCA 削除要請を送る理由があるのか理解できなかった
    おそらく筆者が別のウイルス対策ソフトをリバースエンジニアリングして、その一部をオープンソースプロジェクトに入れたという意味なのだと思う。ただ Impersonating WinDefend のようなタイトルも見えるので、結局のところ筆者が何らかの形で著作権法に違反したのかが気になる

    • 理解した限りでは、署名要件を回避するために別のウイルス対策ツールの外殻を使ったようだ。グレーゾーンなのは分かるし、変容的利用と見なす余地もあると思うが、法律家ではない
    • その通り。既存のウイルス対策プログラムの一部をコピーして著作権法に違反したということ
      引用された段落のすぐ前に、「プロジェクトは既存のウイルス対策ソフトのサードパーティコードを使用し、そのウイルス対策ソフトが WSC にウイルス対策として登録されるよう強制した」と説明されている
  • このコードは呪われている感じがする:
    https://github.com/es3n1n/defendnot/blob/master/defendnot-lo...
    実際に何が起きているのか気になるならここ:
    https://github.com/es3n1n/defendnot/blob/master/cxx-shared/s...

    • C++ の魔法に詳しい人が、何が起きているのか、なぜ呪われていると言われているのか説明してくれないか?
    • 時間の制約で、COM 関連オブジェクトに対して自前で RAII を実装したくなかった。それでも次のアップデートで変更する予定
    • 何が呪われているのか分からない。呼び出し側のシグネチャは個人的な好みで少し違うが、自分のコードでもこのパターンをあちこちで使っている
      例えば D 言語には、スコープ終了時に実行される文(statement)の概念が言語に組み込まれている
    • 「コードとは同僚への接し方である」 - Michael Feather, https://x.com/mfeathers/status/1031176879577780224
      要約すると、AI が書いたものではない。このコードは、オブジェクトがスコープを抜ける時点まで関数呼び出しを遅延させる。実装は C マクロで、必要な C ラムダ/匿名関数定義の一部を省略する短い構文を作り、遅延呼び出しを管理する一意な変数名を生成している
      ただし、結果としての構文は、C マクロを示す一般的な慣例である大文字表記を避けており、一見するとオブジェクトポインタの関数呼び出しのように見える。このパターンに慣れていない人や、マクロは違う形で表示されるはずだと期待している人には混乱を招く可能性がある
      人によっては十分に一般的で有用なため、特定の文脈ではほとんどイディオムのように見なせる。技術的な説明は https://news.ycombinator.com/item?id=43959403#43960905 がマクロの動作をうまく解説している
  • 休暇中に Windows の仮想デスクトップをリバースエンジニアリングしたおかげで、休暇がずっと良いものになった。去年一番の思い出は、リバースエンジニアリングが本当に楽しいと感じたこと
    興味深いこともたくさん学んだ。Windows の RPC の下には、文書化されていないメッセージング層がある: https://csandker.io/2022/05/24/Offensive-Windows-IPC-3-ALPC....

  • プロフィール画像がアニメキャラなら、その記事は良いものだと毎回分かる。あとでひどい Windows 環境に戻ることになったときのために保存しておく

  • 気になる人のために: WSC は Windows Security Center の略。自分も調べる必要があった

    • 記事の中に「この混乱を管理しているシステム部分は Windows Security Center、略して WSC と呼ばれる」と書かれている
  • なぜ WSC を無効化したいのだろう?

    • パフォーマンスのためかもしれないし、マルウェア開発やハッキングのためかもしれない
    • 攻撃者なら、他のエンドポイント検知・対応製品がインストールされていないという幸運な状況を狙える。そうした製品があれば、ほぼ確実に横取りされるだろう
      EDR ベンダーなら、これは Windows Firewall を抑制または無効化するために使える難読化された API 呼び出しだ。例えば CrowdStrike は Windows Firewall を使うことも、自前実装を使うこともどちらも可能だと思う
    • 自分のハードウェアなのだから、自分のしたいようにするだけ
    • すべてのウイルス対策ソフトは、少なくともパワーウイルスだ。誰かが netcat.exe はダメだとベビーシッターのように干渉してくることなど気にしたくない
    • わざわざ自分自身にルートキットを仕込みたい理由がないから
  • 最近 https://nostarch.com/windows-security-internals を読んだところで、この記事のおかげではるかに実感を持って理解できた
    Windows でこうした裏側の動作が大まかにどう回っているのかは少し知っていたが、タイミングがよかった。その本の最終章も、この記事の筆者が扱っているようにトークンと SIDをかなり詳しく掘り下げている