Вывод всех потоков для всех процессов:
!process 0 7
Поиск процесса и вывод стеков только для него
!process 0 0 proga.exe
!process addr 7
Еще вариант:
!stacks 2
Последний вариант интересен тем, что выводит также потоки для потока с ID = 0 ( IDLE ), команда !process "IDLE" за процесс не считает :).
Есть еще команда для вывода стеков активных потоков:
!running -i -t
Все варианты страдают двумя недостатками: проблемы с отображением юзермодных частей стеков ( чтобы они нормально отображались нужно переключаться в контекст конкретного процесса и получать список юзермодных модулей ); стеков много, а метод для фильтрации вывода по сути один - скопировать вывод и анализировать его какой нибудь внешней программой.
Поэтому мы решили немного упростить жизнь kernel-mode разработчиков и сделали скрипт stkwalk
Скрипт stkwalk
Если вы следите за нашим проектом, то, верно, знаете, что не давно мы выпустили новую версию pykd - 0.2.0.16. Если не знаете, то нужно поскорей узнать и обновить, потому что это необходимо для наших дальнейших упражнений. Если вы ленивы и воспользовались для установки pykd автоматический инсталлятор, то вы будете вознаграждены установкой га вашу машину целой толпы очень полезных скриптов. Среди них будет и stkwalk. Начнем знакомство:
!py stkwalk
Получим вывод стеков почти такой же, как выдает !process 0 0, но юзермодные части стеков выводятся более тщательно. Более того, если вы запустите скрипт на x64 системе, вы увидите также и WOW64 части стека. Польза +1.
Тут пора посмотреть, какие возможности предоставляет нам stkwalk. Спросим у него самого
Мы тут видим возможность фильтрации по различным параметрам, с этим мы подробнее разберемся ниже. Последняя опция позволяет выводить только уникальные стеки. Пробуем:
3: kd> !py stkwalk --help
Stack walker. ver 1.0
Usage: stkwalk [options]
Options:
-h, --help show this help message and exit
-p PROCESSFILTER, --process=PROCESSFILTER
process filter: boolean expression with python syntax
-m MODULEFILTER, --module=MODULEFILTER
module filter: boolean expression with python syntax
-f FUNCFILTER, --function=FUNCFILTER
function filter: boolean expression with python syntax
-t THREADFILTER, --thread=THREADFILTER
thread filter: boolean expresion with python syntax
-u, --unique show only unique stacks
Если рассмотреть вывод без фильтрации мы обнаружим в нем многочисленные повторения выделенного стека. И не удивительно - это стек рабочего потока системы, ожидающего задания. Таких потоков может быть довольно много. С другой стороны, данные стеки как правило не интересны для анализа. И так, Польза +1.
3: kd> !py stkwalk -u
Stack walker. ver 1.0
Process ffffffff855a1c40
Name: System Pid: 0x4
......
Thread ffffffff855f7d48, Process: System (ffffffff855a1c40)
ffffffff82c7de06 ffffffff8db6fc1c nt!KiSwapContext+26
ffffffff82c84fa5 ffffffff8db6fc30 nt!KiSwapThread+266
ffffffff82c838a3 ffffffff8db6fc68 nt!KiCommitThreadWait+1df
ffffffff82c84afd ffffffff8db6fc90 nt!KeRemoveQueueEx+4f8
ffffffff82c84393 ffffffff8db6fcf4 nt!ExpWorkerThread+e5
ffffffff82e25ad1 ffffffff8db6fd58 nt!PspSystemThreadStartup+9e
ffffffff82cd7539 ffffffff8db6fd98 nt!KiThreadStartup+19
.......
Фильтрация по процессам
Рассмотрим сначала пример - вывод стеков только для процессов с определенным именем:
Как видим, фильтр задается с помощью ключа -p ( --process ) и последующей строки, задающей сам фильтр. В примере мы закрыли строку, задающую фильтр, в кавычки. В принципе, внешние кавычки можно было бы опустить, но тогда мы должны гарантировать отсутствие пробелов в строке ( иначе, парсер командной строки просто разобьет строку на два параметра и все сломается ). Поэтому, для внутреннего спокойствия лучше поставит кавычки. Само выражение должно быть корректно с точки зрения python. Иначе мы увидим сообщение об ошибке:
3: kd> !py stkwalk -p "name=='winlogon.exe'"
Stack walker. ver 1.0
Process ffffffff88395d40
Name: winlogon.exe Pid: 0x30c
Thread ffffffff88393840, Process: winlogon.exe (ffffffff88395d40)
ffffffff82c7de06 ffffffff90ea3bd4 nt!KiSwapContext+26
ffffffff82c84fa5 ffffffff90ea3be8 nt!KiSwapThread+266
ffffffff82c838a3 ffffffff90ea3c20 nt!KiCommitThreadWait+1df
ffffffff82c7d76f ffffffff90ea3c48 nt!KeWaitForSingleObject+393
ffffffff82e48b45 ffffffff90ea3cc0 nt!NtWaitForSingleObject+c6
ffffffff82c596ea ffffffff90ea3d28 nt!KiFastCallEntry+12a
00000000771b6194 000000000006fad8 ntdll!KiFastSystemCallRet
00000000771b5b0c 000000000006fadc ntdll!NtWaitForSingleObject+c
00000000753b179c 000000000006fae0 KERNELBASE!WaitForSingleObjectEx+98
000000007699efe3 000000000006fb4c kernel32!WaitForSingleObjectExImplementation+75
000000007699ef92 000000000006fb64 kernel32!WaitForSingleObject+12
0000000000df1c9e 000000000006fb78 winlogon!SignalManagerWaitForSignal+e4
0000000000df1a50 000000000006fb98 winlogon!StateMachineRun+271
0000000000dfe343 000000000006fd54 winlogon!WlStateMachineRun+16
0000000000dfe199 000000000006fd68 winlogon!WinMain+af4
0000000000dfe479 000000000006fde8 winlogon!_initterm_e+1a1
00000000769a1154 000000000006fe78 kernel32!BaseThreadInitThunk+e
00000000771cb299 000000000006fe84 ntdll!__RtlUserThreadStart+70
00000000771cb26c 000000000006fec4 ntdll!_RtlUserThreadStart+1b
.....
Выражение должно вернуть булевский результат. В нем могут использоваться следующие переменные:
3: kd> !py stkwalk -p name='winlogon.exe'
Stack walker. ver 1.0
Traceback (most recent call last):
File "C:\proj\pykd-0.2\Snippets\stkwalk.py", line 238, in
- name - имя процесса
- pid- PID процесса
- process - объект типа typedVar( "nt!_EPROCESS", addr )
3: kd> !py stkwalk -p fnmatch(name,'winlogon.*')
3: kd> !py stkwalk -p "pid>100 and pid<1000"
3: kd> !py stkwalk -p "process.Win32Process==0"
Фильтрация по модулям
Разобравшись с фильтрацией по процессам, перейдем к фильтрации по модулям. Фильтрация по модулю задается с помощью ключа --module или, сокращенно, -m. В качестве параметра выступает также логическое выражение, удовлетворяющее синтаксису Python. В выражении можно использовать:- name - имя модуля
- module - объект типа module
0: kd> !py stkwalk -m fnmatch(name,'HTTP*')
0: kd> !py stkwalk -m "module.checksum()==0x8d29e"
0: kd> !py stkwalk -m "name=='HTTP' or name=='tcpip'"
Фильтрация по функциям
Для задания фильтра по функциям служит ключ --function, -m. В выражении, задающем предикат фильтра, можно использовать переменную name - имя функции.
0: kd> !py stkwalk -f fnmatch(name,'*WaitFor*')
Фильтрация по потокам
Для задания фильтра по функциям служит ключ --thread, -t. В выражении, задающем предикат фильтра, можно использовать:
- tid- идентификатор потока
- thread- объект типа typedVar("nt!_KTHREAD", AddressOfThread )
0: kd> !py stkwalk -t tid<0xF
FAQ
Q: У меня нет никакого stkwalk.py. Где его, черт возьми, искать? A: Попробуйте скачать скрипт вручную здесь Q: Какого хрена, ничего не работает! A: Убедитесь, что в находитесь в режиме отладки ядра.
Скрипт stkdelta
При разработке сложных драйверов, таких как фильтры файловых систем, разработчики порой сталкиваются с ситуацией нехватки стека режима ядра. Для анализа потребления стека мы разработали этот маленький скрипт. Он может выводить информацию о потреблении стека каждым вызовом функции или суммарное потребление:
0: kd> !py stkdelta
Stack Base: ffffffff9f387fd0 Limit: ffffffff9f385000 Current: ffffffff9f387754 Used: 87c Unused: 2754
Stack Delta: Function:
56 nt!KiSwapThread
40 nt!KiCommitThreadWait
380 nt!KeWaitForMultipleObjects
652 nt!ObpWaitForMultipleObjects
336 nt!NtWaitForMultipleObjects
0 nt!KiFastCallEntry
-18446744072066663752 ntdll!KiFastSystemCallRet
4 ntdll!ZwWaitForMultipleObjects
156 KERNELBASE!WaitForMultipleObjectsEx
72 kernel32!WaitForMultipleObjectsExImplementation
28 kernel32!WaitForMultipleObjects
36 mpengine!FreeSigFiles
76 mpengine!GetSigFiles
12 mpengine!GetSigFiles
56 msvcrt!_endthreadex
8 msvcrt!_endthreadex
12 kernel32!BaseThreadInitThunk
64 ntdll!__RtlUserThreadStart
24 ntdll!_RtlUserThreadStart
0: kd> !py stkdelta stat
Stack Base: ffffffff9f387fd0 Limit: ffffffff9f385000 Current: ffffffff9f387754 Used: 87c Unused: 2754
Stack usage: Module:
==============================
1464(%11) nt
156 (%1) KERNELBASE
124 (%1) mpengine
112 (%0) kernel32
92 (%0) ntdll
64 (%0) msvcrt
Stack usage: Function
==============================
652 (%5) nt!ObpWaitForMultipleObjects
380 (%3) nt!KeWaitForMultipleObjects
336 (%2) nt!NtWaitForMultipleObjects
156 (%1) KERNELBASE!WaitForMultipleObjectsEx
88 (%0) mpengine!GetSigFiles
72 (%0) kernel32!WaitForMultipleObjectsExImplementation
64 (%0) ntdll!__RtlUserThreadStart
64 (%0) msvcrt!_endthreadex
56 (%0) nt!KiSwapThread
40 (%0) nt!KiCommitThreadWait
36 (%0) mpengine!FreeSigFiles
28 (%0) kernel32!WaitForMultipleObjects
24 (%0) ntdll!_RtlUserThreadStart
12 (%0) kernel32!BaseThreadInitThunk
4 (%0) ntdll!ZwWaitForMultipleObjects