本「
IT談話館」一般公開記事は、10年以上の実務経験を持つ上級Windowsエンジニアを想定しています。
本館は、Windowsカーネル深層を解析し、クラッシュ原因をはじめとするシステム内の「異様な動き」を検出・分析する
超高度な技術と実績を保有しています。
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カーネルアーキテクチャーの上位に位置する概念であり、Windowsデバイス(Clientマシン)とMicrosoftクラウド(ML/DL/AIシステム)を連結しています。Windows 10のソフトウェアセンサー化に興味のある方は、本「IT談話館」の別稿「Windowsセキュリティーメカニズム」に目を通すとよろしいかもれません。本稿では、次のようなアーキテクチャー図を参考にしながら、Windowsのカーネル内部を検討しています。
このアーキテクチャー図にはALPCではなくLPCという旧式の用語が使用されています。
kd> !lpc ?
LPC is now emulated over ALPC. Use !alpc
ALPCについては後程触れることになっています。また、図内ではSystem Service Descriptor Table(SSDT)ではなく、Trap Interfaceなる用語が使われています。次の情報は上図の2年後に当たる、2008年当時の32bit Windows XP SP2環境下で採取されたカーネルメモリダンプの割り込みテーブルの解析結果であり、Trapなる用語がテーブル内で使われています。
kd> vertarget
Windows XP Kernel Version 2600 (Service Pack 2) UP Free x86 compatible
Product: WinNt, suite: TerminalServer SingleUserTS
Built by: 2600.xpsp_sp2_gdr.070227-2254
Machine Name:
Kernel base = 0x804d9000 PsLoadedModuleList = 0x8055c620
Debug session time: Wed Jun 4 19:50:12.855 2008 (UTC + 9:00)
System Uptime: 0 days 0:09:06.425
00 nt!KiTrap00 (804e1350)
01 nt!KiTrap01 (804e14cb)
03 nt!KiTrap03 (804e189d)
04 nt!KiTrap04 (804e1a20)
05 nt!KiTrap05 (804e1b81)
06 nt!KiTrap06 (804e1d02)
07 nt!KiTrap07 (804e236a)
09 nt!KiTrap09 (804e278f)
0a nt!KiTrap0A (804e28ac)
0b nt!KiTrap0B (804e29e9)
0c nt!KiTrap0C (804e2c42)
0d nt!KiTrap0D (804e2f38)
0e nt!KiTrap0E (804e364f)
0f nt!KiTrap0F (804e397c)
10 nt!KiTrap10 (804e3a99)
11 nt!KiTrap11 (804e3bce)
12 nt!KiTrap0F (804e397c)
13 nt!KiTrap13 (804e3d34)
14 nt!KiTrap0F (804e397c)
15 nt!KiTrap0F (804e397c)
16 nt!KiTrap0F (804e397c)
17 nt!KiTrap0F (804e397c)
18 nt!KiTrap0F (804e397c)
19 nt!KiTrap0F (804e397c)
1a nt!KiTrap0F (804e397c)
1b nt!KiTrap0F (804e397c)
1c nt!KiTrap0F (804e397c)
1d nt!KiTrap0F (804e397c)
1e nt!KiTrap0F (804e397c)
1f nt!KiTrap0F (804e397c)
2a nt!KiGetTickCount (804e0b92)
2b nt!KiCallbackReturn (804e0c95)
2c nt!KiSetLowWaitHighThread (804e0e34)
2d nt!KiDebugService (804e177c)
2e nt!KiSystemService (804e0631)
2f nt!KiTrap0F (804e397c)
30 hal!HalpClockInterrupt (806f4d50)
31 fede47a4
32 nt!KiUnexpectedInterrupt2 (804dfd04)
33 nt!KiUnexpectedInterrupt3 (804dfd0e)
34 nt!KiUnexpectedInterrupt4 (804dfd18)
35 812e0044
36 nt!KiUnexpectedInterrupt6 (804dfd2c)
37 nt!KiUnexpectedInterrupt7 (804dfd36)
38 hal!HalpProfileInterrupt (806eeef0)
39 8131383c
3a nt!KiUnexpectedInterrupt10 (804dfd54)
3b feff55c4
[---]
Trap Interfaceという表現は2000年代初頭の一般的な技術水準、つまり、ソフト割り込みとハード割り込みがそれほど区別されない当時の雰囲気を反映しているものと推察されます。いわゆる、不意に発生する割り込みに対してはTrap(罠)を仕掛けておく、といった認識でしょう。なお、SSDT(System Service Description Table)の機能や役割については、本館の「WinDbgとWindows XP/7/8/10割り込みテーブル(IDT)の内部解析」や「Windows 10 Active Memory Dumpとカーネルメモリダンプ」の記事が参考になるかもしれません。
上のアーキテクチャー図は時代の流れを感じさせますが、当時の考え方や概念は時代の流れの中で繰り返し再考され、Cloud側とClient側を連結するセンサー機能を搭載しながら現在でも存在しています。Windows 10システム環境で採取した「Active Memory Dump」をWinDbgで解析すると、次のようなWindowsカーネルアーキテクチャー内部情報を取得することができます。
Pcr->0xfffff8005a350000 Prcb->0xfffff8005a350180 Process->0xffffd88dffb3c080 Thread->0xffffd88dfef55080 notmyfault64.exe
この情報は、上のWindowsアーキテクチャー図でいえば、最下位層のKernel run-time/Hardware Abstraction Layer(HAL)から最上位層のApplicationに相当します。Device、Device Driver、VAD、Session、Section、Token、Job/Silo、VM、NDIS、あるいは、IRPなどの上/下位概念の多くを捨象し、まさに、Windowsカーネルアーキテクチャーの中心概念のみを示しています。本稿では触れない他の上/下位概念に興味のある方は、本館の「応用編」に目を通すとよろしいかもしれません。本「IT談話館」は、Windows 10ソフトウェアセンサー機能に加え、Windowsカーネルアーキテクチャーを構成する各種オブジェクト間の微妙な関係性を詳細に解析し、Windowsシステムの健康度やパフォーマンス低下背景を診断したり、システムクラッシュ発生原因を特定する高度な解析技術力を保有しています。
上のWindowsアーキテクチャー図には、System ServicesやCritical servicesという概念が含まれています。System ServicesはServices.exe(SCM)プロセスの子プロセスですから、その特定は容易ですが、Critical servicesとはいったいなんでしょう?正直に言えば、本「IT談話館」も確かなことは分かりません。しかし、次のような仮説を設定し、その仮説を自力で実証することにより、それらしい情報を取得することができます。
- Critical ServicesはSCMの子プロセスであろう
- Critical ServicesはSession 0内で起動されるプロセスに違いない
- Critical Servicesは動作終了時にはブレークするに違いない
これらの仮説を組み入れた解析コードを独自に作成し、Windows 10 Professional環境で採取した「Active Memory Dump」を解析してみると、次のような情報が返されてきます。
No.001: Parent: 0x002ac Child: 0x00378 SessionId->0 Critical->1 svchost.exe(0xffffd88dfe647080)
No.002: Parent: 0x002ac Child: 0x003a0 SessionId->0 Critical->1 svchost.exe(0xffffd88dfd92a080)
No.003: Parent: 0x002ac Child: 0x004e0 SessionId->0 Critical->1 svchost.exe(0xffffd88dfe9a7080)
TotalProcesses->160
すべての条件をクリアーしたのは、160個のプロセス中、3個だけです。3個のプロセスはすべて「svchost.exe」サービスプロセスですから、なんとなく納得できそうな感じですが、実務解析作業では、「0xffffd88dfe647080」などのプロセス識別情報を頼りにさらに解析作業を進め、”なんとなく納得”ではなく、”事実”を知る必要があります。たとえば、とりあえず、上記3個の「svchost.exe」プロセスが受け取っているコマンドラインパラメータを調査してみるのも一案です。
+0xFFFFD88DFE647080 svchost.exe
CommandLine->C:\WINDOWS\system32\svchost.exe -k DcomLaunch -p
+0xFFFFD88DFD92A080 svchost.exe
CommandLine->c:\windows\system32\svchost.exe -k rpcss -p
+0xFFFFD88DFE9A7080 svchost.exe
CommandLine->C:\WINDOWS\system32\svchost.exe -k LocalServiceNoNetwork -p
赤色で強調されている引数はそれぞれの「svchost.exe」サービスプロセスの役割(Service)を指定しています。DcomはDistributed COM、rpcはRemote Procedure Callをそれぞれ示していますから、これらのサービスプロセスが動作を停止した場合、Windowsシステムの運用に深刻(Critical)な影響が出ます。逆に、この2つのプロセスの動作は実質的に停止することはできませんから、内外からのシステム侵入者に「横展開」され、セキュリティー面での危険性が残されていることになります。便利な機能は誰にとっても便利なわけです。
最後のLocalServiceNoNetworkは依然意味不明ですが、次のような「プロセスとオブジェクト名前空間」の間に成立している関係を解析してみると、LRPC(Local rpc)であることがはっきりします。
HandleCount->0502 svchost.exe ffffd88dfe9a7080
015: Object->0xffffc70ebd13a640 Name->KnownDlls
046: Object->0xffffc70ebcfc9ea0 Name->BaseNamedObjects
061: Object->0xffffd88dfd10adb0 Name->Service-0x0-3e5$
062: Object->0xffffd88dfcf41260 Name->Default
063: Object->0xffffd88dfd10adb0 Name->Service-0x0-3e5$
105: Object->0xffffd88dfe7b64d0 Name->LRPC-1e04123d1c6dafcb51
108: Object->0xffffd88dfe7cda20 Name->CoreMessagingRegistrar
110: Object->0xffffd88dfe7cd550 Name->CoreUISystemWindowIDManager
174: Object->0xffffd88dfdf50970 Name->LRPC-0478b56f5966a113be
184: Object->0xffffd88dfdf28fe0 Name->BFE_Notify_Event_{f53b9da8-b2b0-4953-ae43-04e69107a58c}
187: Object->0xffffd88dfdf958c0 Name->BFE_Notify_Event_{0cafbc46-b5ab-4c15-8971-dfda7deb763f}
191: Object->0xffffd88dfdf98a20 Name->BFE_Notify_Event_{5a40686c-db98-416e-aa26-2f12369eed56}
193: Object->0xffffd88dfdf95550 Name->BFE_Notify_Event_{7ec53fd7-a69f-4f72-a8f8-be819f2dca7b}
195: Object->0xffffd88dfdf98400 Name->BFE_Notify_Event_{646dbf31-a7c9-48f1-b3c5-1c9b46d4f9e0}
212: Object->0xffffc70ec8662fc0 Name->__ComCatalogCache__
229: Object->0xffffd88dfeaf7b40 Name->BFE_Notify_Event_{bc107dad-a0b3-4993-8525-3244e00dcbf5}
230: Object->0xffffd88dfb2f6de0 Name->MaximumCommitCondition
233: Object->0xffffd88dfeaf7b40 Name->BFE_Notify_Event_{bc107dad-a0b3-4993-8525-3244e00dcbf5}
247: Object->0xffffd88dfdee66c0 Name->BFE_Notify_Event_{3aab5344-00d6-4166-8201-552775602cb5}
248: Object->0xffffd88dfdee66c0 Name->BFE_Notify_Event_{3aab5344-00d6-4166-8201-552775602cb5}
0: kd> !object 0xffffd88dfe7b64d0
Object: ffffd88dfe7b64d0 Type: (ffffd88dfb31acf0) ALPC Port
ObjectHeader: ffffd88dfe7b64a0 (new version)
HandleCount: 1 PointerCount: 32768
Directory Object: ffffc70ebd466520 Name: LRPC-1e04123d1c6dafcb51
この解析結果は、ALPCがRPCの応用メカニズムであることを示していますから、LocalServiceNoNetworkの意味がここではっきりします。LRPCは、ローカルシステム上のユーザー空間/カーネル空間やプロセス/スレッド間に適応される相互通信メカニズムであり、ネットワークサービスは提供していません(NoNetwork)。このALPCポートオブジェクトを調査したい場合には、たとえば、「!alpc /p 0xffffd88dfe7b64d0」などのWinDbgコマンドを次のように実行します。
0: kd> !alpc /p 0xffffd88dfe7b64d0
Port ffffd88dfe7b64d0
Type : ALPC_CONNECTION_PORT
CommunicationInfo : ffffc70ec8a08c30
ConnectionPort : ffffd88dfe7b64d0 (LRPC-1e04123d1c6dafcb51)
ClientCommunicationPort : 0000000000000000
ServerCommunicationPort : 0000000000000000
OwnerProcess : ffffd88dfe9a7080 (svchost.exe)
SequenceNo : 0x00000000 (0)
CompletionPort : ffffd88dfe9cf3c0
CompletionList : 0000000000000000
ConnectionPending : No
ConnectionRefused : No
Disconnected : No
Closed : No
FlushOnClose : Yes
ReturnExtendedInfo : No
Waitable : No
Security : Static
Wow64CompletionList : No
10 thread(s) are registered with port IO completion object:
THREAD ffffd88dfd842080 Cid 04e0.1984 Teb: 00000007c7d36000 Win32Thread: 0000000000000000 WAIT
THREAD ffffd88dfffc0700 Cid 04e0.1b3c Teb: 00000007c7dbe000 Win32Thread: 0000000000000000 WAIT
THREAD ffffd88dfe850080 Cid 04e0.1e68 Teb: 00000007c7c04000 Win32Thread: 0000000000000000 WAIT
THREAD ffffd88dfc469040 Cid 04e0.1c10 Teb: 00000007c7c18000 Win32Thread: 0000000000000000 WAIT
THREAD ffffd88e00161080 Cid 04e0.1ff8 Teb: 00000007c7c3e000 Win32Thread: 0000000000000000 WAIT
THREAD ffffd88dffc22340 Cid 04e0.18ec Teb: 00000007c7c40000 Win32Thread: 0000000000000000 WAIT
THREAD ffffd88dff5bc1c0 Cid 04e0.177c Teb: 00000007c7c42000 Win32Thread: 0000000000000000 WAIT
THREAD ffffd88e00127200 Cid 04e0.199c Teb: 00000007c7c44000 Win32Thread: 0000000000000000 WAIT
THREAD ffffd88dff868080 Cid 04e0.1d7c Teb: 00000007c7c46000 Win32Thread: 0000000000000000 WAIT
THREAD ffffd88dff89a700 Cid 04e0.048c Teb: 00000007c7c48000 Win32Thread: 0000000000000000 WAIT
Main queue is empty.
Direct message queue is empty.
Large message queue is empty.
Pending queue is empty.
Canceled queue is empty.
この出力情報を見ると、ほとんどのキューは「空」ですが、IO完了キューには10個のスレッドが入っています。ここで示されている「10個」という数値は正しいでしょうか?キューが破損している可能性はないでしょうか?次のようなコマンド操作をすると、これらの疑問への回答を探すことができます。
0: kd> !thread ffffd88dff89a700
THREAD ffffd88dff89a700 Cid 04e0.048c Teb: 00000007c7c48000 Win32Thread: 0000000000000000 WAIT: (WrQueue) UserMode Alertable
ffffd88dfe9cf3c0 QueueObject
Not impersonating
DeviceMap ffffc70ec7468e30
Owning Process ffffd88dfe9a7080 Image: svchost.exe
Attached Process N/A Image: N/A
Wait Start TickCount 4947807 Ticks: 13689 (0:00:03:33.890)
Context Switch Count 1 IdealProcessor: 1
UserTime 00:00:00.000
KernelTime 00:00:00.000
Win32 Start Address ntdll!TppWorkerThread (0x00007fff20c642c0)
Stack Init ffffd70a80e0cd90 Current ffffd70a80e0c540
Base ffffd70a80e0d000 Limit ffffd70a80e07000 Call 0000000000000000
Priority 8 BasePriority 8 PriorityDecrement 0 IoPriority 2 PagePriority 5
Child-SP RetAddr : Args to Child : Call Site
ffffd70a`80e0c580 fffff800`5b4b1070 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSwapContext+0x76
ffffd70a`80e0c6c0 fffff800`5b4b09ee : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSwapThread+0x190
ffffd70a`80e0c780 fffff800`5b4af87f : ffffd88d`ff89a700 00000000`00000000 ffffd88d`fe9a7000 ffffd88d`fe9cf3c0 : nt!KiCommitThreadWait+0x10e
ffffd70a`80e0c820 fffff800`5b4af379 : 00000000`00000000 00000000`00000001 00000000`00000001 00000000`00000000 : nt!KeRemoveQueueEx+0x24f
ffffd70a`80e0c8c0 fffff800`5b4aef05 : 00000000`00000000 00000000`00000000 000004e8`fffffb30 000004d0`fffffb30 : nt!IoRemoveIoCompletion+0x99
ffffd70a`80e0c9e0 fffff800`5b596203 : 00000000`00000000 fffff800`5b52062b ffffd88d`fe9cf190 00000000`00000000 : nt!NtWaitForWorkViaWorkerFactory+0x305
ffffd70a`80e0cb90 00007fff`20cd3784 : 00007fff`20c649dd 00000007`c7c9b000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ ffffd70a`80e0cc00)
00000007`d107f4e8 00007fff`20c649dd : 00000007`c7c9b000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!NtWaitForWorkViaWorkerFactory+0x14
00000007`d107f4f0 00007fff`201d1fe4 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!TppWorkerThread+0x71d
00000007`d107f880 00007fff`20c9efb1 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
00000007`d107f8b0 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
0: kd> dt _kqueue ffffd88dfe9cf3c0
ntdll!_KQUEUE
+0x000 Header : _DISPATCHER_HEADER
+0x018 EntryListHead : _LIST_ENTRY [ 0xffffd88d`fe9cf3d8 - 0xffffd88d`fe9cf3d8 ]
+0x028 CurrentCount : 0
+0x02c MaximumCount : 4
+0x030 ThreadListHead : _LIST_ENTRY [ 0xffffd88d`fd842288 - 0xffffd88d`ff89a908 ]
0: kd> !validatelist ffffd88dfe9cf3c0+30
Found list end after 10 entries
10個のスレッド数を確認するだけでしたら、このコマンド操作で十分ですが、実務解析作業では次のようなコマンド操作を行い、さらにカーネル深層に踏み込むこともあります。
0: kd> dt _io_workitem ffffd88dfe9cf3c0
nt!_IO_WORKITEM
+0x000 WorkItem : _WORK_QUEUE_ITEM
+0x020 Routine : 0xffffd88d`fe9cf3d8 void +ffffd88dfe9cf3d8
+0x028 IoObject : 0x00000004`00000000 Void
+0x030 Context : 0xffffd88d`fd842288 Void
+0x038 WorkOnBehalfThread : 0xffffd88d`ff89a908 _ETHREAD
+0x040 Type : 0
+0x044 ActivityId : _GUID {00000000-1000-8948-7c24-184c0e000d02}
0: kd> dt _WORK_QUEUE_ITEM ffffd88dfe9cf3c0
ntdll!_WORK_QUEUE_ITEM
+0x000 List : _LIST_ENTRY [ 0x00000000`00100204 - 0xffffd88d`ff5bc300 ]
+0x010 WorkerRoutine : 0xffffd88d`ff89a840 void +ffffd88dff89a840
+0x018 Parameter : 0xffffd88d`fe9cf3d8 Void
0: kd> !validatelist poi(0xffffd88dff89a840)
Found list end after 10 entries
このコマンド操作は、Windowsカーネルアーキテクチャー知識、C++のポインター、および、キャスト(型変換)を応用しています。
本稿で取り上げたALPCは名前付きパイプとともに、プロのWindows内部解析技術者にとっては避けて通れない技術概念の一つです(参照)。従来はこのレベルの技術の習得には海外の資料や人材に頼る必要がありましたが、本「IT談話館」主筆の「豊田孝」はBlackHatなどのカンファレンスでプレゼンする海外の先端技術者に助言を与えています。時代が求める人材の育成と技術の習得には、「時間と予算の投資」が必要です。