BSOD при перезаписи MDL
От: A.Pastukhov  
Дата: 09.11.09 05:44
Оценка:
Всем привет. Бьюсь с перезаписью MDL в стандартном Dispatch-обработчике в драйвере. Цель — перезапись Irp->MdlAddress.
    pSrcMdl = IoAllocateMdl((PVOID)&Data, 2048, FALSE, FALSE, NULL);
    if(pSrcMdl)
    {
        MmBuildMdlForNonPagedPool(pSrcMdl);
        pAddress = MmGetSystemAddressForMdlSafe(pSrcMdl, NormalPagePriority);
            __try
            {
                pDstDddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
            if (pDstDddress)
                {
                    memset(pDstDddress, 0, 2048);
                    for (i = 1; i < 2048; i++)
                    {
                        if (MmIsAddressValid(pAddress)) 
                        *pDstDddress = *pAddress;
                        pAddress++;
                        pDstDddress++;
                     }
                 }
            }
          __except(EXCEPTION_EXECUTE_HANDLER){}
          IoFreeMdl(pSrcMdl);
    }

В результате — BSOD с кодом BAD_POOL_CALLER.
Получается как в анекдоте про Петьку и Васильваныча: "...нутром чувствую, что литр, а доказать не могу". Вижу, что проблемы с выделением памяти, но что делаю неправильно?
Re: BSOD при перезаписи MDL
От: x64 Россия  
Дата: 09.11.09 06:14
Оценка:
Во-первых, что есть Data? Это из non-paged пула? Во-вторых, почему i = 1, нулевой байт не надо копировать? И в-третьих, давай конкретно, что у тебя есть на руках и что ты вообще хочешь с этим сделать? Пока я вижу довольно хаотичные действия, давай постановку задачи.
Re[2]: BSOD при перезаписи MDL
От: A.Pastukhov  
Дата: 09.11.09 09:39
Оценка:
Речь идет о перехвате TDI_SEND и подмене Irp->MdlAddress (в продолжении *моего* предыдущего поста о проблемах редиректа в TDI_CONNECT, там сошлись на странностях поведения некоторых web-серверов и необходимости подмены "на лету" данных в TDI_SEND, в частности поля "Host"). Собственно это я хочу реализовать. Шеф поставил такую задачу — при посещении развлекательных сайтов организовать скрытый редирект на какой-нибудь нейтральный сайт.

static const char Data[] = "GET / HTTP/1.1\r\n"
               "Host: www.rambler.ru\r\n"
                   "Connection: close\r\n"
               "\r\n";

NTSTATUS TdiSend(IN PDEVICE_EXTENSION pde, IN PIRP pIrp, IN PIO_STACK_LOCATION stack)
{
    NTSTATUS status;
    void* p;
    PUCHAR pAddress, pDstDddress;
    ULONG r;
    PMDL pDstMdl, pSrcMdl;

    MappedAddress = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
    p = strstr((PCCHAR)MappedAddress, "Host: www.sex.com");
    if(p != NULL & glRedirectFlag == TRUE)
    {
        pSrcMdl = IoAllocateMdl((PVOID)&Data, 2048, FALSE, FALSE, NULL);
        if(pSrcMdl)
        {
            MmBuildMdlForNonPagedPool(pSrcMdl);
            pAddress = MmGetSystemAddressForMdlSafe(pSrcMdl, NormalPagePriority);
            __try
            {
                            pDstDddress = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
                if (pDstDddress)
                            {
                                memset(pDstDddress, 0, 2048);
                                for (r = 0; r < 2048; r++)
                                {
                                     if (MmIsAddressValid(pAddress)) 
                           *pDstDddress = *pAddress;
                                     pAddress++;
                                     pDstDddress++;
                                }
                         }
                     
             }
        __except(EXCEPTION_EXECUTE_HANDLER){}
               IoFreeMdl(pSrcMdl);
           }
    }
    try
    {
                IoCopyCurrentIrpStackLocationToNext(pIrp);
        IoSetCompletionRoutine(pIrp, DefaultCompletionRoutine, pde, TRUE, TRUE, TRUE);
        status = IoCallDriver(pde->LowerDeviceObject, pIrp);
    }
    finally{}
    return status;
}

