# интёрналсы окон ебучих
Здесь будут собраны мои мучения по WinDBG и всему такому, удачных бессонных ночей в ядре : )
# Как там эти дебаг символы то загружать
### Есть несколько вариантов:
1. переменная среды
2. ручной релоад символов
3. мой странный, но в целом убодный способ с папкой юзера
1) Сетаем переменную среды **_NT_SYMBOL_PATH** в путь с отладочными символами для windbg
Примеры что-то вроде такого:
`SRV*C:\ms_symbols*https://msdl.microsoft.com/download/symbols;c:\symbols`
*** кэшироваться будут в `C:\ProgramData\dbg\sym\`
*** если что-то не работает - **!sym noisy** будет выводиться больше инфы
2) 1. папка **ms_symbols** - выкачиваем сюда все pdb microsoft'a
`.symfix C:\ms_symbols`
`.reload /f`
2. папка **symbols** - наши кастомные pdb'шки
`.sympath+ C:\symbols`
`.reload`
3) Мой странный, но рабочий (остальные постоянно ломаются) способ
Windbg на хосте ищет папку с дебаг символами, но обычно компилим мы на самой вартуалке, поэтому можем просто создать такую же папку на хосте, типо `C:\Users\user\Source\Repos\driver1\x64\Release`
И спокойно положить в неё наш `driver1.pdb`
При обновлении и перекомпиляции всё, что нам необходимо, так это просто закинуть новый файл с символами
профит
# Ищем EPROCESS и ETHREAD, отнимаем байтики
Каждый процесс представлен структурой **EPROCESS** (executive process block) в ядре
**EPROCESS** указывает на число связанных структур, например: у каждого процесса есть 1 или более потоков, которые представляются структурой **ETHREAD**
**EPROCESS** указывает на **PEB** (process environment block) в адресном пространстве процесса
**ETHREAD** указывает на **TEB** (thread environment block) в адресном пространстве процесса

## Что есть в PEB и TEB
### **PEB** - Process Environment Block
- базовая информация об образе (базовый адрес, значение версии, список модулей)
- информации о куче в процессе
- переменные среды
- параметры командной строки
- путь для поиска DLL
#### Чтобы отобразить:
* `!peb`
* `dt nt!_PEB`
#### Чтобы посмотреть для чужого процесса:
`kd> .process /p ffffe20fb340e080; !peb 10f90d5000`
предварительно получив список процессов с адресами: `!process 0 0`
### **TEB** - Thread Environment Block
- информация о стеке (база стека, лимит стека)
- TLS (thread local storage) массив
#### Чтобы отобразить:
* `!teb`
* `dt nt!_TEB`
#### Посмотреть потоки другого процесса:
`kd> !process 0 4 processname.exe`
`kd> dt nt!_KTHREAD ffffe20faeb39080`
## Получаем руками все процессы:
Общая идея примерно такая:
1. Из **PsActiveProcessHead** получаем адрес, где лежит head списка процессов
2. Само значение, которое мы получили, уже является ссылкой на следующий объект списка (то есть FLINK - forward link)
3. Адрес, который мы получили - это структура LIST_ENTRY, в которой два поля: на след объект и на предыдущий, сам по себе этот полученный адрес лежит в середине структуры процесса в ActiveProcessLinks
4. Получаем начало структуры процесса отнимая от полученного адреса сдвиг до начала
```
kd> x nt!PsActiveProcessHead
fffff803`5f01df60 nt!PsActiveProcessHead = <no type information>
```
```
kd> dq fffff803`5f01df60
fffff803`5f01df60 ffffca86`79a5b488 ffffca86`806b5788
fffff803`5f01df70 00000000`000001f4 00000000`00000000
fffff803`5f01df80 00000000`00000000 00000000`00000000
fffff803`5f01df90 fffff803`5ed07ea0 00000000`00000000
```
```
kd> dt nt!_EPROCESS
...
+0x448 ActiveProcessLinks : _LIST_ENTRY
...
```
```
kd> ?ffffca86`79a5b488 - 0x448
Evaluate expression: -58796061380544 = ffffca86`79a5b040
```
```
kd> dt nt!_EPROCESS ffffca86`79a5b040
(как прувнуть, что всё ок)
...
+0x5a8 ImageFileName : [15] "System"
...
```
#### Как получить следующие в списке процессы?
* Берём значение из **ActiveProcessLinks**
* первый адрес - **flink**, второй - **blink**
* отнимаем оффсет
* дампим структуру
#### Автоматизация:
```dt nt!_EPROCESS -l ActiveProcessLinks.Flink ffffca86`79a5b040```
## Получаем руками все потоки процесса:
```
kd> !process 0 0
...
PROCESS ffffca8680630080
SessionId: 1 Cid: 19cc Peb: 002ff000 ParentCid: 0abc
DirBase: 135b3000 ObjectTable: ffffb60d17072980 HandleCount: 57.
Image: threads.exe
...
```
```
kd> dt nt!_EPROCESS ffffca8680630080
...
+0x5e0 ThreadListHead : _LIST_ENTRY [ 0xffffca86`818bc9e8 - 0xffffca86`7bf5e9e8 ]
...
```
```
kd> dt nt!_ETHREAD
...
+0x4e8 ThreadListEntry : _LIST_ENTRY
...
```
```
kd> ?0xffffca86`818bc9e8 - 0x4e8
Evaluate expression: -58795928861440 = ffffca86`818bc500
```
```
kd> dt nt!_ETHREAD -l ThreadListEntry.Flink -y Thread ffffca86`818bc500
...
ThreadListEntry.Flink at 0xffffca86`818bc500
+0x4e8 ThreadListEntry : _LIST_ENTRY [ 0xffffca86`851e59e8 - 0xffffca86`80630660 ]
...
ThreadListEntry.Flink at 0xffffca86`851e5500
+0x4e8 ThreadListEntry : _LIST_ENTRY [ 0xffffca86`7e37c9e8 - 0xffffca86`818bc9e8 ]
...
ThreadListEntry.Flink at 0xffffca86`7e37c500
+0x4e8 ThreadListEntry : _LIST_ENTRY [ 0xffffca86`7bf5e9e8 - 0xffffca86`851e59e8 ]
...
ThreadListEntry.Flink at 0xffffca86`7bf5e500
+0x4e8 ThreadListEntry : _LIST_ENTRY [ 0xffffca86`80630660 - 0xffffca86`7e37c9e8 ]
...
```
#### напочитать:
Английская статья, откуда брал почти весь материал и пара ссылок на структуры
* [Understanding LIST_ENTRY Lists and Its Importance in Operating Systems - Meena Chockalingam](https://www.codeproject.com/Articles/800404/Understanding-LIST-ENTRY-Lists-and-Its-Importance)
* [Ядерные и не только структуры](http://terminus.rewolf.pl/terminus/structures/ntdll/_EPROCESS_x64.html)
* [Чего там с этими LIST_ENTRY то](https://blog.fearcat.in/a?ID=01550-e4fe17fe-3059-472f-97a7-7e77c7b72302)
# Полазаем по стеку и куче в ядре
Структура стека:

Что мы можем прочитать на msdn:
* Each new thread receives its own stack space, consisting of both **committed** and **reserved** memory
* By default, each thread uses **1 Mb of reserved memory**, and **one page of committed memory**
* The system will commit one page block from the reserved stack memory as needed. (see MSDN CreateThread > dwStackSize > "Thread Stack Size")
### Посчитаем руками, сколько у нас commited и reserved memory
```
kd> !teb
TEB at 000000000033f000
...
StackBase: 0000000000700000
StackLimit: 00000000006fc000
...
```
```
kd> dt nt!_TEB DeallocationStack 33f000
+0x1478 DeallocationStack : 0x00000000`00500000 Void
```
```
StackBase - StackLimit = commited memory
kd> ?700000 - 6fc000
Evaluate expression: 16384 = 00000000`00004000
0x1000 - одна страница памяти
0x1000 = 4096 = 4Кб -> 0x4000 - 4 страницы памяти или 16Кб
StackBase - DeallocationStack = reserved memory
kd> ?700000 - 500000
Evaluate expression: 2097152 = 00000000`00200000
0x1000 - одна страница памяти
0x1000 = 4096 = 4Кб -> 0x200000 / 0x1000 = 512 страниц памяти или 2Мб
```
## Как растёт стек

* The **ESP** register points to the current stack location of a thread
* If a program attempts to access an address within a guard page, the system raises a **STATUS_GUARD_PAGE_VIOLATION** (0x80000001) exception. A guard page provides a one-shot alarm for memory page access
* If a stack grows until the end of reserved memory, a **STATUS_STACK_OVERFLOW** is raised
## Заполним стек
```
kd> !teb
...
StackBase: 0000000000700000
StackLimit: 00000000006fc000
...
kd> dt nt!_TEB -y DeallocationStack 000000000033f000
+0x1478 DeallocationStack : 0x00000000`00500000 Void
kd> ?0000000000700000 - 00000000006fc000
Evaluate expression: 16384 = 00000000`00004000
//TODO прописать заполнение адреса
```
## А что там с кучей то?
#### If page heap is disabled (а он по дефолту выключен), apply the following structs:
* **_HEAP struct**
* defined в ntdll.dll: dt nt!_HEAP
* for every HeapCreate there is a unique _HEAP
* !heap -p -all to get addresses for all _HEAP structs in process
* **_HEAP_ENTRY struct**
* defined in ntdll: dt nt!_HEAP_ENTRY
* for every HeapAlloc there is a unique _HEAP_ENTRY
* !heap -p -all to get addresses for all heap entries in process
---
#### If page heap is enabled, apply the following structs:
* **_DPH_HEAP_ROOT struct**
* defined в ntdll.dll: dt nt!_DPH_HEAP_ROOT
* for every HeapCreate there is a unique _DPH_HEAP_ROOT
* !heap -p -all to get addresses for all heap roots in process
- Usually address of a _DPH_HEAP_ROOT = value of HeapHandle + 0x1000
* **_DPH_HEAP_BLOCK struct**
* defined in ntdll: dt nt!_DPH_HEAP_BLOCK
* for every HeapAlloc there is a unique _DPH_HEAP_BLOCK
* !heap -p -all to get addresses for all heap blocks in process
## Кто вызвал HeapAlloc?
Включаем stack traces и page heap для процесса:
* Либо в gui gflags.exe тыкаем **Create user mode stack trace database** и **Enable page heap**
* gflags.exe /i <IMAGE.EXE> +ust +hpa
Меняем контекст процесса:
```
kd> .process /i ffffe78c24835240
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!DbgBreakPointWithStatus:
fffff803`7d9ff050 cc int 3
```
```
Идем до места, где вызываем HeapAlloc и получаем адрес возврата, который является DPH_HEAP_BLOCK
kd> !heap -p -a 282bff41000
address 00000282bff41000 found in
_DPH_HEAP_ROOT @ 282bfd11000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
282bfd17478: 282bff41000 2000 - 282bff40000 4000
ReadMemory error for address 00000282bff41000
00007ff97ca6867b ntdll!RtlDebugAllocateHeap+0x000000000000003b
00007ff97c99d255 ntdll!RtlpAllocateHeap+0x00000000000000f5
00007ff97c99b44d ntdll!RtlpAllocateHeapInternal+0x0000000000000a2d
00007ff72cfa107a +0x00007ff72cfa107a
00007ff72cfa12e0 +0x00007ff72cfa12e0
00007ff97bcd7034 +0x00007ff97bcd7034
00007ff97c9c2651 ntdll!RtlUserThreadStart+0x0000000000000021
```
```
kd> dt ntdll!_DPH_HEAP_BLOCK StackTrace 282bfd17478
+0x060 StackTrace : 0x00000282`be539400 _RTL_TRACE_BLOCK
```
```
посмотрим на stack trace
kd> dq /c1 0x00000282`be539400 L10
00000282`be539400 00000000`00000000
00000282`be539408 00070000`00003801 0033:00007ff9`7ca68675 call qword ptr [ntdll!_guard_dispatch_icall_fptr (00007ff9`7caf3000)]
00000282`be539410 00007ff9`7ca6867b ----> 0033:00007ff9`7ca6867b mov rbx,qword ptr [rsp+70h]
0033:00007ff9`7c99d250 call ntdll!RtlDebugAllocateHeap (00007ff9`7ca68640)
00000282`be539418 00007ff9`7c99d255 ----> 0033:00007ff9`7c99d255 jmp ntdll!RtlpAllocateHeap+0xc6 (00007ff9`7c99d226)
0033:00007ff9`7c99b448 call ntdll!RtlpAllocateHeap (00007ff9`7c99d160)
00000282`be539420 00007ff9`7c99b44d ----> 0033:00007ff9`7c99b44d mov rdi,rax
непосредственный вызов функи HeapAlloc из моего кода
0033:00007ff7`2cfa1074 call qword ptr [00007ff7`2cfa2008] (HeapAlloc)
00000282`be539428 00007ff7`2cfa107a ----> 0033:00007ff7`2cfa107a call qword ptr [00007ff7`2cfa2010] ds:002b:00007ff7`2cfa2010=00007ff97bcd5bb0 (GetProcessHeap)
0033:00007ff7`2cfa12db call 00007ff7`2cfa1000 (main из crt start)
00000282`be539430 00007ff7`2cfa12e0 ----> 0033:00007ff7`2cfa12e0 mov ebx,eax
00000282`be539438 00007ff9`7bcd7034
ntdll!RtlUserThreadStart:
0033:00007ff9`7c9c2630 sub rsp,78h
0033:00007ff9`7c9c2634 mov r9,rcx
0033:00007ff9`7c9c2637 mov rax,qword ptr [ntdll!Kernel32ThreadInitThunkFunction (00007ff9`7cad9ff0)]
0033:00007ff9`7c9c263e test rax,rax
0033:00007ff9`7c9c2641 je ntdll!RtlUserThreadStart+0x23 (00007ff9`7c9c2653)
0033:00007ff9`7c9c2643 mov r8,rdx
0033:00007ff9`7c9c2646 mov rdx,rcx
0033:00007ff9`7c9c2649 xor ecx,ecx
0033:00007ff9`7c9c264b call qword ptr [ntdll!_guard_dispatch_icall_fptr (00007ff9`7caf3000)]
00000282`be539440 00007ff9`7c9c2651 -----> 0033:00007ff9`7c9c2651 jmp ntdll!RtlUserThreadStart+0x43 (00007ff9`7c9c2673)
00000282`be539448 00000000`00000000
00000282`be539450 00000000`00000000
00000282`be539458 00000000`00000000
00000282`be539460 00000000`00000000
00000282`be539468 00000000`00000000
00000282`be539470 00000000`00000000
00000282`be539478 00000000`00000000
```
## Ищем утечки памяти на хипе
Summary about memory usage for your process.
If **RegionUsageHeap** or **RegionUsagePageHeap** is growing constantly, then you might have a memory leak on the heap. Proceed with the following steps.
`!address --summary`
Сделаем фиктивную утечку памяти и посмотрим на неё, можно что-то в этом духе:

```
До аллокации
kd> !heap -stat -h 0
Allocations statistics for
heap @ 0000021a6eb40000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
1000 3 - 3000 (27.66)
1200 1 - 1200 (10.37)
c38 1 - c38 (7.04)
120 9 - a20 (5.83)
400 2 - 800 (4.61)
200 4 - 800 (4.61)
100 8 - 800 (4.61)
790 1 - 790 (4.36)
6de 1 - 6de (3.96)
1d8 3 - 588 (3.19)
470 1 - 470 (2.56)
228 2 - 450 (2.48)
390 1 - 390 (2.05)
50 b - 370 (1.98)
348 1 - 348 (1.89)
238 1 - 238 (1.28)
10 1d - 1d0 (1.04)
20 c - 180 (0.86)
168 1 - 168 (0.81)
158 1 - 158 (0.77)
Allocations statistics for
heap @ 0000021a6e8e0000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
```
```
После аллокации
kd> !heap -stat -h 0
Allocations statistics for
heap @ 0000021a6eb40000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
2625a00 1 - 2625a00 (99.89)
1000 3 - 3000 (0.03)
1200 1 - 1200 (0.01)
c38 1 - c38 (0.01)
120 9 - a20 (0.01)
400 2 - 800 (0.01)
200 4 - 800 (0.01)
100 8 - 800 (0.01)
790 1 - 790 (0.00)
6de 1 - 6de (0.00)
1d8 3 - 588 (0.00)
470 1 - 470 (0.00)
228 2 - 450 (0.00)
390 1 - 390 (0.00)
50 b - 370 (0.00)
348 1 - 348 (0.00)
238 1 - 238 (0.00)
10 20 - 200 (0.00)
20 c - 180 (0.00)
168 1 - 168 (0.00)
Allocations statistics for
heap @ 0000021a6e8e0000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
```
```
Найдём все аллокации с нашим размером 2625a00
kd> !heap -flt s 2625a00
_DPH_HEAP_ROOT @ 297e9ed1000
Freed and decommitted blocks
DPH_HEAP_BLOCK : VirtAddr VirtSize
Busy allocations
DPH_HEAP_BLOCK : UserAddr UserSize - VirtAddr VirtSize
00000297e9edb068 : 00000297ec170600 0000000002625a00 - 00000297ec170000 0000000002627000
_HEAP @ 297eaf60000
_DPH_HEAP_ROOT @ 297eb061000
Freed and decommitted blocks
DPH_HEAP_BLOCK : VirtAddr VirtSize
Busy allocations
DPH_HEAP_BLOCK : UserAddr UserSize - VirtAddr VirtSize
_HEAP @ 297ec160000
```
```
kd> !heap -p -a 00000297ec170600
address 00000297ec170600 found in
_DPH_HEAP_ROOT @ 297e9ed1000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
297e9edb068: 297ec170600 2625a00 - 297ec170000 2627000
00007ff97ca6867b ntdll!RtlDebugAllocateHeap+0x000000000000003b
00007ff97c99d255 ntdll!RtlpAllocateHeap+0x00000000000000f5
00007ff97c99b44d ntdll!RtlpAllocateHeapInternal+0x0000000000000a2d
00007ff97a8cfde6 ucrtbase!_malloc_base+0x0000000000000036
00007ff724531717 threads+0x0000000000001717
00007ff724531058 threads+0x0000000000001058
00007ff7245314f8 threads+0x00000000000014f8
00007ff97bcd7034 KERNEL32!BaseThreadInitThunk+0x0000000000000014
00007ff97c9c2651 ntdll!RtlUserThreadStart+0x0000000000000021
```
## Critical Sections
```
kd> !cs
...
-----------------------------------------
DebugInfo = 0x00000297e9f95fd0
Critical section = 0x00007ff97a52d000 (KERNELBASE!ConsoleStateLock+0x0)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0000000000000000
-----------------------------------------
...
```
```
kd> !cs -s -o 0x00007ff97a52d000
-----------------------------------------
Critical section = 0x00007ff97a52d000 (KERNELBASE!ConsoleStateLock+0x0)
DebugInfo = 0x00000297e9f95fd0
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0000000000000000
Stack trace for DebugInfo = 0x00000297e9f95fd0:
0x00007ff97a2b0cee: KERNELBASE!_KernelBaseBaseDllInitialize+0x44E
0x00007ff97a2b071d: KERNELBASE!KernelBaseDllInitialize+0xD
0x00007ff97c989a1d: ntdll!LdrpCallInitRoutine+0x61
0x00007ff97c9dc1e7: ntdll!LdrpInitializeNode+0x1D3
0x00007ff97c9dbf7a: ntdll!LdrpInitializeGraphRecurse+0x42
0x00007ff97c9dc000: ntdll!LdrpInitializeGraphRecurse+0xC8
0x00007ff97c9ad937: ntdll!LdrpPrepareModuleForExecution+0xBF
0x00007ff97c98fbae: ntdll!LdrpLoadDllInternal+0x19A
0x00007ff97c9873e4: ntdll!LdrpLoadDll+0xA8
0x00007ff97c986af4: ntdll!LdrLoadDll+0xE4
0x00007ff97ca4372f: ntdll!LdrpInitializeProcess+0x1ACF
0x00007ff97c9e4cdb: ntdll!LdrpInitialize+0x15F
0x00007ff97c9e4b63: ntdll!LdrpInitialize+0x3B
0x00007ff97c9e4b0e: ntdll!LdrInitializeThunk+0xE
```
#### напочитать:
Классная преза, с кучей полезного по windbg
* [WinDbg. From A to Z! - Robert Kuster](http://windbg.info/download/doc/pdf/WinDbg_A_to_Z_color.pdf)
# Pseudo-Registers and Expressions in WinDbg
* Virtual registers provided by the debugger
* Begin with a dollar sign ($)
1. **Automatic pseudo-registers**
* are set by the debugger to certain useful values
* examples: $ra, $peb, $teb, ..
2. **User-defined pseudo-registers**
* there are twenty user-defined registers: $t0, $t1, $t2, .., $t19
* integer variables that can be used to store intermediate data
* can additionally hold type-information
* r? assigns a typed result to an lvalue
* r? $t0 = @peb->ProcessParameter
- Assigns a typed value to $t0
- $t0’s type is remembered so it can be used in further expressions
* ?? @$t0->CommandLine
## Automatic Pseudo-Registers
| Команда | Пояснение к ней |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| $ra | Return address currently on the stack. <br /> Useful in execution commands, i.e.: “g $ra” |
| $ip | The instruction pointer <br /> x86 = EIP, Itanium = IIP, x64 = RIP |
| $exentry | Entry point of the first executable of the current process |
| $retreg | Primary return value register <br /> x86 = EAX, Itanium = ret0, x64 = rax|
| $csp | Call stack pointer <br /> X86 = ESP, Itanium = BSP, x64 = RSP |
| $peb | Address of the process environment block (PEB) |
| $teb | Address of the thread environment block (TEB) of current thread |
| $tpid | Process ID (PID) |
| $tid | Thread ID (tID) |
| $ptrsize | Size of a pointer |
| $pagesize | Number of bytes in one page of memory |
## Expressions
1. **MASM expressions**
- evaluated by the ? command
- each symbol is treated as an addresses (the numerical value of a symbol is the memory address of that symbol to get its value you must dereference it with poi)
- source line expressions can be used (`myfile.c:43`)
- the at sign for register values is optional (eax or @eax are both fine)
- used in almost all examples in WinDbg’s help
- the only expression syntax used prior to WinDbg version 4.0 of Debugging Tools
- The numerical value of any symbol is its memory address
- Any operator can be used with any number
- Numerals: are interpreted according to the current radix: n [8 | 10 | 16]
Can be overridden by a prefix: 0x (hex), 0n (decimal), 0t (octal), 0y (binary)
2. **C++ expressions**
- evaluated by the ?? command
- symbols are understood as appropriate data types
- source line expressions cannot be used
- the at sign for register values is required (eax will not work)
- The numerical value of a variable is its actual value
- Operators can be used only with corresponding data types
- A symbol that does not correspond to a C++ data type will result in a syntax error
- Data structures are treated as actual structures and must be used accordingly. They do not have numerical values.
- The value of a function name or any other entry point is the memory address, treated as a function pointer
- Numerals: the default is always decimal
Can be overridden by a prefix: 0x (hex), 0 (=zero- octal)
MASM operations are always byte based. C++ operations follow C++ type rules (including the scaling of
pointer arithmetic). In both cases numerals are treated internally as ULON64 values.
```
kd> ?? ((ntdll!_TEB*) 0x4c17960000)->ClientId
+0x000 UniqueProcess : 0x00000000`000001ac Void
+0x008 UniqueThread : 0x00000000`00001758 Void
```
```
kd> ?? @$teb->ClientId
struct _CLIENT_ID
+0x000 UniqueProcess : 0x00000000`000001ac Void
+0x008 UniqueThread : 0x00000000`00001758 Void
```
```
kd> .process /p ffff8a06a72452c0
kd> r? $t0 = @$peb->ProcessParameters
kd> ?? @$t0->CommandLine
struct _UNICODE_STRING
""C:\Windows\system32\cmd.exe" "
+0x000 Length : 0x3c
+0x002 MaximumLength : 0x3e
+0x008 Buffer : 0x000001ab`8c572280 ""C:\Windows\system32\cmd.exe" "
```
## GFlags
* **GFlags** enables and disables features by editing the Windows registry
* **GFlags** can set **system-wide** or **image-specific** settings
* Image specific settings are stored in:
* * HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\\**Image File Execution Options**\\**ImageFileName**\GlobalFlag
* The OS reads these settings and adopts its functionality accordingly
* GFlags can be run from the command line or by using a dialog box
* We can also use !gflags in WinDbg to set or display the global flags
* With GFlags we can enable:
* * heap checking
* * heap tagging
* * Loader snaps
* * Debugger for an Image (automatically attached each time an Image is started)
* * Application verifier:
* * * is a runtime verification tool for Windows applications
* * * is monitoring an application's interaction with the OS
* * * profiles and tracks:
* * * * Microsoft Win32 APIs (heap, handles, locks, threads, DLL load/unload, and more)
* * * * Exceptions
* * * * Kernel objects
* * * * Registry
* * * * File system
* * * with !avrf we get access to this tracking information
**Note**: Under the hood Application Verifier injects a number of DLLs (verifier.dll, vrfcore.dll, vfbasics.dll, vfcompat.dll, and more) into the target application. More precisely: It sets a registry key according to the selected tests for the image in question. The windows loader reads this registry key and loads the specified DLLs into the applications address space while starting it.
## Application Verifier Variants
**GFlags Application Verifier**:
* Only verifier.dll is injected into the target process
* verifier.dll is installed with Windows XP
* Offers a very limited subset of Application Verifier options
* Probably this option in GFlags is obsolete and will eventually be removed (?)
**Application Verifier**:
* Can freely be downloaded and installed from the MS website
* Additionally installs vrfcore.dll, vfbasics.dll, vfcompat.dll, and more into Windows\System32
* Enables much more test options and full functionality of the !avrf extension
#### напочитать:
По псевдо регистрам офиц ман
* [Pseudo-Register Syntax](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/pseudo-register-syntax)
Куча полезностей по командам
* [Common windbg commands](http://windbg.info/doc/1-common-cmds.html)
# немного о прерываниях, их обработке и есесна ядре
### Тут будет перевод одной статейки для погружения в теорию
[статейка](https://codemachine.com/articles/interrupt_dispatching.html)
## Interrupt Dispatching Internals
Microsoft изменили способ обработки прерываний в последних версиях Windows. Были опубликованы некоторые [публичные ресёрчи](http://phrack.org/issues/65/4.html) по обработке прерываний на старых версиях Windows и на 32-битных системах, однако не так много информации можно найти о том, как это работает в современном мире. В этой статье я попытаюсь привести описание обработки исключений на 64-битной Windows 10, в особенности Windows 10 RS1 Anniversary Update Build 10.0.10586
Прерывания используются операционными системами, чтобы получать сообщения об ивентах, происходящих на оборудовании. Обработка исключений - это механизм, в котором процессор передаёт контроль исполнения программному обеспечению, чтобы обработать событие на оборудовании. Прерывания обрабатываются ядром Windows, которое сначала выполняет некоторые служебные действия перед передачей контроля исполнения драйверам железа, которые в свою очередь регистрируют ISR (функции обработчика прерывания). IDT (Interrupt Descriptor Table) - это основная структура, задействованная в обработке исключений и её формат [устанавливает разработчик процессора](https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3c-part-3-manual.pdf). IDT должна быть заполнена на этапе загрузки и соответственно должна использоваться процессором для обработки прерываний, приходящих с устройств
### IDTR Register
У процессоров есть встроенный регистр, называемый IDTR, который Windows заполняет виртуальным адресом IDT в ядре, который он устанавливает для каждого процессора на этапе загрузки.

Значение регистра IDTR для каждого процессора. На мульти процессорной системе каждый процессор имеет свой IDTR регистр, который указывает на локальную приватную копию IDT
```
0: kd> ~0
0: kd> r @idtr
idtr=fffff8051ae62000
0: kd> ~1
1: kd> r @idtr
idtr=ffffb70107dad000
1: kd> ~2
2: kd> r @idtr
idtr=ffffb701077ea000
```
### Interrupt Descriptor Table
IDT содержит всего 256 значений, некоторые из которых используются для исключений, некоторые для программных прерываний, а остальные для прерываний железа. Индекс в IDT, по которому выбирают конкретный элемент, называется вектором прерывания. Формат каждого элемента IDT описывается разработчиком процессора.

Ядро Windows определяет структуру **KIDTENTRY64**, которая представляет собой один элемент IDT на 64-битном процессоре. Используя вывод предыдущей команды "r @idtr", мы можем вывести нулевой элемент IDT, на который указывает IDTR
```
0: kd> ~0
0: kd> r @idtr
idtr=fffff8051ae62000
0: kd> dt nt!_KIDTENTRY64 fffff8051ae62000
+0x000 OffsetLow : 0x1c00
+0x002 Selector : 0x10
+0x004 IstIndex : 0y000
+0x004 Reserved0 : 0y00000 (0)
+0x004 Type : 0y01110 (0xe)
+0x004 Dpl : 0y00
+0x004 Present : 0y1
+0x006 OffsetMiddle : 0x1800
+0x008 OffsetHigh : 0xfffff805
+0x00c Reserved1 : 0
+0x000 Alignment : 0x18008e00`00101c00
```
Комбинация **OffsetHigh**, **OffsetMiddle** и **OffsetLow** даёт нам виртуальный адрес, куда процессор передаст поток выполнения, когда произойдёт прерывание. В выводе выше виртуальный адрес - **0xfffff80518001c00**. Это совпадает с выводом "**!idt 0**" и указывает на фукнцию **KiDivideErrorFault()**. Значение поля Type в выводе выше (0xe) показывает, что поле в IDT представляет собой **Interrupt Gate**
```
0: kd> !idt 0
Dumping IDT: fffff8051ae62000
00: fffff80518001c00 nt!KiDivideErrorFault
```
Первые **N** элементов в IDT нужны для обработки исключений и определены разработчиком процессора. Остальные элементы или используются для программных прерываний, или для хардварных, или не используются вовсе. В выводе "!idt" хардварные прерывания очень просто определить: у них есть указатель на структуру **KINTERRUPT**. "!idt -a" показывает значения всей IDT
```
0: kd> !idt -a
Dumping IDT: fffff8051ae62000
00: fffff80518001c00 nt!KiDivideErrorFault
01: fffff80518001f40 nt!KiDebugTrapOrFault Stack = 0xFFFFF8051AEA0000
02: fffff80518002440 nt!KiNmiInterrupt Stack = 0xFFFFF8051AE92000
03: fffff80518002900 nt!KiBreakpointTrap
04: fffff80518002c40 nt!KiOverflowTrap
05: fffff80518002f80 nt!KiBoundFault
06: fffff805180034c0 nt!KiInvalidOpcodeFault
07: fffff805180039c0 nt!KiNpxNotAvailableFault
08: fffff80518003cc0 nt!KiDoubleFaultAbort Stack = 0xFFFFF8051AE8B000
09: fffff80518003fc0 nt!KiNpxSegmentOverrunAbort
0a: fffff805180042c0 nt!KiInvalidTssFault
0b: fffff805180045c0 nt!KiSegmentNotPresentFault
0c: fffff80518004980 nt!KiStackFault
0d: fffff80518004cc0 nt!KiGeneralProtectionFault
0e: fffff80518005000 nt!KiPageFault
0f: fffff80517ff99e8 nt!KiIsrThunk+0x78
10: fffff80518005640 nt!KiFloatingErrorFault
11: fffff80518005a00 nt!KiAlignmentFault
12: fffff80518005d40 nt!KiMcheckAbort Stack = 0xFFFFF8051AE99000
13: fffff80518006840 nt!KiXmmException
14: fffff80518006c00 nt!KiVirtualizationException
15: fffff80518007100 nt!KiControlProtectionFault
16: fffff80517ff9a20 nt!KiIsrThunk+0xB0
17: fffff80517ff9a28 nt!KiIsrThunk+0xB8
18: fffff80517ff9a30 nt!KiIsrThunk+0xC0
19: fffff80517ff9a38 nt!KiIsrThunk+0xC8
1a: fffff80517ff9a40 nt!KiIsrThunk+0xD0
1b: fffff80517ff9a48 nt!KiIsrThunk+0xD8
1c: fffff80517ff9a50 nt!KiIsrThunk+0xE0
1d: fffff80517ff9a58 nt!KiIsrThunk+0xE8
1e: fffff80517ff9a60 nt!KiIsrThunk+0xF0
1f: fffff80517ffb220 nt!KiApcInterrupt
20: fffff80517ffce00 nt!KiSwInterrupt
```
В этой статье мы сфокусируемся на хардварных прерываниях, соответственно последние элементы IDT. hex значение в первой колонке - это вектор или индекс прерывания, по которому и находится конкретное прерывание в IDT. Как было сказано ранее, каждый элемент IDT указывает на набор инструкций, которые будут выполнены, как один из этапов обработки исключения.
Давайте возьмём второй элемент в хардварной части IDR, вектор 0x50
```
0: kd> !idt 50
Dumping IDT: fffff8051ae62000
50: fffff80517ff9bf0 dxgkrnl!DpiFdoLineInterruptRoutine (KINTERRUPT ffffb70107b9c500)
```
Выведем IDT 0x50, используя IDTR
```
0: kd> dt nt!_KIDTENTRY64 @idtr+0x50*0x10
+0x000 OffsetLow : 0x9bf0
+0x002 Selector : 0x10
+0x004 IstIndex : 0y000
+0x004 Reserved0 : 0y00000 (0)
+0x004 Type : 0y01110 (0xe)
+0x004 Dpl : 0y00
+0x004 Present : 0y1
+0x006 OffsetMiddle : 0x17ff
+0x008 OffsetHigh : 0xfffff805
+0x00c Reserved1 : 0
+0x000 Alignment : 0x17ff8e00`00109bf0
0: kd> dt @idtr + @@c++(0x50 * sizeof(nt!_KIDTENTRY64)) nt!_KIDTENTRY64
+0x000 OffsetLow : 0x9bf0
+0x002 Selector : 0x10
+0x004 IstIndex : 0y000
+0x004 Reserved0 : 0y00000 (0)
+0x004 Type : 0y01110 (0xe)
+0x004 Dpl : 0y00
+0x004 Present : 0y1
+0x006 OffsetMiddle : 0x17ff
+0x008 OffsetHigh : 0xfffff805
+0x00c Reserved1 : 0
+0x000 Alignment : 0x17ff8e00`00109bf0
```
Когда появляется прерывание, исполнение кода передаётся в **0xfffff80517ff9bf0**
Этот адрес указывает на исполняемую страницу памяти в NTOSKRNL и содержит следующие инструкции:
```
0: kd> u 0xfffff80517ff9bf0 L3
nt!KiIsrThunk+0x280:
fffff805`17ff9bf0 6a50 push 50h
fffff805`17ff9bf2 55 push rbp
fffff805`17ff9bf3 e989050000 jmp nt!KiIsrLinkage (fffff805`17ffa181)
```
Переменная **KiIsrThunk** из **NTOSKRNL** указывает на ядреную страницу кода, которая содержит 256 темплейтов, похожих на инструкции выше. После push interrupt vector (0x50) в этом случае и содержимого RBP регистра на стек, KiIsrThunk заглушка передаёт управление **KiIsrLinkage()**. Это 2 элемента на стеке используются функцией **KiIsrLinkage()** через структуру **KTRAP_FRAME**.
### KiIsrLinkage()
KiIsrLinkage() выполняет множество служебных задач:
* Сохраняет контекст изменяемого регистра в части **KTRAP_FRAME**, созданном в стеке
* Проверяет, выполнял ли во время прерывания процессор инструкции внтури конкретного региона функции **ExpInterlockedPopEntrySList()** и, если выполнял, он сбрасывает регистр RIP на допустимую инструкцию возобновления цикла в функции
* Проверяет, выключены ли прерывания и, если это так, багчекает систему на с кодом остановки **TRAP_CAUSE_UNKNOWN**
* Получает указатель на структуру прерывания, ассоциированную с прерыванием и обрабатывает прерывание
* Восстанавливает котекст изменяемого регистра из **KTRAP_FRAME**
* Возвращается из прерывания
Интересно, что большинство частей функции **KiIsrLinkage()** созданы из макросов, многие из которых доступны в заголовочном файле WDK **kxamd64.inc**, например **GENERATE_INTERRUPT_FRAME**, **ENTER_INTERRUPT**, **EXIT_INTERRUPT** и **RESTORE_TRAP_STATE**
### KINTERRUPT
Структура **KINTERRUPT** - основной ключ к обработке прерываний, она содержит всю информацию, необходимую для вызова ISR(interrupt service routine), зарегистрированной драйвером. **KiIsrLinkage()** определяет, где находится структура KINTERRUPT, связанная с вектором прерывания, используя его, как индекс в массиве указателей структур KINTERRUPT, находищихся в **KPCR.CurrentPrcb.InterruptObject[]**. Функция **KiGetInterruptObjectAddress()** из NTOSKRNL получает указатель на объект **KINTERRUPT**, показано ниже:
```
0: kd> uf nt!KiGetInterruptObjectAddress
nt!KiGetInterruptObjectAddress:
fffff805`17f771d0 65488b142520000000 mov rdx,qword ptr gs:[20h]
fffff805`17f771d9 4881c240310000 add rdx,3140h
fffff805`17f771e0 8bc1 mov eax,ecx
fffff805`17f771e2 488d04c2 lea rax,[rdx+rax*8]
fffff805`17f771e6 c3 ret
```
```
0: kd> !idt 50
Dumping IDT: fffff8051ae62000
50: fffff80517ff9bf0 dxgkrnl!DpiFdoLineInterruptRoutine (KINTERRUPT ffffb70107b9c500)
0: kd> dt @$pcr nt!_KPCR -a Prcb.InterruptObject[50]
+0x180 Prcb :
+0x3140 InterruptObject : [80] 0xffffb701`07b9c500 Void
0: kd> dt nt!_KINTERRUPT 0xffffb70107b9c500
+0x000 Type : 0n22
+0x002 Size : 0n288
+0x008 InterruptListEntry : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]
+0x018 ServiceRoutine : 0xfffff805`1b051e60 unsigned char dxgkrnl!DpiFdoLineInterruptRoutine+0
+0x020 MessageServiceRoutine : (null)
+0x028 MessageIndex : 0
+0x030 ServiceContext : 0xffff990e`5377a030 Void
+0x038 SpinLock : 0
+0x040 TickCount : 0
+0x048 ActualLock : 0xffff990e`530eea10 -> 0
+0x050 DispatchAddress : 0xfffff805`17ff8c70 void nt!KiInterruptDispatch+0
+0x058 Vector : 0x50
+0x05c Irql : 0x5 ''
+0x05d SynchronizeIrql : 0x5 ''
+0x05e FloatingSave : 0 ''
+0x05f Connected : 0x1 ''
+0x060 Number : 0
+0x064 ShareVector : 0x1 ''
+0x065 EmulateActiveBoth : 0 ''
+0x066 ActiveCount : 0
+0x068 InternalState : 0n0
+0x06c Mode : 0 ( LevelSensitive )
+0x070 Polarity : 0 ( InterruptPolarityUnknown )
+0x074 ServiceCount : 0
+0x078 DispatchCount : 0
+0x080 PassiveEvent : (null)
+0x088 TrapFrame : 0xfffff805`1ae6e520 _KTRAP_FRAME
+0x090 DisconnectData : (null)
+0x098 ServiceThread : (null)
+0x0a0 ConnectionData : 0xffff990e`53931cc0 _INTERRUPT_CONNECTION_DATA
+0x0a8 IntTrackEntry : 0xffff990e`532cc500 Void
+0x0b0 IsrDpcStats : _ISRDPCSTATS
+0x110 RedirectObject : (null)
+0x118 PhysicalDeviceObject : 0xffff990e`50fc2360 Void
```
Поля структуры KINTERRUPT, которые относятся к обработке прерываний:
| Название | Описание |
| -------- | -------- |
| DispatchAddress | Указатель на начальный программный обработчик прерываний в NTOSKRNL (KiChainedDispatch() ) для общих прерываний и KiInterruptDispatch() для других |
| ServiceRoutine | Указатель на программный обработчик прерываний, зарегистрированный драйвером с помощью API ядра IoConnectInterrupt() или IoConnectInterruptEx() |
| MessageServiceRoutine | Используется только для MSI (message signaled interrupts - прерывания, инициируемые сообщениями), т.е. прерывания, которые доставляются путём записи в зарезервированные участи памяти вместо переключения аппаратных линий. Эти прерывания показываются, как отрицательные числа в device manager'e. Для таких прерываний ServiceRoutine указывает на ядерную функцию KiInterruptMessageDispatch(), которая вызывает ISR, связанную с драйвером в MessageServiceRoutine |
| MessageIndex | Индекс MSI, передаваемый, как параметр в ISR у MessageServiceRoutine |
В старых версиях Windows **KINTERRUPT** аллоцировалась из исполняемого невыгружаемого пула памяти, так как содержала начальный код обработки, который был зарегистрирован прямо в IDT. Из-за перехода к механизму из **KiIsrThunk()** и **KiIsrLinkage()**, описанному выше, начальная заглушка для прерывания теперь находится в исполняемой памяти в **NTOSKRNL** и, соответственно, структуре KINTERRUPT больше не нужно быть аллоцированной из исполняемой памяти. Структуры **KINTERRUPT** теперь пре-аллоцируются и хранятся в списке в **KPCR.Prcb.InterruptObjectPool**. Функция **KeAllocateInterrupt()** забирает пре-аллоцированную структуру KINTERRUPT из списка, когда вызывается для аллокации новой структуры KINTERRUPT. Когда этот список заканчивается, алооцируется ещё одна страница со структурами с помощью **MmAllocateIndependentPages()**, и добалвяет их в список.
### Interrupt Dispatching
Одним из важных шагов, предпринятых KiIsrLinkage, является вызов функции в KINTERRUPT.DispatchAddress, что приводит к вызову либо KiInterruptDispatch(), либо KiChainedDispatch(). Обе эти функции вызываются с указателем на структуру KINTERRUPT, как будто у них есть доступ ко всей информации, относящейся к обработке прерывания.
Новые системы используют APIC (Advanced Programmable Interrupt Controller) для обработки прерываний с устройств. Устройства отправляют свои прерывания на процессор с помощью IRQ линий. Однако, устройств больше, чем IRQ линий. Общие прерывания убирают проблему позволяя использовать одни и те же IRQ линии множеству устройств. Когда IRQ шарится, множество драйверов регистрирует свои ISR'ы для одного и того же IRQ и вектора прерывания. Из этого вытекает множественная структура KINTERRUPT, соответствующая устройствам, которые делят прерывание, и на них ссылаются вместе с помощью их полей **KINTERRUPT.InterruptListEntry**. Увидеть это можно с помощью "!idt -a", когда одному вектору прерывания соответствует множество структур KINTERRUPT, связанных с ним. **KiChainedDispatch()** обрабатывает прерывания, которые шарятся с множеством устройств, а **KiInterruptDispatch()** обрабатывает остальные прерывания.
Функции **KiInterruptDispatch()** и KiChainedDispatch меняются в зависимости от стека прерывания процессора, указатель на который хранится в **KPCR.Prb.IsrStack**. Этот стек аллоцируется функцией **MmAllocateIsrStack()**. Размер ISR стека 0x7000 байт, как определено переменными **ISR_STACK_SIZE** и **PAGE_SIZE** в заголовочном файле **ksamd64.inc** WDK. Непосредственный переход на стек ISR происходит с помощью макроса SWITCH_TO_ISR_STACK и также доступен в **ksamd64.inc**.
Как только выполнение перешло на стек ISR, функции **KiInterruptDispatch()** и **KiChainedDispatch()** передают выполнение следующей стадии, вызывая **KiInterruptSubDispatch()** или **KiScanInterruptObjectList()** соответственно.
**KiInterruptSubDispatch()** вызывает **KiCallInterruptServiceRoutine()** для одиночной структуры **KINTERRUPT**.
**KiScanInterruptObjectList()** итерируется по всем объектам **KINTERRUPT**, зарегистрированным для одного вектора прерывания, используя список **KINTERRUPT.InterruptListEntry** и вызывает **KiCallInterruptServiceRoutine()** для каждого **KINTERRUPT** в цепочке.
#### KiCallInterruptServiceRoutine() выполняет следующие задачи:
* Помечает прерывание, как активное в KINTERRUPT.IsrDpcStats.IsrActive
* Записывает время начала ISR в KINTERRUPT.IsrDpcStats.IsrTimeStart
* Получает спин-блокировку прерывания в KINTERRUPT.ActualLock
* Вызывает драйвер, зарегистрированный ISR в KINTERRUPT.ServiceRoutine
* Записывает длительность ISR в KINTERRUPT.IsrDpcStats.IsrTime
* Если ISR была прервана другой ISR с большим уровнем IRQL, он подстраивает IsrTime для точного учёта времени
* Помечает прерывание как неактивное в KINTERRUPT.IsrDpcStats.IsrActive
* Инкрементирует счётчик экземпляров прерываний в IsrCount
*
Драйвер, который регистрировал ISR, может сообщить вызывающей функции **KiCallInterruptServiceRoutine()**, забрал ли он на обработку прерывание, вернув TRUE. Это становится важным в случае пошареных прерываний, где решение вызвать ISR в следующем **KINTERRUPT** в цепочке или нет зависит от того, забрал ли текущий ISR прерывание на обработку.
Следующая диаграмма показывает все структуры, описанные выше и отношения между ними.

Как и в предыдущих версиях Windows 64, и IDTR, и содержимое IDT защищено **PatchGuard** (kernel patch protection). Делая структуру **KINTERRUPT** неисполняемой и удаляю код обработки из структуры, мы закрываем ещё один вектор subversion. Однако, даже с этими новыми изменениями в обработке исключений всё равно возможно для драйвера ядра хукнуть ISR в системе для реализации своего функционала, например для кейлоггера. ISR драйвера в поле **KINTERRUPT**.ServiceRoutine может быть заменено указателем на хук-функцию и **PatchGuard** этого не заметит. Так же не заметит, если **KINTERRUPT**, хранящийся в **KPCR.Prcb.InterruptObject[]** будет заменён клонированной структурой **KINTERRUPT**, который будет вести к выполнению кода.
**IDT** - Interrupt Descriptor Table (таблица дескрипторов прерываний)
Там хранятся элементы **\_KIDENTRY** или **\_KIDENTRY64** соответственно
В каждой из них есть ссылка на **ISR** (Interrupt Service Routine) - непостредственно функция, которая вызывается
### Вторая статья и практика-практика-практика
**Механизм прерываний** - ещё один важный элемент уровня железа
Прерывания можно рассматривать, как события уровня железа, использующиеся для сигнализирования процессору, что что-то требует немедленного внимания
- **Прерывания устройств**
Устройства (сетевая карта, клавиатура и тд) вызовут прерывание, чтобы
сигнализировать процессору, что у них есть новая информация для обработки
(входящий сетевой пакет, нажатие на клавишу и тд)
- **Ловушки / исключения**
Эти вещи обычно происходят, когда процессор сталкивается с ошибкой,
такой как деление на ноль или ошибка страницы
- **Программные прерывания**
Это такие прерывания, которые генерируются программами, например **INT 2E** (syscall)
используется для перехода из user mode в kernel mode. **INT 3** используется
для генерации программного брейкпоинта и тд
Значение, которое идёт за инструкцией **INT** называется вектором прерывания, это просто индекс в **IDT** (*Interrupt Descriptor Table*). **IDT** ассоциирует вектор прерывания с конкретной функцией, которая будет обрабатывать вызванное прерывание. В **WDK** (*Windows Driver Kit*) такая функция называется **ISR** (*Interrupt Service Routine*)
С точки зрения железа прерывания обрабатываются конкретным куском железа, называемым **PIC** (*Programmable Interrupt Controller* - контроллер прерываний). Сейчас у нас обычно стоит новая версия PIC - **APIC** (*Advanced Programmable Interrupt Controller*), встроенная прямо в процессов
**Плюсы APIC:**
* Поддержка многопроцессорности
* Больше линий прерываний (256 vs 15 для **PIC**)
Для каждого CPU свой **APIC**, и каждый **APIC** может коммуницировать с другими **APIC**'ами через **IPI** (*Inter-processor interrupt message*)
Одна большая задача в обработке прерываний, которую выполняет **APIC** - это управление приоритетами прерываний. Каждой линии прерываний выдан свой приоритет и **APIC** проверяет, что ни один входящий запрос на прерывание с приоритетом ниже или равным текущему обрабатываемому прерыванию не достигнет процессор, обычно это называют **Interrupt Masking**
Заметьте, что некоторые особые прерывания не могут был замаскированы и всегда будут достигать процессор, они называются **NMI** (*Non-maskable interrupt*). Они обычно предназначены для неустранимого сбоя оборудования, что означает, что у вас серьёзные проблемы с железом.
Прерывания, приходящие от устройств, сначала обрабатываются I/O APIC, специальным чипом, встроенным в чипсет, его роль - распределять прерывания по локальным APIC'ам всех CPU, таким образом включая **SMP** (*Symmetric multiprocessing* - Симметричная многопроцессорность)

Когда прерывание достигает CPU, процессор и процедура прерывания ОС сохранят стостояние значения регистров в стеке ядра, чтобы можно было восстановить предыдущий поток выполнения и продолжить исполнение кода. Этот набор сохраняемых регистров и некотороая дополнителья информация (например код ошибки) обычно называются **Trap Frame** (.trap в windbg)
Углубимся немного в механизм обработки прерываний. Откуда процессор знает, где расположения **IDT**? Ответ - в регистре **IDTR**. 48-битный регистр делится на две части: 16-бит - **IDT limit** и 32-бита - **base address**
Максимальное количество записей в **IDT** - 256. Каждая запись - 8 бит, **содержит флаги**, **сегментные селекторы**, **gate type** и оффсет или адрес **ISR**.
Оффсет тоже разделён на две части: биты 0..15 - для младших битив и 48..63 - для старших битов
В винде **IDT entry** - это **\_KIDTENTRY**
```
kd> dt nt!_KIDTENTRY
+0x000 Offset : Uint2B
+0x002 Selector : Uint2B
+0x004 Access : Uint2B
+0x006 ExtendedOffset : Uint2B
```
Чтобы отобразить IDT в windbg есть **!idt**
```
kd> !idt
Dumping IDT: 8003f400
30: 806f5d50 hal!HalpClockInterrupt
31: 89ec9044 i8042prt!I8042KeyboardInterruptService (KINTERRUPT 89ec9008)
38: 806efef0 hal!HalpProfileInterrupt
39: 89fed174 ACPI!ACPIInterruptServiceRoutine (KINTERRUPT 89fed138)
NDIS!ndisMIsr (KINTERRUPT 89f228d8)
3a: 89f24044 VIDEOPRT!pVideoPortInterrupt (KINTERRUPT 89f24008)
USBPORT!USBPORT_InterruptService (KINTERRUPT 89eae008)
3b: 8a01d6c4 VBoxGuest+0x27c0 (KINTERRUPT 8a01d688)
portcls!CKsShellRequestor::`scalar deleting destructor'+0x26 (KINTERRUPT 89f179c0)
3c: 89ead564 i8042prt!I8042MouseInterruptService (KINTERRUPT 89ead528)
3e: 89fea9d4 atapi!IdePortInterrupt (KINTERRUPT 89fea998)
3f: 8a03d044 atapi!IdePortInterrupt (KINTERRUPT 8a03d008)
```
Например разберём поближе **i8042prt!I8042KeyboardInterruptService**
Для проверки, что это такое вообще (ну вдруг мы по названию не догадались) поставим бряку на него
```
kd> u i8042prt!I8042KeyboardInterruptService
i8042prt!I8042KeyboardInterruptService:
f76a7495 6a18 push 18h
f76a7497 68a8a76af7 push offset i8042prt!`string'+0x154 (f76aa7a8)
f76a749c e8fa000000 call i8042prt!_SEH_prolog (f76a759b)
f76a74a1 8b7d0c mov edi,dword ptr [ebp+0Ch]
f76a74a4 8b7728 mov esi,dword ptr [edi+28h]
f76a74a7 837e3001 cmp dword ptr [esi+30h],1
f76a74ab 0f8582130000 jne i8042prt!I8042KeyboardInterruptService+0xa2 (f76a8833)
f76a74b1 a100a96af7 mov eax,dword ptr [i8042prt!Globals (f76aa900)]
kd> bu i8042prt!I8042KeyboardInterruptService
kd> bl
0 e Disable Clear f76a7495 0001 (0001) i8042prt!I8042KeyboardInterruptService
kd> g
Breakpoint 0 hit
i8042prt!I8042KeyboardInterruptService:
f76a7495 6a18 push 18h
```

Мы нажали на любую кнопку --> наш брейкпоинт сработал
Но давайте доберёмся до кода в статике, ведь то, что написано в выводе команды **!idt **(31: 89ec9044 i8042prt! ...) не совпадает с фактическим адресом **ISR**
Каждый элемент **IDT** занимает 8 байт, мы решили, что нам нужен индекс 31 (такой индекс у нужной нам функции), что нам нужно сделать?
**idtr + 0x31 * 8** -->
```
kd> r idtr
idtr=8003f400
kd> dd @idtr+8*0x31
```
8003f588 - 0008**9044 89ec**8e00 0008dd14 804d8e00 --> **0x89ec9044**
8003f598 - 0008dd1e 804d8e00 0008dd28 804d8e00
8003f5a8 - 0008dd32 804d8e00 0008dd3c 804d8e00
8003f5b8 - 0008dd46 804d8e00 0008fef0 806e8e00
8003f5c8 - 0008d174 89fe8e00 00084044 89f28e00
8003f5d8 - 0008d6c4 8a018e00 0008d564 89ea8e00
8003f5e8 - 0008dd82 804d8e00 0008a9d4 89fe8e00
8003f5f8 - 0008d044 8a038e00 0008dda0 804d8e00
И так наш ISR адрес **0x89ec9044**, но мы же вроде бы только что дампили **I8042KeyboardInterruptService** и его адрес был **0xf76a7495**, непонятно
Чтож, перед тем, как вызывать **ISR**'ры драйверов системе нужно выполнить некоторые задачи: маскирование прерываний с более низким приоритетом в **APIC**, поднятие уровня **IRQL** и тд
Так что вместо того, чтобы заполнить **IDT** **ISR**'ами, система заполняет их **glue** кодом или же иначе функциями-темплейтами
Каждая темплейт-функция взята (скопирована) из KiInterruptTemplate функции и динамически модифицирована, чтобы подходить соответствующему **ISR**'у
Давайте посмотрим на темплейт нашей **KeyboardInterruptService**:

Мы можем заметить, что почти весь код скопирован с оригинального **KiInterruptTemplate**. Однако есть одна интересная особенность:
темплейт функции клавиатуры вызывает **KiInterruptDispatch** и кладёт в **EDI** адрес 0x89EC9008
Этот адрес указывает на **interrupt object** с типом **\_KINTERRUPT**:
```
kd> dt nt!_KINTERRUPT 0x89EC9008
+0x000 Type : 0n22
+0x002 Size : 0n484
+0x004 InterruptListEntry : _LIST_ENTRY [ 0x89ec900c - 0x89ec900c ]
+0x00c ServiceRoutine : 0xf76a7495 unsigned char i8042prt!I8042KeyboardInterruptService+0
+0x010 ServiceContext : 0x89f259d0 Void
+0x014 SpinLock : 0
+0x018 TickCount : 0xffffffff
+0x01c ActualLock : 0x89f25a90 -> 0
+0x020 DispatchAddress : 0x804da8e8 void nt!KiInterruptDispatch+0
+0x024 Vector : 0x31
+0x028 Irql : 0x1a ''
+0x029 SynchronizeIrql : 0x1a ''
+0x02a FloatingSave : 0 ''
+0x02b Connected : 0x1 ''
+0x02c Number : 0 ''
+0x02d ShareVector : 0 ''
+0x030 Mode : 1 ( Latched )
+0x034 ServiceCount : 0
+0x038 DispatchCount : 0xffffffff
+0x03c DispatchCode : [106] 0x56535554
```
Как видно выше, как раз в **ServiceRoutine** хранится адрес **ISR**
Если мы теперь посмотрим на **KiInterruptDispatch** мы увидим, что он вызывает interrupt object **ServiceRoutine**
```
kd> u nt!KiInterruptDispatch L30
nt!KiInterruptDispatch:
804da8e8 ff05c4f5dfff inc dword ptr ds:[0FFDFF5C4h]
804da8ee 8bec mov ebp,esp
804da8f0 8b4724 mov eax,dword ptr [edi+24h]
804da8f3 8b4f29 mov ecx,dword ptr [edi+29h]
804da8f6 50 push eax
804da8f7 83ec04 sub esp,4
804da8fa 54 push esp
804da8fb 50 push eax
804da8fc 51 push ecx
804da8fd ff1504764d80 call dword ptr [nt!_imp__HalBeginSystemInterrupt (804d7604)]
804da903 0bc0 or eax,eax
804da905 7436 je nt!KiInterruptDispatch+0x55 (804da93d)
804da907 83ec0c sub esp,0Ch
804da90a 833d0c23568000 cmp dword ptr [nt!PPerfGlobalGroupMask (8056230c)],0
804da911 c745f400000000 mov dword ptr [ebp-0Ch],0
804da918 752b jne nt!KiInterruptDispatch+0x5d (804da945)
804da91a 8b771c mov esi,dword ptr [edi+1Ch]
804da91d 8b4710 mov eax,dword ptr [edi+10h]
804da920 50 push eax
804da921 57 push edi
804da922 ff570c call dword ptr [edi+0Ch]
kd> dt nt!_KINTERRUPT 0x89EC9008
...
+0x00c ServiceRoutine : 0xf76a7495 unsigned char i8042prt!I8042KeyboardInterruptService+0
...
```
Вся структура вызовов:

И как создаётся interrupt object?
Это роль драйвера заполнить структуру вызвав **IoConnectInterrupt**
### Добавим ещё немного экспериментов:
Давайте посмотрим на прерывание и исключение деления на ноль
Оно у нас самое первое в таблице **IDT**
```
kd> !idt -a
Dumping IDT: 8003f400
00: 804df370 nt!KiTrap00
01: 804df4eb nt!KiTrap01
02: Task Selector = 0x0000
03: 804df8bd nt!KiTrap03
04: 804dfa40 nt!KiTrap04
05: 804dfba1 nt!KiTrap05
06: 804dfd22 nt!KiTrap06
07: 804e038a nt!KiTrap07
```
Поставим бряку
`bu nt!KiTrap00`
И на машине скомпилим какой-нибудь такой код:
```
#include <stdio.h>
#include <Windows.h>
int main() {
sleep(5);
printf("Go!\n");
int x = 10;
int y;
scanf("%f", &y);
printf("%f", x / y);
return 0;
}
```
Вводим ноль и Viola! брякаемся
```
kd> !process 0 0
Failed to get VadRoot
PROCESS 897cf578 SessionId: 0 Cid: 06ac Peb: 7ffdc000 ParentCid: 0e48
DirBase: 825e9000 ObjectTable: e2c24c78 HandleCount: 7.
Image: Untitled1.exe
kd> .process /i 897cf578
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
ReadVirtual: 8a01c688 not properly sign extended
kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
804e351a cc int 3
ReadVirtual: 8a01c688 not properly sign extended
00401500 55 push ebp
00401501 89e5 mov ebp,esp
00401503 83e4f0 and esp,0FFFFFFF0h
00401506 83ec20 sub esp,20h
00401509 e8b2090000 call 00401ec0
0040150e c7042405000000 mov dword ptr [esp],5
00401515 e8b6100000 call 004025d0
0040151a c7042400404000 mov dword ptr [esp],404000h
00401521 e832110000 call 00402658 <-- printf("Go!\n")
00401526 c744241c0a000000 mov dword ptr [esp+1Ch],0Ah
0040152e 8d442418 lea eax,[esp+18h]
00401532 89442404 mov dword ptr [esp+4],eax
00401536 c7042404404000 mov dword ptr [esp],404004h
0040153d e81e110000 call 00402660 <-- scanf
00401542 8bd9 mov ebx,ecx
00401544 2418 and al,18h
00401546 8b44241c mov eax,dword ptr [esp+1Ch]
0040154a 99 cdq
0040154b f7f9 idiv eax,ecx
0040154d 89442404 mov dword ptr [esp+4],eax
00401551 c7042404404000 mov dword ptr [esp],404004h
00401558 e80b110000 call 00402668
0040155d b800000000 mov eax,0
00401562 c9 leave
```
Падаем в обработку

```
...
804df3ea 55 push ebp
804df3eb e8b6431400 call nt!Ki386CheckDivideByZeroTrap (806237a6)
...
```
## Посмотрим на 64-битную 10-ку
Тут всё выглядит поинтереснее, мб из-за отсутсвия дебаг символом на XP'хе, а мб и нет
```
kd> !idt
Dumping IDT: fffff80335462000
00: fffff80330a01c00 nt!KiDivideErrorFault
01: fffff80330a01f40 nt!KiDebugTrapOrFault Stack = 0xFFFFF803354A0000
02: fffff80330a02440 nt!KiNmiInterrupt Stack = 0xFFFFF80335492000
03: fffff80330a02900 nt!KiBreakpointTrap
04: fffff80330a02c40 nt!KiOverflowTrap
05: fffff80330a02f80 nt!KiBoundFault
06: fffff80330a034c0 nt!KiInvalidOpcodeFault
07: fffff80330a039c0 nt!KiNpxNotAvailableFault
08: fffff80330a03cc0 nt!KiDoubleFaultAbort Stack = 0xFFFFF8033548B000
09: fffff80330a03fc0 nt!KiNpxSegmentOverrunAbort
0a: fffff80330a042c0 nt!KiInvalidTssFault
0b: fffff80330a045c0 nt!KiSegmentNotPresentFault
0c: fffff80330a04980 nt!KiStackFault
0d: fffff80330a04cc0 nt!KiGeneralProtectionFault
0e: fffff80330a05000 nt!KiPageFault
10: fffff80330a05640 nt!KiFloatingErrorFault
11: fffff80330a05a00 nt!KiAlignmentFault
12: fffff80330a05d40 nt!KiMcheckAbort Stack = 0xFFFFF80335499000
13: fffff80330a06840 nt!KiXmmException
14: fffff80330a06c00 nt!KiVirtualizationException
15: fffff80330a07100 nt!KiControlProtectionFault
1f: fffff803309fb220 nt!KiApcInterrupt
20: fffff803309fce00 nt!KiSwInterrupt
29: fffff80330a07600 nt!KiRaiseSecurityCheckFailure
2c: fffff80330a07940 nt!KiRaiseAssertion
2d: fffff80330a07c80 nt!KiDebugServiceTrap
2f: fffff803309fd3c0 nt!KiDpcInterrupt
30: fffff803309fb7c0 nt!KiHvInterrupt
31: fffff803309fbaa0 nt!KiVmbusInterrupt0
32: fffff803309fbd80 nt!KiVmbusInterrupt1
33: fffff803309fc060 nt!KiVmbusInterrupt2
34: fffff803309fc340 nt!KiVmbusInterrupt3
35: fffff803309f9b18 nt!HalpInterruptCmciService (KINTERRUPT fffff803312f2f40)
36: fffff803309f9b20 nt!HalpInterruptCmciService (KINTERRUPT fffff803312f3180)
50: fffff803309f9bf0 dxgkrnl!DpiFdoLineInterruptRoutine (KINTERRUPT ffffa600bf1fb500)
60: fffff803309f9c70 USBPORT!USBPORT_InterruptService (KINTERRUPT ffffa600bf1fb780)
70: fffff803309f9cf0 VBoxGuest+0x22e0 (KINTERRUPT ffffa600bf1fbb40)
80: fffff803309f9d70 storport!RaidpAdapterInterruptRoutine (KINTERRUPT ffffa600bf1fbc80)
HDAudBus!HdaController::Isr (KINTERRUPT ffffa600bf1fb640)
90: fffff803309f9df0 i8042prt!I8042MouseInterruptService (KINTERRUPT ffffa600bf1fb8c0)
a0: fffff803309f9e70 i8042prt!I8042KeyboardInterruptService (KINTERRUPT ffffa600bf1fba00)
b0: fffff803309f9ef0 ACPI!ACPIInterruptServiceRoutine (KINTERRUPT ffffa600bf1fbdc0)
ce: fffff803309f9fe0 nt!HalpIommuInterruptRoutine (KINTERRUPT fffff803312f3ba0)
d1: fffff803309f9ff8 nt!HalpTimerClockInterrupt (KINTERRUPT fffff803312f3960)
d2: fffff803309fa000 nt!HalpTimerClockIpiRoutine (KINTERRUPT fffff803312f3840)
d7: fffff803309fa028 nt!HalpInterruptRebootService (KINTERRUPT fffff803312f3600)
d8: fffff803309fa030 nt!HalpInterruptStubService (KINTERRUPT fffff803312f33c0)
df: fffff803309fa068 nt!HalpInterruptSpuriousService (KINTERRUPT fffff803312f32a0)
e1: fffff803309fd8b0 nt!KiIpiInterrupt
e2: fffff803309fa080 nt!HalpInterruptLocalErrorService (KINTERRUPT fffff803312f34e0)
e3: fffff803309fa088 nt!HalpInterruptDeferredRecoveryService (KINTERRUPT fffff803312f3060)
fd: fffff803309fa158 nt!HalpTimerProfileInterrupt (KINTERRUPT fffff803312f3a80)
fe: fffff803309fa160 nt!HalpPerfInterrupt (KINTERRUPT fffff803312f3720)
```
Возьмём снова наш обработчик клавиатуры по оффсету **a0**
```
kd> dt _kidtentry64 (idtr + (0xa0*0x10))
ntdll!_KIDTENTRY64
+0x000 OffsetLow : 0x9e70
+0x002 Selector : 0x10
+0x004 IstIndex : 0y000
+0x004 Reserved0 : 0y00000 (0)
+0x004 Type : 0y01110 (0xe)
+0x004 Dpl : 0y00
+0x004 Present : 0y1
+0x006 OffsetMiddle : 0x309f
+0x008 OffsetHigh : 0xfffff803
+0x00c Reserved1 : 0
+0x000 Alignment : 0x309f8e00`00109e70
```
Найдём ISR entry point для него, теперь для 64 бит схема немного другая:
**OffsetHigh + OffsetMiddle + OffsetLow**
0xfffff803309f9e70
```
Offset: 0xfffff803309f9e70
fffff803`309f9e70 6aa0 push 0FFFFFFFFFFFFFFA0h
fffff803`309f9e72 55 push rbp
fffff803`309f9e73 e909030000 jmp nt!KiIsrLinkage (fffff803`309fa181)
```
Если в табличке !idt искать KINTERRUPT не хочется, можно сделать так:
```
kd> dt @$pcr nt!_KPCR -a Prcb.InterruptObject[0xa0]
+0x180 Prcb :
+0x3140 InterruptObject : [160] 0xffffa600`bf1fba00 Void
kd> dt nt!_KINTERRUPT ffffa600bf1fba00
+0x000 Type : 0n22
+0x002 Size : 0n288
+0x008 InterruptListEntry : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]
+0x018 ServiceRoutine : 0xfffff803`36096790 unsigned char i8042prt!I8042KeyboardInterruptService+0
+0x020 MessageServiceRoutine : (null)
+0x028 MessageIndex : 0
+0x030 ServiceContext : 0xffffcf8b`4e304040 Void
+0x038 SpinLock : 0
+0x040 TickCount : 0
+0x048 ActualLock : 0xffffcf8b`4e3041a0 -> 0
+0x050 DispatchAddress : 0xfffff803`309f8c70 void nt!KiInterruptDispatch+0
+0x058 Vector : 0xa0
+0x05c Irql : 0xa ''
+0x05d SynchronizeIrql : 0xa ''
+0x05e FloatingSave : 0 ''
+0x05f Connected : 0x1 ''
+0x060 Number : 0
+0x064 ShareVector : 0 ''
+0x065 EmulateActiveBoth : 0 ''
+0x066 ActiveCount : 0
+0x068 InternalState : 0n0
+0x06c Mode : 1 ( Latched )
+0x070 Polarity : 0 ( InterruptPolarityUnknown )
+0x074 ServiceCount : 0
+0x078 DispatchCount : 0
+0x080 PassiveEvent : (null)
+0x088 TrapFrame : 0xfffffb82`3ab14a20 _KTRAP_FRAME
+0x090 DisconnectData : (null)
+0x098 ServiceThread : (null)
+0x0a0 ConnectionData : 0xffffcf8b`4e467d00 _INTERRUPT_CONNECTION_DATA
+0x0a8 IntTrackEntry : 0xffffcf8b`4ccac690 Void
+0x0b0 IsrDpcStats : _ISRDPCSTATS
+0x110 RedirectObject : (null)
+0x118 PhysicalDeviceObject : (null)
```
#### напочитать:
* [Ориг статья с экспериментами](http://trapframe.github.io/just-enough-kernel-to-get-by)
* https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/interrupt-descriptor-table-idt
* https://vivek-arora.com/?p=801
* https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/glimpse-into-ssdt-in-windows-x64-kernel
* https://codemachine.com/articles/interrupt_dispatching.html
# PatchGuard
# Скрываемся в списке процессов в ядре
Попробуем скрыть процесс в ядре, чтобы в юзерспейсе пользователь не увидел его. Идём по мануалу [Manipulating ActiveProcessLinks to unlink processes in userland](https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/manipulating-activeprocesslinks-to-unlink-processes-in-userland)
Про **\_EPROCESS** мы уже знаем из лабы выше. Двухсвязный список, тип **\_LIST_ENTRY** и всё такое. Задача у нас довольно простая, но хочется проделать это руками: переписать указатели так, чтобы спрятать наш процесс:

Находим адрес процесса (notepad.exe) и берём его **FLINK** и **BLINK**
```
kd> dt _eprocess fffffa80047ce060
nt!_EPROCESS
...
+0x188 ActiveProcessLinks : _LIST_ENTRY [ 0xfffffa80`061457d8 - 0xfffffa80`070f9c88 ]
...
kd> dq fffffa80047ce060+0x188 L2
fffffa80`047ce1e8 fffffa80`061457d8 fffffa80`070f9c88
```
Можем глянуть, куда указывают флинк и блинк:
```
kd> dt _eprocess fffffa80`061457d8-0x188
nt!_EPROCESS
...
+0x2e0 ImageFileName : [15] "mscorsvw.exe"
...
kd> dt _eprocess fffffa80`070f9c88-0x188
nt!_EPROCESS
...
+0x2e0 ImageFileName : [15] "taskhost.exe"
...
```
Получим пиды окружающих наш процесс процессов:
```
kd> dt _eprocess fffffa8006145650
nt!_EPROCESS
...
+0x180 UniqueProcessId : 0x00000000`00000a4c Void
+0x188 ActiveProcessLinks : _LIST_ENTRY [ 0xfffff800`02a38940 - 0xfffffa80`047ce1e8 ]
...
kd> dt nt!_EPROCESS fffffa80070f9b00
...
+0x180 UniqueProcessId : 0x00000000`00000994 Void
+0x188 ActiveProcessLinks : _LIST_ENTRY [ 0xfffffa80`047ce1e8 - 0xfffffa80`070375c8 ]
...
kd> dd fffffa8006145650+188-8 L1
fffffa80`061457d0 00000a4c
kd> dd fffffa80070f9b00+188-8 L1
fffffa80`070f9c80 00000994
```
| Image | PID | EPROCESS | ActiveProcessLinks | FLINK | BLINK |
| -------- | -------- | -------- | -------- | -------- | -------- |
| taskhost.exe | 994 | fffffa80\`070f9b00 | fffffa80\`070f9c88 | fffffa80\`047ce1e8 | fffffa80\`070375c8 |
| notepad.exe | 854 | fffffa80\`047ce060 | fffffa80\`047ce1e8 | fffffa80\`061457d8 | fffffa80\`070f9c88 |
| mscorsvw.exe | a4c | fffffa80\`06145650 | fffffa80\`061457d8 | fffff800\`02a38940 | fffffa80\`047ce1e8 |
Очевидно, нам нужна немного другая табличка)
| Image | PID | EPROCESS | ActiveProcessLinks | FLINK | BLINK |
| -------- | -------- | -------- | -------- | -------- | -------- |
| taskhost.exe | 994 | fffffa80\`070f9b00 | fffffa80\`070f9c88 | ~~fffffa80\`047ce1e8~~<br />fffffa80\`061457d8 | fffffa80\`070375c8 |
| ~~notepad.exe~~ | ~~854~~ | ~~fffffa80\`047ce060~~ | ~~fffffa80\`047ce1e8~~ | ~~fffffa80\`061457d8~~ | ~~fffffa80\`070f9c88~~ |
| mscorsvw.exe | a4c | fffffa80\`06145650 | fffffa80\`061457d8 | fffff800\`02a38940 | ~~fffffa80\`047ce1e8~~<br />fffffa80\`070f9c88 |
```
kd> eq fffffa80`070f9c88 fffffa80`061457d8
kd> eq fffffa80`061457d8+8 fffffa80`070f9c88
```

# Абуз токенов для повышения привилегий в ядре
Будут рассмотрены две техники:
1) **Кража/подмена токена** - низкоуровневый токен подменяется высокоуровневым
2) **Корректирование привилегий токена** - добавление привилегий текущему токену
Новая ядерная структура - **\_TOKEN**
### Попробуем сначала первый способ: берём токен процесса System, меняем свой, профит
```
kd> dt nt!_EPROCESS fffffa800372cb00
...
+0x208 Token : _EX_FAST_REF
...
kd> dq fffffa800372cb00+0x208 L1
fffffa80`0372cd08 fffff8a0`01b46a5b
kd> !token fffffa800372cb00+0x208
The address 0xfffffa80038fe4a8 does not point to a token object.
kd> dt _EX_FAST_REF
ntdll!_EX_FAST_REF
+0x000 Object : Ptr64 Void
+0x000 RefCnt : Pos 0, 4 Bits
+0x000 Value : Uint8B
kd> dt _EX_FAST_REF
ntdll!_EX_FAST_REF
+0x000 Object : 0xfffff8a0`01b46a5b Void
+0x000 RefCnt : 0y1011
+0x000 Value : 0xfffff8a0`01b46a5b
```
Выходит, что последние 4 бита - это счётчик референсов, уберём их, получим адрес токена:
**0xfffff8a0\`01b46a5b & 0xf0 --> 0xfffff8a0\`01b46a50**
```
kd> !token 0xfffff8a0`01b46a50
_TOKEN 0xfffff8a001b46a50
TS Session ID: 0x1
User: S-1-5-21-2382729903-2716558127-3398458678-1003
User Groups:
00 S-1-5-21-2382729903-2716558127-3398458678-513
Attributes - Mandatory Default Enabled
01 S-1-1-0
Attributes - Mandatory Default Enabled
02 S-1-5-21-2382729903-2716558127-3398458678-1000
Attributes - Mandatory Default Enabled
03 S-1-5-32-545
Attributes - Mandatory Default Enabled
04 S-1-5-4
Attributes - Mandatory Default Enabled
05 S-1-2-1
Attributes - Mandatory Default Enabled
06 S-1-5-11
Attributes - Mandatory Default Enabled
07 S-1-5-15
Attributes - Mandatory Default Enabled
08 S-1-5-113
Attributes - Mandatory Default Enabled
09 S-1-5-5-0-112295
Attributes - Mandatory Default Enabled LogonId
10 S-1-2-0
Attributes - Mandatory Default Enabled
11 S-1-5-64-10
Attributes - Mandatory Default Enabled
12 S-1-16-8192
Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-21-2382729903-2716558127-3398458678-513
Privs:
19 0x000000013 SeShutdownPrivilege Attributes -
23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
25 0x000000019 SeUndockPrivilege Attributes -
33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes -
34 0x000000022 SeTimeZonePrivilege Attributes -
Authentication ID: (0,1b6e2)
Impersonation Level: Anonymous
TokenType: Primary
Source: User32 TokenFlags: 0x2200 ( Token in use )
Token ID: 4bdf2 ParentToken ID: 0
Modified ID: (0, 4b6ff)
RestrictedSidCount: 0 RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 3e7
```
Его же мы получим, если сделаем
```
kd> !process fffffa800372cb00 1
PROCESS fffffa800372cb00
SessionId: 1 Cid: 0928 Peb: 7fffffde000 ParentCid: 07f0
DirBase: 68d15000 ObjectTable: fffff8a000d3a940 HandleCount: 271.
Image: powershell.exe
VadRoot fffffa800691fd80 Vads 234 Clone 0 Private 8427. Modified 22. Locked 0.
DeviceMap fffff8a0013e5b40
Token fffff8a001b46a50
ElapsedTime 00:00:06.874
UserTime 00:00:00.250
KernelTime 00:00:00.015
QuotaPoolUsage[PagedPool] 304952
QuotaPoolUsage[NonPagedPool] 28324
Working Set Sizes (now,min,max) (15169, 50, 345) (60676KB, 200KB, 1380KB)
PeakWorkingSetSize 16026
VirtualSize 553 Mb
PeakVirtualSize 565 Mb
PageFaultCount 46113
MemoryPriority FOREGROUND
BasePriority 8
CommitCharge 13117
```
`kd> eq fffffa800372cb00+0x208 fffff8a000004040`

### Второй способ
Заберём права токена процесса System себе

```
kd> !token fffff8a0022b7060
_TOKEN 0xfffff8a0022b7060
TS Session ID: 0x1
User: S-1-5-21-2382729903-2716558127-3398458678-1003
User Groups:
00 S-1-5-21-2382729903-2716558127-3398458678-513
Attributes - Mandatory Default Enabled
01 S-1-1-0
Attributes - Mandatory Default Enabled
02 S-1-5-21-2382729903-2716558127-3398458678-1000
Attributes - Mandatory Default Enabled
03 S-1-5-32-545
Attributes - Mandatory Default Enabled
04 S-1-5-4
Attributes - Mandatory Default Enabled
05 S-1-2-1
Attributes - Mandatory Default Enabled
06 S-1-5-11
Attributes - Mandatory Default Enabled
07 S-1-5-15
Attributes - Mandatory Default Enabled
08 S-1-5-113
Attributes - Mandatory Default Enabled
09 S-1-5-5-0-112295
Attributes - Mandatory Default Enabled LogonId
10 S-1-2-0
Attributes - Mandatory Default Enabled
11 S-1-5-64-10
Attributes - Mandatory Default Enabled
12 S-1-16-8192
Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-21-2382729903-2716558127-3398458678-513
Privs:
19 0x000000013 SeShutdownPrivilege Attributes -
23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
25 0x000000019 SeUndockPrivilege Attributes -
33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes -
34 0x000000022 SeTimeZonePrivilege Attributes -
Authentication ID: (0,1b6e2)
Impersonation Level: Anonymous
TokenType: Primary
Source: User32 TokenFlags: 0x2200 ( Token in use )
Token ID: 70533 ParentToken ID: 0
Modified ID: (0, 70234)
RestrictedSidCount: 0 RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 3e7
```
Видим те же права у токена, что и на картинке
Токен системы:
```
kd> !token fffff8a000004040
_TOKEN 0xfffff8a000004040
TS Session ID: 0
User: S-1-5-18
User Groups:
00 S-1-5-32-544
Attributes - Default Enabled Owner
01 S-1-1-0
Attributes - Mandatory Default Enabled
02 S-1-5-11
Attributes - Mandatory Default Enabled
03 S-1-16-16384
Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-18
Privs:
02 0x000000002 SeCreateTokenPrivilege Attributes -
03 0x000000003 SeAssignPrimaryTokenPrivilege Attributes -
04 0x000000004 SeLockMemoryPrivilege Attributes - Enabled Default
05 0x000000005 SeIncreaseQuotaPrivilege Attributes -
07 0x000000007 SeTcbPrivilege Attributes - Enabled Default
08 0x000000008 SeSecurityPrivilege Attributes -
09 0x000000009 SeTakeOwnershipPrivilege Attributes -
10 0x00000000a SeLoadDriverPrivilege Attributes -
11 0x00000000b SeSystemProfilePrivilege Attributes - Enabled Default
12 0x00000000c SeSystemtimePrivilege Attributes -
13 0x00000000d SeProfileSingleProcessPrivilege Attributes - Enabled Default
14 0x00000000e SeIncreaseBasePriorityPrivilege Attributes - Enabled Default
15 0x00000000f SeCreatePagefilePrivilege Attributes - Enabled Default
16 0x000000010 SeCreatePermanentPrivilege Attributes - Enabled Default
17 0x000000011 SeBackupPrivilege Attributes -
18 0x000000012 SeRestorePrivilege Attributes -
19 0x000000013 SeShutdownPrivilege Attributes -
20 0x000000014 SeDebugPrivilege Attributes - Enabled Default
21 0x000000015 SeAuditPrivilege Attributes - Enabled Default
22 0x000000016 SeSystemEnvironmentPrivilege Attributes -
23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
25 0x000000019 SeUndockPrivilege Attributes -
28 0x00000001c SeManageVolumePrivilege Attributes -
29 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default
30 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default
31 0x00000001f SeTrustedCredManAccessPrivilege Attributes -
32 0x000000020 SeRelabelPrivilege Attributes -
33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes - Enabled Default
34 0x000000022 SeTimeZonePrivilege Attributes - Enabled Default
35 0x000000023 SeCreateSymbolicLinkPrivilege Attributes - Enabled Default
Authentication ID: (0,3e7)
Impersonation Level: Anonymous
TokenType: Primary
Source: *SYSTEM* TokenFlags: 0x2000 ( Token NOT in use )
Token ID: 3eb ParentToken ID: 0
Modified ID: (0, 3ec)
RestrictedSidCount: 0 RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 0
```
```
kd> dt _token
nt!_TOKEN
...
+0x040 Privileges : _SEP_TOKEN_PRIVILEGES
...
powershell process
kd> dt _sep_token_privileges fffff8a0022b7060+0x40
nt!_SEP_TOKEN_PRIVILEGES
+0x000 Present : 0x00000006`02880000
+0x008 Enabled : 0x800000
+0x010 EnabledByDefault : 0x800000
System process
kd> dt _sep_token_privileges fffff8a000004040+0x40
nt!_SEP_TOKEN_PRIVILEGES
+0x000 Present : 0x0000000f`f2ffffbc
+0x008 Enabled : 0x0000000e`60b1e890
+0x010 EnabledByDefault : 0x0000000e`60b1e890
```

```
eq fffff8a0022b7060+0x40 0x0000000f`f2ffffbc
eq fffff8a0022b7060+0x40+8 0x0000000f`f2ffffbc
kd> dt _sep_token_privileges fffff8a0022b7060+0x40
nt!_SEP_TOKEN_PRIVILEGES
+0x000 Present : 0x0000000f`f2ffffbc
+0x008 Enabled : 0x0000000f`f2ffffbc
+0x010 EnabledByDefault : 0x800000
```
# SSDT / Hooking / прерывания
### Service Descriptor Table
**Service Descriptor Table** - структура ядра, показанная ниже, содержит 4 **System Service Table**
```
typedef struct tagSERVICE_DESCRIPTOR_TABLE {
SYSTEM_SERVICE_TABLE nt;
SYSTEM_SERVICE_TABLE win32k;
SYSTEM_SERVICE_TABLE sst3;
SYSTEM_SERVICE_TABLE sst4;
} SERVICE_DESCRIPTOR_TABLE;
```
В системе две **Service Descriptor Tables**:
- nt!KeServiceDescriptorTable
- nt!KeServiceDescriptorTableShadow
### System Service Table (SST)
**SST** структура, показанная ниже, содержит поле **ServiceTable**, которое является указателем на первый элемент массива указателей на рутины(функции) ядра в случае 32-битной ОС или указателем на массив адресов (и некоторой доп информации о количестве параметров), relative to the base address pointed to by it в случае 64-битной ОС
```
typedef struct tagSYSTEM_SERVICE_TABLE {
PULONG ServiceTable;
PULONG_PTR CounterTable;
ULONG_PTR ServiceLimit;
PBYTE ArgumentTable;
} SYSTEM_SERVICE_TABLE;
```
```
kd> dps nt!KeServiceDescriptorTable L10
fffff800`02aba900 fffff800`028b3200 nt!KiServiceTable
fffff800`02aba908 00000000`00000000
fffff800`02aba910 00000000`00000191
fffff800`02aba918 fffff800`028b3e8c nt!KiArgumentTable
fffff800`02aba920 00000000`00000000
fffff800`02aba928 00000000`00000000
fffff800`02aba930 00000000`00000000
fffff800`02aba938 00000000`00000000
fffff800`02aba940 00000000`00000001
fffff800`02aba948 00000000`00000000
fffff800`02aba950 00000000`00000003
fffff800`02aba958 00000000`00060107
fffff800`02aba960 fffff800`02aba960 nt!MmPagedPoolInfo+0x20
fffff800`02aba968 fffff800`02aba960 nt!MmPagedPoolInfo+0x20
fffff800`02aba970 00000000`00000000
fffff800`02aba978 00000000`02000000
kd> dps nt!KeServiceDescriptorTableShadow L10
fffff800`02aba9c0 fffff800`028b3200 nt!KiServiceTable
fffff800`02aba9c8 00000000`00000000
fffff800`02aba9d0 00000000`00000191
fffff800`02aba9d8 fffff800`028b3e8c nt!KiArgumentTable
fffff800`02aba9e0 fffff960`00165900 win32k!W32pServiceTable
fffff800`02aba9e8 00000000`00000000
fffff800`02aba9f0 00000000`0000033b
fffff800`02aba9f8 fffff960`0016761c win32k!W32pArgumentTable
fffff800`02abaa00 c0000044`00000005
fffff800`02abaa08 c0000044`00000005
fffff800`02abaa10 c000012c`00000000
fffff800`02abaa18 c00000a1`00000000
fffff800`02abaa20 c0000001`00000002
fffff800`02abaa28 fffff800`00a01310
fffff800`02abaa30 fffff800`00a012c0
fffff800`02abaa38 00000000`00000002
```
### SSDTs
**System Service Dispatch Table** или **SSDT** - это просто таблица функций ядра.
Как говорилось выше, есть массив указателей в случае 32-битной ОС или массив относительных адресов в случае 64-битной, на который указывает поле **ServiceTable** **SST(System Service Table)**, этот массив - просто **SSDT**
Смотря на результаты WinDBG выше, мы видем, что есть только одна явная **SST** таблица в случае **nt!KeServiceDescriptorTable** и две в случае **nt!KeServiceDescriptorTableShadow**. Хотя мы видим больше данных во второй, из доступной информации мы можем заключить лишь то, что из возможных 4-ёх **SST** элементов **nt!KeServiceDescriptorTable** использует только первый - он описывает **SSDT** для **Windows Native APIs**, экспортируемых **ntoskrnl.exe**'ом. **nt!KeServiceDescriptorTableShadow** использует 2 SST элемента: первый - копия **nt!KiServiceTable** из **nt!KeServiceDescriptorTable**, второй - **win32k!W32pServiceTable**, описывающий **SSDT** для **User** и **GDI** функции, экспортируемые **win32k.sys**.
### Как используются SSDT
SSDT предоставляют функциональность и приложениям пространства пользователя (разумеется, неявно), и драйверам ядра.
#### Посмотрим на случай с user mode'ом.
Когда **user mode** приложение вызывает, явно или неявно, некоторые функции **Windows API**, множество функций ядра будут вызваны в процессе. Для перехода в режим ядра из режима пользователя используется **Sysenter** (или **Syscall** для 64-бит) ассемблерная инструкция (раньше это было 0x2E прерывание, которое ещё актуально, хотя используется не так часто). Конкретная функция обработки будет вызвана по номеру, **Dispatch ID**, значение которого было в регистре **EAX** до вызова инструкции **Sysenter** / **Syscall**.
Первые 12 бит **Dispatch ID** - это индекс в **SSDT**. Биты 12-ый и 13-ый указывают, который из **SSDT**. Это означает, что **Dispatch ID** **до** **0xFFF** будет обработан **nt!KiServiceTable** **SSDT**, а **Dispatch ID** **между** **0x1000** и **0x1FFF** будет обработан **win32k!W32pServiceTable SSDT**.
Когда вызывается функция в пространстве пользователя, например **CreateFile**, в итоге управление передаётся в **ntdll!NtCreateFile** и с помощью сискола в ядро **nt!NtCreateFile** (ntoskrnl)
По сути, **сисколы** и **SSDT** (KiServiceTable) работают вместе, как мост между API функциями пространства пользователя и соответсвующими им функциями в пространестве ядра, позволяя ядру понять, которая из функций должна быть выполнена для конкретного сискола, вызванного ещё в пространстве пользователя.

Мы можем найти структуру **Service Descriptor Table** в **KeServiceDescriptorTable**. Первый элемент будет KiServiceTable - указатель на саму **SSDT**
```
kd> dps nt!keservicedescriptortable L4
fffff800`02b04900 fffff800`028fd200 nt!KiServiceTable
fffff800`02b04908 00000000`00000000
fffff800`02b04910 00000000`00000191
fffff800`02b04918 fffff800`028fde8c nt!KiArgumentTable
```
Значения из **SSDT**
```
kd> dd /c1 KiServiceTable L5
fffff800`028fd200 03c19000
fffff800`028fd204 02528d00
fffff800`028fd208 ffffb400
fffff800`028fd20c 02560005
fffff800`028fd210 02b4ef06
```
На 64-битных системах **SSDT** содержит относительные сдвиги на ядерные функции. Чтобы получить абсолютный адрес для сдивига, нужно использовать следующую формулу:
> RoutineAbsoluteAddress = KiServiceTableAddtess + (routineOffset >>> 4)
```
KiServiceTable (fffff800`028fd200) + (03c19000 >>> 4)
kd> u KiServiceTable + (03c19000 >>> 4)
nt!NtMapUserPhysicalPagesScatter:
fffff800`02cbeb00 48895c2408 mov qword ptr [rsp+8],rbx
fffff800`02cbeb05 4c89442418 mov qword ptr [rsp+18h],r8
fffff800`02cbeb0a 55 push rbp
fffff800`02cbeb0b 56 push rsi
fffff800`02cbeb0c 57 push rdi
fffff800`02cbeb0d 4154 push r12
fffff800`02cbeb0f 4155 push r13
fffff800`02cbeb11 4156 push r14
```
(пример на картинке из статьи)

#### На 32-битных системах ещё проще
```
kd> u ntdll!NtCreateFile
ntdll!NtCreateFile:
77ab3250 b875010000 mov eax,175h
77ab3255 e803000000 call ntdll!NtCreateFile+0xd (77ab325d)
77ab325a c22c00 ret 2Ch
77ab325d 8bd4 mov edx,esp
77ab325f 0f34 sysenter
kd> dps nt!KiServiceTable L176
8190f20c 818c573a nt!NtAccessCheck
8190f210 818cbfd8 nt!NtWorkerFactoryWorkerReady
8190f214 81b033b8 nt!NtAcceptConnectPort
.....
190f7d8 81ad7b18 nt!NtCreateTimer2
8190f7dc 81af323a nt!NtCreateIoCompletion
8190f7e0 81a66958 nt!NtCreateFile
```
### Попробуем найти ядерную функцию для конкретного сискола
Возьмём для примера функцию **Sleep**. Её функция в nt - это **NtDelayExecution**
```
kd> u NtDelayExecution L3
ntdll!NtDelayExecution:
00000000`78eb9be0 4c8bd1 mov r10,rcx
00000000`78eb9be3 b831000000 mov eax,31h
00000000`78eb9be8 0f05 syscall
```
Оффсеты(сдвиги) в **KiServiceTable** 4-ёх байтовые, так что нам нужно посмотреть значение на 0x31-ой позиции
```
kd> dd /c1 kiservicetable+4*0x31 L1
fffff800`028fd2c4 023d1440
```
Используем формулу
```
kiservicetable(fffff800`028fd200) + 4 * 0x31
kd> dd kiservicetable+4*0x31 L1
fffff800`028fd2c4 023d1440
kd> u kiservicetable + (023d1440>>>4) L1
nt!NtDelayExecution:
fffff800`02b3a344 4883ec28 sub rsp,28h
```

### Найдём адреса всех функций в SSDT
`.foreach /ps 1 /pS 1 ( offset {dd /c 1 nt!KiServiceTable L poi(keservicedescriptortable+0x10) }){ dp kiservicetable + ( offset >>> 4 ) L1 }` (ПИЗДЕЦ)
`.foreach /ps 1 /pS 1 ( offset {dd /c 1 nt!KiServiceTable L poi(nt!KeServiceDescriptorTable+10)}){ r $t0 = ( offset >>> 4) + nt!KiServiceTable; .printf "%p - %y\n", $t0, $t0 }` (ОТВАЛ БАШКИ)
```
kd> .foreach /ps 1 /pS 1 ( offset {dd /c 1 nt!KiServiceTable L poi(nt!KeServiceDescriptorTable+10)}){ r $t0 = ( offset >>> 4) + nt!KiServiceTable; .printf "%p - %y\n", $t0, $t0 }
fffff80002cbeb00 - nt!NtMapUserPhysicalPagesScatter (fffff800`02cbeb00)
fffff80002b4fad0 - nt!NtWaitForSingleObject (fffff800`02b4fad0)
fffff800128fcd40 - fffff800`128fcd40
fffff80002b53200 - nt!NtReadFile (fffff800`02b53200)
fffff80002bb20f0 - nt!NtDeviceIoControlFile (fffff800`02bb20f0)
fffff80002b53c10 - nt!NtWriteFile (fffff800`02b53c10)
fffff80002b52c40 - nt!NtRemoveIoCompletion (fffff800`02b52c40)
fffff80002b6c0a0 - nt!NtReleaseSemaphore (fffff800`02b6c0a0)
fffff80002b44060 - nt!NtReplyWaitReceivePort (fffff800`02b44060)
...
```
### Почему SSDT так важны?
Всё просто, в 32-битную эру руткиты модифицировали **nt!KiServiceTable** ил **win32k!W32pServiceTable**, чтобы вызвать свой код. Также, многие антивирусы использовали хуки SSDT, чтобы получать мгновенный алёрт об атаке.
Однако с 64-битных ОС был представлен **Kernel Patch Protection** (PatchGuard). PatchGuard делает периодические проверки, чтобы убедиться, что критические системные структуры, включая SSDT, не были модифицированы в недавнее время.
### 32-битный и 64-битный код для поиска SSDT
Цель кода не хукнуть системную функцию в SSDT (это довольно просто без PatchGuard'а), об этом уже много написано
Целью и сложной частью является поиск SSDT. В особенности **win32k!W32pServiceTable** SSDT, на который указывает вторая запись SST **nt!KeServiceDescriptorTab1eShadow**. С другой стороны найти **nt!KiServiceTable** SSDT ждя 32-битной ОС легко, символы экспортируются, просто нужно прочитать значение. Символы для **nt!KiServiceTable** не экспортируются в 64-битной ОС.
В коде мы найдём **nt!KeServiceDescriptorTab1eShadow**. И тогда мы будем знать расположения **nt!KiServiceTable** и **win32k!W32pServiceTable**.
Билдим драйвер ядра и приложение user mode'а. Visual Studio 2015 solution - one project for the driver (both 32-bit and 64-bit) and another for the driver (both 32-bit and 64-bit)
#### 32-бит
- Энумерируем все процессы, ища PID, полученный из user mode'а.
- Нашли - энумерируем потоки. ища поток GUI - у них ненулевое значение Win32Thread в KTHREAD
- GUI потоки гарантированно имеют элемент ServiceTable в KTHREAD, указывающий на nt!KeServiceDescriptorTab1eShadow
#### 64-бит
- Читаем регистр MSR CPU, используя значение IA32_LSTAR.
- Это вернёт адрес 64-bit service call dispatcher (nt!kiSystemCall64). Не дальше, чем 512 байт лежит nt!KiSystemServiceRepeat.
- Дизассемблируем, находим ссылку на nt!KeServiceDescriptorTableShadow.
```
kd> u nt!KiSystemServiceRepeat
nt!KiSystemServiceRepeat:
fffff803`f28060a4 4c8d15d5e72700 lea r10,[nt!KeServiceDescriptorTable (fffff803`f2a84880)]
fffff803`f28060ab 4c8d1d4eb42600 lea r11,[nt!KeServiceDescriptorTableShadow (fffff803`f2a71500)]
fffff803`f28060b2 f7437840000000 test dword ptr [rbx+78h],40h
```
Теперь осталось найти паатерн, вытащить RIP адрес и посчитать адрес nt!KeServiceDescriptorTableShadow
### SSDT Hook
#### x64
##### SSDT:
```
kd> dps nt!KeServiceDescriptorTable
fffff800`02b05900 fffff800`028fe200 nt!KiServiceTable
fffff800`02b05908 00000000`00000000
fffff800`02b05910 00000000`00000191
fffff800`02b05918 fffff800`028fee8c nt!KiArgumentTable
kd> dd /c 1 nt!KiServiceTable L10
fffff800`028fe200 03c19000 # function offset
fffff800`028fe204 02528d00
fffff800`028fe208 ffffb400
fffff800`028fe20c 02560005
fffff800`028fe210 02b4ef06
```
##### SSDT Shadow:
```
kd> dps nt!KeServiceDescriptorTableShadow
fffff800`02b059c0 fffff800`028fe200 nt!KiServiceTable # SSDT base address
fffff800`02b059c8 00000000`00000000
fffff800`02b059d0 00000000`00000191
fffff800`02b059d8 fffff800`028fee8c nt!KiArgumentTable
fffff800`02b059e0 fffff960`00115900 win32k!W32pServiceTable # SSDT Shadow base address
fffff800`02b059e8 00000000`00000000
fffff800`02b059f0 00000000`0000033b
fffff800`02b059f8 fffff960`0011761c win32k!W32pArgumentTable
Перед дампом SSDT Shadow нужно приаттачиться к любом юзермодному гуи приложению (например winlogon.exe)
kd> !process 0 0 winlogon.exe
PROCESS fffffa80058c48f0
SessionId: 1 Cid: 01b0 Peb: 7fffffdf000 ParentCid: 017c
DirBase: 32bc7000 ObjectTable: fffff8a0081ea6e0 HandleCount: 116.
Image: winlogon.exe
kd> .process /p fffffa80058c48f0
kd> .reload
kd> dd /c 1 win32k!W32pServiceTable L10
fffff960`00115900 fff35f00 # GDI Function offset
fffff960`00115904 fff06a81
fffff960`00115908 00020700
```
#### Индекс функции на адрес реальной фукнции:
readAddress = (ULONG_PTR)(ntTable[FunctionIndex] >> 4) + SSDT(Shadow)BaseAddress;
#### Найти адрес таблицы в драйвере
Найти **nt!KeServiceDescriptorTable** и **nt!KeServiceDescriptorTableShadow** в функции ядра **nt!KiSystemServiceStart**
```
kd> u nt!KiSystemServiceStart
nt!KiSystemServiceStart:
fffff800`0290885e 4889a3d8010000 mov qword ptr [rbx+1D8h],rsp
fffff800`02908865 8bf8 mov edi,eax
fffff800`02908867 c1ef07 shr edi,7
fffff800`0290886a 83e720 and edi,20h
fffff800`0290886d 25ff0f0000 and eax,0FFFh
nt!KiSystemServiceRepeat:
fffff800`02908872 4c8d1587d01f00 lea r10,[nt!KeServiceDescriptorTable (fffff800`02b05900)]
fffff800`02908879 4c8d1d40d11f00 lea r11,[nt!KeServiceDescriptorTableShadow (fffff800`02b059c0)]
fffff800`02908880 f7830001000080000000 test dword ptr [rbx+100h],80h
```
Как видно выше, мы можем искать по паттерну KiSystemServiceStart от начала ядра
```
const unsigned char KiSystemServiceStartPattern[] = {
0x8B, 0xF8, // mov edi, eax
0xC1, 0xEF, 0x07, // shr edi, 7
0x83, 0xE7, 0x20, // and edi, 20h
0x25, 0xFF, 0x0F, 0x00, 0x00 // and eax, 0fffh
};
bool found = false;
ULONG KiSSSOffset;
for(KiSSSOffset = 0; KiSSSOffset < kernelSize - signatureSize; KiSSSOffset++)
{
if(RtlCompareMemory(
((unsigned char*)kernelBase + KiSSSOffset),
KiSystemServiceStartPattern, signatureSize) == signatureSize)
{
found = true;
break;
}
}
if(!found)
return NULL;
```
После того, как нашли нужную функцию, мы можем получить адрес **nt!KeServiceDescriptorTableShadow** из инструкции:
`fffff80002908879 4c8d1d40d11f00 lea r11, [nt!KeServiceDescriptorTableShadow (fffff80002b059c0)]`
```
ULONG_PTR addressAfterPattern = kernelBase + kiSSSOffset + signatureSize
ULONG_PTR address = addressAfterPattern + 7 // Skip lea r10,[nt!KeServiceDescriptorTable]
LONG relativeOffset = 0;
// lea r11, KeServiceDescriptorTableShadow
if((*(unsigned char*)address == 0x4c) &&
(*(unsigned char*)(address + 1) == 0x8d) &&
(*(unsigned char*)(address + 2) == 0x1d))
{
relativeOffset = *(LONG*)(address + 3);
}
if(relativeOffset == 0)
return NULL;
SSDTStruct* shadow = (SSDTStruct*)( address + relativeOffset + 7 );
```
Затем можем получить адреса **nt!KiServiceTable(SSDT)** и **win32k!W32pServiceTable(SSDT Shadow)**
#### напочитать:
* [System Service Descriptor Table - SSDT](https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/glimpse-into-ssdt-in-windows-x64-kernel#finding-address-of-all-ssdt-routines)
* [The Quest for the SSDTs](https://www.codeproject.com/Articles/1191465/The-Quest-for-the-SSDTs)
* [Hook SSDT(Shadow)](https://m0uk4.gitbook.io/notebooks/mouka/windowsinternal/ssdt-hook)
* [Detecting Hooked Syscalls](https://www.ired.team/offensive-security/defense-evasion/detecting-hooked-syscall-functions)
# Команды WinDBG
Тематически сгруппированные команды WinDBG
[Common WinDbg Commands (Thematically Grouped)](https://web.archive.org/web/20081217233217/http://software.rkuster.com/windbg/printcmd.htm)
| Команда | Пояснение к ней |
| -------- | -------- |
| !peb | display formatted PEB |
| dt nt!\_PEB Addr | full PEB dump |
| lm | list loaded and unloaded modules |
| lm vm kernel32 | verbose output (incl image, syml information) |
| !dlls | display loaded modules with loader info |
| !imgreloc | display relocation info |
| !dh kernel32 | display headers |
| !gle | Get Last Error |
| !process 0 4 processname.exe | print all threads of process |
| !teb | display formatted teb |
| dt nt!\_TEB Addr | full TEB dump |
| k | display call stack for current thread |
| kP | P == full parameters for each function called |
| kf | f == distance between adjacent frames to be displayed (useful to check stack consumption of each frame) |
| kv | v == display FPO information + calling convention |
| kb | b == display the first three parameters passed to each function |
| d, dd, da, du... | Display memory <br /> dd == double word values <br /> da == display ASCII characters <br /> du == display Unicode characters |
| f 0012ff40 L20 'A' 'B' 'C' | fill 20 elements with ABC starting at address |
| !vprot MyAddr | Displays virtual memory protection information for MyAddr |
| !address MyAddr | Display information (type, protection, usage, ..) about the memory specified by MyAddr |
| !heap | print all heaps |
| !locks | displays a list of locked critical sections for the process |
| !locks -v | display all critical sections for the process |
| !cs -l [CsAddr] | Displays one or more critical sections, or the entire critical section tree. -l == display only locked sections|
| !cs -s [CsAddr] | -s == causes each CS’s initialization stack to be displayed |
| !cs -o [CsAddr] | -o == causes the owner’s stack to be displayed |
| !cs -t [CsAddr] | -t == display critical section tree -> EnterCntr, WaitCnt, … |
| !avrf -cs | Display a list of deleted critical sections (DeleteCriticalSection API) |
| !critsec [CsAddr] | displays the same collection of information as !ntsdexts.locks |
| dt | Display information about a local variable, function parameter, global variable or data type |
| dv | Display local variables |
| dv /t /i /V | Display local variables <br /> /i == classify them into categories (parameters or locals) <br /> /V == show addresses and offsets for the relevant base frame register (usually EBP) <br /> /t == display type information |
| dd 0046c6b0 L1 | display 1 dword at 0046c6b0 |
| dd 0046c6b0 L3 | display 3 dwords at 0046c6b0 |
| du 0046c6b0 | display Unicode chars at 0046c6b0 |
| as Name Equivalent <br /> as /ma Name Address <br /> as /mu Name Address | Set alias <br /> Set alias to the NULL-terminated ASCII string at Address <br /> Set alias to the NULL-terminated Unicode string at Address |
| ad Name <br /> ad * | Delete alias with Name <br /> Delete all aliases |
| al | List user-named aliases |
| ${Alias} <br /><br /> ${/f:Alias} <br /><br /> ${/n:Alias} <br /><br /> ${/d:Alias} | ${Alias} is replaced by the alias equivalent, even if it is touching other text. If the alias is not defined, the ${Alias} is not replaced <br /><br /> Same as above except that ${/f:Alias} is replaced with an empty string if the alias is not defined <br /><br /> Evaluates to the alias name <br /><br /> Evaluates: 1 = alias defined; 0 = alias not defined |
| bp <br /> bu <br /> ba <br /> bc <br /> be, bd | Set Breakpoint <br /> Set Unresolved Breakpoint: defers the actual setting of the breakpoint until the module is loaded <br /> Break on Access <br /> Breakpoint Clear <br /> Breakpoint Enable, Disable |
| ba r4 0012fe34 <br /> ba w2 0012fe38 | break on access (read or write); monitor 4 bytes <br /> break on access (write); monitor 2 bytes |
| bu kernel32!LoadLibraryExW 5 | Breakpoint that will starts hitting after 5 passes |
| ~1 bu kernel32!LoadLibraryExW | Break only if called from thread ~1 |
| bp mod!myFunc* | Break at all symbols with pattern myFunc* |
| .lastevent | first-change or second-chance? |
| !analyze -v | Displays detailed information about the current exception |
| .exr -1 | Display most recent exception |
| .exr Addr | Display exception at Addr |
| !cppexr Addr | Display c++ exception at address Addr |
| g, gH <br /> gN| Go with Exception Handled <br /> Go with Exception Not Handled |
| .dump /ma D:\large.dmp | all possible data: full memory, code sections, PEB and TEB’s, handle data, thread time information, unloaded module lists, and more |
| .dump /m d:\small.dmp | only basic information: module information (signatures), thread and stack information |
| r | print all registers |
| d * | view memory |
| e * | edit memory |
| ~1 ~2 | change context to processor 1/2 |
| ed nt!Kd_Default_Mask 8 | Включить DbgPrint прям в консоль windbg |
| | |
| | |
| | |
| | |
| | |
| | |