TDI-фильтр и AccessViolation

Тема в разделе "WASM.NT.KERNEL", создана пользователем Nicky, 28 дек 2009.

  1. Nicky

    Nicky New Member

    Публикаций:
    0
    Регистрация:
    28 дек 2009
    Сообщения:
    16
    Здраствуйте
    Заранее извините за большое количество текста :)
    Пишу TDI-фильтр (блокирование траффика Tcp) и столкнулся с проблемой. Сам фильтр блокирует все исходящие соединения, кроме оных по одному определенному адресу.
    Я написал простую тестовую программку — состоящую из 3-х строчек
    Код (Text):
    1. socket()
    2. connect()
    3. send()
    которая подключается к этому разрешенному хосту.
    Если эти операции происходят в таком порядке "быстро", программа вылетает с ошибкой Access violation Code=0xc0000005, Address=0x0000000000000000 (никаких BSODов не происходит)
    Если "медленно", с задержкой (system("PAUSE")) после каждого шага — данные успешно передаются.

    Вот как выглядит процедура-перехватчик IRP_MN_INTERNAL_IOCTL
    Код (Text):
    1. NTSTATUS InternalIOCTLHook(PDEVICE_OBJECT pDeviceObject, PIRP pIrp) {
    2.     NTSTATUS status;
    3.     UCHAR query;
    4.     PDEVICE_EXTENSION pdx;
    5.     PTRANSPORT_ADDRESS connect_destaddress;
    6.     ULONG ipaddress;
    7.     LONG eventtype;
    8.     PIO_STACK_LOCATION pIrpstack = IoGetCurrentIrpStackLocation(pIrp);
    9.     //DbgPrint("IRP_MJ_INTERNAL_DEVICE_CONTROL cаught\n");
    10.     query = pIrpstack->MinorFunction;
    11.     switch(query) {
    12.         case TDI_ACCEPT:    //or
    13.         case TDI_RECEIVE_DATAGRAM:
    14.             DbgPrint("TDI_ACCEPT (or TDI_RECEIVE_DATAGRAM) catch, reflecting\n");
    15.             pIrp->IoStatus.Status = STATUS_CONNECTION_REFUSED;
    16.             IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    17.             return STATUS_SUCCESS;
    18.         case TDI_CONNECT:    //if this is a connection to server - pass it, otherwise - reject
    19.             connect_destaddress = (PTRANSPORT_ADDRESS)(((PTDI_REQUEST_KERNEL_CONNECT)(&pIrpstack->Parameters))->RequestConnectionInformation->RemoteAddress);
    20.             if(connect_destaddress->Address[0].AddressType==TDI_ADDRESS_TYPE_IP) {
    21.                 ipaddress = ((TDI_ADDRESS_IP *)(connect_destaddress->Address->Address))->in_addr;
    22.                 DbgPrint("IP TDI_CONNECT detected: %u \n", ipaddress);
    23.                 if(ipaddress==SERVER_ADDRESS) {
    24.                     DbgPrint("Connection allowed for privileged address\n");
    25.                     goto passit;
    26.                 }
    27.             }
    28.         case TDI_SEND_DATAGRAM:
    29.             DbgPrint("TDI_SEND_DATAGRAM (or TDI_CONNECT), reflecting\n");
    30.             pIrp->IoStatus.Status = STATUS_ACCESS_DENIED;
    31.             IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    32.             return STATUS_SUCCESS;
    33.         case TDI_SET_EVENT_HANDLER:
    34.             eventtype = ((PTDI_REQUEST_KERNEL_SET_EVENT)(&pIrpstack->Parameters))->EventType;
    35.             DbgPrint("TDI_SET_EVENT_HANDLER\n");
    36.             if(eventtype==TDI_EVENT_CONNECT || eventtype==TDI_EVENT_RECEIVE_DATAGRAM) {
    37.                 DbgPrint("TDI_SET_EVENT_HANDLER (connect or receive datagram) catch, reflecting\n");
    38.                 pIrp->IoStatus.Status = STATUS_ACCESS_DENIED;
    39.                 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    40.                 return STATUS_SUCCESS;
    41.             }
    42.     }
    43. //pass packet
    44. passit:
    45.     pdx = (PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;
    46.     IoSkipCurrentIrpStackLocation(pIrp);
    47.     status = IoCallDriver(pdx->pPrevDevObjInStack, pIrp);
    48.     return status;
    49. }
    И вот что выдает отладчик после запуска тестирующей программки
    Код (Text):
    1. IP TDI_CONNECT detected: 30976192
    2. Connection allowed for privileged address
    3. TDI_SET_EVENT_HANDLER
    4. TDI_SET_EVENT_HANDLER
    5. TDI_SET_EVENT_HANDLER
    6. TDI_SET_EVENT_HANDLER
    7. TDI_SET_EVENT_HANDLER
    Я думал, что SocketAPI устанавливает события ожидания (SET_EVENT_HANDLER) для соединения, если оно происходит "слишком быстро", но ведь DbgPrint-сообщений вида
    TDI_SET_EVENT_HANDLER (connect or receive datagram) catch, reflecting


    отладчик не выдает, значит эти EVENT_HANDLER's не устанавливаются..

    Чем можно объяснить такое поведение? И почему не проиходит BSOD?
    Только начал разбираться в драйверах и TDI, прошу прощения за вермишелеподобный код

    Система Windows XP
    Спасибо

    И хотелось бы спросить — правильно ли, что ожидать TDI_SEND_DATAGRAM и TDI_RECEIVE_DATAGRAM (равно как и событий со словом *DATAGRAM* в их идентификаторах) на \Device\Tcp бессмысленно? И чтоб организовать фильтрацию Udp нужно отдельно создать DEVICE_OBJECT, присоединить его к открытому объекту \Device\Udp? Или можно обойтись одним объектом-фильтром DEVICE_OBJECT (тогда к чему его присоединять?) для обоих протоколов?
    Еще раз извините за глупые вопросы

    (Задал эти же вопросы на rsdn, правда безуспешно)
     
  2. x64

    x64 New Member

    Публикаций:
    0
    Регистрация:
    29 июл 2008
    Сообщения:
    1.370
    Адрес:
    Россия
    Код показывай.

    Событие TDI_EVENT_CONNECT это только для серверов (входящие соединения).

    Поведение чего? Приложения? Не знаю, возможно, ошибка в коде.

    Встречный вопрос - а почему должен быть? Код фильтра вполне корректный.

    Да.

    Нет.

    Можешь считать себя просветлённым ибо теперь ты знаешь разницу между форумом и IM.
     
  3. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    К сказанному x64 добавить, в принципе, нечего)

    вермишелеподобный код (кстати классное слово, спасибо! взял на заметку ;)) возникает отнюдь не от незнания техники, а от нежелания писать читабельно :)

    Как ты аттачнешь его к двум девайсам сразу?

    Вообщем, надо код самой программки
     
  4. Nicky

    Nicky New Member

    Публикаций:
    0
    Регистрация:
    28 дек 2009
    Сообщения:
    16
    x64
    Спасибо за ответ
    Программа-сервер запускается на "чистой" (без установленного драйвера-фильтра) машине. Эта машина и есть тот единственный "разрешенный" хост в драйвере, куда пропускаются пакеты:
    Код (Text):
    1. #include <iostream>
    2. #include <winsock2.h>
    3. #include <windows.h>
    4. #include <stdlib.h>
    5. #pragma comment(lib, "ws2_32.lib")
    6. using namespace std;
    7.  
    8. int main(int argc, char **argv) {
    9.     WSADATA wsadata;
    10.     WSAStartup(MAKEWORD(2, 0),&wsadata);
    11.     int PORT=9482;
    12.     if (argc>1) {
    13.         PORT = atoi(argv[1]);
    14.         cout << "Using custom port: " << argv[1]<<endl;
    15.     }
    16.  
    17.     char buf[100];
    18.     cout << "Creating a server socket\n";
    19.     SOCKET servsock = socket(AF_INET, SOCK_STREAM, 0);
    20.     sockaddr_in addr;
    21.     addr.sin_addr.S_un.S_addr = ADDR_ANY;
    22.     addr.sin_family = AF_INET;
    23.     addr.sin_port = htons(PORT);
    24.     bind(servsock, (sockaddr*)&addr, sizeof(sockaddr));
    25.     cout << "bind() done\n";
    26.     listen(servsock, 0);
    27.     cout << "listen() enabled\n";
    28.     int size;
    29.     cout << "listening...\n";
    30.     SOCKET clsock = accept(servsock, (sockaddr*)&addr, &size);
    31.     cout << "accepted!\n";
    32.  
    33.     int ri = recv(clsock, buf, 99, 0);
    34.     cout << "Received "<<ri<<" bytes: "<<buf;
    35.     //cout.write(buf, 100);
    36.     cout.flush();
    37.     cin.get();
    38.     WSACleanup();
    39. }
    Клиент запускается на машине с установленным фильтром.
    Код (Text):
    1. #include <iostream>
    2. #include <winsock2.h>
    3. #include <windows.h>
    4. #include <string.h>
    5. #include <stdlib.h>
    6. #pragma comment(lib, "ws2_32.lib")
    7. using namespace std;
    8. #define REP(a) if (report) {cout << a << endl << flush; cin.get();}
    9.  
    10. int main(int argc, char **argv) {
    11.     WSADATA wsadata;
    12.     WSAStartup(MAKEWORD(2, 0),&wsadata);
    13.     int report = 0;
    14.     cout << "Creating a client socket\n";
    15.     int PORT=9482;
    16.     if (argc>1) {
    17.         PORT = atoi(argv[1]);
    18.         cout << "Using custom port: " << argv[1]<<endl;
    19.         if(argc==3) {
    20.             if(!strcmp(argv[2], "report")) {
    21.                 cout << "Using manual reporting mode\n";
    22.                 report = 1;
    23.             }
    24.         }
    25.     }
    26.     REP("Begin: create socket")
    27.     SOCKET clsock =socket(AF_INET, SOCK_STREAM, 0);
    28.     if(clsock==INVALID_SOCKET) {
    29.         cout << "Socket() error!\n"<<endl;
    30.         cin.get();
    31.         return 0;
    32.     }
    33.     REP("End: create socket")
    34.     sockaddr_in addr;
    35.     addr.sin_addr.S_un.S_addr = inet_addr("192.168.216.1");
    36.     addr.sin_family = AF_INET;
    37.     addr.sin_port = htons(PORT);
    38.     REP("Begin: connect()")
    39.         if(connect(clsock, (sockaddr*)(&addr), sizeof(sockaddr))) {
    40.             cout << "CONNECT() ERROR!\n"<<endl<<flush;
    41.             cin.get();
    42.             return 0;
    43.         };
    44.     REP("End connect()")
    45.     cout << "connect() done\n";
    46.     char buf[]="Hello from usermode ClSock!";
    47.     REP("Begin: send()")
    48.     if(send(clsock, buf, strlen(buf)+1, 0)==SOCKET_ERROR) {
    49.         cout << "send() error!\n";
    50.         return 0;
    51.     };
    52.     REP("End: send()")
    53.     cout << "sent!\n";
    54.     cout.flush();
    55.     cin.get();
    56.     WSACleanup();
    57. }
    Если эти программы запустить как
    Код (Text):
    1. server.exe 1234
    2. client.exe 1234 report
    и быстро нажимать enter, можно поймать Access violation
    Гарантированно AV появляется, если запустить так:
    Код (Text):
    1. server.exe 1234
    2. client.exe 1234
    Что-то подсказывает, что исключение происходит где-то в недрах той части Winsock что в режиме пользователя. Может из-за того что возвращаю STATUS_ACCESS_DENIED вместо ожидаемого этой библиотекой STATUS_CONNECTION_REFUSED? Разобраться в недрах Winsock к сожалению не позволяет квалификация :dntknw:

    Объект \Device\Tcp открывается ZwCreateFile, где в качестве параметра pEaBuffer передается лок. порт 0, лок. адрес 0.0.0.0
    затем к этому pTcpDeviceObject аттачится только что созданный объект-фильтр.
    Этот же файловый объект используется для передачи/приема. Сейчас уже не уверен, что так можно делать, может имеет смысл создать свои объекты файл отдельно для фильтрации и отдельно для приема/отсылки по сети?
    Еще раз благодарю, если дочитали это до конца :)
     
  5. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Не надо домыслов, достаточно посмотреть call stack на момент исключения в том же WinDbg, запустив процесс под его отладкой.
     
  6. Nicky

    Nicky New Member

    Публикаций:
    0
    Регистрация:
    28 дек 2009
    Сообщения:
    16
    По правде говоря, WinDbg использовал только на уровне консоли вывода сообщений DbgPrint, я не обладаю достаточной квалификацией в реверсинге к сожалению, чтоб разобрать дамп, хотя хотелось бы..
    Вот что я получил в Ollydbg
    [​IMG]
    может вы подскажете, что не так?

    x64
    Простите что надоедаю :), но можно узнать ваше мнение по поводу
    ?
     
  7. Nicky

    Nicky New Member

    Публикаций:
    0
    Регистрация:
    28 дек 2009
    Сообщения:
    16
    Great
    x64
    Вобщем спасибо, теперь работает :)
    Как оказалось, программа режима пользователя вылетала из-за того, что в драйвере-фильтре один pTcpFileObject использовался и для отсылки данных, и для получения pTcpDeviceObject (ObReferenceObjectByHandle), по которому я получал DEVICE_OBJECT tcp TDI-транспорта, и к которому аттачился.
    Так и не понял, почему нельзя было делать так, как я сделал в первом варианте (и почему вылетала программа пользователя, а не вся система в bsod (причем блокирование происходило нормально, без ошибок, а вот если соединятся с "разрешенным" в фильтре хостом - получался AV)). Но в общем, это не настолько важно, главное что не вылетает :)
     
  8. Nicky

    Nicky New Member

    Публикаций:
    0
    Регистрация:
    28 дек 2009
    Сообщения:
    16
    Ошибся, IoGetRelatedDeviceObject, а не ObReferenceObjectByHandle