Вы находитесь на странице: 1из 3

!!

Введение

!!!! Шаг 1. Начало работы


Для установки лучше всего воспользоваться автоматическим инсталлятором. Он
установит pykd в нужно место, а также установит и зарегистрирует все необходимые
компоненты.

Если установка завершилась без ошибок, пора познакомится с pykd. Для этого стартуем
windbg и начинаем отладочную сессию ( открываем процесс, дамп или устанавливаем
соединение с отладчиком ядра ). Теперь можно загрузить pykd. Для этого выполняем
команду:
.load pykd.pyd
Если во время загрузки случится какая либо ошибка - windbg выдаст сообщение.
Отсутствие каких либо сообщений свидетельствует об удачной загрузки расширения.

Теперь можно начинать работу. Выполним команду !pycmd. После ее выполнения


отладчик перейдет в режим ввода пользовательских данных. Весь пользовательский ввод
будет обрабатываться интерпретатором python.
{{
0:000> !pycmd
Python 2.6.6 (r266:84297, Aug 24 2010, 18:13:38) [MSC v.1500 64 bit (AMD64)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> print "Hello world!"
Hello world!
>>>
}}
Тут самое время ознакомится с синтаксисом python, если кто еще не знаком. Уверяю,
это не должно занять много времени: python очень прост в освоении.

Давайте вспомним базовые основы синтаксиса python:


{{
>>> def printHello():
... i = 0
... while i < 4:
... print "Hello #%d" % i
... i += 1
...
>>> printHello()
Hello #0
Hello #1
Hello #2
Hello #3
>>>
}}
Обратите внимание: вложенность блоков задается количеством лидирующих пробелов.
Это, так сказать, "фирменная" особенность python. Пока этих знаний нам будет
вполне достаточно. Двигаемся дальше.

!!!! Шаг 2. Доступ к регистрам.

Любой отладчик должен предоставлять три базовые возможности: чтение регистров


процессора, чтение памяти и управление режимом отладки. Начнем с регистров. С pykd
это делается довольно просто:
{{
>>> print hex(reg("eip"))
0x778ecb60
>>> print hex(reg("esp"))
0x1ef0e0
>>> print hex(reg("esp")+4)
0x1ef0e4
}}
В данном случае, мы используем функцию PYKD *reg*. Она осуществляет чтение
регистров процессора по имени. Пытливый читатель может спросить: как мы используем
функции из PYKD без импортирования самого модуля? На самом деле, модуль конечно
надо импортировать. Просто PYKD это сделал автоматически при конструировании
консоли Python.
Давайте напишем небольшой пример и посмотрим, куда указывает текущий счетчик
инструкций:
{{
>>> print findSymbol(reg("eip"))
ntdll!LdrpDoDebuggerBreak+30
}}
Функция *findSymbol* пытается для данного адреса найти т.н. отладочный символ. В
данном случае мы видим, что счетчика инструкций равен смещению 0x30 относительно
функции LdrpDoDebuggerBreak, т.е мы попросту находимся внутри функции
LdrpDoDebuggerBreak, находящейся в модуле ntdll. Мы это смогли выяснить, поскольку
у нас есть отладочная информация для модуля ntdll.dll ( соответствующий pdb файл ).
Если у вас по какой то причине символы не показываются, необходимо проверить
настройки путей к символам в windbg.

!!!! Шаг 4. Доступ к памяти


Для доступа к памяти PYKD предлагает большой набор функций. Их можно разделить на 3
группы:
* Чтение значения из памяти:
*ptrByte*
*ptrWord*
*ptrDWord*
*ptrQWord*
И другие, с полным набором можно ознакомится в [справке по API|PYKD 0.2. API
Reference]
Все функции принимают в качестве параметра адрес и возвращают значение, хранящееся
по данному адресу.
* Чтение массивов
*loadBytes*
*loadWords*
*loadDWords*
*loadQWords*
Все функции принимают в качестве параметров указатель на начало массива и его длину
в элементах. Возвращают объект *list* c элементами массива
* Чтение строк
*loadCStr*
*loadWStr*
Функции читают из памяти 0-терминированные строки возвращают python строки.
Давайте модифицируем предыдущий пример и выведем аргументы функции. Будем считать,
что функция имеет соглашение о вызове stdcall и ее параметры адресуются регистром
ebp
{{
>>> def printFunc():
... print findSymbol( reg("eip") )
... params = [ ptrDWord( reg("ebp") + 4*(i+1) ) for i in range(0,3) ]
... print "var1: %x var2: %x var3: %x" % ( params[0], params[1], params[2] )
...
>>> print printFunc()
ntdll32!LdrpDoDebuggerBreak+2c
var1: 774b1383 var2: fffdd000 var3: fffde000
None
>>>
}}
Обратите внимание на конструкцию:
{{
params = [ ptrDWord( reg("ebp") + 4*(i+1) ) for i in range(0,3) ]
}}
Это т.н генератор списка - специальная конструкция python, которую можно
использовать для инициализации списков. Это конструкция эквивалентна следующей
{{
[ ptrDWord( reg("ebp") + 4) ), ptrDWord( reg("ebp") + 8) ), ptrDWord( reg("ebp")
+ 0xC) ) ]
}}

!!!! Шаг 5. Доступ к памяти с учетом типа.

При отладке программ мы чаще всего сталкиваемся с типизированными переменными.


PYKD имеет богатые возможности для доступа к переменным с учетом типа. По сути, эта
главная "фишка" всего проекта: доступ к полям структур и классов осуществляется
очень похожим на исходный код способом. Например, допустим у нас есть следующий код
на Си:
{{
struct STRUCT_A {
int filed1;
char field2;
};

STRUCT_A a = { 100, 2}
}}
Теперь во время отладки мы хотим проверить состояние переменной 'a' c помощью PYKD:
{{
a = typedVar( "module!STRUCT_A", getOffset("module!a") )
if a.field1!=100 or a.field2!=2:
print "ERROR! a is not poperly initialized!"
}}

Вам также может понравиться