C "r = 1" ступил, каюсь. Переменная glRedirectFlag устанавливается в TRUE при отлове "плохих" IP-адресов в обработчике TDI_CONNECT.
Re[3]: BSOD при перезаписи MDL
От: x64 Россия  
Дата: 09.11.09 11:48
Оценка:
AP>Речь идет о перехвате TDI_SEND и подмене Irp->MdlAddress (в продолжении *моего* предыдущего поста о проблемах редиректа в TDI_CONNECT, там сошлись на странностях поведения некоторых web-серверов и необходимости подмены "на лету" данных в TDI_SEND, в частности поля "Host"). Собственно это я хочу реализовать. Шеф поставил такую задачу — при посещении развлекательных сайтов организовать скрытый редирект на какой-нибудь нейтральный сайт.

Понятно, тогда скажи сразу — тебе тяп-ляп надо (чтоб ночальнег отстал) или серьёзное решение, работающее корректно в любых условиях?
Re[4]: BSOD при перезаписи MDL
От: A.Pastukhov  
Дата: 09.11.09 12:05
Оценка:
Здравствуйте, x64, Вы писали:

x64>Понятно, тогда скажи сразу — тебе тяп-ляп надо (чтоб ночальнег отстал) или серьёзное решение, работающее корректно в любых условиях?


Скорее, первый вариант. Задание звучало "...шоб было и работало, а то запарили парнуху смареть да в одноклассниках сидеть". Доступа к шлюзу нет, а его админ не подписывается на установку фильтров.
Поэтому порешили сделать скрытый редирект и в случае вопросов все валить или на провайдер, или на админов шлюза, или на вмешательство высших сил.
Опыт кодинга в ядре у меня есть, хотя именно TDI ковырял немного и неглубоко, поэтому был бы рад любому решению.
Re[5]: BSOD при перезаписи MDL
От: A.Pastukhov  
Дата: 09.11.09 12:36
Оценка:
Как вариант, у меня еще была мысль при коннекте к нежелательному IP-адресу отменить IRP-пакет, там же создать и отослать новый пакет в другом, нужном направлении
Re[5]: BSOD при перезаписи MDL
От: x64 Россия  
Дата: 09.11.09 13:17
Оценка: 4 (2)
AP>Доступа к шлюзу нет, а его админ не подписывается на установку фильтров.

Печально, потому что это было бы самое грамотное решение в данной ситуации.

AP>Опыт кодинга в ядре у меня есть, хотя именно TDI ковырял немного и неглубоко, поэтому был бы рад любому решению.


ok, в таком случае обрисую несколько наиболее важных моментов. Во-первых, никакого глобального флага, сигнализирующего о начале замены, не должно быть, потому как ты не сможешь синхронизировать соединение и отсылку запроса (точнее, сможешь, но получишь дикие тормоза сети при этом). Флажок этот должен быть per-connection, соответственно, карту соединений так или иначе вести придётся (об этом чутка у меня в блоге есть), что равносильно реализации конечного автомата для соединений (ну тут ничего не поделаешь, — надо). Ключом будет pIoStack->FileObject соответствующий connection endpoint, он будет у тебя одинаковый и в TDI_CONNECT и в TDI_SEND, т.е. при соединении ставим флажок для этого элемента карты, а при первой же отправке проверяем. При этом, что важно: если флажок установлен, тогда следует подменить буфер и сбросить флажок, потому как в последующих запросах на отправку данных HTTP-заголовка уже скорее всего не будет, следовательно, обрабатывать их уже не нужно (для этого и сбрасываем флажок). В этом одно из главных отличий тяп-ляп от решения, в первом случае проверяем только первый запрос TDI_SEND, во втором же реализуем полноценную Http State Machine (конечный автомат то бишь), это нужно, потому как в общем случае ты не можешь быть уверен, что весь заголовок (включая поле Host) придёт тебе в первом же TDI_SEND. Кто-нибудь хитрожопый запросто может висеть фильтром выше тебя и кромсать запросы на куски, да и полагаться на детали реализации сокетных драйверов типа AFD тоже не стоит. Но ты сказал тебе нужно тяп-ляп, значит не парься, — лови только первый TDI_SEND и меняй буфер там.

