воскресенье, 24 апреля 2011 г.

Анализ сетевых пакетов в Vista/Windows 7

Сетевые пакеты в Vista/Windows 7 описываются структурой NET_BUFFER и списками NET_BUFFER_LIST. Мы написали скрипт, который анализирует содержимое пакетов с учетом сетевых протоколов. Сам скрипт можно взять здесь: nbl.py

Использовать его просто. Установим брейкпойнт:
bp ndis!NdisFIndicateReceiveNetBufferLists

При его срабатывании (а это при наличии сетевого подключения случится очень быстро ) выполним команду:
!py nbl @rdx ( для amd64 в rdx лежит указатель на NET_BUFFER_LIST )

В результате получим что то вроде:

Length: 92 bytes
Ethernet header: OK
    Dest MAC: ff-ff-ff-ff-ff-ff
    Src MAC: 20-cf-30-70-25-e2
    Type: IPv4
IPv4 header: OK
    version: 4
    header length: 20 bytes
    total length: 78 bytes
    protocol: UDP
    TTL: 128
    Src addr: 10.244.0.50
    Dest addr: 10.244.7.255
UDP header: OK
    Src port: 137
    Dest port: 137
    Length: 58
    Checksum: 0xec8a

Поодерживается разбор заголовков Ethernet, IPv4, UDP, TCP. В будущем, возможно, добавим поддержку и других протоколов. Если кому то скрипт окажется полезен - пишите, и это будущее может стать настоящим :).

UPD1:
Eще у нас есть скрипт, отображающий внтуренние структуры NDIS ( минпорты, протоколы и.т.д ) и их взаимосвязь: ndis.py, иногда бывает полезно, надеемся, будет и вам.

пятница, 22 апреля 2011 г.

Работа с памятью

Все данные лежат в памяти, это очевидно. Ранее, мы уже рассмотрели (ссылка), как с помощью фцнкции typedVar получить доступ к содержимому переменной с учетом ее типа. Но бывают ситуации, когда мы не знаем тип, или содержимое памяти представляет собой сырой нетипизированный массив. Для работы с такими массивами предназначены следующие функции:
loadBytes
loadWords
loadDWords
loadQWords
- эти функции возвращают списки ( list ) беззнаковых целых соответствующей разрядности;

loadSignBytes
loadSignWords
loadSignDWords
loadSignQWords
- эти функции возвращают списки целых чисел со знаком.

Все функции имеют одинаковый прототип:

list  loadIntegerType( address, number, phyAddr = False )

address - адрес, по которому расположен массив
number - количество элементов массива
phyAddr - является ли адрес физическим ( не виртуальным ).

Остается привести какой-нибудь разумный пример:
from pykd import *

def dd( addr ):
    buf = loadDWords( addr, 16 )
    for i in xrange( len(buf) ):
        dprint( "%08x" % buf[i] ) 
        dprint( (i % 4 < 3) and "  " or "\n" )
Данный код выводит на экран дамп 4 байтных чисел. Можно доработать данный код и получить полный аналог команды отладчика dd.

Для обработки сырых буферов средставами станадртных или сторонних библиотек, добавлена функция loadChars. Она работает также как и loadBytes, но возвращает не список, а строку ( string ). К примеру, разбирать данные можно с помощью стандартного модуля struct:
from struct import unpack
shortField1, shortField2, longField = unpack('hhl', loadChars( addr, 8 ) )

От функции loadChars плавно перейдем к функциям работы со строками: loadCStr и loadWStr. Эту функции предназначены для работы с 0-терминированными строками. Первая для работы с ANSI-строками, вторая - с UNICODE. На правах КО обращу внимание, что работа с этими функциями небезопасна в том смысле, что наличие терминирующего нуля никем не гарантируется.

Для горячо любимых нами разработчиков системного ПО для Windows есть еще две полезных функции: loadUnicodeString и loadAnsiString. Они трактуют переданный адрес как указатель на структуру ANSI_STRING или UNICODE_STRING и, соответственно, избавляют от необходимости в ручную разбирать эти стуркутры для получения строки.

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

Release 0.0.17

