Пользоваться просто:
2: kd> !py ctlcode 0x1401c8 Device type: FILE_DEVICE_NETWORK_FILE_SYSTEM Function: 0x72(114) Method: BUFFERED Access: FILE_ANY_ACCESSВзять можно здесь.
2: kd> !py ctlcode 0x1401c8 Device type: FILE_DEVICE_NETWORK_FILE_SYSTEM Function: 0x72(114) Method: BUFFERED Access: FILE_ANY_ACCESSВзять можно здесь.
Начиная с 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 ):
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.
Доброго всем дня! Новая ветка pykd 0.1.x переведена в публичный статус. Релиз 0.1.0.14 рекомендован для использования. Страница загрузки: Pykd 0.1.0.14
1. Для доступа к символьной информации используется библиотека MS DIA. Это позволило нам реализовать то, что не получалось сделать через Debug Engine: Доступ к битовым полям; Работа с многомерными массивами; Работа с энумераторами.
2. Новый класс dbgClient позволяет работать одновременно с несколькими отладочными сессиями.
3. Команда !pycmd реализует полноценную python консоль
4. Улучшена работа с классами исключений
5. Исправлено множество багов, написано более 200 тестов, покрывающих практически весь функционал.
6. Наконец то написано ( пока с некоторыми пробелами ) руководство пользователя
Внимание: API претерпел небольшие изменения, так что, возможно, придется переписать некоторые скрипты. Но изменения в API небольшие и портирование скриптов на pykd 0.1.x не должно отнять много времени. Кроме того, часть функционала пока не реализована ( в основном, это касается создания собственных типов через интерфейс класса typeInfo ). В следующих релизах мы обязательно восполним все пробелы.
Большое спасибо всем, кто использует наш продукт! Как всегда, мы будем очень рады любым отзывам, предложениям и, особенно, указанием на наши ошибки.
Большое спасибо всем участникам проекта, трудившимся над веткой 0.1.x! Работа длилась почти целый год. За это время код был серьезно модифицирован, существенная часть его была переписана заново. Надеемся, пользователи оценят наши усилия.
PyErr_SetString( PyExc_IndexError, "Index out of range" );Не очень то удобно использовать эту конструкцию внтури С++ кода. Хотелось бы кидать С++ исключение и чтобы оно волшебным образом превращалось в python исключение. К счастью, boost::python предоставляет эту возможность.
// класс исключения, для использования в C++ коде class PyException : public std::exception { public: PyException( PyObject* pyObj, const std::string &desc ) : std::exception( desc.c_str() ), m_typeObj( pyObj ) {} static void exceptionTranslate(const PyException &e ) { PyErr_SetString( e.m_typeObj, e.what() ); } private: PyObject* m_typeObj; }; void myFunc() { throw PyException( PyExc_IndexError, "Index out of range" ); } BOOST_PYTHON_MODULE( my_module ) { // регистрируем транслятор boost::python::register_exception_translator<PyException>( &PyException::exceptionTranslate ); // функция, вызываемая из python программы boost::python::def( "myFunc", &myFunc ); }
// класс исключения, для использования в C++ коде class MyException : public std::exception { public: MyException ( const std::string &desc ) : std::exception( desc.c_str() ) {} static void setTypeObject(PyObject *p) { exceptTypeObject = p; python::register_exception_translator<MyException>( &exceptionTranslate ); } private: static PyObject *exceptTypeObject; static void exceptionTranslate(const MyException &e ) { boost::python::object pyExcept(e); PyErr_SetObject( exceptTypeObject , pyExcept.ptr()); } }; void myFunc() { throw MyException( "Something's wrong" ); } BOOST_PYTHON_MODULE( my_module ) { // функция, вызываемая из python программы boost::python::def( "myFunc", &myFunc ); // регистрирем тип исключения и его транслятор MyException::setTypeObject( boost::python::class_<MyException>( "MyException", "MyException error" ).ptr() ); }
using boost::python; // Базовым типом у нас будет PyExc_Exception handle<> basedtype = handle<>(PyExc_Exception); // Всякий объект в питоне имеет свой список свойств dict ob_dict; ob_dict["__doc__"] = "MyException" // Объект в питоне кстате может иметь много пап. Но у нас будет один - самый лучший! tuple ob_bases = make_tuple( basedtype ); // Вот тут весь оргазм: // Py_TYPE(basedtype.get()) - вернет нам тип от объекта PyExc_Exception. Тип типа - это метатип, мы это выясняли // object( boost::python::handle<>(Py_TYPE(basedtype.get()) ) ) - это будет у нас объект меаттипа // Что такоей вызов SomeType( a, b, c) ? - это конструирование объекта этого типа // Теперь собираем пазл во-едино: мы создаем новый тип, который имеет такой же метатип как и PyExc_Exception // и является наследником PyExc_Exception object ob = object( boost::python::handle<>(Py_TYPE(basedtype.get()) ) )( "MyException", ob_bases, ob_dict ); // остается пристроить новорожденного в приличное место, а именно в скоуп модуля scope().attr( "MyException" ) = ob;
template< class TExcept > struct exceptPyType{ static python::handle<> pyExceptType; }; template< class TExcept, class TBaseExcept = python::detail::not_specified > class exception { public: exception( const std::string& className, const std::string& classDesc ) { python::handle<> basedtype; if ( boost::is_same<TBaseExcept, python::detail::not_specified>::value ) { basedtype = python::handle<>(PyExc_Exception); } else { basedtype = exceptPyType<TBaseExcept>::pyExceptType; } python::dict ob_dict; ob_dict["__doc__"] = classDesc; python::tuple ob_bases = python::make_tuple( basedtype ); python::object ob = python::object( python::handle<>(Py_TYPE(basedtype.get()) ) )( className, ob_bases, ob_dict ); python::scope().attr( className.c_str() ) = ob; exceptPyType<TExcept>::pyExceptType = python::handle<>( ob.ptr() ); python::register_exception_translator<TExcept>( &exceptionTranslate ); } static void exceptionTranslate(const TExcept &e ) { python::object exceptObj = python::object( exceptPyType<TExcept>::pyExceptType )( e.what() ); PyErr_SetObject( exceptPyType<TExcept>::pyExceptType.get(), exceptObj.ptr()); } };
BOOST_PYTHON_MODULE( mymodule ) { exception<MyException>( "MyException", "description" ); exception<MyExceptionEx,MyException>( "MyExceptionEx", "description" ); }