По поводу как менять буфер. Прежде всего, тупо копировать ничего нельзя, потому как размер предоставленного тебе буфера в запросе может быть меньше, чем тебе нужно. Буфер надо создавать собственный, а лучше создавать собственный запрос. Лучше запрос, потому что тогда всю заботу об исходном MDL'е можно будет возложить на ядро. Итого, делаешь новый буфер (проверку ошибок сам добавишь):

PVOID pNewData = NULL;
PVOID pSourceData = NULL;
PMDL pNewMdlAddress = NULL;

...

//
// Создаём новый буфер и проецируем исходный в а.п. ядра.
//

pNewData = ExAllocatePool (NonPagedPool, ...);
pSourceData = MmGetSystemAddressForMdlSafe (pIrp -> MdlAddress, NormalPagePriority);

//
// Копируешь в новый буфер старые данные, одновременно меняя Host.
// Для этого используешь полученные указатели pSourceData и pNewData.
//

...

//
// Создаём MDL для нового запроса.
//

pNewMdlAddress = IoAllocateMdl (pNewData, ...).
MmBuildMdlForNonPagedPool (pNewMdlAddress).


Далее создаёшь новый TDI_SEND запрос:

KEVENT kEvent = {0};
PIRP pNewRequest = NULL;
IO_STATUS_BLOCK IoStatus = {0};
PTDI_REQUEST_KERNEL_SEND pTdiSend = &pIoStack -> Parameters;

...

//
// Инициализируем локальное событие.
//

KeInitializeEvent (
  &kEvent,
  NotificationEvent,
  FALSE);

//
// Создаём новый запрос.
//

pNewRequest = TdiBuildInternalDeviceControlIrp (
  TDI_SEND,
  pLowerDeviceObject,
  pIoStack -> FileObject,
  &kEvent,
  &IoStatus);

//
// Настраиваем новый запрос.
//

TdiBuildSend (
  pNewRequest,
  pLowerDeviceObject,
  pIoStack -> FileObject,
  NULL,
  NULL,
  pNewMdlAddress,
  pTdiSend -> SendFlags,
  MmGetMdlByteCount (pNewMdlAddress));


Теперь свежесозданный запрос следует отправить вниз вместо исходного, при этом нам следует дождаться его окончания и только после этого завершить оригинальный запрос:

NTSTATUS Status = STATUS_UNSUCCESSFUL;

...

//
// Отсылаем новый запрос нижележащему драйверу.
//

Status = IoCallDriver (
  pLowerDeviceObject,
  pNewRequest);

if (Status == STATUS_PENDING)
{
  KeWaitForSingleObject (
    &kEvent,
    Executive,
    KernelMode,
    FALSE,
    NULL);
}

//
// Копируем результат нашего запроса в буфер исходного,
// после чего завершаем исходный запрос как будто это сделал
// транспорт.
//

pIrp -> IoStatus = IoStatus;
IoCompleteRequest (pIrp, IO_NO_INCREMENT);

//
// Освобождать созданные нами IRP и MDL после вызова
// IoCallDriver() уже не нужно, т.к. это сделает I/O Manager
// автоматически.
//
Re[6]: BSOD при перезаписи MDL
От: x64 Россия  
Дата: 09.11.09 13:23
Оценка:
AP>Как вариант, у меня еще была мысль при коннекте к нежелательному IP-адресу отменить IRP-пакет, там же создать и отослать новый пакет в другом, нужном направлении