Представляем очередной релиз pykd: 0.0.17. И так что нового:

Документация

Прежде всего, мы сделали нечеловеческое усилие над собой и добавили документацию для всех функций и классов. Правда документация пока очень-очень-очень скупая, но мы обещаем над этим поработать. А чтобы продемонстрировать, как это работает, мы написали скрипт help.py ( его можно взять тут ) выводящий интерактивный хелп в windbg. Пробуем:
0. загружаем плагин .load pykd.pyd
1. нажимаем ctrl+N
2. в строке ввода набираем !py help
4. наслаждаемся.

Добавили в API несколько функций:

bool isDumpAnalyzing()
Позволяет определить, анализируется ли в данным момент дамп или производится отладка живой системы. Если в скрипте используются функции управления отладчиком ( go, управления точками останова и.т.д ), имеет смысл вставить данный вызов и уберечь пользователя от неизбежных ошибок при попытке исполнения скрипта во время анализа дампа

bool isWindbgExt()
Позволяет определить, исполняется ли скрипт внутри WinDbg или вызван процессом интерпретатором python. Ранее для этого использовался неявный метод и вызов isSesionStart. Теперь isSessionStart удалена, этот вызов следует заменить в коде на isWindbgExt.

string loadChars(address, number)
В стандартной библиотеке python существует ряд средств для работы с массивами "сырых" данных. Все они для представления буфера данных используют класс string. Для того, чтобы не заниматься преобразованием списка байт, которые возвращает функция loadBytes мы добавили функцию loadChars.
Ее удобно использовать, например, вместе с модулем struct:
>>> from struct import unpack
>>> shortField1, shortField2, longField = unpack('hhl', loadChars( addr, 8 ) )

Методы checksum() and timestamp() класса dbgModuleClass
Из названия методов должно быть ясно, их назначение. Если Вам придет в голову написать собственный загрузчик символов ( почему бы и нет? ), то они вам могут пригодится.

callbacks for bp class ( breakpoint )
Это, можно сказать, изюминка данного релиза. До сих пор, пользоваться точками останова ( breakpoint ) из pykd было не слишком удобно. Теперь при создании новой точки останова можно задать функцию обратного вызова и обрабатывать из в событийно-ориентированном стиле.

Пример:
from pykd import *

def bpCallback():

    if is64bitSystem():
        objAttr = typedVar( "ntdll", "_OBJECT_ATTRIBUTES", reg("r8") ) 
    else:
        objAttr = typedVar( "ntdll", "_OBJECT_ATTRIBUTES", ptrPtr(reg("esp") + 0xC) )  

    name = loadUnicodeString( objAttr.ObjectName )

    dprintln( "NtCreateFile: " + name )

    return DEBUG_STATUS_GO_HANDLED

if not isWindbgExt():
    startProcess("notepad.exe")

if not isDumpAnalyzing() and not isKernelDebugging():        
    nt = loadModule("ntdll")
    b1 = bp( nt.NtCreateFile, bpCallback )   
    # wait for user break, exceptions or process exit
    go()
    dprintln( "stopped" )    
else:
    dprintln( "The debugger must be connected to live usermode process" )

typedVarList
При работе со связанными списками в (ядре) Windows часто используется структура LIST_ENTRY. Изначально функция typedVarList была ориентирована на такие списки. Однако, гораздо чаще используется другой подход для создания связанных списков: когда структура содержит указатель ( Next ) на начало следующей структуры. Теперь, typedVarList поддерживает оба случая. Какой вариант связного списка использовать, определяется по типу поля-указателя на следующий элемент.

Новый инсталлятор

К релизу 0.0.17 мы существенно переработали инсталятор:
  • Новый дизайн
  • Добавили деинсталлятор ( вдруг кому-нибудь понадобится? )
  • Добавили примеры в комплект иснталлятора
  • Выбор дополнительных компонентов по желанию пользователя ( VCRedist, python, примеры )

Bug fixed

