diff --git a/docs/ru/documentation.txt b/docs/ru/documentation.txt
new file mode 100644
index 0000000..44b5aad
--- /dev/null
+++ b/docs/ru/documentation.txt
@@ -0,0 +1,898 @@
+{anchor:table}
+! Оглавление
+* [1. Введение|#intro]
+** [1.1 Общие сведения|#commoninfo]
+** [1.2 Быстрый старт|#gettingStarted]
+** [1.3 Сборка из исходников|#Building]
+** [1.4 Ручная установка|#Installing]
+** [1.5 Изменения в API|#APIchange]
+* [2. Команды для windbg|#windbg]
+** [2.1 Загрузка плагина|#loadplugin]
+** [2.2 Запуск скрипта|#runscript]
+** [ 2.3 режим консоли|#console]
+* [3. Управление отладкой|#debugging]
+** [3.1 Остановка и возобновление процесса отладки|#break]
+** [3.2 Пошаговое выполнение|#step]
+** [3.3 Управление отладкой из python приложений|#consoledebug]
+** [3.4 Печать отладочной информации|#dbgprint]
+** [3.5 Выполнение команд отладчика|#dbgcommand]
+** [3.6 Создание креш-дампа|#createcrash]
+* [4. Работа с памятью и регистрами|#memory]
+** [4.1 Доступ к регистрам общего назначения|#reg]
+** [4.2 Доступ к модельно специфичным регистрам|#msr]
+** [4.3 Нормализация виртуальных адресов|#addr64]
+** [4.4 Прямой доступ к памяти|#memaccess]
+** [4.5 Ошибки доступа к памяти|#memerror]
+** [4.6 Чтение строк из памяти|#memstr]
+* [5. Модули|#modules]
+** [5.1 Класс module|#moduleclass]
+** [5.2 События загрузки и выгрузки модулей|#moduleload]
+* [6. Получение символьной информации|#syminfo]
+** [6.1 Символьные ( pdb ) файлы|#pdbfile]
+** [6.2 Информация о типах|#types]
+* [7. Типизированные переменные|#typedVar]
+** [7.1 Класс typedVar|#typedVar]
+** [7.2 Создание экземпляра класса typedVar|#typedVarClass]
+** [7.3 Методы класса typedVar|#typedVarMethod]
+** [7.4 Классы и структуры|#typedVarUDT]
+** [7.5 Массивы и указатели|#typedVarArray]
+** [7.6 Энумераторы|#typedVarEnum]
+** [7.7 Приведениe к другим типам|#typedVarCast]
+* [8. Процессы и потоки|#ProcessThreads]
+** [8.1 Потоки в пользовательском режиме|#UserModeThreads]
+** [8.2 Потоки в режиме ядра|#KernelModeThreads]
+** [8.3 Процессы в режиме ядра|#KernelModeProcess]
+* [9. Локальные переменные|#Locals]
+** [9.1 Текущие локальные переменные|#CurrentLocals]
+* [10. Точки останова|#breakpoints]
+** [10.1 Задание точек останова|#setBreakpoints]
+** [10.2 Условные точки останова|#condBreakpoints]
+* [11. Отладочные события|#eventHandler]
+* [12. Класс disasm|#disasm]
+* [API Reference|PYKD 0.2. API Reference]
+{anchor:intro}
+! 1. Введение
+{anchor:commoninfo}
+!!! 1.1 Общие сведения
+Проект pykd стартовал в 2010 году. Основным мотивом для разрботки было неудобство встроенных средств для написания отладочных скриптов для windbg. Язык python был выбран в качестве альтернативного скриптового двжика по многим причинам: легкость изучения самого языка, наличие большой стандартной библиотеки, наличие мощного и удобного фреймворка для создания модулей расширения. Pykd представляет собой модуль для интерпретатора CPython. Сам pykd написан на C++ и использует Boost.Python для экспорта функция и классов в Python. Pykd предоставляет доступ к управлению отладкой на платформе Windows через библиотеку Debug Engine и получению символьной информации через библиотеку MS DIA. Отметим, что pykd не дает прямого доступа к COM интерфейсам Debug Engine и MS DIA. Вместо этого он реализует собственный интерфейс, делающий процесс разработки более быстрым и удобным ( мы на это надеемся ).
+
+Отметим, что pykd может работать в двух режимах: как плагин для windbg, в этом случае он предоставляет команды для запуска скриптов в контексте отладочной сесии; как отдельный модуль для интерпретатора python. Последний режим может быть полезен для создания автоматических средств разбора креш-дампов например.
+[Содержание|#table]
+{anchor:gettingStarted}
+!!! 1.2 Быстрый старт
+Для быстрого старта лучше всего скачать автоматический инсталлятор. Он сам установит все необходимые компоненты ( в т.ч. и Python если он еще не установлен ). Чтобы убедится, что установка прошла успешно, запускаем winbg и начинаем отладку приложения и или анализ дампа. Загружаем *pykd*:
+{{
+.load pykd.pyd
+}}
+Если не возникло никаких сообщений об ошибках, значит все нормально. Но на всякий случай убедимся, что все действительно работает:
+{{
+>!pycmd
+Python 2.6.5 (r265:79096, Mar 19 2010, 18:02:59) [MSC v.1500 64 bit (AMD64)] on win32
+Type "help", "copyright", "credits" or "license" for more information.
+(InteractiveConsole)
+>>>print "Hello world!"
+Hello world!
+>>>quit()
+>
+}}
+Пробуем запустить скрипты из примеров:
+{{
+!py help
+!py samples
+}}
+Если все получилось, можно переходить к написанию собственных скриптов.
+[Содержание|#table]
+{anchor:Building}
+!!! 1.3 Сборка из исходников
+!!!! Забираем исходники из [url:репозитория|http://pykd.codeplex.com/SourceControl/list/changesets].
+!!!! Устанавливаем python
+Скачать нужную версию можно [url:здесь|http://www.python.org].
+!!!! Устанавливаем и настраиваем boost.
+Если кто не знает, где взять: [url:http://www.boost.org]. Там же есть инструкция по установке и сборке.
+!!!! Устанавливаем переменные окружения.
+Для сборки требуются следующие переменные окружения:
+{"$(DIA_SDK_ROOT)"} - путь к библиотеке MS DIA. Он должен выглядеть примерно так: C:\Program Files (x86)\Microsoft Visual Studio 9.0\DIA SDK. Библиотека MS DIA устанавливается вместе с Visual Studio.
+{"$(DBG_SDK_ROOT)"} - путь к Debug Engine SDK Он должен выглядеть примерно так: C:\Program Files (x86)\Debugging Tools for Windows (x86)\sdk. Debug Engine SDK устанавливается вместе с Debugging Tools for Windows ( в настоящее время, входит в Platform SDK ).
+{"$(BOOST_ROOT)"}- путь к каталогу, куда установлен boost
+{"$(PYTHON_ROOT)"} - путь к каталогу, куда установлен python. Предполагается, что в системе установлены обе версии x86 и x64 и структура каталогов с python такая:
+C:\Python26\x86\...
+C:\Python26\x64\...
+Переменная {"$(PYTHON_ROOT)"} в этом случае должна быть равна C:\Python26. Если в пути установки python а отсутсвует указани платформы, то надо будет подправить проектный файл.
+!!!! Сборка библиотек boost.python
+Для сборки потребуются статические библиотеки boost.python. В проектном файле прописаны следеующие пути к библиотекам boost.python:
+{"$(BOOST_ROOT)\stage"} - для х86 сборки
+{"$(BOOST_ROOT)\stage64"} - для х64 сборки
+
+Собрать их можно с помощью команд:
+{"bjam --stagedir=stage --with-python stage"}
+{"bjam address-model=64 --stagedir=stage64 --with-python stage"}
+
+Если у вас не установлен еще bjam, то скачать его можно [url:здесь|http://www.boost.org/users/download/boost_jam_3_1_18.html]
+[Содержание|#table]
+{anchor:Installing}
+!!! 1.4 Ручная установка
+Для ручной установки потребуется сам модуль pykd.pyd {"C+ редистрибутив runtime С++"} от visual studio ( vcredist ), при чем именно тот, с которым был собрано моудль. Если вы собиралм его самостоятельно, с этим проблем быть не должно. Если скачали с сайта - также проблем быть не должно, так как zip архив содержит нужный vcredist ( если конечно мы ничего не напутали при релизе :) ).
+!!!! Куда скопировать pykd.pyd?
+Это зависит от сценария использования. Если pykd будет использоваться как плагин к windbg, то имеет смысл скопировать его в каталог winext ( он находится в каталоге, куда установлен windbg ). В этом случае, его можно переименовать в pykd.dll, чтобы при загрузке можно было опустить расширение файла:
+{{
+kd>.load pykd
+}}
+Если pykd будет использовать для написания своих python программ, то его нужно разместить там, где его сможет найти интерпретатор python. Есть три варианта:
+* Подкаталог Lib в катлоге, куда установлен python
+* Любой свой каталог. Путь к нему необходимо указать в переменной окружения $(PATHONPATH} или задать через реестр
+* Любой свой, путей нигде не прописывать, запускать python всегда из каталога с pykd.pyd
+!!!! Установка vcredist
+Конечно vcredist нужно установить. Иначе зачем его было скачивать?
+!!!! Регистрация MS DIA
+Библиотека MS DIA будет установлена во время инсталляции vcredist. Но для работы ее нужно еще зарегистрировать. Для этого необходимо найти каталог, куда был проинсталлирован модуль msdia90.dll и из этого каталога выполнить команду:
+{{
+regsvr32 msdia90.dll
+}}
+Если вы собирали модуль самостоятельно и использовали Visual Studio, то никаких действий с vcredist ом производить не надо - он уже есть на вашей машине и MS DIA также на месте.
+[Содержание|#table]
+{anchor:APIchange}
+!!! 1.5 Изменения в API
+!!!! loadModule
+Функция loadModule убрана. Вместо нее необходимо использовать конструктор класса *module*
+{{
+# mod = loadModule('mymodule")
+mod = module("mymodule")
+}}
+[Содержание|#table]
+{anchor:windbg}
+! 2. Команды для windbg
+{anchor:loadplugin}
+!!! 2.1 Загрузка плагина
+Для загрузки плагина в windbg необходимо выполнить команду:
+{{
+kd>.load pykd_path/pykd.pyd
+ }}
+Если pykd.pyd находится в каталоге winext ( подкаталог в Debugging Tools for Windows ), то путь к pykd можно не указывать:
+{{
+kd>.load pykd.pyd
+}}
+Если pykd.pyd переименовать в pykd.dll, то расширение можно не указывать:
+{{
+kd>.load pykd
+}}
+Просмотреть загруженные расширения windbg можно с помощью команды .chain
+{{
+kd>.chain
+}}
+Выгрузить плагин:
+{{
+kd>.unload pykd_path/pykd.pyd
+kd>.unload pykd.pyd
+kd>.unload pykd
+}}
+Чтобы не загружать pykd каждый раз, можно после загрузки плагина выполнить команду "Save Workspace". После этого pykd будет загружаться автоматически для данного воркспейса.
+[Содержание|#table]
+{anchor:runscript}
+!!! 2.2 Запуск скрипта
+Запуск скрипта осуществляется с помощью команды *!py*
+{{
+kd>!py script_path/script_name.py param1 param2 ...
+}}
+Расширение .py можно опустить. Чтобы не указывать полный путь к скрипту, нужно прописать его в переменную окружения PYTHONPATH или ( это более предпочтительный путь ), добавить ключ pykd в раздел реестра
+{"HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.6\PythonPath"}
+В этом случае пути к скриптам задаются в Default значении созданного ключа.
+
+К переменным в скрипте можно получить доступ через список sys.argv:
+{{
+import sys
+print "script path: " + sys.argv[0]
+print "param1: " + sys.argv[1]
+print "param2: " + sys.argv[2]
+}}
+[Содержание|#table]
+{anchor:console}
+!!! 2.3 режим консоли
+Запуск консоли python осуществляется командой !pycmd:
+{{
+1: kd> !pycmd
+Python 2.6.5 (r265:79096, Mar 19 2010, 18:02:59) [MSC v.1500 64 bit (AMD64)] on win32
+Type "help", "copyright", "credits" or "license" for more information.
+(InteractiveConsole)
+>>>
+}}
+Перед запуском будет автоматически выполнен импорт pykd, так что сразу можно вызывать функции pykd. Напомним, что выход из консольного режима осуществляется через функцию quit(). При этом состояние python интерператора сохраняется:
+{{
+>>> a = 10
+>>> quit(0)
+1: kd> !pycmd
+Python 2.6.5 (r265:79096, Mar 19 2010, 18:02:59) [MSC v.1500 64 bit (AMD64)] on win32
+Type "help", "copyright", "credits" or "license" for more information.
+(InteractiveConsole)
+>>> print a
+10
+>>>
+}}
+[Содержание|#table]
+{anchor:debugging}
+! 3. Управление отладкой
+{anchor:break}
+!!! 3.1 Остановка и возобновление процесса отладки
+В windbg для этого служат команды Break ( Ctrl+Break ) и Go ( F5 ).
+Их аналоги в pykd:
+*go()*
+*breakin()*
+
+*go* возобновляет процесс отладки и вернет управление только когда отладчик будет снова остановлен - сработает точка останова или отладка будет остановлена вручную через Ctrl+Break.
+Это нужно учитывать при написании скриптов. Функция может вернуть исключение *DbgException*. Обычно это происходит если отлаживаемый процесс завершается.
+{{
+try:
+ while True:
+ go()
+ print "break"
+ except:
+ print "process terminted"
+}}
+Данный скрипт будет обрабатывать любые остановки отладчика и автоматически возобновлять исполнение.
+
+Использовать функцию *breakin* при обычной работе врядли понадобится. Дело в том, что скрипты как правило выполняются только во время остановки отладчика. А в этот момент вызов функции *breakin* не имеет смысла. Для того, чтобы можно было останавливать процесс отладки из скрипта, придется создать отдельный поток в котором и вызывать эту функцию.
+*Внимание!* Не пытайтесь использовать функции *breakin*, *go*, *trace* внутри обработчиков отладочных событий ( например, в условных точках останова ).
+[Содержание|#table]
+{anchor:step}
+!!! 3.2 Пошаговое выполнение
+Для пошаговой отладки ( трассировки ) служат две функции:
+*step()*
+*trace()*
+Из действие аналогично командам отладки "trace into" и "trace over". Обе функции могут вернуть исключение *DbgException*, если отлаживаемый процесс уже завершился.
+[Содержание|#table]
+{anchor:debugstatus}
+{anchor:consoledebug}
+!!! 3.3 Управление отладкой из python приложений.
+Если вы хотите исполнять свои скрипты вне windbg, то первым шагом, который вам необходимо сделать, будет создание отладочной сессии. Более подробно управление сессиями будет рассмотрено в соответствующем разделе. Если ваше приложение не планрует использовать несколько сессий отладки, то заботится об этом не надо - первая сессия будет создана автоматически при следующих вызовах:
+*loadDump( dumpName )* - загружает креш-дамп
+*id startProcess( imageName)* - запускает в режиме отладки новый процесс
+*id attachProcess( processId)* - присоединяет отладчик к существующему процессу
+*attachKernel( parameterStr)* - присоединяет отладчик к ядру отлаживаемой системы
+
+Для отсоединения отладчика от отлаживаемого процесса служит вызов *detachProcess(id)*
+
+Для остановки отладки и удаления отлаживаемого процесса служит
+функция *killProcess(id)*
+
+Узнать, режим отладчика можно с помощью вызовов:
+* bool isDumpAnalyzing()
+* bool isKernelDebugging()
+Первая функция позволяет определить, находится ли отладчик в состоянии "живой" отладки или анализируется дамп памяти. Вторая функция позволяет различать отладку режима ядра или пользовательского режима. Если скрипт использует специфичные системные символы ( к примеру, символы ядра Windows ), то будет полезно вставить такую проверку в начале скрипта: это позволит сообщить пользователю, что он пытается запустить скрипт в неподходящей ситуации.
+
+[Содержание|#table]
+{anchor:dbgprint}
+!!! 3.4 Печать отладочной информации
+Для вывода информации на экран можно воспользоваться стандартным оператором *print*. Но рекомендуется использовать специальные функции:
+*dprint( message, dml = False )*
+*dprintln( message, dml = False )*
+Вторая функция отличается от первой тем, что автоматически добавляет символ перевода строки. Опциональный параметр *dml* включает вывод с DML разметкой ( работает только в windbg ). Разметка DML похожа на очень-очень упрощенный HTML. Форматирование текста осуществляется с помощью специальных тегов:
+* - выделенный шрифт
+* - курсив
+* - шрифт с подчеркиванием
+* command - выполнение команды ( похоже на тег в HTML )
+{{
+dprinln("This command reload all symbols", True)
+dprinln("reload", True)
+}}
+[Содержание|#table]
+{anchor:dbgcommand}
+!!! 3.5 Выполнение команд отладчика
+Для выполнения команды отладчика служит функция:
+*commandOutput dbgCommand( commandStr )*
+{{
+s = dbgCommand("!analyze -v")
+dprint(s)
+}}
+Для вычисления выражения ( аналог команды отладчика "?" ) служит функция
+*expressionStr expr( expressionStr )*
+{{
+expr("@rax + 10")
+}}
+При выполнении python программы вам могут понадобиться команды из стандартных расширений windbg ( например, !analyze ). Их придется загрузить вручную. Для этого служит функция:
+*long loadExt( extensionPath )*
+Эта функция возвращает дескриптор расширения, который нужен для вызова функция расширения:
+* str callExt( extHandle, command, params )
+И, если необходимо, для выгрузки расширения:
+* removeExt( extHandle )
+*Внимание!* Работа с раcширениями windbg отличается от версии *pykd* 0.1! В версии 0.2 упразднен класс *ext* и можно не заботится о времени жизни загруженного расширения.
+[Содержание|#table]
+{anchor:createcrash}
+!!! 3.6 Создание креш-дампа
+Сохранить состояние системы в виде креш-дампа можно с помощью ф. *writeDump*. Функция доступна для режима ядра и пользовательского режима. Второй параметр задает тип дампа ( False - полный дамп, True - минидамп )
+{{
+writeDump( r"C:\dump\fulldump.dmp", False )
+writeDump( r"C:\dump\minidump.dmp", True )
+}}
+[Содержание|#table]
+{anchor:memory}
+! 4. Работа с памятью и регистрами
+{anchor:reg}
+!!! 4.1 Доступ к регистрам общего назначения.
+Доступ к регистрам общего назначения ( GPR ) осуществляется с помощью ф. *reg*:
+* cpuReg reg( regName )
+* cpuReg reg( regIndex )
+Первый варинат принимает символьное имя регистра, второй - целочисленных индекс. Вторую форму можно использовать для перечисления регистров:
+{{
+import pykd
+
+try:
+ i = 0
+ while True:
+ r = pykd.reg(i)
+ pykd.dprintln( "%s %x ( %d )" % ( r.name(), r, r )
+ i += 1
+except pykd.BaseException:
+ pass
+}}
+Оба варианта функции *reg* возвращают экземпляр класса *cpuReg*. Если информация о регистре не может быть получена, будет возбужено исключение *BaseException*
+
+Класс *cpuReg* имеет два метода:
+* name()
+* index()
+Класс *cpuReg* может использовать в целочисленных вычислениях без дополнительных приведений типа:
+{{
+r = reg("eax")
+print r / 10 * 234
+}}
+*Внимание:*
+Текущая реализация *pykd* поддерживает работу только с целочисленными регистрами. Работа с FPU, MMX, SSE регистрами не поддерживается. Поддержка планируется в следующих версиях.
+[Содержание|#table]
+{anchor:msr}
+!!! 4.2 Доступ к модельно-специфичным регистрам ( MSR )
+Доступ к модельно-специфичный регистрам осуществляется через ф. *rdmsr( msrNumber )*:
+{{
+>>> print findSymbol( rdmsr( 0x176 ) )
+nt!KiFastCallEntry
+}}
+[Содержание|#table]
+{anchor:addr64}
+!!! 4.3 Нормализация виртуальных адресов
+Все функции *pykd* возвращают виртуальные адреса в т.н нормализованном виде. Он представляет собой 64 битное целое число. Для 32 битных платформ адрес раширяется с учетом знака до 64 битного. Эта операция на С выглядит так:
+{{
+ULONG64 addr64 = (ULONG64)(LONG)addr;
+}}
+Таким образом адреса будет преобразовываться так:
+0x00100000 -> 0x00000000 00100000
+0x80100000 -> 0xFFFFFFFF 80100000
+Это нужно учитывать, если адреса, которые возвращают функции *pykd*, участвуют в арифметических операциях. Для исключения возможных ошибок сравнения рекомендуется использовать ф. *addr64()*:
+{{
+import pykd
+nt = pykd.module("nt")
+if nt > addr64( 0x80000000 ):
+ print "nt module is in highest address space"
+}}
+[Содержание|#table]
+{anchor:memaccess}
+!!! 4.4 Прямой доступ к памяти
+Для доступа к памяти отлаживаемой системы *pykd* представляет большой набор функций.
+Для чтения целых беззанковых чисел служат следующие функции:
+* ptrByte( va )
+* ptrWord( va )
+* ptrDWord( va )
+* ptrQWord( va )
+Для получения результата в виде целых чисео со знаком служат аналогичные функции:
+* ptrSignByte( va )
+* ptrSignWord( va )
+* ptrSignDWord( va )
+* ptrSignQWord( va )
+Для удобства разработки кроссплатформенных скриптов служат функции:
+* ptrMWord(va)
+* ptrSignMWord(va)
+* ptrPtr(va)
+Они возвращают результат в зависимости от битности платформы - 32 или 64 бита
+Часто требуется прочесть блок памяти. Для этого служат функции:
+* loadBytes( va, count )
+* loadWords( va, count )
+* loadDWords( va, count )
+* loadQWords( va, count )
+* loadSignBytes( va, count )
+* loadSignWords( va, count )
+* loadSignDWords( va, count )
+* loadSignQWords( va, count )
+* loadPtrs( va, count )
+Все функции возвращают объект *list*.
+[Содержание|#table]
+{anchor:memerror}
+!!! 4.5 Ошибки доступа к памяти
+Все функции работы с памятью при невозможности прочесть данные по указанному адресу возвращают исключение *MemoryException*.
+{{
+try:
+ a = ptrByte( 0 )
+except MemoryException:
+ print "memory exception ocurred"
+}}
+Проверить валидность виртуально адреса можно с помощью ф. *isValid(va)*
+[Содержание|#table]
+{anchor:memstr}
+!!! 4.6 Чтение строк из памяти
+Часто приходится читать из памяти строковые данные. Конечно, для этого можно было бы использовать ф. *loadBytes*, но это не всегда удобно. Поэтому в *pykd* добавлен набор функций, возвращающих данные в виде строки.
+В первую очередь это:
+* loadChars( va, count )
+* loadWChars( va, count )
+Они работают совершенно также как loadBytes и loadWords. Отличие только в возвращаемом значении ( *string* вместо *list* ). Это позволяет использовать их, например, совместно с модулем *struct*:
+{{
+from struct import unpack
+shortField1, shortField2, longField = unpack('hhl', loadChars( addr, 8 ) )
+}}
+Для чтения 0-терминированных строк из памяти служат функции:
+* loadСStr( va )
+* loadWStr( va )
+Обе возвращают строки ( loadWStr - UNICODE ). Отметим небезопасность использования данных функций - ведь наличие терминирующего нуля никто не гарантирует! *Внимание!* Максимальная длина строки ограничена 64K. При попытке прочесть строку длинее, будет возвращено исключение MemoryException.
+
+В ядре Windows для представления строк используются структуры {"UNICODE_STRING"} и {"ANSI_STRING"}. Для работы с ними существуют соответствующие функции:
+* loadAnsiString
+* loadUnicodeString
+[Содержание|#table]
+{anchor:modules}
+! 5. Модули
+{anchor:moduleclass}
+!!! 5.1 Класс module
+Модуль - это исполняемый файл, отображенный на память. Обычная программа состоит из главного модуля ( как правило, с расширением .exe ) и набора библиотек. Работа с модулями осуществляется с помощью класса *module*.
+
+!!!! 5.1.1 Создание экземпляра класса *module*:
+Класс *module* имеет две формы конструктора:
+* module( moduleName )
+* module( va )
+Первая форма создает модуль по его имени, второя - по виртуальному адресу, принадлежащему модулю. Если модуль не найден, конструктор возбудит исключение BaseException.
+Пример:
+{{
+from pykd import *
+try
+ ntdll = module( "ntdll" )
+ print ntdll.name(), hex(ntdll.begin()), hex(ntdll.size())
+except BaseException:
+ print "module not found"
+}}
+!!!! 5.1.2 Получение информации о модуле
+Для этого служат слежующие методы класса *module*:
+* name() - возвращает имя модуля
+* image() - возвращает имя исполняемого файла
+* pdb() - возвращает имя и полный путь к файлу с символьной информацией
+* begin() - возвращает виртуальный адрес, по которому загржен модуль
+* end() - возвращает виртуальный адрес конца модуля
+* checksum() - возвращает контрольную сумму
+* timestamp() - возвращает временную метку
+* getVersion() - возвращает кортеж, предсталяющий версию модуля. Например: ( 1, 0, 6452, 0 )
+* queryVersion( valueName ) - возвращает значение из ресурсов моудля
+!!!! 5.1.3 Загрузка и доступ к символам.
+Для загрузки символьной информации служит метод *reload()*
+Для поиска виртуального адреса, соответствующего нужному символу служит метод *offset( symName )*. Если указаный символ не найден, будет возбужедено исключение *BaseException*. Вместо явного вызова ф. *offset* можно получить адрес, соответствующий символу, обратившись к нему как свойству класса *module*
+{{
+>>> nt = module("nt")
+>>> print hex( nt.offset("PsLoadedModuleList") )
+0xfffff801acb5ae80L
+>>> print hex( nt.__getattr__("PsLoadedModuleList") )
+0xfffff801acb5ae80L
+>>> print hex( nt.PsLoadedModuleList )
+0xfffff801acb5ae80L
+}}
+Иногда может понадобится RVA символа, получить его можно с помощью ф. *rva( symbolName )*.
+!!!! 5.1.4 Приведения к другим типам
+Экземпляр класса *module* имеет операторы приведения к строке ( __str__ ) и целому числу:
+{{
+>>> nt = module("nt")
+>>> print nt
+Module: nt
+Start: fffff801ac882000 End: fffff801acfc8000 Size: 746000
+Image: ntkrnlmp.exe
+Pdb: c:\sym\ntkrnlmp.pdb\569F266AE67D457D969D92298F8F98082\ntkrnlmp.pdb
+Timestamp: 4f7118bb
+Check Sum: 6b3b15
+
+>>> print hex(nt)
+fffff801ac882000
+}}
+Кроме того, экземпляр класса *module* может участвовать в арифметических операциях:
+{{
+>>> print hex( nt + 10 )
+0xfffff801ac88200aL
+}}
+!!!! 5.1.5 Получение информации о типе
+Кроме символов, описывающих переменные и функции ( сущности, которые имеют RVA ), могут быть символы, описывающие типы. Для них, естественно, RVA не задан.
+Если для модуля есть информация о типах, то ее можно получить через функцию *type( typeName )*. Эта функция возвращает экземпляр класса *typeInfo*, работа с которым будет рассмотрена позже.
+{{
+>>> nt = module("nt")
+>>> print nt.type("_MDL")
+struct/class: _MDL Size: 0x1c (28)
+ +0000 Next : _MDL*
+ +0004 Size : Int2B
+ +0006 MdlFlags : Int2B
+ +0008 Process : _EPROCESS*
+ +000c MappedSystemVa : Void*
+ +0010 StartVa : Void*
+ +0014 ByteCount : ULong
+ +0018 ByteOffset : ULong
+}}
+!!!! 5.1.6 Типизированные переменные
+*pykd* позволяет упростить работу с сложными типами, такими как классы и структуры. За это отвечает специальный класс *typedVar*. Получить экземпляр класса *typedVar* можно через методы класса *module*:
+* typedVar( va )
+* typedVar( symbolName )
+* typedVar( typeName, va )
+{{
+>>> nt = module("nt")
+>>> print nt.typedVar( "_LIST_ENTRY", nt.PsLoadedModuleList )
+struct/class: _LIST_ENTRY at 0xfffff8000369c650
+ +0000 Flink : _LIST_ENTRY* 0xfffffa8003c64890
+ +0008 Blink : _LIST_ENTRY* 0xfffffa80092f8f30
+}}
+[Содержание|#table]
+{anchor:moduleload}
+!!! 5.2 Обработка событий загрузки и выгрузки модуля
+Для обработки событий загрузки и выгрузки модуля надо создать наследника класса [eventHandler|#eventHandler].
+Обработка события загрузки модуля осуществляется методом [onLoadModule|#eventHandler_onLoadModule]. Обработка события выгрузки модуля - [onUnloadModule|#eventHandler_onUnloadModule]
+[Содержание|#table]
+{anchor:syminfo}
+! 6. Получение символьной информации
+{anchor:pdbfile}
+!!! 6.1 Символьные ( pdb ) файлы
+При сборке модуля создается файл с символьной ( отладочной ) информацией ( обычно, с раширением pdb ). В зависимости от настроек компилятора он может содержать полную или обрезанную информацию ( т.н "публичные символы" ). Символьные файлы могут содержать следующую информацию:
+* Имена, типы и относительные смещения глобальных переменных и констант
+* Имена, параметры и относительные смещения функций и методов классов
+* Имена типов, определенных пользователем ( структур, классов, перечислений )
+* Значения констант
+* Информацию о локальных переменных функций и методов классов.
+Для работы с символьными файлами Microsoft предоставляет специальную библиотеку MS DIA. *Pykd* использует ее для работы с символами. Для непосредственного доступа к символьной инфорамции *pykd* реализует свой собственный интерфейс.
+[Содержание|#table]
+{anchor:types}
+!!! 6.2 Информация о типах
+{anchor:typeInfo}
+!!!! 6.2.1 Класс представления типа
+Для представления информации о типе в питон экспортируется класс *typeInfo*. Этим классом описываются структуры, классы, объединения, перечисления, битовые поля, указатели и базовые типы.
+Класс представляет следующие методы:
+* *name* - получение имени типа
+* *size* - получение полного размера типа
+* *staticOffset* - получение смещения статического поля
+* *fieldOffset* - получение смещения поля
+* *bitOffset* - получение смещения битового поля
+* *bitWidth* - получение размера битового поля
+* *field* - получение поля
+* *asMap* - получение словаря для значения преречисления
+* *deref* - разыменование указателя
+* *ptrTo* - формирование указателя на тип
+* *arrayOf* - формирование массива, элементами которого является тип
+* *append* - добавление поля (метод для структур и перечислений, созданных с использованием [typeBuilder|#typeBuilder])
+
+[Содержание|#table]
+{anchor:get_typeInfo}
+!!!! 6.2.2 Получение объекта типа
+Объект типа можно получить вызовом конструктора, передав в него имя типа. Передаваемая строка может содержать как полную спецификацию типа ("имя_модуля!имя_типа"), так и просто "имя_типа".
+Объект типа можно получить _косвенным_ образом:
+* метод *type* у объекта типа *module* - формирование объекта типа по имени
+* метод *type* у объекта типа *typedVar* - формирование объекта типа, который имеет переменная
+
+Пример (печать структуры {"_UNICODE_STRING"} из ntdll):
+{{
+>>> us = module("ntdll").type("_UNICODE_STRING")
+>>> print us
+class/struct : _UNICODE_STRING Size: 0x10 (16)
+ +0000 Length : UInt2B
+ +0002 MaximumLength : UInt2B
+ +0008 Buffer : UInt2B*
+}}
+Для получения всех типов модуля можно использовать *enumTypes* у объекта типа *module*, который возвращает список имен типов, информация о которых представлена в отладочных символах модуля.
+
+[Содержание|#table]
+{anchor:typeBuilder}
+!!!! 6.2.3 Создание типов, не представленных в отладочных символах
+Часто при отладке нужные типы данных отсутствуют в отладочных символах. Для удобства работы был написан отдельный класс по созданию собственных структур и объединений - *typeBuilder*.
+Сложные типы данных, как правило, сводятся к набору базовых типов. Для получения базовых типов объект *typeBuilder* имеет следующие поля:
+* UInt1B
+* UInt2B
+* UInt4B
+* UInt8B
+* Int1B
+* Int2B
+* Int4B
+* Int8B
+* Long
+* ULong
+* Bool
+* Char
+* WChar
+* VoidPtr
+
+Отдельно стоит сказать и поле VoidPtr - указатель на void. Размер этого типа зависит от платформы и текущего режима отладки. Но для удобства при создании объекта *typeBuilder* в конструктор можно передать желаемый размер указателя.
+Имея базовые типы, а так же типы из отладочной информации модулей, можно строить произвольные типы. Для этого объект *typeBuilder* имеет два метода: *createStruct* и *createUnion*. Оба метода первым параметром методы принимают строку имени типа. При создании структуры вторым необязательным параметром можно указать желаемое выравнивание.
+Пример (создание и печать собственной структуры {"_UNICODE_STRING"}):
+{{
+>>> tb = typeBuilder()
+>>> us = tb.createStruct("_UNICODE_STRING")
+>>> us.append("Length", tb.UInt2B)
+>>> us.append("MaximumLength", tb.UInt2B)
+>>> us.append("Buffer", tb.WChar.ptrTo())
+>>> print us
+class/struct : _UNICODE_STRING Size: 0x10 (16)
+ +0000 Length : UInt2B
+ +0002 MaximumLength : UInt2B
+ +0008 Buffer : WChar*
+}}
+
+[Содержание|#table]
+{anchor:typedVar}
+! 7. Типизированные переменные
+{anchor:typedVar}
+!!! 7.1 Класс typedVar
+Ранее мы рассмотрели пример, как можно прочесть из памяти структурированные данные:
+{{
+from struct import unpack
+shortField1, shortField2, longField = unpack('hhl', loadChars( addr, 8 ) )
+}}
+Очевидно, что работать с большими структрами, содержащими сотни полей, так не очень удобно. Поэтому в *pykd* реализован специальный класс: *typedVar*, позволяющий работать со сложными структурами данных. Информацию о типе данных *typedVar* получает из символьной информации.
+[Содержание|#table]
+{anchor:typedVarClass}
+!!! 7.2 Создание экземпляра класса typedVar
+Существует несколько перегруженных конструкторов класса *typedVar*:
+* typedVar( symbolName )
+* typedVar( typeName, va )
+* typedVar( typeInfo, va )
+{{
+t1 = typedVar( "MyModule!MyVar" )
+t2 = typedVar( "MyModule!MyType", addr )
+ti = typeInfo( "MyModule!MyType" )
+t3 = typedVar( ti, addr )
+}}
+Все три способа приведут к одинаковому результату, если _addr_ - адрес переменной MyVar. Отметим, что все эти способы ( и в особенности первый ) не являются оптимальными в плане производительности, так как существенное время может уходить на поиск символьной информации. Если есть возможность, лучше воспользоваться методами класса *module*:
+* module.typedVar( va )
+* module.typedVar( symbolName )
+* module.typedVar( typeName, va )
+{{
+mod = module("MyModule")
+t4 = mod.typedVar( addr )
+t5 = mod.typedVar( "MyVar" )
+t6 = mod.typedVar( "MyType", addr )
+}}
+Результат будет аналогичный прямому вызову конструктора. Однако экземпляр класса *module* оптимизирует доступ к символьной информации.
+
+В случае ошибки в задании имени переменной или типа будет возбуждено исключение *SymbolException*.
+{{
+try:
+ typedVar( "MyModule!NotExistVar")
+except SymbolException:
+ print "The var does not exist"
+}}
+[Содержание|#table]
+{anchor: typedVarMethod}
+!!! 7.3 Методы класса typedVar
+* getAddress() - возвращает адрес переменной
+* sizeof() - возвращает размер переменной
+* offset() - если переменная является полем родительской структуры, то возвращает смещение относительно родителя.
+* field( fieldName ) - возвращает поле структуры как экземпляр класса *typedVar*
+* deref() - для указателей выполняет т.н. разыменование и возвращает результат в виде экземпляра класса *typedVar*
+* type() - возвращает тип переменной в виде экземпляра класса *typeInfo*
+[Содержание|#table]
+{anchor:typedVarUDT}
+!!! 7.4 Классы и структуры
+Получить доступ к полям структуры можно с помощью метода *field*. Для удобства использования добавлен метод для доступ к полям как к аттрибутам класса:
+{{
+>>>tv = typedVar( "structVar")
+>>>tv.field("m_field) == tv.m_field
+True
+}}
+Кроме того, можно получить доступ к полям структуры по индексу:
+{{
+tv = typedVar( "structVar")
+for i in range(0,len(tv) )
+ fieldName, fieldValue = tv[i]
+ print fieldName, fieldValue
+}}
+Как видно из примера, в этом случае возвращается кортеж ( tuple ) из имени поля и его значения. Тот же пример можно записать короче:
+{{
+tv = typedVar( "structVar")
+for fieldName, fieldValue in tv:
+ print fieldName, fieldValue
+}}
+
+Переменные типа *typedVar* могут участвовать в арифметических операцих. В качестве значения берется адрес переменной. *Внимание:* при арифметических операциях *не действуют* правила адресной аримфетики Си. Адрес будет трактоваться просто как число и , соответственно, var+1 просто инкрементирует знаечние адреса.
+
+[Содержание|#table]
+{anchor:typedVarArray}
+!!! Массивы и указатели
+Класс *typedVar* позволяет работать с массивами, в том числе многомерными. Для доступа к элементам массива нужно использовать оператор индекса []:
+{{
+>>> tv = typedVar( "intMatrix" )
+>>> print tv
+Int4B[2][3] at 0x13f159150
+>>> print tv[1]
+Int4B[3] at 0x13f15915c
+>>> print tv[1][2]
+Int4B at 0x13f159164 Value: 0x5 (5)
+}}
+Класс *typedVar* может работать так же и с указателями. Для "разыменования" указателя служит функция *deref()*:
+{{
+>>> tv = typedVar("ptrIntMatrix")
+>>> print tv
+Ptr Int4B(*)[2][3] at 0x13f1591c0 Value: 0x13f159150
+>>> print tv.deref()
+Int4B[2][3] at 0x13f159150
+>>> print tv.deref()[1][2]
+Int4B at 0x13f159164 Value: 0x5 (5)
+}}
+
+Переменные *typedVar* могут участвовать в арифметический выражениях. Для массивов в качестве значения берется его адрес, для указателя - значение указателя ( т.е куда он указывает ). *Внимание:* при арифметических операциях *не действуют* правила адресной аримфетики Си.
+
+[Содержание|#table]
+{anchor:typedVarEnum}
+!!! 7.6 Энумераторы
+Для работы с энумераторами будет полезно получить доуступ к информации о типе энумератора, так как именно через нее можно получить соответствие численных констант и символьных имен. Сделать это можно через метод *type*, который возвращает ссылку на переменную типа *typeInfo*:
+{{
+var = typedVar( "myStruct" )
+if var.structType == var.structType.type().TYPE_ONE:
+ print "TYPE_ONE"
+else:
+ print "ANOTHER_TYPE"
+}}
+Класс *typeInfo* имеет метод *asMap()*, который для энумераторов возвращает объект типа dict, в котором ключами являются числовые константы, а значениями - их символьное представление:
+{{
+var = typedVar( "myStruct" )
+{
+"TYPE_ONE" : lambda var.field_one
+"TYPE_TWO" : lambda var.field_two
+}[ var.type().asMap[ var.structType ] ]()
+}}
+Данный пример во-первых демонстрирует как сделать на python логическую структуру, аналогичную оператору switch в Cи: для этого можно использовать тип dict, значения полей которого являются лямбда-выражениями. Во-вторых, он показывает как для энумератора получить соответствие численной константы и символьного имени.
+
+Экземпляры *typedVar*, содержащие энумераторы, можно использовать в арифметических операциях:
+{{
+>>>var = typedVar( "myStruct" )
+>>>print var.structType *2 + 10
+}}
+[Содержание|#table]
+{anchor:typedVarCast}
+!!! 7.7 Приведениe к другим типам
+Класс *typedVar* имеет операторы приведения к строке (__str__) и к целому числу ( __long__ ).
+{{
+>>> print str( typedVar("g_struct") )
+struct/class: struct3 at 0x13f4391f8
+ +0000 m_arrayField : Int4B[2]
+ +0008 m_noArrayField : Int4B 0x3 (3)
+}}
+Целочисленное значение, возвращаемое функций long(), зависит от типа данных, хранящизся в typedVar:
+* Базовые типы - возвращается непосредственное значение
+* Структуры, классы и объединения - возвращается значение указателя на начало данных
+* Энумераторы - возвращается непосредственное занчение
+* Указатели - возвращается непосредственное значение
+* Массивы - возвращается значение указателя на начало данных
+{{
+>>> long( typedVar("g_struct").m_noArrayField )
+3L
+>>> hex( long( typedVar("g_struct").m_arrayField ) )
+'0x13f4391f8L'
+>>>
+>>> long( typedVar("g_struct").m_arrayField[1] )
+2L
+}}
+[Содержание|#table]
+{anchor:ProcessThreads}
+! 8. Процессы и потоки
+{anchor:UserModeThreads}
+!!! 8.1 Потоки в пользовательском режиме
+В пользовательском режиме отладчик работает в контексте отлаживаемого процесса. Если процесс имеет несколько потоков, в режиме отладки можно переключить контекст на другой поток. Нужно различать текущий поток и поток, на который переключился отладчик. В оригинале они называются: "current thread" - поток который продолжит выполнение после возобновления отладки, "implicit thread" - поток, в контексте которого находится отладчик. Контекстом потока мы называем совокупность регистров процессора, в том числе и указатель текущей инструкции и стека.
+Для смены контекста потока служит функция *setImplicitThread*. В качестве параметра она принимает указатель на TEB (thread enviroment block). Получить указатель на TEB можно с помощью:
+* *getImplicitThread* - TEB текущего потока
+* *getProcessThreads* - список TEB-ов всех потоков процесса
+[Содержание|#table]
+{anchor:KernelModeThreads}
+!!! 8.2 Потоки в режиме ядра
+В режиме ядра есть некоторые особенности.
+Во-первых, функции *setImplicitThread* и *getImplicitThread* работают с указателями на ETHREAD, а не с TEB, как в пользовательском режиме. Во-вторых, ф. *getProcessThreads* для режима ядра не доступна. Если нужно получить список потоков какого либо процесса придется делать это вручную разбирая структуру EPROCESS. К счастью, это не сложно:
+{{
+nt = module("nt")
+process = nt.typedVar( "_EPROCESS", processAddr )
+threadLst = nt.typedVarList(process.ThreadListHead, "_ETHREAD", "ThreadListEntry")
+}}
+Остается добавить, что переключение контекста отлаживаемого потока не приводит к переключению контекста процесса. Подробнее об этом в следующем разделе.
+[Содержание|#table]
+{anchor:KernelModeProcess}
+!!! 8.3 Процессы в режиме ядра
+[Содержание|#table]
+{anchor:Locals}
+! 9. Локальные переменные
+{anchor:CurrentLocals}
+!!! 9.1 Текущие локальные переменные
+При отладке приложения, ядра системы или анализе аварийного дампа, всегда присутствует текущий поток, а в этом потоке есть текущий фрейм. Если у нас есть отладочная информация о модуле, котрому принадлежит данный фрейм и в отладочной информации присутствует информация о локальных переменных, то мы можем получить доступ к ним в удобной форме, без явных операций с регистрами и стеком. Для этого служит ф. *getLocals()*. Она возвращает объект типа *dict*, ключом является имя переменной, а значением - экземпляр класса *typedVar*:
+{{
+# print local variable "argc"
+print getLocals()["argc"]
+
+# print all local vairables in the current frame
+for varName, varValue in getLocals().items():
+ print varName, varValue
+}}
+[Содержание|#table]
+{anchor:breakpoints}
+! 10. Точки останова
+{anchor:setBreakpoints}
+!!! 10.1 Задание точек останова
+Для задания точки останова служит функция *setBp*. Она позволяет устанавливать как программные точки останова, так и аппаратные. Функция возвращает числовой идентификатор, который можно в последствии использовать для удаления точки останова через ф. *removeBp*.
+Установка программной точки останова:
+{{
+nt = module("nt")
+bpid = setBp( nt.NtCreateFile )
+}}
+Установка аппаратной точки останова:
+{{
+nt = module("nt")
+bpid = setBp( nt.NtCreateFile, 1, 4 )
+}}
+Второй параметр - размер памяти, к которой осуществляется доступ, Третий параметр - тип доступа ( 1 - чтение, 2- запись, 4 - исполнение, типы доступа работают как флаги и могут быть объединены. Например, 3 - чтение + запись ).
+
+[Содержание|#table]
+{anchor:condBreakpoints}
+!!! 10.2 Условные точки останова
+Для организации точек останова с условием используется функция обратного вызова, которая передается в качестве параметра в вызов *setBp*. Эта функция обязана принимать один параметр ( туда передается идентификатор сработавшей точки останова ). Чтобы точка останова сработала, функция обратного вызова должна вернуть *True*
+{{
+import fnmatch
+from pykd import *
+
+nt = module('nt')
+objAttrType = nt.type( "_OBJECT_ATTRIBUTES" )
+
+def onCreateFile( id ):
+ objattr = typedVar( objAttrType, ptrPtr( reg('esp') + 0xC ) )
+ return fnmatch.fnmatch( loadUnicodeString( objattr.ObjectName ), '*.exe' )
+
+setBp( nt.NtCreateFile, onCreateFile )
+}}
+Нужно обратить внимание, что в качестве функции обратного вызова может выступать лямбда-функция:
+{{
+setBp( myAddr, lambda id: reg('rax') > 0x1000 )
+}}
+Помните о времени жизни объектов, создаваемых в скриптах! Функции - в python такие же объекты и при завершении виртуальной машины python они будут удалены. И тут возможна следующая ловушка: выполнив предыдущий скрипт с помощью команды "!py setmybreak.py" мы не получим ожидаемого срабатывания точки останова, она будет удалена во время завершения работы скрипта. Что же делать? Есть два варианта:
+1. Использовать в скрипте управление отладкой, примерно так:
+{{
+setBp( nt.NtCreateFile, onCreateFile )
+go()
+}}
+В таком случае, мы поймаем ровно одно срабатывание точки останова, далее скрипт завершится.
+2. Использовать для установки точки останова команду !pycmd.
+Напомним данная команда создает _глобальный_ интерпретатор python и все объекты python продолжают быть доступными даже после выполнения команды _quit()_:
+{{
+>!pycmd
+>>>import setmybreak
+>>>quit()
+>g
+}}
+При импортировании модуля будут выполнены все действия по установке точке останова и даже после выхода из консоли функции обратного вызова на python будут работать!
+
+*Внимание!*
+Функции обратного вызова имеют некоторые ограничения на использование API *pykd*:
+* Нельзя вызывать функции, которые могут изменить состояние отладчика: go, breakin, trace
+* Нельзя вызывать функции, которые могут привести к появлению или уничтожению отладочных сессий: startProcess, killProcess, openDump и.т.д
+* Нельзя манипулировать контекстами потоков и процессов ( setCurrentProcess, setImplicitThread )
+
+[Содержание|#table]
+{anchor:eventHandler}
+! 11. Отладочные события
+[Содержание|#table]
+{anchor:eventHandler_onBreakpoint}
+!!! 11.1 Обработка точек останова (метод onBreakpoint)
+[Содержание|#table]
+{anchor:eventHandler_onException}
+!!! 11.2 Обработка исключительных ситуаций (метод onException)
+[Содержание|#table]
+{anchor:eventHandler_onLoadModule}
+!!! 11.3 Обработка события загрузки исполняемого модуля (метод onLoadModule)
+[Содержание|#table]
+{anchor:eventHandler_onUnloadModule}
+!!! 11.4 Отработка события выгрузки исполняемого модуля (метод onUnloadModule)
+[Содержание|#table]
+{anchor:disasm}
+! 12. Класс disasm
+[disasm reference|PYKD 0.1. API Reference#disasm]
+Класс *disasm* является оболочкой над дизассемблером из Debug Engine. Соответственно, результаты его работы такие же, как и у команды u в kd/cdb/windbg.
+Класс *disasm* имеет следующие методы:
+* {"__init__()"} - создает дизассемблер, который начнет работу с текущей инструкции
+* {"__init__( offset )"} - создает дизассемблер, который начнет работу с указанного смещения
+* disasm() - возвращает дизассемблированное представление инструкции CPU с текущего смещения и переходит к следующей инструкции
+* disasm( offest ) - возвращает дизассмблированное представление инструкции CPU с текущего смещения и переходит к следующей инструкции
+* asm( code ) - ассемблирует указанную инструкциб и меняет машинный код по указанному смещению
+* begin() - возвращает смещение, заданное при созданни экземпляра класса *disasm*
+* current() - возвращает текущее смещение
+* length() - возвращает длину текущей инструкции CPU
+* instruction() - возвращает дизассемблированное представление инструкции CPU по текущему смещению
+* ea() - возвращает эффективный адрес последней дизассемблированной инструкции или 0
+* reset() - аналог вызова self.disasm( self.begin() )
+
+Эффективный адрес - это адрес операнда находящегося в памяти. Например, для инструкции
+{"mov ecx, [esi+0x10]"}
+Эффективным адресом будет занчение esi + 0x10. Очевидно, что это значение имеет смысл при дизассемблировании текущей инструкции.
+[Содержание|#table]
\ No newline at end of file
diff --git a/docs/ru/tutorial.txt b/docs/ru/tutorial.txt
new file mode 100644
index 0000000..92443b6
--- /dev/null
+++ b/docs/ru/tutorial.txt
@@ -0,0 +1,125 @@
+!! Введение
+
+!!!! Шаг 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!"
+}}
+
+
+
+
+
+
+
+
+
+