Это ничего не изменит в данном случае.
Re[7]: BSOD при перезаписи MDL
От: A.Pastukhov  
Дата: 09.11.09 13:35
Оценка:
Вот теперь все более-менее встало на свои места. Буду пробовать.
thx, x64, за разъяснение
Re[6]: BSOD при перезаписи MDL
От: donkombat  
Дата: 04.05.10 15:58
Оценка:
Пожалуйста, уважайте коллег и не допускайте излишнего цитирования. Для неуважающих напомню, что есть правила форума и ресурса + санкции за несоблюдение оных. Модератор

Использую этот метод:

if( IsBlockedURL(pFullLink) )
        {
            status = STATUS_INVALID_CONNECTION;
            //меняю www.mail.ru на www.mysite1.com
            uNewGetSize = uFirstGetSize - HostSize + 11;
            pNewData = malloc_np(uNewGetSize);
            memset( pNewData, 0, uNewGetSize);

            uSizeCopy = pHost - pInfo;
            memcpy( pNewData, pInfo , uSizeCopy);
            //сделаем новое имя хоста
            memcpy( (PCHAR)pNewData + uSizeCopy, "mysite1.com" , 11 );
            //скопируем все до конца
            uSizeCopy1 = uFirstGetSize - (pHost + HostSize - pInfo);
            memcpy( (PCHAR)pNewData + uSizeCopy + 11, pHost + HostSize  , uSizeCopy1 );
            
            // Создаём MDL для нового запроса.
            pNewMdlAddress = IoAllocateMdl(    pNewData, uNewGetSize, NULL, FALSE, NULL);
            MmBuildMdlForNonPagedPool (pNewMdlAddress);
            
            pNewRequest = NULL;
            
            pTdiSend = &(irps->Parameters);
            
            KeInitializeEvent (
                &kEvent,
                NotificationEvent,
                FALSE);

            old_devobj = get_original_devobj(DeviceObject, NULL);
            if (old_devobj == NULL) 
                return STATUS_SUCCESS;
            //
            // Создаём новый запрос.
            //
            //Callers of TdiBuildInternalDeviceControlIrp must be running at IRQL = PASSIVE_LEVEL.
            //Consequently, a client should preallocate a few IRPs with this function so the client can use
            //them to submit TDI_XXX requests when it is running at a higher IRQL. 

            pNewRequest = TdiBuildInternalDeviceControlIrp (
                TDI_SEND,
                old_devobj,
                irps -> FileObject,
                &kEvent,
                &IoStatus);

            if(pNewRequest == NULL)
            {
                DbgPrint("ProcessGetIrp error : pNewRequest == NULL \n");
                free(pFullLink);
                return STATUS_SUCCESS;
            }

            //
            // Настраиваем новый запрос.
            //

            TdiBuildSend (
                pNewRequest,
                old_devobj,
                irps -> FileObject,
                NULL,
                NULL,
                pNewMdlAddress,
                pTdiSend -> SendFlags,
                MmGetMdlByteCount (pNewMdlAddress));
            //
            // Отсылаем новый запрос нижележащему драйверу.
            //
            Status = IoCallDriver ( old_devobj , pNewRequest );
            if ( Status == STATUS_PENDING )
            {
                KeWaitForSingleObject (
                    &kEvent,
                    Executive,
                    KernelMode,
                    FALSE,
                    NULL);
            }
            
            // Копируем результат нашего запроса в буфер исходного,
            // после чего завершаем исходный запрос как будто это сделал
            // транспорт.
            //
            
            *pRefreshStatus = IoStatus.Status;
            irp -> IoStatus = IoStatus;
            IoCompleteRequest (irp, IO_NO_INCREMENT);

            //
            // Освобождать созданные нами IRP и MDL после вызова
            // IoCallDriver() уже не нужно, т.к. это сделает I/O Manager
            // автоматически.
            //
            free(pNewData);
        }


