本「
IT談話館」一般公開記事は、10年以上の実務経験を持つ上級Windowsエンジニアを想定しています。
本館は、Windowsカーネル深層を解析し、クラッシュ原因をはじめとするシステム内の「異様な動き」を検出・分析する
超高度な技術と実績を保有しています。
WinDbgとWindowsレジストリ解析(基礎)
本「IT談話館」の「一般公開記事」は、「Active Memory Dump とカーネルメモリダンプ」の解析結果を基に起草されています。「本館」主筆の「豊田孝」はDKOM(Direct Kernel Object Manipulation)ベースの解析手法の第一人者であり、Windowsカーネル空間の解析分野では世界の先頭を走っています。
現在、セキュリティー問題を無視することはできません。Microsoft社側の負担だけではなく、同社製品の利用者側の負担も増しています。困ったことではありますが、当面避けられません。セキュリティーの視点から「Windows10ソフトウェアセンサー」を見た場合、本「IT談話館」の確認範囲では、「カーネル層保護ロジック」に加え、次のような保護メカニズム階層が考案・実装されています。下記リンクはすべて本館記事を指しています。
- Silo/Server Silo
- Job
- Session
- Protected Process
- Mandatory Integrity Control(MIC)
- Windows API(+CPU)
- CPU
本稿では、Windows 10環境で採取した次のような新旧2種類の「Active Memory Dump」内に保存されているWindowsレジストリ情報を本「IT談話館」の独自解析コードで解析します。
1: kd> vertarget
Windows 10 Kernel Version 17134 MP (2 procs) Free x64
Product: WinNt, suite: TerminalServer SingleUserTS
Built by: 17134.1.amd64fre.rs4_release.180410-1804
Machine Name:
Kernel base = 0xfffff802`9ea1b000 PsLoadedModuleList = 0xfffff802`9edc9150
Debug session time: Sun Mar 10 06:57:53.854 2019 (UTC + 9:00)
System Uptime: 2 days 22:17:18.721
0: kd> vertarget
Windows 10 Kernel Version 18362 MP (2 procs) Free x64
Product: WinNt, suite: TerminalServer SingleUserTS
Built by: 18362.1.amd64fre.19h1_release.190318-1202
Machine Name:
Kernel base = 0xfffff804`59a00000 PsLoadedModuleList = 0xfffff804`59e480b0
Debug session time: Sun Nov 3 09:56:47.110 2019 (UTC + 9:00)
System Uptime: 0 days 1:42:54.949
Windowsレジストリ情報は、オブジェクト名前空間にKeyオブジェクトとして記録されます。まずは、独自の解析コードを実行し、解析対象プロセスの「ハンドルテーブル」からオブジェクト名前空間へアクセスし、数あるオブジェクトの中からKeyオブジェクトを検出します。古いWindows 10ビルド環境で採取したメモリダンプを解析すると、次のようなレジストリ情報を取得することができます。なお、解析コードの開発知識の習得には、「時間と予算の投資」が必要です。
No.029 Object->0xffffc70ebe079c60 ObjectType->0xffffd88dfb31dd10 Name->Key
Index->0x02b Handle->0x074
0: kd> !object 0xffffc70ebe079c60
Object: ffffc70ebe079c60 Type: (ffffd88dfb31dd10) Key
ObjectHeader: ffffc70ebe079c30 (new version)
HandleCount: 1 PointerCount: 28875
Directory Object: 00000000 Name: \REGISTRY\MACHINE
Keyオブジェクトを検出した後は、WinDbg付属の「!reg」コマンドで次のように調査します。
0: kd> !reg kbody 0xffffc70ebe079c60
Type : KEY_BODY_TYPE
KCB : ffffc70ebc83c138
NotifyBlock : 0000000000000000
KeyBodyList : 0xffffc70ec8580e90 0xffffc70ebc83c1a8
0: kd> !reg kcb ffffc70ebc83c138
Key : \REGISTRY\MACHINE
RefCount : d4
Flags : CompressedName,
ExtFlags :
Parent : 0xffffc70ebc83c008
KeyHive : 0xffffc70ebc823000
KeyCell : 0x170 [cell index]
TotalLevels : 2
LayerHeight : 0
MaxNameLen : 0x16
MaxValueNameLen : 0x0
MaxValueDataLen : 0x0
LastWriteTime : 0x 1d39e07:0x3f20c700
KeyBodyListHead : 0xffffc70ebe079c80 0xffffc70ecef327f0
SubKeyCount : 6
ValueCache.Count : 0
Owner : 0x0000000000000000
KCBLock : 0xffffc70ebc83c228
KeyLock : 0xffffc70ebc83c238
このコマンド操作では、上位概念から解析を開始する必要から、「kbody」と「kcb」という2つの引数を「!reg」コマンドに渡しています。上の実行結果を見ると、希望する情報が返されています。ところが、比較的新しいWindows 10ビルド環境で採取したメモリダンプ上で同じコマンド操作を行うと、次のようなエラーが返されてしまいます。
0: kd> !reg kbody 0xffff8d8c493159a0
Type : KEY_BODY_TYPE
KCB : ffff8d8c47d43520
NotifyBlock : 0000000000000000
KeyBodyList : 0xffff8d8c47d43598 0xffff8d8c506c1100
0: kd> !reg kcb ffff8d8c47d43520
Not enough space to read nt!_CM_KEY_CONTROL_BLOCK-RefCount
c0000005 Exception in kdexts.reg debugger extension.
PC: 00007ff9`5ca7a839 VA: 00000012`07d7e30a R/W: d98f8ef8 Parameter: 00000000`00000000
0: kd> dt nt!_CM_KEY_CONTROL_BLOCK ffff8d8c47d43520 ref*
+0x000 RefCount : 0x7f
この例外発生の背景には、次のような内部変更の影響が推察されます。
1: kd> vertarget
Windows 10 Kernel Version 17134 MP (2 procs) Free x64
Product: WinNt, suite: TerminalServer SingleUserTS
Built by: 17134.1.amd64fre.rs4_release.180410-1804
Machine Name:
Kernel base = 0xfffff802`9ea1b000 PsLoadedModuleList = 0xfffff802`9edc9150
Debug session time: Sun Mar 10 06:57:53.854 2019 (UTC + 9:00)
System Uptime: 2 days 22:17:18.721
1: kd> dt nt!_CM_KEY_CONTROL_BLOCK ref*
+0x000 RefCount : Uint4B
0: kd> vertarget
Windows 10 Kernel Version 18362 MP (2 procs) Free x64
Product: WinNt, suite: TerminalServer SingleUserTS
Built by: 18362.1.amd64fre.19h1_release.190318-1202
Machine Name:
Kernel base = 0xfffff804`59a00000 PsLoadedModuleList = 0xfffff804`59e480b0
Debug session time: Sun Nov 3 09:56:47.110 2019 (UTC + 9:00)
System Uptime: 0 days 1:42:54.949
0: kd> dt nt!_CM_KEY_CONTROL_BLOCK ref*
+0x000 RefCount : Uint8B
この情報は、kdexts.regの開発担当者が必要とされるメモリ量が4バイトから8バイトに変更されたことをうっかり忘れていたことを示唆しています。同じWindows 10と一口に言ってもビルド番号が異なれば、このような細かな内部変更も発生しており、米Microsoft社内の上級エンジニアでさえも、(配属部署が異なれば)すべての変更内容を把握することは難しい、あるいは、巨大プロジェクトの進行過程では(切羽詰まると)担当部署間の連携にほころびが出てしまう、と推察されます。
本館はこの例外メッセージに興味を引かれたため、本館独自の解析コードを開発し、上の「Built by: 18362.1」メモリダンプに適応し、Keyオブジェクト自体の設計上の変更を確認してみることにしました。独自解析コードの開発知識の習得には、「時間と予算の投資」が必要です。
次のような結果が返されてきます。
ObjectTable->0xffff8d8c4c8e8000 HandleCount->0235 ffffc489264e2080->notmyfault64.e
kbody->0xffff8d8c493159a0 kcb->0xffff8d8c47d43520
FullKeyName->\REGISTRY\MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options RefCount->0127
Found list end after 122 entries
Length->28 ShrtKeyName->IMAGE FILE EXECUTION OPTIONSME)E10318}
kbody->0xffff8d8c4d710940 kcb->0xffff8d8c43e857f0
FullKeyName->***
RefCount->0113
Found list end after 106 entries
Length->15 ShrtKeyName->SESSION MANAGER
kbody->0xffff8d8c4d70f980 kcb->0xffff8d8c44246d40
FullKeyName->***
RefCount->0123
Found list end after 119 entries
Length->8 ShrtKeyName->VERSIONSAPPING
kbody->0xffff8d8c4d7103a0 kcb->0xffff8d8c43e58ad0
FullKeyName->***
RefCount->0171
Found list end after 161 entries
Length->7 ShrtKeyName->MACHINE
kbody->0xffff8d8c4d710700 kcb->0xffff8d8c43e58ad0
FullKeyName->***
RefCount->0171
Found list end after 161 entries
Length->7 ShrtKeyName->MACHINE
kbody->0xffff8d8c4d710820 kcb->0xffff8d8c47e653f0
FullKeyName->\REGISTRY\MACHINE\SOFTWARE\Microsoft\Ole RefCount->0124
Found list end after 120 entries
Length->3 ShrtKeyName->OLE
[---]
この情報には2箇所のデータが赤色で表示されています。2番目の赤色の「SESSION MANAGER」のFullKeyName欄は「***」となっており、希望する文字列が返されていません。この背景やFullKeyNameを取得する利点に興味のある方は「WinDbgとWindowsレジストリ解析(応用)」を一読してみるとよいでしょう。FullKeyNameを取得すると、レジストリ内の検索技術と各キーの解析技術をかなり理解できるようになります。
本稿では赤色の数値「122」部分を解析します。この数値はKeyBodyListを構成する要素数を示している思われます。本館はさらに、この数値は該当キーを参照しているプロセスオブジェクトの数をも示しているのではないか?と仮定し、なら、プロセス名を取得してみようと思い立ちました。そこで、新規に解析コードを開発し、次のような情報を取得してみました。
Kbody->0xffff8d8c493159a0 ProcessId->0x1514
Found list end after 122 entries
0000: 0xffffc489241113c0 ProcessId->0x0270 csrss.exe
0001: 0xffffc48924130080 ProcessId->0x02ac services.exe
0002: 0xffffc48924131080 ProcessId->0x02b4 lsass.exe
0003: 0xffffc489241d5080 ProcessId->0x0314 svchost.exe
0004: 0xffffc4892481c080 ProcessId->0x032c svchost.exe
0005: 0xffffc48924843080 ProcessId->0x0374 fontdrvhost.ex
0006: 0xffffc4892486e0c0 ProcessId->0x03b8 fontdrvhost.ex
0007: 0xffffc489227730c0 ProcessId->0x03e0 svchost.exe
0008: 0xffffc489248c4080 ProcessId->0x01b0 svchost.exe
0009: 0xffffc48924879080 ProcessId->0x02d0 dwm.exe
0010: 0xffffc4892494f080 ProcessId->0x0220 svchost.exe
0011: 0xffffc48924991280 ProcessId->0x0428 svchost.exe
0012: 0xffffc48924997080 ProcessId->0x0430 svchost.exe
0013: 0xffffc489249ca080 ProcessId->0x0470 svchost.exe
0014: 0xffffc489249e90c0 ProcessId->0x0490 svchost.exe
0015: 0xffffc489249f1080 ProcessId->0x04a0 svchost.exe
0016: 0xffffc48924a30080 ProcessId->0x0508 svchost.exe
0017: 0xffffc48924ac03c0 ProcessId->0x0540 svchost.exe
0018: 0xffffc48924af7080 ProcessId->0x056c WUDFHost.exe
0019: 0xffffc48924a890c0 ProcessId->0x058c svchost.exe
0020: 0xffffc48924b18080 ProcessId->0x05dc svchost.exe
0021: 0xffffc48924b1c080 ProcessId->0x05e4 svchost.exe
0022: 0xffffc48924b5a080 ProcessId->0x062c svchost.exe
0023: 0xffffc48924b2d080 ProcessId->0x0610 svchost.exe
0024: 0xffffc48924b2f080 ProcessId->0x0620 svchost.exe
[---]
今回は、「\REGISTRY\MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options」キーを参照しているプロセスを検出しました。他のキーも同じように解析することができます。使用した解析コードは「ハンドルテーブル」をはじめとする複数の概念の上に実装されています。各概念は論理的に関連付けられており、その関連付け論理(設計上の考え方自体)はWindows 10ビルド間でかなり安定してきています。
レジストリ情報はメモリフォレンジックなどの作業では極めて重要な調査対象とされています。実務作業では「kbody」と「kcb」などの上位概念の解析から入り、その後、作業目的に応じて下位概念の解析に進むことになります。必要以上にレジストリ深層を解析する必要はありませんが、現在のソフトウェアの更新と提供状況を考えると、”そこはどういう意味だろう?”という疑問を解消する技術は独自に習得しておく必要があります。この姿勢を堅持していれば、たとえば、今回のようなWinDbgコマンドのエラー発生時に対処できる知識が自然と身に付きます。