Вопрос по контекстам, в которых выполняются обработчики этих драйверов.
Обработчики драйвера 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 байт, однако иногда длина может увеличиваться за счет дополнительной информации, читайте доки . Еще совет — все описания должны быть сделаны с однобайтовым выравниванием, иначе поля разъедутся.
TC>Нет. NDIS_PACKET описывает один сетевой пакет. Он может быть устроен примерно так: первый буфер — заголовок эзернет, второй буфер — загловок IP, третий — заголовок UDP, четвертый — пользовательские данные. Так сделано, чтобы избежать лишних операций копирования при обработке пакета сетевым стеком.
Но ведь тогда возникает проблема — как найти начало ETHERNET_FRAME ??? Другими словами, как понять, что вот этот пакет — есть ETHERNET_FRAME, а этот вот — TCP_HEADER ?
TC>Еще совет — все описания должны быть сделаны с однобайтовым выравниванием, иначе поля разъедутся.
Здравствуйте, Аноним, Вы писали:
А>Но ведь тогда возникает проблема — как найти начало ETHERNET_FRAME ??? Другими словами, как понять, что вот этот пакет — есть ETHERNET_FRAME, а этот вот — TCP_HEADER ?
Нет никаких проблем — это гарантировано логической вложенностью протоколов. Всегда последовательность такая: ethernet заголовок — ip заголовок — tcp заголовок — данные. Если вы начинаете раскручивать пакет с головы ( вы по-моему в посте попутали пакет с буфером ), то всегда первым идет ethernet заголовок с фиксированным размером 14 байт, ну и так далее.
Чтобы не сложилось неверного представления, замечу, что все может быть и в одном буфере. Так обычно бывает при приеме. Или вообще частично объединено. Например так:
TC>
TC>Чтобы не сложилось неверного представления, замечу, что все может быть и в одном буфере. Так обычно бывает при приеме. Или вообще частично объединено. Например так:
Так... ага... т.е. получается, что ETHERNET_FRAME есть в каждом пакете NDIS'а, и при чём всегда в начале, так?
А>Так... ага... т.е. получается, что ETHERNET_FRAME есть в каждом пакете NDIS'а, и при чём всегда в начале, так?
Так, с небольшим уточнением — если Medium ( среда ) — NdisMedium802_3 или NdisMediumWan ( см DDK OID_GEN_MEDIA_SUPPORTED ), т.е для ethernet сетей. Если сеть — token ring или еще какая нибудь — будет соответствующий заголовок.
А>>Так... ага... т.е. получается, что ETHERNET_FRAME есть в каждом пакете NDIS'а, и при чём всегда в начале, так? TC>Так, с небольшим уточнением — если Medium ( среда ) — NdisMedium802_3 или NdisMediumWan ( см DDK OID_GEN_MEDIA_SUPPORTED ), т.е для ethernet сетей. Если сеть — token ring или еще какая нибудь — будет соответствующий заголовок.
Ага, т.е. вот как всё хитро оказывается Значит, если среда другая — то структура первого заголовка будет не ETHERNET_FRAME, а что-то вроде ANOTHER_FRAME_TYPE_STRUCT ?
Отсюда напрашивается вопрос — как же понять, что за кадр нам пришёл? Ethernet — не Ethernet... Может функция какая-то предусмотрена в NDIS для запроса информации о пакете?..
А>Может функция какая-то предусмотрена в NDIS для запроса информации о пакете?..
Фильтр или протокол, обрабатывающий пакет, точно знает тип физической среды. Во-первых он это указывает при установке драйвера ( в инф файле ), во вторых ему это сообщают при инициализации ( см NdisOpenAdapter — 4-ый параметр ). Так что политика такая — что умеем, то и фильтруем, тем более ethernet — это 99% ( учитывая, что ndiswan.sys весь траффик от модемных подключений, туннелей и.т.п превращает в ethrnet формат ).
А>>Может функция какая-то предусмотрена в NDIS для запроса информации о пакете?..
TC>Фильтр или протокол, обрабатывающий пакет, точно знает тип физической среды. Во-первых он это указывает при установке драйвера ( в инф файле ), во вторых ему это сообщают при инициализации ( см NdisOpenAdapter — 4-ый параметр ). Так что политика такая — что умеем, то и фильтруем, тем более ethernet — это 99% ( учитывая, что ndiswan.sys весь траффик от модемных подключений, туннелей и.т.п превращает в ethrnet формат ).
Что вы имеете в виду? Что нужно хучить ещё и NdisOpenAdapter(), чтобы узнать binding handle, который потом придёться отслеживать далее? Или вы рекомендуете просто забить и анализировать все пакеты как ethernet'овские?
TC>Они все используют двухуровневую фильтрацию. Верхний уровень ( TDI фильтр ) отслеживает выделении сетевых ресурсов ( локальные порты, запросы на соединения и.т.п ). Все эти действия происходят в контексте процесса. Затем на основе этих данных создаются фильтрующие правила для нижнего уровня ( NDIS фильтра ). Таким образом, NDIS фильтр не знает, что SYN запрос послан конкретным приложением, он лишь предполагает это на основе работы своего верхнего фильтра. В принципе, TDI фильтр можно заменить на опрос драйвера tcpip.sys ( аналог netstat в режиме ядра ), но смысл от этого не меняется.
можно поподробней, что значит опрос драйвера tcpip.sys ? как это сделать? какие функции существуют?
Здравствуйте, Аноним, Вы писали:
_>>можно поподробней, что значит опрос драйвера tcpip.sys ? как это сделать? какие функции существуют?
А>Запрос IOCTL_TCP_QUERY_INFORMATION_EX. Возвращает список сетевых соединений (включая неактивные).
а из этого списка что-ли можно узнать какой процесс установил соединение ?
Здравствуйте, TarasCo, Вы писали:
TC>Для ТСР уникален квартет: пара адресов ( локальный и удаленный ) и пара портов ( локальный и удаленный ). Именно по этим значением NDIS фильтр и считает пакеты принадлежащим определенному процессу. Он конечно может анализировать еще другие поля сетевых пакетов, чтобы предотвратить прохождение "похожих" пакетов. Например, отслеживать номера последовательностей для ТСР потока, соответствие MAC и ip и.т.д, но это не имеет отношения к разграничению доступа процессов к сети.
1)а для других протоколов (не TCP) как быть? что для них уникально?
2)я пока экспериментировал на уровне NDIS, MPSendPackets, там такие Ethernet-фрэймы:
(IP) — 0x0800
(ARP) — 0x0806;
могут быть еще какие-нибудь типы фрэймов в стандартной винде (XP)?
3)еще заметил, что пакеты могут посылаться без установления соединения... что это значит?
4) что значит адрес с таким типом TDI_ADDRESS_TYPE_UNSPEC??? там обычно нули лежат...
A>>Каким же образом верхний фильтр понимает, что это запрос от одного из процессов, которого он отфильтровал ранее? Может быть возможно сопоставить по номеру локального порта, он всегда уникален?
TC>Для ТСР уникален квартет: пара адресов ( локальный и удаленный ) и пара портов ( локальный и удаленный ). Именно по этим значением NDIS фильтр и считает пакеты принадлежащим определенному процессу. Он конечно может анализировать еще другие поля сетевых пакетов, чтобы предотвратить прохождение "похожих" пакетов. Например, отслеживать номера последовательностей для ТСР потока, соответствие MAC и ip и.т.д, но это не имеет отношения к разграничению доступа процессов к сети.
есть NDIS фильтр и TDI фильтр, я беру из NDIS-фильтра пакет, беру из пакета адреса и пытаюсь их найти в TDI фильтре...
иногда получается находить, но иногда встречаются такие "квартеты", которых нету в TDI (там есть похожие, но часть — нули)
вот примеры:
1)на уровне NDIS
b100a8c0 8900 -> ff00a8c0 8900 (LocalAddr LocalPort -> RemoteAddr RemotePort)
на уровне TDI в фильтре:
b100a8c0 8900 -> 0 0
2)NDIS
b100a8c0 4d04 -> 1a3943c2 5000
TDI
0 4d04 -> 1a3943c2 5000
3)NDIS
b100a8c0 104 -> 100a8c0 3500
TDI
???? ничего не нашел
я только догадываюсь в случае 2), что биндится адрес 0 — а потом припысывается на уровне NDIS адрес адаптера
1) и 3) не знаю...
разъясните плиз
А>>Запрос IOCTL_TCP_QUERY_INFORMATION_EX. Возвращает список сетевых соединений (включая неактивные).
_>а из этого списка что-ли можно узнать какой процесс установил соединение ?
Можно, но не всегда.
Если нужно узнать процесс — imho, лучше приаттачить девайс на \Device\Tcp и в обработчиках смотреть текущий процесс.
Здравствуйте, TarasCo, Вы писали:
TC>Для ТСР уникален квартет: пара адресов ( локальный и удаленный ) и пара портов ( локальный и удаленный ). Именно по этим значением NDIS фильтр и считает пакеты принадлежащим определенному процессу. Он конечно может анализировать еще другие поля сетевых пакетов, чтобы предотвратить прохождение "похожих" пакетов. Например, отслеживать номера последовательностей для ТСР потока, соответствие MAC и ip и.т.д, но это не имеет отношения к разграничению доступа процессов к сети.
и еще вопрос :
как определить MAC адрес на уровне tdi для данного соединения???
Здравствуйте, Аноним, Вы писали:
_>>на уровне TDI (LocalAddr==0)&&(LocalPort!=0)
А>Чо паникуешь? Local Address == 0 на уровне TDI — это нормально. Точнее не 0, а 0.0.0.0 или ещё может быть 127.0.0.1.
да не паникую, я не знаю че делать...
ну и как с этим бороться?
допустим у меня 2 сетевые карты: 10.0.0.1 и 10.0.0.2
и процессы биндят 2 одинаковых порта port=123 на обеих картах
тогда получается на уровне TDI не различить их 0.0.0.0:123 ???
Здравствуйте, sergei_132, Вы писали:
_>Здравствуйте, Аноним, Вы писали:
_>>>на уровне TDI (LocalAddr==0)&&(LocalPort!=0)
А>>Чо паникуешь? Local Address == 0 на уровне TDI — это нормально. Точнее не 0, а 0.0.0.0 или ещё может быть 127.0.0.1.
_>да не паникую, я не знаю че делать...
_>ну и как с этим бороться?
_>допустим у меня 2 сетевые карты: 10.0.0.1 и 10.0.0.2
_>и процессы биндят 2 одинаковых порта port=123 на обеих картах
_>тогда получается на уровне TDI не различить их 0.0.0.0:123 ???
а вообще я пытаюсь определить pid на уровне NDIS, для этого у фильтра TDI смотрю информацию...
получается только для уникальных (не нулевых) адресов определять...
как же тогда эти фаерволы узнают, если 2 сетевые карты ? как-то же это решено
_>как же тогда эти фаерволы узнают, если 2 сетевые карты ? как-то же это решено
Да йопты, фаеры всё узнают на верху, на TDI уровне. Они узнают локальный адрес:порт и удалённый адрес:порт, на этом же уровне смотрят процесс и добавляют в свой внутренний список. Далее на NDIS уровне смотрят в своём супир-пупир списке какой процесс соответствует этой паре адрес:порт. Вот так происходит связывание адресов с процессами. Я так понял тебе именно это нужно?
Здравствуйте, Аноним, Вы писали:
_>>как же тогда эти фаерволы узнают, если 2 сетевые карты ? как-то же это решено
А>Да йопты, фаеры всё узнают на верху, на TDI уровне. Они узнают локальный адрес:порт и удалённый адрес:порт, на этом же уровне смотрят процесс и добавляют в свой внутренний список. Далее на NDIS уровне смотрят в своём супир-пупир списке какой процесс соответствует этой паре адрес:порт. Вот так происходит связывание адресов с процессами. Я так понял тебе именно это нужно?
это я уже давно понял
я говорю в чем проблема: если у меня 2 сетевые карты
то 0.0.0.0 какой карте соответствует на уровне TDI ???
_>то 0.0.0.0 какой карте соответствует на уровне TDI ???
Вот это не скажу, но мне кажеться это определяется на уровне NDIS, а не TDI. Попробуй помучить DDK Documentation на тему "что происходит с коннектом далее, после того, как он проходит уровень tcpip.sys".
Здравствуйте, Аноним, Вы писали:
_>>то 0.0.0.0 какой карте соответствует на уровне TDI ???
А>Вот это не скажу, но мне кажеться это определяется на уровне NDIS, а не TDI. Попробуй помучить DDK Documentation на тему "что происходит с коннектом далее, после того, как он проходит уровень tcpip.sys".
Блин, дошло кажется!!!
если у нас есть удаленный адрес, то мы можем в таблицу маршрутизации посмотреть, какому интерфейсу это соответствует.
и тогда просто заменить LocalAddr==0.0.0.0 на IpAddr из таблицы маршрутизации
_>если у нас есть удаленный адрес, то мы можем в таблицу маршрутизации посмотреть, какому интерфейсу это соответствует. _>и тогда просто заменить LocalAddr==0.0.0.0 на IpAddr из таблицы маршрутизации
Ну да, наверно можно и так. Но не уверен. Попробуй.