8470 ( python.exe crashes after first pykd call )
8614 ( go() works incorrectly while process is terminating )
8499 ( !py command crashs with wrong script's path )
8578 ( findModule returns None for WOW64 process )
8493 ( loadPtrs returns dict instead list )
8469 ( dprintln does not work in console mode )

Надеемся, релиз 0.0.17 сделает вашу работу с pykd более приятной и плодотворной!

воскресенье, 3 апреля 2011 г.

HOWTO

1. Как получить указатель на PEB ?
peb = typedVar( "ntdll", "_PEB", getCurrentProcess() ) 
2. Какому модулю принадлежит адрес?
module = findModule( addr )
print module==None and "module not found" or module.name() 
3. Как вывести текст с DML разметкой?
dprintln( "<u>Hello <b>World</b></u>", True )
4. Как передать в скрипт значение регистра?

Также, как и в любую команду windbg:
!py myscript @eax
Чтобы это работало, в скрипте нужно предусмотреть вычисление выражений:
param1 = expr( sys.argv[1] ) 
5. Как выполнить команду отладчика и получить ее результат?
print dbgCommand("!for_each_module")
Результат команды можно обработать к примеру с помощью RE.

6. Как получить размер структуры?

Проще всего так:
print hex( sizeof( "nt", "_ERESOURCE") ) 
Можно с через класс, предсталяющий информацию о типе ( typeClass):
print hex(getTypeClass("nt", "_ERESOURCE").sizeof())
Справедливо и для объектов дочернего класса typedVarClass, например:
print hex(typedVar("nt", "_ERESOURCE", getOffset("nt", "CmpRegistryLock")).sizeof())

7. Как получить смещение поля структры?

Через класс, предсталяющий информацию о типе ( typeClass):
print hex(getTypeClass("nt", "_ETHREAD").Tcb.ThreadListEntry.offset())
Справедливо и для объектов дочернего класса typedVarClass, например:
print hex(typedVar("nt", "_ETHREAD", getImplicitThread()).Tcb.ThreadListEntry.offset()) 
Но сравните результат выполнения двух команд:
print hex(getTypeClass("nt", "_ETHREAD").Tcb.ThreadListEntry.Flink.offset())
print hex(typedVar("nt", "_ETHREAD", getImplicitThread()).Tcb.ThreadListEntry.Flink.
offset()) 
Первая выводит ожидаемое значение, вторая - возвращает ошибку. Дело в том, что аттрибут 'Flink' в объекте typedVarClass имеет встроенный тип python-а long и, соответственно, не имеет метода offset ( а также, getAddress() )

8. Есть ли в pykd команда help или другой способ получить справку?

В pykd - нет. Но такая возможность встроена в сам python! Пробуем:

>!pycmd
>>> help()
help>pykd.typedVar

9. Как отлаживать скрипт?

Небольшие скрипты можно отладить прямо в windbg ( или в консоле ). Для этого используем встроенный отладчик pdb:

>!py pdb my_script.py
(pdb) s
-> import pykd
(pdb)
В более сложных случаях можно использовать любой отладчик python кода. Например, eсlipse или Visual Studio 2010 с плагином pytools

10. Как сравнивать адреса?

Вопрос не так прост. DbgEng и все функции pykd возращают адреса в 64 битном формате, даже для x86 платформы. Адреса, находящиеся в верхней половине адресного пространства, расширяются:
0x804f8925 -> 0xFFFFFFFF'804f8925
В следствии этого, в коде могут возникнуть непрогнозируемые ошибки. Рассмотрим пример:
if reg("eax")==getOffset( "nt", "NtCreateFile")
    print "eax point to NtCreateFile"
На x86 платформе данный код будет работать неправильно. Переменная a будет трактоваться как 32 битное целое, а функция getOffset вернет адрес, расширенный до 64 бит. И даже если регистр eax действительно указывает на NtCreateFile, проверка на равенство не сработает. Чтобы избежать этого, скастим значение регистра к указателю с помощью функции addr64:
if addr64(reg("eax"))==getOffset("nt","NtCreateFile")
    print "eax point to NtCReateFile" 
 
Надеюсь, эта информация поможет начать работу с pykd. На все вопросы мы с удовольствием ответим в этом блоге или по электронной почте: pykd@hotmail.com.