truebad0ur
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # интёрналсы окон ебучих Здесь будут собраны мои мучения по 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) в адресном пространстве процесса ![](https://i.imgur.com/whphHi6.png) ## Что есть в 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) # Полазаем по стеку и куче в ядре Структура стека: ![](https://i.imgur.com/Zz8CDIY.png) Что мы можем прочитать на 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Мб ``` ## Как растёт стек ![](https://i.imgur.com/xPxKnSo.png) * 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` Сделаем фиктивную утечку памяти и посмотрим на неё, можно что-то в этом духе: ![](https://i.imgur.com/RL4R28Y.png) ``` До аллокации 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 в ядре, который он устанавливает для каждого процессора на этапе загрузки. ![](https://i.imgur.com/SGixpmf.png) Значение регистра 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 описывается разработчиком процессора. ![](https://i.imgur.com/W4Jl2xP.png) Ядро 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 прерывание на обработку. Следующая диаграмма показывает все структуры, описанные выше и отношения между ними. ![](https://i.imgur.com/kY2HPqJ.png) Как и в предыдущих версиях 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* - Симметричная многопроцессорность) ![](https://i.imgur.com/K2qrYOY.png) Когда прерывание достигает 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 ``` ![](https://i.imgur.com/2wVFncm.png) Мы нажали на любую кнопку --> наш брейкпоинт сработал Но давайте доберёмся до кода в статике, ведь то, что написано в выводе команды **!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**: ![](https://i.imgur.com/BkaAFln.png) Мы можем заметить, что почти весь код скопирован с оригинального **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 ... ``` Вся структура вызовов: ![](https://i.imgur.com/p9zOkt1.png) И как создаётся 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 ``` Падаем в обработку ![](https://i.imgur.com/UvFFLnd.png) ``` ... 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** и всё такое. Задача у нас довольно простая, но хочется проделать это руками: переписать указатели так, чтобы спрятать наш процесс: ![](https://i.imgur.com/0grvUei.png) Находим адрес процесса (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 ``` ![](https://i.imgur.com/Ph6pGht.png) # Абуз токенов для повышения привилегий в ядре Будут рассмотрены две техники: 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` ![](https://i.imgur.com/YAeyqKX.png) ### Второй способ Заберём права токена процесса System себе ![](https://i.imgur.com/876jTqS.png) ``` 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 ``` ![](https://i.imgur.com/jVtFrLi.png) ``` 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 функциями пространства пользователя и соответсвующими им функциями в пространестве ядра, позволяя ядру понять, которая из функций должна быть выполнена для конкретного сискола, вызванного ещё в пространстве пользователя. ![](https://i.imgur.com/ina61G6.png) Мы можем найти структуру **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 ``` (пример на картинке из статьи) ![](https://i.imgur.com/Two1FHf.png) #### На 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 ``` ![](https://i.imgur.com/BXNF8L5.png) ### Найдём адреса всех функций в 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 | | | | | | | | | | | | | | | | | | |

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully