пятница, 8 июня 2012 г.

IOCTL декодер

Бывает нужно из IOCTL извлечь номер функции или определить буферизированный запрос или нет. Есть вот такой онлайн декодер . Мы написали аналогичный по функциональности скрипт для windbg.
Пользоваться просто:
2: kd> !py ctlcode 0x1401c8
Device type: FILE_DEVICE_NETWORK_FILE_SYSTEM   Function: 0x72(114)   Method: BUFFERED  Access:  FILE_ANY_ACCESS
Взять можно здесь.

пятница, 1 июня 2012 г.

Отладка WFP callout драйверов

Начиная с Windows Vista MS упорно продвигает свой фреймворк WFP ( Windows Filtering Platform ). В предверии Windows 8 Microsoft еще более закручивают гайки: теперь продукт не может получить логотип о совместимоcти с Windows 8, если он содержит TDI или LSP фильтры. Между нами, скажу по секрету шепотом свое мнение: WFP - это полное говно, писать на нем определенные вещи крайне неудобно. Но это все эмоции, говно - не говно, отлаживать драйвера надо. Кто знаком с WFP, тот помнит, что там все делает через одно место, а именно через функцию classsifyFn. В зависимости от версии WFP, эта функция может имееть разные сигнатуры, но в любом случае, они содержат параметеры inFixedValues и inMetaValues в которых, собственно, и содержится вся информация о сетевом событии. И конечно, при отладке хочется посмотреть, что содержат эти переменные. Но это не так просто, inFixedValues - это массив переменной длины, а в inMetaValues доступность полей регулируется флагами. Поэтому, я решил написать python скрипт, который будет выводить эту информацию.

Сам скрипт можно взять здесь: wfp.py download.
Использовать его довольно просто: Ставим брейкпойнт на интересующей нас ф. classifyFn. При срабатывании брейкпойнта, вводим команду: !py wfp /fixed poi(inFixedValues).
Примерный вывод команды:

FWPS_INCOMING_VALUES0:
 Layer: FWPS_LAYER_STREAM_V4
 Value: 7
    FWPS_FIELD_STREAM_V4_IP_LOCAL_ADDRESS
      Type: FWP_UINT32
      Value: 0xaf4087c
    FWPS_FIELD_STREAM_V4_IP_LOCAL_ADDRESS_TYPE
      Type: FWP_UINT8
      Value: 0x1
    FWPS_FIELD_STREAM_V4_IP_REMOTE_ADDRESS
      Type: FWP_UINT32
      Value: 0x40040b19
    FWPS_FIELD_STREAM_V4_IP_LOCAL_PORT
      Type: FWP_UINT16
      Value: 0xc02b
    FWPS_FIELD_STREAM_V4_IP_REMOTE_PORT
      Type: FWP_UINT16
      Value: 0x50
    FWPS_FIELD_STREAM_V4_DIRECTION
      Type: FWP_UINT32
      Value: 0x0
    FWPS_FIELD_STREAM_V4_FLAGS
      Type: FWP_UINT32
      Value: 0x0
Для 64 битной платформы, потребуется указать регистр rcx ( именно через него передается inFixedValues ):
!py wfp /fixed @rcx.
Для вывода метаинформации следует использовать команду:
!py wfp /meta poi(inMetaValues)
FWPS_INCOMING_METADATA_VALUES0:
    FWPS_METADATA_FIELD_FLOW_HANDLE: 0x210
    FWPS_METADATA_FIELD_SYSTEM_FLAGS: 0x1

Теперь на примере данного скрипта рассмотрим некоторые приемы написания скрипта для pykd. Первым делом, нам нужно получить доступ к полям переменной inFixedValues. Это делается так:

inFixedValue = typedVar( "FWPS_INCOMING_VALUES0_", addr )
dprintln( " Layer: " + fwpsLayer[ inFixedValue.layerId ] )
dprintln( " Value: %d" % inFixedValue.valueCount )
Класс typedVar позволяет осуществить удобный доступ к полям стурктур - просто как к аттрибутам. Именно так выводится количество записей в массиве:
dprintln( " Value: %d" % inFixedValue.valueCount )
Поле inFixedValue.layerId содержит численный идентификатор уровня фильтрации. Но мы хотим получить его символьное представление, которое задается энумератором FWPS_BUILTIN_LAYERS_. И тут нам приходит на помощь новый функционал pykd версии 0.1.x:
fwpsLayer = typeInfo( "FWPS_BUILTIN_LAYERS_" ).asMap()
Вызов typeInfo( "FWPS_BUILTIN_LAYERS_" ) возвращает информацию о типе FWPS_BUILTIN_LAYERS_. А метод asMap() конвертирует его в словарь вида: { число : "имя" }. Таким образом, зная идентификатор уровня фильтрации мы легко конвертируем его в символьное представление:
dprintln( " Layer: " + fwpsLayer[ inFixedValue.layerId ] )
Теперь нам необходимо вывести информацию о каждом элементе массива inFixedValue.incomingValue. Это можно было бы сделать с помощью цикла:
values = []
for i in range( 0, inFixedValue.valueCount ):
    values.append( typedVar( "FWPS_INCOMING_VALUE0_", inFixedValue.incommingValue + typeInfo("FWPS_INCOMING_VALUE0_").size() * i ).value )
Но есть способ гораздо проще, через специальный метод для работы с массивами:
values = [ x.value for x in typedVarArray( inFixedValue.incomingValue, "FWPS_INCOMING_VALUE0_", inFixedValue.valueCount ) ]
typedVarArray возвращает список переменных типа typedVar с inFixedValue.valueCount элементов и начинающийся с адреса inFixedValue.incomingValue. Далее мы используем генератор списков, чтобы трансформировать список.

Ну вот, собственно и все. Надеюсь данный скрипт будет полезен в работе и в качестве примера работы c pykd.