и постоянно получаю синий экран:
PFN_LIST_CORRUPT (4e)
где может быть проблема?
Re[7]: BSOD при перезаписи MDL
От: Valery A. Boronin Россия linkedin.com/in/boronin
Дата: 04.05.10 20:06
Оценка: 1 (1)
Коллега, на первый раз убрал Ваш оверквотинг и выписал предупредительный 1-day бан — уважайте коллег и не допускайте излишнего цитирования. Для неуважающих напомню, что есть правила форума и ресурса + санкции за несоблюдение оных. Модератор

PS по проблеме см ссылку ниже и используйте пожалуйста поиск по ключевым словам — PFN_LIST_CORRUPT в данном случае.

D>и постоянно получаю синий экран:

D>PFN_LIST_CORRUPT (4e)
D>где может быть проблема?
Re[3]: Маппирование Irp-&gt;UserBuffer
Автор: Valerio
Дата: 03.11.03
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
Valery A. Boronin, RSDN Team, linkedin.com\in\boronin
R&D Mgmt & Security. AppSec & SDL. Data Protection and Systems Programming. FDE, DLP, Incident Management. Windows Filesystems and Drivers.
Re[7]: BSOD при перезаписи MDL
От: Аноним  
Дата: 05.05.10 14:35
Оценка:
вот что решило проблему:
MmProbeAndLockPages(pNewMdlAddress, KernelMode, IoModifyAccess);


Я избежал синего экрана, заменил строку, которая идет после Host: в запросе, но браузер говорит что невозможно отобразить страницу, т.е редиректа на нужный сайт не происходит.
Может кроме строки с названием хоста надо менять еще что-нибудь?
Re[8]: BSOD при перезаписи MDL
От: Аноним  
Дата: 05.05.10 16:53
Оценка:
А>вот что решило проблему:
А>MmProbeAndLockPages(pNewMdlAddress, KernelMode, IoModifyAccess);
А>

А>Я избежал синего экрана, заменил строку, которая идет после Host: в запросе, но браузер говорит что невозможно отобразить страницу, т.е редиректа на нужный сайт не происходит.

А>Может кроме строки с названием хоста надо менять еще что-нибудь?

при этом я наблюдаю логи
[TDI TDI_SEND] URL -> mail.ru
[TDI TDI_SEND] reverse key = mail.ru on mysite1.com
[TDI TDI_SEND] URL -> mysite1.com

т.е перезапись + создание и посылка irp проходит нормально. где я мог напортачить? выложить побольше кода?
Re[9]: BSOD при перезаписи MDL
От: Аноним  
Дата: 05.05.10 17:01
Оценка:
Здравствуйте, Аноним, Вы писали:

А>при этом я наблюдаю логи

А>[TDI TDI_SEND] URL -> mail.ru
А>[TDI TDI_SEND] reverse key = mail.ru on mysite1.com
А>[TDI TDI_SEND] URL -> mysite1.com

таких логов наблюдается подряд очень много, т.е складывается впечатление что мой фильтр принимает много irp с запросом к mail, и во всех них я меняю хост, создаю новый irp и отправляю его, потом получаю в своем драйвере уже вновь созданный мной же irp.
Re[6]: BSOD при перезаписи MDL
От: donkombat  
Дата: 06.05.10 09:42
Оценка:
Здравствуйте, x64, Вы писали:

x64>ok, в таком случае обрисую несколько наиболее важных моментов. Во-первых, никакого глобального флага, сигнализирующего о начале замены, не должно быть, потому как ты не сможешь синхронизировать соединение и отсылку запроса (точнее, сможешь, но получишь дикие тормоза сети при этом). Флажок этот должен быть per-connection, соответственно, карту соединений так или иначе вести придётся (об этом чутка у меня в блоге есть), что равносильно реализации конечного автомата для соединений (ну тут ничего не поделаешь, — надо). Ключом будет pIoStack->FileObject соответствующий connection endpoint, он будет у тебя одинаковый и в TDI_CONNECT и в TDI_SEND, т.е. при соединении ставим флажок для этого элемента карты, а при первой же отправке проверяем. При этом, что важно: если флажок установлен, тогда следует подменить буфер и сбросить флажок, потому как в последующих запросах на отправку данных HTTP-заголовка уже скорее всего не будет, следовательно, обрабатывать их уже не нужно (для этого и сбрасываем флажок). В этом одно из главных отличий тяп-ляп от решения, в первом случае проверяем только первый запрос TDI_SEND, во втором же реализуем полноценную Http State Machine (конечный автомат то бишь), это нужно, потому как в общем случае ты не можешь быть уверен, что весь заголовок (включая поле Host) придёт тебе в первом же TDI_SEND. Кто-нибудь хитрожопый запросто может висеть фильтром выше тебя и кромсать запросы на куски, да и полагаться на детали реализации сокетных драйверов типа AFD тоже не стоит. Но ты сказал тебе нужно тяп-ляп, значит не парься, — лови только первый TDI_SEND и меняй буфер там.


