Вопрос по контекстам, в которых выполняются обработчики этих драйверов.
Обработчики драйвера TCPIP.SYS и аналогичных (которые указываются в DriverObject -> MajorFunction [...]) выполняются насколько мне известно в контексте потока, вызвавшего DeviceIoControl(), IoCallDriver(), ...
А вот обработчики состояния NDIS (RecieveHandler, SendHandler, RecieveCompleteHandler, SendCompleteHandler, ...) в контексте какого потока/процесса вызываются?
Возможно ли как-то узнать какой процесс/поток был инициатором запроса на соединение на NDIS-уровне?
Здравствуйте, Аноним, Вы писали:
А>Вопрос по контекстам, в которых выполняются обработчики этих драйверов.
А>Обработчики драйвера TCPIP.SYS и аналогичных (которые указываются в DriverObject -> MajorFunction [...]) выполняются насколько мне известно в контексте потока, вызвавшего DeviceIoControl(), IoCallDriver(), ...
Это так, только для случая, когда клиентом транспортного драйвера является приложение пользовательского режима. Если клиентом является драйвер, то вызовы некоторые вызовы ( вроде TDI_CONNECT ) будут проходить в контексте процесса SYSTEM, а некоторые ( вроде TDI_SEND ) могут проходить в контексте текущего потока, обрабатывающего DPC.
А>А вот обработчики состояния NDIS (RecieveHandler, SendHandler, RecieveCompleteHandler, SendCompleteHandler, ...) в контексте какого потока/процесса вызываются?
обработчики вроде SendHandler обычно ( хотя это и не обязательно ), работают в контексте процесса, инициировавшего отправку данных, остальные — как придется. Механизм примерно таков: драйвер сетевой карты обрабатывает прерывания в своей ISR и далее инициирует DPC для дальнейшей обработки. При обработки DPC как раз и срабатывают обработчики типа RecieveHandler. DPC процедуры срабатывают как только IRQL опустится до уровня DISPATCH_LEVEL и контекст, в котором это происходит никак не связан с контекстом процесса, которому в конце-концов данные адресуются.
А>Возможно ли как-то узнать какой процесс/поток был инициатором запроса на соединение на NDIS-уровне?
Сказать, что отправка SYN пакета будет всегда происходить в контексте процесса, устанавливающего соединение, нельзя. Кроме всего прочего, процедура отправки еще зависит и от платформы и состава драйверов. Например, я могу написать NDIS фильтр, который будет отправлять все пакеты из собственного рабочего потока и всякие предположения о контексте станут неправомочными.
А>>А вот обработчики состояния NDIS (RecieveHandler, SendHandler, RecieveCompleteHandler, SendCompleteHandler, ...) в контексте какого потока/процесса вызываются?
TC>обработчики вроде SendHandler обычно ( хотя это и не обязательно ), работают в контексте процесса, инициировавшего отправку данных, остальные — как придется. Механизм примерно таков: драйвер сетевой карты обрабатывает прерывания в своей ISR и далее инициирует DPC для дальнейшей обработки. При обработки DPC как раз и срабатывают обработчики типа RecieveHandler. DPC процедуры срабатывают как только IRQL опустится до уровня DISPATCH_LEVEL и контекст, в котором это происходит никак не связан с контекстом процесса, которому в конце-концов данные адресуются.
Предположим, я повесил хук на обработчики драйвера NDIS, которые будут вызваны при наступлении того или иного события. Мне нужно связать отправляемые/принимаемые пакеты, коннекты и т.п. с потоком-инициатором. Т.е. грубо говоря нужно в обработчике понять: этот ETHREAD инициировал коннект или нет?..
А>>Возможно ли как-то узнать какой процесс/поток был инициатором запроса на соединение на NDIS-уровне? TC>Сказать, что отправка SYN пакета будет всегда происходить в контексте процесса, устанавливающего соединение, нельзя. Кроме всего прочего, процедура отправки еще зависит и от платформы и состава драйверов. Например, я могу написать NDIS фильтр, который будет отправлять все пакеты из собственного рабочего потока и всякие предположения о контексте станут неправомочными.
Так, хорошо... Я понимаю, что всё жутко сложно и вариантов 1000. Один только вопрос: ведь персональные фаеры (например, Outpost) как-то определяют процесс, инициировавший соединение на уровне NDIS! Как же они это делают???
Здравствуйте, Аноним, Вы писали:
А>Так, хорошо... Я понимаю, что всё жутко сложно и вариантов 1000. Один только вопрос: ведь персональные фаеры (например, Outpost) как-то определяют процесс, инициировавший соединение на уровне NDIS! Как же они это делают???
Они все используют двухуровневую фильтрацию. Верхний уровень ( TDI фильтр ) отслеживает выделении сетевых ресурсов ( локальные порты, запросы на соединения и.т.п ). Все эти действия происходят в контексте процесса. Затем на основе этих данных создаются фильтрующие правила для нижнего уровня ( NDIS фильтра ). Таким образом, NDIS фильтр не знает, что SYN запрос послан конкретным приложением, он лишь предполагает это на основе работы своего верхнего фильтра. В принципе, TDI фильтр можно заменить на опрос драйвера tcpip.sys ( аналог netstat в режиме ядра ), но смысл от этого не меняется.
А>>ведь персональные фаеры (например, Outpost) как-то определяют процесс, инициировавший соединение на уровне NDIS! Как же они это делают??? TC>Они все используют двухуровневую фильтрацию.
Это я понял. Ну допустим NDIS-фильтр сообщает более высокоуровневому фильтру, что сейчас будет выполнен запрос туда-то. Каким же образом верхний фильтр понимает, что это запрос от одного из процессов, которого он отфильтровал ранее? Может быть возможно сопоставить по номеру локального порта, он всегда уникален?
A>Каким же образом верхний фильтр понимает, что это запрос от одного из процессов, которого он отфильтровал ранее? Может быть возможно сопоставить по номеру локального порта, он всегда уникален?
Для ТСР уникален квартет: пара адресов ( локальный и удаленный ) и пара портов ( локальный и удаленный ). Именно по этим значением NDIS фильтр и считает пакеты принадлежащим определенному процессу. Он конечно может анализировать еще другие поля сетевых пакетов, чтобы предотвратить прохождение "похожих" пакетов. Например, отслеживать номера последовательностей для ТСР потока, соответствие MAC и ip и.т.д, но это не имеет отношения к разграничению доступа процессов к сети.
В PNDIS_PACKET находится весь сетевой пакет, вместе со всеми заголовками. Форматы сетевых протоколов известны, нужно его разобрать — сначала ethernet заголовок, потом ip ( если это ip пакет ) — там адреса, потом ТСР заголовок — там порты.
TC>В PNDIS_PACKET находится весь сетевой пакет, вместе со всеми заголовками. Форматы сетевых протоколов известны, нужно его разобрать — сначала ethernet заголовок, потом ip ( если это ip пакет ) — там адреса, потом ТСР заголовок — там порты.
Извините, я вас совсем достал наверно Я никогда не работал с NDIS, только с TDI немного. Не могли бы вы малюсенький кусочек кода дать, как это всё извлечь?
TC>>В PNDIS_PACKET находится весь сетевой пакет, вместе со всеми заголовками. Форматы сетевых протоколов известны, нужно его разобрать — сначала ethernet заголовок, потом ip ( если это ip пакет ) — там адреса, потом ТСР заголовок — там порты.
Подскажите хотя бы как называются эти структуры-заголовки пакетов plz
А>Подскажите хотя бы как называются эти структуры-заголовки пакетов plz
А никак не называются. В DDK структуры сетевых пакетов не описываются. Я их задавал по необходимости сам в удобной мне форме. Найти описание того или иного протокола — не проблема. Например: http://book.itep.ru/1/intro1.htm — очень неплохой ресурс.
TC>А никак не называются. В DDK структуры сетевых пакетов не описываются. Я их задавал по необходимости сам в удобной мне форме. Найти описание того или иного протокола — не проблема. Например: http://book.itep.ru/1/intro1.htm — очень неплохой ресурс.
А> в моём случае мне нужно сделать примерно так ... Я прав?
На уровне псевдокода да.... Если по-настоящему, то у Вас две больших ошибки:
1. NDIS_BUFFER — это не буфер с данными, это псевдоним для широкоизвестной структуры MDL, т.е это не сам буфер — это его описатель. Чтобы получить указатель на сами данные нужно позвать NdisQueryBuffer(Safe).
2. NDIS_PACKET может ( и для случая передачи будет одназначно ) содержать несколько буферов ( цепочку ) NDIS_BUFFER. Т.е сетевой пакет фрагментирован по отношению к виртуальной памяти. NdisQueryPacket вернет только первый буфер цепочки. Затем нужно в цикле вызывать NdisGetNextBuffer для анализа последующих данных.
Нет, Вы не ошиблись, первый вариант — правильный. Преамбула, стартовый байт ( еще тогда уже нужно помянуть и трейлер ) — все остается на уровне сетевой карты — это формат данных на уровне физического канала. Вы же получаете девственно чистый эзернет заголовок.
TC>1. NDIS_BUFFER — это не буфер с данными, это псевдоним для широкоизвестной структуры MDL, т.е это не сам буфер — это его описатель. Чтобы получить указатель на сами данные нужно позвать NdisQueryBuffer(Safe). TC>2. NDIS_PACKET может ( и для случая передачи будет одназначно ) содержать несколько буферов ( цепочку ) NDIS_BUFFER. Т.е сетевой пакет фрагментирован по отношению к виртуальной памяти. NdisQueryPacket вернет только первый буфер цепочки. Затем нужно в цикле вызывать NdisGetNextBuffer для анализа последующих данных.
1. Понятно, ну это несложно исправить. Кстати чем отличаются NdisQueryBuffer() от NdisQueryBufferSafe() ?
2. Я так понимаю, каждый из буферов в цепочке будет содержать именно кадры? Т.е. каждый буфер будет начинаться со структуры ETHERNET_FRAME и т.д. ?
Большое спасибо! А структуры я правильно описал (см. пост ниже — там немного поправил структуру кадра)? Я не очень понимаю, там нужно поле padding или нет?
TC>Нет, Вы не ошиблись, первый вариант — правильный. Преамбула, стартовый байт ( еще тогда уже нужно помянуть и трейлер ) — все остается на уровне сетевой карты — это формат данных на уровне физического канала. Вы же получаете девственно чистый эзернет заголовок.
Спасибо, разъяснили! А как насчёт поля padding в других заголовках? Оно нужно?
А> чем отличаются NdisQueryBuffer() от NdisQueryBufferSafe() ?
В документации это описано. Если драйвер разрабатывается для NT >= 5.0, то рекомендуется использовать безопасный вариант NdisQueryBufferSafe
А>2. Я так понимаю, каждый из буферов в цепочке будет содержать именно кадры? Т.е. каждый буфер будет начинаться со структуры ETHERNET_FRAME и т.д. ?
Нет. NDIS_PACKET описывает один сетевой пакет. Он может быть устроен примерно так: первый буфер — заголовок эзернет, второй буфер — загловок IP, третий — заголовок UDP, четвертый — пользовательские данные. Так сделано, чтобы избежать лишних операций копирования при обработке пакета сетевым стеком.
А>А структуры я правильно описал (см. пост ниже — там немного поправил структуру кадра)? Я не очень понимаю, там нужно поле padding или нет?
обычно padding не нужен, в основном ip имеет длину заголовка 20 байт, однако иногда длина может увеличиваться за счет дополнительной информации, читайте доки . Еще совет — все описания должны быть сделаны с однобайтовым выравниванием, иначе поля разъедутся.