четверг, 26 мая 2011 г.

Обработка событий загрузки и выгрузки модулей отлаживаемой системы

Одно из нововведений релиза pykd 0.0.18 это базовый класс для обработки событий от Debug Engine. В текущей версии реализованы только события загрузки и выгрузки модулей. Об этом было рассказано в обзоре релиза.
Реализация сделана через базовый класс debugEvent, реализация которого расположена в модуле pykd. Для обработки соответствующего события необходимо написать свой класс, в котором переопределить методы onLoadModule(...) или onUnloadModule(...), в зависимости от того, какие события необходимо обрабатывать. Входным параметром для этих методов выступает экземпляр класса загруженного модуля dbgModuleClass.
В качестве демонстрации этого функционала рассмотрим часть скрипта samples\goLoad.py:
from pykd import *

import fnmatch
import sys

class loadHandler(debugEvent):
def __init__(self, mask):
debugEvent.__init__(self)
self.mask = mask
  def onLoadModule(self, module):

if fnmatch.fnmatch( module.name(), self.mask ):
return DEBUG_STATUS_BREAK
return DEBUG_STATUS_NO_CHANGE

if __name__ == "__main__":
if len(sys.argv) == 2:
loadHandler = loadHandler( sys.argv[1] )
go()

Фактически, скрипт продолжает исполнение целевой системы, пока не будет загружен модуль, имя которого будет совпадать с указанной маской (передается первым параметром в скрипт). Передаваемая маска имени модуля может содержать wildcard'ы: символы * и ?. То есть, например, что бы при старте системы дождаться загрузки модуля beep.sys в память, нужно выполнять команду: "!py goLoad *beep*". Это может быть полезно, когда нужно, что бы отладчик остановился перед вызовом точки входа целевого модуля, но уже при загруженном целевом модуле. В этот момент можно расставлять точки останова в загруженном модуле или, например, дождаться исполнения точки входа в драйвер командой "g beep!DriverEntry"

P.S. Сейчас в планах развивать функционал фильтрации отладочных событий. Хотелось бы услышать мнение конечных пользователей о том, какие событий представляют наибольший интерес (соответственно, реализация этих событий будет более приоритетной задачи). В число событий, для которых в pykd планируется реализовать механизмы фильтрации, входят:
  • Создание и уничтожение процесса
  • Возникновение исключения
  • Создание и уничтожение нити (thread)
  • Изменение состояния отлаживаемой сесии
  • Срабатывание точки останова. pykd уже предоставляет класс bp. Но планируется так же продублировать функционал в debugEvent, что бы была возможность написать единый диспетчер событий Debug Engine.

среда, 25 мая 2011 г.

Release 0.0.18

Хочу представить очередной релиз pykd. Хотя список изменений может показаться не впечатляющим, но за этими изменениями стоит напряженная работа. Были серьезно переработаны ключевые части проекта. Но сначала приведем формальный список изменений:

Новые функции:

loadWChars - юникодная версия существующей loadChars
rdmsr - возвращает значение MSR регистра

Поддержка исключений:

BaseException - базовый класс для всех исключений
MemoryException - класс исключений для ошибок чтения памяти
TypeException - класс исключений при работе с типами

Новые классы:

typedVar - переработанная версия класса typedVarClass
typeInfo - переработанная версия класса typeClass
debugEvent - базовый класс для создания обработчиков событий отладчика

Bugs fixed:

8669 - функция typedVar (теперь это класс ) создает объект для несуществующего типа
8655 - не поддерживается работа с неименованными членами структур

Прочее:

Добавили мелкую, но очень удобную фичу для команды pycmd: теперь модуль pykd импортируется при старте расширения автоматически. Иными словами, не надо писать from pykd import *.

Теперь подробнее о ключевых моментах. Их три:

Исключения

Начиная с этого релиза мы переходим к концепции возвращения ошибок через исключения. По нашему мнению, это упрощает и делает более стройным и логичным как код скриптов на питоне, так и внутреннюю реализацию на С++. В текущем релизе исключения возвращают все функции работы с типами ( через классы typeInfo и typedVar ) и все функции работы с памятью. Предлагаю попробовать выполнить в интерпретаторе python следующие команды:
>>> typeInfo( "nt", "AAAAAAA" )
>>> typedVar( "nt", "AAAAAA", 0xDEADBEEF )
>>> typedVar( "nt", "_EPROCESS", 0xDEADBEEF )
>>> loadWChars( 0xDEADBEEF, 100 )

Динамическое определение типов

Теперь создать типизированную переменную можно, даже если нет соответствующей символьной информации. Для этого у класса typeInfo предусмотрен метод append. Работать с ним не сложно:
>>>a = typeInfo()
>>>a.append( ushort_t, "ShortField")
>>>a.append( uchar_t, "CharArray", 10 )
>>>print a
unnamed   size = 12(0xc)
 +0  unsigned short   ShortField
 +2  unsigned char[]   CharArray
Хочу обратить внимание на переменные uchar_t и ushort_t. Откуда они взялись? Для базовых типов в модуль pykd добавлены глобальные переменные с типом typeInfo. Вот их полный список:
char_t
uchar_t
short_t
ushort_t
long_t
ulong_t
int_t
uint_t
ptr_t
double_t
longlong_t
ulonglong_t

Кроме того, говоря про класс typeInfo, хотелось бы отметить, что доступ полям описания типа возможен не только по имени, но и по индексу, в том числе с использованием нотации слайсов. Это может понадобится, если вы решите написать свою питоновскую версию команды отладчика dt.

Обработка событий отладчика

Если вы планируете с помощью pykd написать автоотладочное средство, а может даже и полноценный отладчик, скорее всего вам понадобится получать и обрабатывать различные события: загрузку и выгрузку модулей, старт процессов и так далее. Для этого мы разработали класс debugEvent. Он содержит виртуальные функции-обработчики событий. Если вы хотите реализовать собственную обработку того или иного события, вам достаточно создать класс-наследник debugEvent и переопределить нужные вам методы-обработчики. Текущая реализация содержит только два обработчика: onLoadModule и onUnloadModule.

Проиллюстрирую-ка я все это примером, а то слишком много слов и мало дела:
class ModuleEventMgr( debugEvent ):
 
    def onLoadModule( self, module ):
        dprintln( module.name() )
        return DEBUG_STATUS_GO_HANDLED  

evnetMgr = ModuleEvent()

Данный код будет выводить имена загружаемых модулей на экран. Все просто.


На этом хотелось бы закончить обзор релиза 0.0.18. Спасибо вам за то, что внимательно прочли. Как всегда, ждем от вас вопросов, предложений, благодарности и/или кусочки ненависти - все сгодится :).

И спасибо разработчикам за их нелегкий труд.