понедельник, 28 февраля 2011 г.

Синтетические символы: делаем дизассемблер WinDbg дружелюбнее

Многие не любят использовать WinDbg для отладки "чужих" модулей, (т.е. тех, к которым нет символов) из-за его не самого дружелюбного дизассемблера. Простой пример это отображение импортов. Например, имеем небольшую функцию:
0:000> uf 00401090
00401090 51 push ecx
00401091 56 push esi
00401092 8b742410 mov esi,dword ptr [esp+10h]
00401096 6a00 push 0
00401098 6a00 push 0
0040109a 8d44240c lea eax,[esp+0Ch]
0040109e 50 push eax
0040109f 6800040000 push 400h
004010a4 56 push esi
004010a5 6a00 push 0
004010a7 6800110000 push 1100h
004010ac ff1510c04000 call dword ptr [image+0xc010 (0040c010)]
004010b2 8b4c2404 mov ecx,dword ptr [esp+4]
004010b6 8b54240c mov edx,dword ptr [esp+0Ch]
004010ba 51 push ecx
004010bb 56 push esi
004010bc 52 push edx
004010bd 6824c24000 push offset image+0xc224 (0040c224)
004010c2 6a10 push 10h
004010c4 68fcc14000 push offset image+0xc1fc (0040c1fc)
004010c9 e832ffffff call image+0x1000 (00401000)
004010ce 8b44241c mov eax,dword ptr [esp+1Ch]
004010d2 83c418 add esp,18h
004010d5 50 push eax
004010d6 ff1514c04000 call dword ptr [image+0xc014 (0040c014)]
004010dc 5e pop esi
004010dd 59 pop ecx
004010de c3 ret

