---
# System prepended metadata

title: Windows kernel
tags: [windows, kernel]

---

###### tags: `windows` `kernel`

# Windows kernel

## Windowsの_OBJECT_TYPE
[ObReferenceObjectByHandle](https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-obreferenceobjectbyhandle)はハンドルからオブジェクトのアドレスへ変換する関数

\_OBJECT\_TYPEはプロセスやスレッド、ミューテックスなどの色んなオブジェクトの定義を格納しているWindowsの重要な構造体の1つ
[POBJECT_TYPEについてはこのブログで確認できる](https://docs.microsoft.com/ja-jp/archive/blogs/doronh/what-is-pobject_type)

![\_OBJECT_TYPEの構造体](https://i.imgur.com/cXfBexO.jpg)

### 特定のオブジェクトを見つける
notepad.exeを例にして、プロセスのオブジェクトを見つける流れを見てみる
![windbg_process](https://i.imgur.com/8EvU3ZZ.jpg)
上記の画像のPROCESSの横に書かれているアドレス(`ffffdb8b26f38300`)がEPROCESSのアドレス

Windowsは各オブジェクト(例：\_EPROCESS)を次のように保存する
1. _POOL_HEADER
1. _OBJECT_QUOTA_CHARGES (optional)
1. _OBJECT_HANDLE_DB (optional)
1. _OBJECT_NAME (optional)
1. _OBJECT_CREATOR_INFO (optional)
1. _OBJECT_HEADER
1. object body (e.g _EPROCESS)

EPROCESSからsizeof(\_OBJECT\_HEADER)を引くと\_OBJECT\_HEADERのアドレスになる
![windbg_object_header](https://i.imgur.com/OZl2RLs.jpg)

`sizeof(_OBJECT_HEADER)=0x38`ですが、最後のBodyメンバーは\_EPROCESSの本体なので、0x30だけを引く
```c=
ffffdb8b26f38300 - 0x30 = ffffdb8b`26f382d0
```

![windbg_object_header](https://i.imgur.com/9BSACZY.jpg)

これは[!objectコマンド](https://docs.microsoft.com/ja-jp/windows-hardware/drivers/debugger/-object)から確認することもできる
![windbg_object](https://i.imgur.com/nfZTVeE.jpg)

上記の結果にTypeがある
このアドレスは`nt!_OBJECT_TYPE`

![windbg_object_type](https://i.imgur.com/2vg6Qs5.jpg)
この\_OBJECT\_TYPE(プロセス)は`nt!PsProcessType`と呼ばれるグローバル変数にマップされている(exp: PsProcessType, PsThreadType)
これらのグローバル変数は_OBJECT_TYPE\*\*の単一の間接ポインター
![windbg_psprocesstype](https://i.imgur.com/gWRJOjH.jpg)


### _OBJECT_HEADERのTypeIndex
\_OBJECT\_HEADERの重要なメンバーの1つに`TypeIndex`がある
![windbg_object_header_typeindex](https://i.imgur.com/Bz962lt.jpg)
TypeIndexはXORによって難読化されている

`ObGetObjectType`はオブジェクトのアドレスを引数として渡し、オブジェクトのタイプを戻り値として返す
![windbg_nt_obgetobjecttype](https://i.imgur.com/1G47K7S.jpg)
```
TypeIndex ^ (_OBJECT_HEADERの最後から2番目のバイト) ^ ObHeaderCookie
```

![windbg_](https://i.imgur.com/goyrEsB.jpg)

`nt!ObTypeIndexTable`を使って_OBJECT_TYPEのアドレスを取得する

![](https://i.imgur.com/kfAimQe.jpg)

### 参考
- [Reversing Windows Internals (Part 1) – Digging Into Handles, Callbacks & ObjectTypes](https://rayanfam.com/topics/reversing-windows-internals-part1/)
- [解析PspCidTable句柄表](http://blog.leanote.com/post/only_the_brave/861cd6f40678)
- [https://codemachine.com/articles/object_headers.html](https://codemachine.com/articles/object_headers.html)


## PspCidTable解析
PspCidTableはプロセスとスレッドのカーネルオブジェクト(EPROCESS/ETHREAD)を格納するカーネルハンドルテーブル
PIDとTIDによってインデックスが付けられる(プロセスIDとスレッドIDは必ず一意である)

PspCidTableはグローバル変数なのでその情報を確認する
![windbg_dq_ntoskrnl_pspcidtable_l1](https://i.imgur.com/XGxoa3F.jpg)

![ida_ntoskrnl_explookuphandletableentry](https://i.imgur.com/9tzD1gN.jpg)

![windbg_dt_ntoskrnl_handle_table](https://i.imgur.com/INNy68E.jpg)
TableCodeの下位2ビットはハンドルテーブルのレベルを記録する
0(00)は第1レベルのテーブルを意味し、1(01)は第2レベルのテーブル、2(10)は第3レベルのテーブルを表す
ここでの`0xffffc78e4bb88001`は、セカンダリテーブルであることを意味する

第1レベルのテーブルにはプロセスオブジェクトとスレッドオブジェクト(暗号化されており、復号化するには計算が必要)が格納され、第2レベルのテーブルには第1レベルのテーブルへのポインターが格納され、第3レベルのテーブルには第2レベルのテーブルへのポインターが格納される

x64システムでは、各テーブルのサイズは0x1000(4096)であり、第1レベルのテーブルには_handle_table_entry構造(size=16)が格納され、第2レベルと第3レベルのテーブルにはポインター(size=8)が格納される

![ida_explookuphandletableentry_level_1](https://i.imgur.com/n6QJmFp.jpg)

`0xffffc78e4bb88001 & 0xfffffffffffffff0 = 0xffffc78e4bb88000`、第2レベルのテーブルに50個の第1レベルのテーブルポインターがある
![windbg_dq_level_2](https://i.imgur.com/naJeZFt.jpg)

最初の第1レベルのテーブル
![windbg_dq_level_1](https://i.imgur.com/R7ZAp23.jpg)

例として、pid=4のプロセスを確認する
第1レベルのアドレスの探し方
```
第1レベルアドレス+<pid/tid>*0x4
```

第1レベルの値をデコードする
```
win10: (value >> 0x10) | 0xffff000000000000
```
![windbg_dq_level_1_decode](https://i.imgur.com/tBOPxTR.jpg)

デコードした値がオブジェクトのアドレス
![windbg_dt_ntoskrnl_eprocess_uniqueprocessid_imagefilename](https://i.imgur.com/V36uQjn.jpg)


### 参考
- [X64驱动 遍历 PSPCIDTABLE 枚举隐进程和线程](https://www.freesion.com/article/7384815990/)
- [解析PspCidTable句柄表](http://blog.leanote.com/post/only_the_brave/861cd6f40678)


## ObpKernelHandleTable解析
![windbg_u_ntoskrnl_obreferenceobjectbyhandle](https://i.imgur.com/fl9s7Bq.jpg)

`ObpReferenceObjectByHandleWithTag`を解析したところ、ObpKernelHandleTableが見つかった
![ida_ntoskrnl_obreferenceobjectbyhandlewithtag](https://i.imgur.com/tx2ew4u.jpg)

`nt!ObReferenceObjectByHandleWithTag`のベースアドレスが0xfffff8035f7f5b80
![windbg_u_ntoskrnl_obreferenceobjectbyhandlewithtag_l1](https://i.imgur.com/pZVHYn2.jpg)

`0xea3 - 0xb80 = 0x323`
![windbg_u_ntoskrnl_obpreferenceobjectbyhandlewithtag_323](https://i.imgur.com/6anJlS4.jpg)

ObpKernelHandleTableはグローバル変数なのでその情報を確認する
![windbg_dq_ntoskrnel_obpkernelhandletable_l1](https://i.imgur.com/nL17AGE.jpg)

解析手順はPsPCidTableと同じで、最後の8bitから第2レベルのテーブルであることがわかる
![windbg_ntoskrnl_handle_table_obpkernelhandletable](https://i.imgur.com/nwhzmBi.jpg)

第2レベルのテーブルを確認する
![windbg_dq_obpkernelhandletable_l_1](https://i.imgur.com/wDeo8XD.jpg)

Cid=4を調べる
`第1レベルテーブルのアドレス+0x4*<handle value>`
アドレスが難読化されているのでデコードする
`win10: (value >> 0x10) | 0xffff000000000000`

PspCidTableから得られたデコードしたアドレスはオブジェクトへのアドレスだったが、
ObpKernelHandleTableから得られたデコードしたアドレスは`_OBJECT_HEADER`へのアドレス
なので、_OBJECT_HEADERから_OBJECT_TYPEへの計算をする
```c=
value = TypeIndex ^ (アドレスの最後から2番目のバイト) ^ ObHeaderCookieのバイト
poi(nt!ObjectTypeIndexTable+value*@$ptrsize)
```

`dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable+0x7*@$ptrsize) Name`の結果からオブジェクトがProcessだということがわかるのでオブジェクト自体は`_EPROCESS`の構造体である

![windbg_sabotta](https://i.imgur.com/pwyyCeo.jpg)

### 参考
[解析ObpKernelHandleTable内核句柄表](http://blog.leanote.com/post/only_the_brave/%E8%A7%A3%E6%9E%90ObpKernelHandleTable%E5%8F%A5%E6%9F%84%E8%A1%A8)


## EPROCESSのObjectTypeから_OBJECT_TYPEを見つける
### 目標
![processhacker_notepad_pid_2184](https://i.imgur.com/NVn1JQi.jpg)
notepad.exeのスレッドハンドルを確認する

### 解析
まずはnotepad.exeの_EPROCESSを見つける
![windbg_process_notepad.exe_eprocess](https://i.imgur.com/t8WO27z.jpg)

_EPROCESSのObjectTypeの値を確認
![windbg_dt_ntoskrnl_eprocess_objecttable](https://i.imgur.com/oXzbVsX.jpg)

_HANDLE_TABLEの情報を確認
![windbg_dt_ntoskrnl_handle_table](https://i.imgur.com/GdrTCdB.jpg)
TableCodeの値から第1レベルのテーブルの値だということがわかる

次のように、最初のテーブルの内容を確認します。このテーブルの各メンバーは_HANDLE_TABLE_ENTRY構造体です（サイズは0x10）。
テーブルの内容を確認
このテーブルの各メンバーは_HANDLE_TABLE_ENTRY構造体で登録されている
![windbg_sizeof_ntoskrnl_handle_table_entry_dq_level_1](https://i.imgur.com/s5vzaIC.jpg)

ObjectPointerBitsメンバーは_OBJECT_HEADER構造体を指す
![windbg_dt_ntoskrnl_handle_table_entry](https://i.imgur.com/OIprnlS.jpg)

_OBJECT_HEADERのアドレスを計算する
`(_HANDLE_TABLE_ENTRY.ObjectPointerBits << 0x4) | 0xffff000000000000`

![windbg_object_header_calc](https://i.imgur.com/H3Y1Ssg.jpg)

![windbg_ntoskrnl_object_header](https://i.imgur.com/VhCTAZ6.jpg)

TypeIndexを使って_OBJECT_TYPEが何であるかを確認する
![windbg_object_type_calc](https://i.imgur.com/kmMjVR2.jpg)

![windbg_object_type_thread](https://i.imgur.com/4Kxo4SP.jpg)

### 参考
- [分析进程句柄表](http://blog.leanote.com/post/only_the_brave/%E5%88%86%E6%9E%90%E8%BF%9B%E7%A8%8B%E5%8F%A5%E6%9F%84%E8%A1%A8)
- [Windows Process Internals: A few Concepts to know before jumping on Memory Forensics [Part 5] – A Journey in to the Undocumented Process Handle Structures (_handle_table & _handle_table_entry) | By Kirtar Oza](https://eforensicsmag.com/windows-process-internals-a-few-concepts-to-know-before-jumping-on-memory-forensics-part-5-a-journey-in-to-the-undocumented-process-handle-structures-_handle_table-_handle_table_entry/)


## SSDTの解析
SSDTはSystem Service Descriptor Tableの略
32bitの場合はカーネルルーチンへの相対アドレス、64bitの場合は同じルーチンへの相対アドレス

SSDTの構造体
```c++=
typedef struct tagSERVICE_DESCRIPTOR_TABLE {
    SYSTEM_SERVICE_TABLE nt; //effectively a pointer to Service Dispatch Table (SSDT) itself
    SYSTEM_SERVICE_TABLE win32k;
    SYSTEM_SERVICE_TABLE sst3; //pointer to a memory address that contains how many routines are defined in the table
    SYSTEM_SERVICE_TABLE sst4;
} SERVICE_DESCRIPTOR_TABLE;

typedef struct SystemServiceTable {
    UINT32* ServiceTable;
    UINT32* CounterTable;
    UINT32 ServiceLimit;
    UINT32* ArgumentTable;
} SST;
```
※どっちが正しいかわからないから両方書いた

64bitの場合Ring3からsyscallを行う時は必ず、下記のようなスタブが存在する
```asm=
4C 8B D1            mov r10, rcx
B8 ?? 00 00 00      mov eax, {Syscall Number}
0F 05               syscall
C3                  retn
```

下記にNtCreateFileの例を示す
![windbg_u_ntdll_ntcreatefile_l6](https://i.imgur.com/ScY0q7I.jpg)

`KeServiceDescriptorTable`でSERVICE_DESCRIPTOR_TABLE(SST)の構造体を確認することができる
![windbg_dq_ntoskrnl_keservicedescriptortable](https://i.imgur.com/egZNvZ4.jpg)
![windbg_dps_ntoskrnl_keservicedescriptortalbe_l4](https://i.imgur.com/9NPhls8.jpg)
最初のメンバーがSSDTへのポインターになる

SSDTに登録されている先頭2つを出力する
![](https://i.imgur.com/rJBTteo.jpg)

64bitでは、SSDTにカーネルルーチンの相対アドレスが格納されている
特定のオフセットから絶対アドレスを取得する方法
```=
絶対アドレス = (オフセット >> 0x4) + SSDTのベースアドレス
```

先ほどntdll!NtCreateFileの例を示したので今回はnt!NtCreateFileのアドレスを調べる
ntdll!NtCreateFileのsyscall番号は0x55である
オフセットが格納されている位置の求め方は
```=
nt!KiServiceTable+0x4*<syscall number>
```
![windbg_ntoskrnl_kiservicetable_syscall_0x55](https://i.imgur.com/8qE2chX.jpg)

オフセットがわかったので計算をするとnt!NtCreateFileのアドレスがわかる
![windbg_ntoskrnl_kiservicetable_offset_rsh_0x4](https://i.imgur.com/mGjId6S.jpg)

### 参考
- [System calls on Windows x64](https://www.n4r1b.com/posts/2019/03/system-calls-on-windows-x64/)
- [win 64 SSDT HOOK](https://www.cnblogs.com/freesec/p/7617752.html)
- [System Service Descriptor Table - SSDT](https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/glimpse-into-ssdt-in-windows-x64-kernel)


## IDTの解析
![windbg_loaded_module_reload_i8042prt_loaded_module](https://i.imgur.com/XLeqD4S.jpg)

IDTはInterrupt Descriptor Tableの略
割り込みはOSによって使用され、ハードウェアデバイスからのイベントを通知で受ける
割り込みディスパッチはCPUが実行制御をソフトウェアに渡して、ハードウェアのイベントを処理するメカニズム
割り込みは割り込みサービスルーチン(Intrrupt Service Routine/ISR)を登録しているハードウェアドライバーによって実行制御を移す前に(システム運用で)Windowsカーネルによってディスパッチされている
割り込み記述子テーブル(IDT)は割り込みのディスパッチに関係する主要なデータ構造があり、その形式はCPUベンダーによって指定されている
IDTは起動時にOSによって設定される必要があり、その後ハードウェアデバイスから到達する割り込みをディスパッチするためにCPUによって使用される

CPUにはIDTRレジスタと呼ばれる組み込みレジスタがあり、WindowsはIDTのカーネル仮想アドレスを登録する
IDTはシステムの起動時にCPUごとに登録される
![windbg_r_idtr](https://i.imgur.com/VkbPwjA.jpg)

IDTには**256**(0x100)のエントリーが含まれていて、そのうちいくつかは例外に、一部はソフトウェア割り込みに使われ、残りはハードウェア割り込みに使用される
特定のエントリを選択するIDTへのインデックスは、**割り込みベクタ**と呼ばれる
![codemachine_intrrupt_descriptor_table_structure](https://codemachine.com/articles/figures/figure_interruptdispatching_2.png)

Windowsカーネルは64bitCPU上のIDTの個々のエントリーを表す_KIDTENTRY64の構造体を持っている
![windbg_dt_ntoskrnl_kidtentry64_idtr](https://i.imgur.com/FEouP9p.jpg)
OffsetHighとOffsetMiddle、OffsetLowと組み合わせることで割り込みが発生したときにCPUが制御を転送する仮想アドレスがわかる

![windbg_kidtentry64_offsethigh_middle_low](https://i.imgur.com/ZLW3kx4.jpg)
この結果は`!idt 0`の出力と同じ
![](https://i.imgur.com/f487Du8.jpg)

IDTの19(0x13)のエントリーは例外をディスパッチするためのもので、CPUベンダーによって定義されている
IDTの残りのエントリは、ソフトウェア割り込みまたはハードウェア割り込みに使用されるか、未使用のままになります
IDTの残りのエントリーはソフトウェア割り込みかハードウェア割り込みに使用されるか、未使用
`!idt`でハードウェア割り込みは、KINTERRUPT構造体へのポインターの存在によって簡単に識別できる

![windbg_idt_](https://i.imgur.com/EMAhNG0.jpg)

### キーボード割り込みのISR
先ほどが画像からキーボードの割り込みのベクタは0x80だということがわかる
![windbg_idt_keyboard_interrupt](https://i.imgur.com/jOqScMt.jpg)

キーボード割り込み(0x80)の_KIDTENTRY64を確認する
![windbg_ntoskrnl_kidtentry64_keyboard_interrupt](https://i.imgur.com/nEir1GZ.jpg)

ISRのアドレスを計算する
![windbg_keyboard_interrupt_service_routine_calc](https://i.imgur.com/LDjzJfu.jpg)
先ほど!idtでの出力結果のキーボード割り込みのアドレスと同じ

以下に割り込みが発生して、CPUが実行する(ISRエントリポイント)での命令を示す
- ジャンプ前に`0xFFFFFFFFFFFFFF80`をスタックにプッシュする
- nt!KxIsrLinkageShadow(0xfffff8041a413b00)にジャンプする

![](https://i.imgur.com/TxyczzO.jpg)

最終的に`i8042prt!I8042KeyboardInterruptService`へヒットしていることを確認する
`0xfffff803288136c0`でブレークポイントがヒットし、`i8042prt!I8042KeyboardInterruptService`でヒットしていることがわかる
![](https://i.imgur.com/vTbuSQU.jpg)

### _KINTERRUPT
`_KINTERRUPT`の主要なメンバーの1つに`ServiceRoutine`がある
`ServiceRoutine`はキーボードドライバーの`i8042prt!I8042KeyboardInterruptService`を指していることがわかる
![](https://i.imgur.com/x2cV9eN.jpg)

_KINTERRUPT構造体は割り込みディスパッチするために重要であり、ドライバーによって登録されたISRを呼び出すために必要な情報が含まれている
_KPCR構造体は特定のプロセッサの情報を含まれている

KiIsrLinkage関数は`KPCR.CurrentPrcb.InterruptObject[]`に格納されている_KINTERRUPT構造体を検索している
ntoskrnlのKiGetInterruptObjectAddress関数は以下に示すように_KINTERRUPTオブジェクトへのポインターを取得する
![](https://i.imgur.com/0I67bLz.jpg)

`!idt`の結果と手動で解析した_KINTERRUPTのアドレスが同じことがわかる
![](https://i.imgur.com/xriyZyS.jpg)

![](https://codemachine.com/articles/figures/figure_interruptdispatching_3.png)

### 参照
- [Interrupt Dispatching Internals](https://codemachine.com/articles/interrupt_dispatching.html)
- [Interrupt Descriptor Table - IDT](https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/interrupt-descriptor-table-idt)