幽霊レジストリのあぶり出し:メモリ上からハイブを強奪する実務的アプローチ
ディスクイメージをフォレンジックワークステーションにマウントし、System32\config を覗き込んで「よし、異常なし」と安堵しているアナリストを見ると、私はいつも苦笑いしたくなる。洗練されたマルウェアや、国家背景を持つAPTグループが、そんな分かりやすい場所に永続化の痕跡(Persistence)をそのまま残すと思っているのだろうか。
彼らは実体のない「ファイルレス」を気取るか、あるいはディスク上のハイブファイルをアンマウント・アクセス制御で隠蔽し、生きたメモリ(RAM)の奥深くへ潜伏する。Windowsカーネルがメモリ上に展開した _CMHIVE 構造体を直接ハックしない限り、真の侵入の全貌は永遠に闇の中だ。
今回は、メモリフォレンジックの真髄である「メモリ上のレジストリハイブ抽出と解析」について、教科書には載っていない実践的なハンズオンとアーキテクチャの裏側を解説しよう。
—
1. なぜディスクではなく「メモリ」なのか:カーネル空間の隠蔽工作
Windowsのレジストリは、ディスク上では SYSTEM や SOFTWARE といったハイブファイルとして存在するが、システムが起動すると、これらはメモリ(カーネルプール)上にロードされ、Configuration Manager によって管理される。
巧妙なインプラントは、ディスク上の Run キーやサービス登録を一切行わず、メモリ上にロードされたレジストリのインメモリイメージ(In-Memory Hive)を直接操作するか、あるいは未公開のカーネルAPIを叩いて、ディスク上に痕跡を残さずに自動実行設定を書き換える。
ここでフォレンジックアナリストが直面するのが、「ディスクとメモリの乖離(Registry Discrepancy)」だ。ディスクをいくらスキャンしても検知できない自動実行キーが、メモリ上では堂々と存在している。この乖離を暴く唯一の手段が、揮発性メモリからのハイブ抽出である。
—
2. 内存上のレジストリ構造体と Volatility 3 による抽出ロジック
メモリダンプ(1 や RAW 形式)からレジストリハイブを復元するためには、Windowsカーネルの内部構造、具体的には _CMHIVE と _HBASE_BLOCK の関係を理解する必要がある。
カーネルメモリ内において、各ハイブの先頭には _CMHIVE 構造体が存在し、その内部に実際のレジストリデータ(セル)を保持するメモリ領域へのポインタが格納されている。
現代のメモリフォレンジックにおいて、この複雑な構造体パースを自動化し、安全にハイブをファイルとしてダンプするためのデファクトスタンダードが Volatility 3 である。
以下のコマンドは、ターゲットのメモリダンプから SOFTWARE ハイブの仮想アドレスを特定し、ディスク上に復元する際の手順の一例だ。
# Volatility 3を使用して、メモリ上のレジストリハイブの一覧と仮想アドレス(Offset)を特定する
python3 vol.py -f memdump.raw windows.registry.hives.Hives
# 特定したオフセット(例: 0xfa80031a2000)を指定して、ハイブを物理的にファイルとして抽出する
python3 vol.py -f memdump.raw windows.registry.hives.Hives --dump --offset 0xfa80031a2000
このコマンドを実行すると、指定したオフセットのメモリ領域からバイナリデータが切り出され、hive.0xfa80031a2000.dat のような形式でダンプされる。これが、まさに攻撃者がメモリ上で改ざんを加えていた「生きたレジストリ」の残骸である。
—
3. 抽出したハイブのディープ解析:自動実行キーとカーネルの痕跡
ダンプしたハイブファイルは、通常のオフラインレジストリ解析ツールで読み込むことができる。しかし、GUIツール(Registry Explorerなど)で眺めるだけでは、プロのアナリストとは言えない。
Pythonの regipy ライブラリなどを使用し、自動化されたスクリプトで不審なタイムスタンプ(LastWrite時間)や、隠しキー、不正なバイナリデータをプログラム的にあぶり出すのが実務の現場では求められる。
以下は、抽出したハイブから Run キー周辺の不審なエントリを精査するためのPythonスクリプトの断片である。
import sys
from regipy.registry import RegHive
def analyze_memory_hive(hive_path):
try:
# 抽出したメモリハイブをロードする
hive = RegHive(hive_path)
print(f"[*] 正常にハイブをロードしました: {hive_path}")
# 一般的な永続化ポイント(Runキー)のパスを指定
# メモリ上であっても構造は通常のハイブと同一
run_key_path = "Microsoft\\Windows\\CurrentVersion\\Run"
key = hive.get_key(run_key_path)
if key:
print(f"[+] キーを発見しました: {run_key_path}")
for value in key.get_values():
print(f" - 値の名前: {value['name']}")
print(f" データ: {value['data']}")
print(f" 最終更新日時: {key.header.last_modified}")
else:
print("[-] 指定されたRunキーが見つかりませんでした。")
except Exception as e:
print(f"[!] ハイブの解析中にエラーが発生しました: {e}", file=sys.stderr)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python analyze_hive.py <dumped_hive_file>")
sys.exit(1)
analyze_memory_hive(sys.argv[1])
このスクリプトを走らせることで、ディスク上の静的な設定と、メモリ上で動的に書き換えられた設定の差異を比較するベースラインが完成する。もしディスク上に存在せず、メモリ上のハイブからのみ検出されたエントリがあれば、それは高確率でファイルレスマルウェアやインメモリインジェクションによる永続化の証拠となる。
—
4. 高度な防御と監査へのフィードバック:EDRの盲点を突く攻撃者への対抗策
ここまでメモリ上のレジストリハイブ抽出と解析の技術的側面を解説したが、チーフホワイトハッカーやセキュリティアーキテクトとして重要なのは、このインシデントから何を学び、次世代の防御アーキテクチャにどう還元するかだ。
多くの商用EDR(Endpoint Detection and Response)は、ユーザーランドのAPIフックや、ディスク上のファイル作成・変更イベント(FileCreate、RegistryEvent)を中心にシグネチャを構築している。そのため、カーネルメモリ上で直接 CmRegisterCallback などの監視をバイパスしてレジストリを操作する高度な手法(Direct Kernel Object Manipulation: DKOMの応用など)に対しては、検知の網をすり抜けることが多い。
この盲点を埋めるためには、以下のアーキテクチャレイヤでの対策が不可欠である。
1. 定期的なメモリハイブの整合性監査:
ホストベースのセキュリティエージェントから定期的にカーネル上の _CMHIVE リストを走査し、ディスク上のハイブファイルとのハッシュ値や構造体の乖離を検知するカスタムセンサーの導入。
2. 仮想化ベースのセキュリティ(VBS / HVCI)の強制:
カーネルメモリ領域の完全性をハイパーバイザー層で保護し、未承認のカーネルメモリ書き換えや不正なドライバロードをハードウェアレベルで阻止する基盤の構築。
メモリフォレンジックは、単なる「インシデント発生後の証拠集め(事後対応)」ではない。攻撃者が隠れ蓑に使う低レイヤの挙動を熟知することこそが、次世代のゼロトラスト環境における強固な防御壁を設計するための唯一の羅針盤となるのだ。
コメント