Так мы ее видим в статике дизассемблера WinDbg. Но стоит дойти до адреса 004010ac, как WinDbg вычислит значение по адресу 0040c010 и выведет следующую строку:
call dword ptr [image+0xc010 (0040c010) ds:0023:0040c010={kernel32!FormatMessageW (77e4f831)]
На самом деле, по адресу 0040c010 расположен элемент IAT (директория импортов PE). При этом логика WinDbg ясна: значение по адресу 0040c010 может измениться, поэтому он и не вычисляет значение содержимого IAT при статическом дизассемблировании. Но если мы уверены, что содержимое таблицы импорта уже сформировано, то было бы неплохо добавить символы на обращения по этим адресам.
Для подобного рода задач в Debugger Engine расширений WinDbg введено понятие синтетических символов (Synthetic Symbols). Фактически, это символ, добавленный пользователем по заданному виртуальному адресу. Синтетический символ, как и любой другой символ модуля, имеет атрибуты имени и размера. Смысл атрибута имени очевиден, а размер символа нужен для формирования символов вида: module!SymName+33. Важная особенность: при перезагрузке символов для модуля (reload), все добавленные синтетические символы удаляются.
Для добавления синтетического имени в расширении pykd есть 2-а пути:
  • Функция addSynSymbol(адрес_символа, размер_символа, имя_символа)
  • Метод addSynSymbol(смещение_символа, размер_символа, имя_символа) класса dbgModuleClass
Добавление синтетического символа через метод addSynSymbol касса dbgModuleClass отличается только тем, что в качестве адреса необходимо передать смещение относительно начала модуля.
А теперь вернемся к примеру функции, дизассемблерный листинг которой был приведен ранее. Для демонстрации работы запустим скрипт ~\samples\synimp.py, который принимает на вход адрес внутри модуля. Скрипт, для указанного модуля, добавляет импорты, как синтетические символы с префиксом "_imp_". После работы скрипта функция выглядит намного понятнее и о ее назначении можно догадаться без трассировки:
0:000> uf 00401090
00401090 51 push ecx
00401091 56 push esi
00401092 8b742410 mov esi,dword ptr [esp+10h]
00401096 6a00 push 0
00401098 6a00 push 0
0040109a 8d44240c lea eax,[esp+0Ch]
0040109e 50 push eax
0040109f 6800040000 push 400h
004010a4 56 push esi
004010a5 6a00 push 0
004010a7 6800110000 push 1100h
004010ac ff1510c04000 call dword ptr [image!_imp_kernel32!FormatMessageW (0040c010)]
004010b2 8b4c2404 mov ecx,dword ptr [esp+4]
004010b6 8b54240c mov edx,dword ptr [esp+0Ch]
004010ba 51 push ecx
004010bb 56 push esi
004010bc 52 push edx
004010bd 6824c24000 push offset image+0xc224 (0040c224)
004010c2 6a10 push 10h
004010c4 68fcc14000 push offset image+0xc1fc (0040c1fc)
004010c9 e832ffffff call image+0x1000 (00401000)
004010ce 8b44241c mov eax,dword ptr [esp+1Ch]
004010d2 83c418 add esp,18h
004010d5 50 push eax
004010d6 ff1514c04000 call dword ptr [image!_imp_kernel32!LocalFree (0040c014)]
004010dc 5e pop esi
004010dd 59 pop ecx
004010de c3 ret

Стоит обратить внимание, что функционал синтетических символов будет включен в сборку pykd с версии 0.0.16

воскресенье, 6 февраля 2011 г.

Release 0.0.15

Вышел очередной релиз pykd.

Исправили ряд мелких и не очень багов:

#8229 - При вызове функции loadModule для модулей отличных от nt появлялось сообщение об ошибке. Теперь - все работает четко. 
#8236 - Функции вывода текста ( dprint/dprintln ) не работали с UNICODE. В результате, приходилось писать:
dprint( str( loadUnicodeString( strAddr ) )
Теперь str - можно ( и нужно ) опустить.
#8239 - ф. ptrSignByte возвращала строку, а не число со знаком, что естсетвенно приводило к неправильной работе скриптов.

Добавили следующие функции:
locals()
Возвращает коллекцию локальных переменных в текущей области видимости. Применять можно при отладке, например так:
a = locals().a
while a > 10:
trace()
a = locals().a

Также, локальные переменные можно посмотреть для любого фрейма в коллекции, которую возвращает функция getCurrentStack. Примерно так:
frames = getCurrentStack()
for f in frames: dprintln( f.locals )  
typedVarArray( offset, moduleName, symbolName, arraySize )
Возвращает коллекцию элементов типа moduleName!symbolName, которые располагались в памяти в виде массива размерностью arraySize. Использовать можно примерно так:

nt = loadModule("nt")
poolVector = typedVarArray( nt.PoolVector, "nt", "_POOL_DESCRIPTOR*", 2 )

dprintln( "non paged pool: %x" % poolVector[0] )
dprintln( "paged pool: %x" % poolVector[1] )


четверг, 3 февраля 2011 г.

Сборка pykd вручную.

Без лишней воды, по пунктам:

1. Забираем исходники

2. Настраиваем boost::python 
2.1 Переменная окружения BOOST_ROOT
Должна указывать соответственно на каталог, где лежит используемая версия boost.
У меня это:  С:\lib\boost_1_40_0
2.2 Сборка boost::python
В настройках проекта забиты следующие пути к собранным библиотекам:
$(BOOST_ROOT)\stage - для х86 сборки
$(BOOST_ROOT)\stage64 - для х64 сборки
Чтобы собрать соответствующие библиотеки, нужно выполнить следующие команды, установив текущую директорию в $(BOOST_ROOT)
Для  x86:
bjam --stagedir=stage --with-python stage
 И для x64:
bjam address-model=64 --stagedir=stage64 --with-python stage
Возможно, вам понадобится сначала установить bjam  ( ссылка )
2.3 Указание версии python
Если у вас на машине есть несколько версий python и вы хотите явно указать, какую использовать, необходимо отредактировать файл user-config.jam и добавить в него строчки, к примеру, такие:
using python : 2.6 ;
using python : 2.7 ;
Теперь можно собрать библиотеки явно указав версию python
Для  x86:
bjam --stagedir=stage --with-python stage python=2.7
И для x64:
bjam address-model=64 --stagedir=stage64 --with-python stage python=2.7
Тут можно подробнее почитать:
http://www.boost.org/doc/libs/1_45_0/libs/python/doc/building.html

3. Настройка путей к python
Для сборки понадобятся заголовочные файлы и библиотека экспорта от соответствующей версии python. Для указания путей используется переменная окружения PYTHON_ROOT, которая должна указывать на каталог установки Python. Если при сборки boost::python была задана конкртная версия python, то перемнная PYTHON_ROOT должна ссылаться на инсталляцию python соответствующей версии.
К примеру, у меня на машине переменная PYTHON_ROOT = C:\Python26.  И установлены две версии ( x86 и x64) python:
C:\Python26\x86\ 
C:\Python26\x64\

4. Настройка путей к DBG SDK
Пути к DBG SDK  задаются через переменную окружения  DBG_SDK_ROOT. Сам DBG SDK находится в каталоге установки windbg.