А в TDI_CONNECT перед требуется менять значения удаленной пары IP:Port на пару для заведомо нейтрального сайта ?
Re[7]: BSOD при перезаписи MDL
От: x64 Россия  
Дата: 06.05.10 10:14
Оценка: 1 (1)
D>А в TDI_CONNECT перед требуется менять значения удаленной пары IP:Port на пару для заведомо нейтрального сайта ?

Вопрос, честно говоря, не очень понял, но вообще если тебе нужно сделать редирект, то подмена непосредственно адреса, это само собой разумеющееся. Короче говоря, для корректного редиректа, как минимум, потребуется подменить IP-адрес, порт и поле Host в Http-заголовках (ну и плюс ещё имя объекта в самом начале заголовка по желанию).
Re[8]: BSOD при перезаписи MDL
От: donkombat  
Дата: 06.05.10 12:03
Оценка:
Здравствуйте, x64, Вы писали:

x64>Короче говоря, для корректного редиректа, как минимум, потребуется подменить IP-адрес, порт и поле Host в Http-заголовках (ну и плюс ещё имя объекта в самом начале заголовка по желанию).


В данный момент, я обрабатываю TDI_SEND, как было описано в вашем предыдущем сообщении: http://www.rsdn.ru/forum/asm/3596121.1.aspx
Автор: x64
Дата: 09.11.09
. В результате получаю вот это : http://www.rsdn.ru/forum/asm/3797684.1.aspx
Автор:
Дата: 05.05.10

Т.е похоже, что мне удалось подменить Host в Http-заголовках.
А где подменять IP-адрес и порт? в TDI_CONNECT, при составлении карты соединений?
Re[9]: BSOD при перезаписи MDL
От: Аноним  
Дата: 06.05.10 13:28
Оценка: 1 (1) +1
D>А где подменять IP-адрес и порт? в TDI_CONNECT, при составлении карты соединений?

Угу. Т.е. тебе для редиректа нужно заранее иметь карту "запрещенных" IP-адресов, что при установлении ссвязи с ними твой редиректор автоматов изменял target-IP на твой. Сложность редиректа с TDI-уровня именно в этом и заключается.
Re[10]: BSOD при перезаписи MDL
От: donkombat  
Дата: 12.05.10 13:39
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Угу. Т.е. тебе для редиректа нужно заранее иметь карту "запрещенных" IP-адресов, что при установлении ссвязи с ними твой редиректор автоматов изменял target-IP на твой. Сложность редиректа с TDI-уровня именно в этом и заключается.


сделал тестовый пример: редирект с mail.ru на rambler.ru. В TDI_CONNECT учел, что mail.ru имеет несколько Ip.
Страница rambler отображается почтя вся, только 2 маленькие картинки подвисают.
Очень благодарен за понятные ответы, долго так пытался и наконец получилось
Re[10]: BSOD при перезаписи MDL
От: donkombat  
Дата: 12.05.10 15:27
Оценка:
Хочу сделать соответствие GET запросу и html странице, которую получаю в ответ на этот запрос. Предполагаю, что если я сохраню FileObject при TDI_SEND (содержит GET запрос) то html страницу в ответ на этот GET я буду получать при TDI_RECEIVE с таким же FileObject. Это верно?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.