В свое время, при написании анти-руткита мне понадобилось восстановить структуру спроецированных файлов в пользовательское пространство процесса. Выделения памяти и проекции секций процесса описываются механизмом Virtual Address Descriptors (VAD), указатель, на вершину которого находится в структуре _EPROCESS. VAD в Windows — это сбалансированное бинарное дерево. Первое, что приходит в голову — обходить это дерево рекурсивно. Это оказалось большой ошибкой.
Указатель на вершину дерева можно получить из информации о процессе:
lkd> !process 88a61670
PROCESS 88a61670 SessionId: 0 Cid: 1068 Peb: 7ffdc000 ParentCid: 0534
DirBase: 7f4b48c0 ObjectTable: e3227a00 HandleCount: 31.
Image: Far.exe
VadRoot 88b984e8 Vads 53 Clone 0 Private 331. Modified 1. Locked 0.
DeviceMap e2693a78
Token e483d9e0
ElapsedTime 00:00:31.000
UserTime 00:00:00.031
KernelTime 00:00:00.265
QuotaPoolUsage[PagedPool] 48236
QuotaPoolUsage[NonPagedPool] 2216
Working Set Sizes (now,min,max) (917, 50, 345) (3668KB, 200KB, 1380KB)
PeakWorkingSetSize 917
VirtualSize 28 Mb
PeakVirtualSize 30 Mb
PageFaultCount 1023
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 387
WinDbg может обойти дерево и вывести необходимую информацию:
lkd> !vad 88b984e8 0
VAD level start end commit
88dc2268 ( 6) 10 11 2 Private READWRITE
89038678 ( 5) 20 20 1 Private READWRITE
8a64c350 ( 6) 30 12f 67 Private READWRITE
88fbdea8 ( 4) 130 134 0 Mapped READONLY
8919ac60 ( 6) 140 23f 10 Private READWRITE
8909d2e8 ( 5) 240 24f 0 Mapped READWRITE
88e7a368 ( 6) 250 265 0 Mapped READONLY
89852ef8 ( 3) 270 2b0 0 Mapped READONLY
888a0a28 ( 5) 2c0 300 0 Mapped READONLY
8924fdc0 ( 4) 310 315 0 Mapped READONLY
88accba0 ( 5) 320 3e7 0 Mapped EXECUTE_READ
89070470 ( 6) 3f0 3f0 1 Private READWRITE
8a712130 ( 2) 400 45c 16 Mapped Exe EXECUTE_WRITECOPY
88a11e70 ( 6) 460 562 0 Mapped READONLY
88cf6e70 ( 5) 570 86f 0 Mapped EXECUTE_READ
8968a6f8 ( 4) 870 870 1 Private READWRITE
88d44148 ( 6) 880 880 0 Mapped READONLY
891d0b10 ( 5) 890 89f 4 Private READWRITE
88d69538 ( 6) 8a0 8a2 0 Mapped READONLY
88c0a1f8 ( 3) 8b0 8b0 0 Mapped READONLY
88c51528 ( 5) 8c0 8cf 4 Private READWRITE
887ca3d8 ( 4) 8d0 8d0 0 Mapped READONLY
897081e8 ( 1) 8e0 8ef 4 Private READWRITE
88aa1e70 ( 6) 8f0 8f0 0 Mapped READONLY
88aae5e8 ( 5) 900 90f 4 Private READWRITE
8908de90 ( 6) 910 91f 3 Private READWRITE
8a720680 ( 4) 920 921 0 Mapped READONLY
89599ce0 ( 6) 930 a2f 16 Private READWRITE
890d3408 ( 5) a30 e2f 128 Private NO_ACCESS
8905bec8 ( 3) 1110 1111 0 Mapped READONLY
88864880 ( 4) 6d020 6d034 4 Mapped Exe EXECUTE_WRITECOPY
890963e8 ( 5) 6d4c0 6d4c5 2 Mapped Exe EXECUTE_WRITECOPY
894e2630 ( 2) 6d710 6d722 3 Mapped Exe EXECUTE_WRITECOPY
88b841b8 ( 5) 6d730 6d742 3 Mapped Exe EXECUTE_WRITECOPY
88a63440 ( 4) 71bd0 71be0 2 Mapped Exe EXECUTE_WRITECOPY
890bd8c8 ( 3) 73070 73096 3 Mapped Exe EXECUTE_WRITECOPY
895b6a18 ( 5) 76290 762ac 2 Mapped Exe EXECUTE_WRITECOPY
895ab100 ( 4) 76b70 76b7a 5 Mapped Exe EXECUTE_WRITECOPY
8967a328 ( 5) 76f50 76f62 2 Mapped Exe EXECUTE_WRITECOPY
88b984e8 ( 0) 77380 77410 3 Mapped Exe EXECUTE_WRITECOPY
89680c28 ( 4) 77420 77522 2 Mapped Exe EXECUTE_WRITECOPY
88fd42c8 ( 3) 77ba0 77bf9 8 Mapped Exe EXECUTE_WRITECOPY
88ff3c68 ( 2) 77c00 77c48 3 Mapped Exe EXECUTE_WRITECOPY
88fcc868 ( 3) 77c50 77cee 2 Mapped Exe EXECUTE_WRITECOPY
895481b0 ( 4) 77da0 77df1 3 Mapped Exe EXECUTE_WRITECOPY
896999d0 ( 1) 77e40 77f41 6 Mapped Exe EXECUTE_WRITECOPY
88ce7810 ( 4) 77f50 77feb 5 Mapped Exe EXECUTE_WRITECOPY
8887ec88 ( 3) 7c800 7c8bf 6 Mapped Exe EXECUTE_WRITECOPY
88fa00e8 ( 5) 7c8d0 7d0ce 31 Mapped Exe EXECUTE_WRITECOPY
88faf9a8 ( 4) 7f6f0 7f7ef 0 Mapped EXECUTE_READ
89545a38 ( 2) 7ffb0 7ffd3 0 Mapped READONLY
88c670e0 ( 4) 7ffdc 7ffdc 1 Private READWRITE
88c036e8 ( 3) 7ffdf 7ffdf 1 Private READWRITE
Total VADs: 53 average level: 5 maximum depth: 6
Почему же использование рекурсии в ядре является ошибкой? В частном случае, на x86 системе размер ядерного стека равен двенадцати килобайтам (12Kb). Как известно, рекурсия сильно «пожирает» стек, особенно если вы используете структуры, выделяемые на стеке. Когда ядерный стек закончится, вы увидите прекрасное синее окно, на котором чаще всего будет написано:
Bug Check 0x7F: UNEXPECTED_KERNEL_MODE_TRAP
Первым параметром, скорее всего, будет число 0×00000008, означающее Double Fault.
Выходом из данной ситуации является полнейший отказ от использования рекурсии или же использование лимитированной рекурсии со счетчиком. Я выбрал первый вариант и полностью отказался от использования рекурсии, учитывая то, что бинарное дерево можно обойти нерекурсивно (см. приложение к статье).
Ссылки по теме:
Приложения:
прикольно)
пиши еще)
з.ы. а ты мог бы поделится какими-нибудь трюками при отладке кода))) может ты знаеш чтото особенного)
Да, у меня есть мысль сделать отдельный раздел, что-то вроде «Трюки WinDbg» и постепенно его наполнять. Хотя вряд ли я знаю нечто экстраординарное :) Я все больше в IDA, статически.
Есть такое.
Обход дерева легко с помощью списка совершить.
Однако странно, что рекурсия с глубиной 6 забивает весь стек.
Ну, на самом деле vad-дерево в Windows не совсем простое, у меня там получался бесконечный цикл и сам понимаешь. Да, кстати, согласен, что можно использовать список, но мне кажется, что это решение не очень элегантное :)