Всем привет. Бьюсь с перезаписью 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.
Получается как в анекдоте про Петьку и Васильваныча: "...нутром чувствую, что литр, а доказать не могу". Вижу, что проблемы с выделением памяти, но что делаю неправильно?
Во-первых, что есть Data? Это из non-paged пула? Во-вторых, почему i = 1, нулевой байт не надо копировать? И в-третьих, давай конкретно, что у тебя есть на руках и что ты вообще хочешь с этим сделать? Пока я вижу довольно хаотичные действия, давай постановку задачи.
Речь идет о перехвате TDI_SEND и подмене Irp->MdlAddress (в продолжении *моего* предыдущего поста о проблемах редиректа в TDI_CONNECT, там сошлись на странностях поведения некоторых web-серверов и необходимости подмены "на лету" данных в TDI_SEND, в частности поля "Host"). Собственно это я хочу реализовать. Шеф поставил такую задачу — при посещении развлекательных сайтов организовать скрытый редирект на какой-нибудь нейтральный сайт.
AP>Речь идет о перехвате TDI_SEND и подмене Irp->MdlAddress (в продолжении *моего* предыдущего поста о проблемах редиректа в TDI_CONNECT, там сошлись на странностях поведения некоторых web-серверов и необходимости подмены "на лету" данных в TDI_SEND, в частности поля "Host"). Собственно это я хочу реализовать. Шеф поставил такую задачу — при посещении развлекательных сайтов организовать скрытый редирект на какой-нибудь нейтральный сайт.
Понятно, тогда скажи сразу — тебе тяп-ляп надо (чтоб ночальнег отстал) или серьёзное решение, работающее корректно в любых условиях?
Здравствуйте, x64, Вы писали:
x64>Понятно, тогда скажи сразу — тебе тяп-ляп надо (чтоб ночальнег отстал) или серьёзное решение, работающее корректно в любых условиях?
Скорее, первый вариант. Задание звучало "...шоб было и работало, а то запарили парнуху смареть да в одноклассниках сидеть". Доступа к шлюзу нет, а его админ не подписывается на установку фильтров.
Поэтому порешили сделать скрытый редирект и в случае вопросов все валить или на провайдер, или на админов шлюза, или на вмешательство высших сил.
Опыт кодинга в ядре у меня есть, хотя именно TDI ковырял немного и неглубоко, поэтому был бы рад любому решению.
Как вариант, у меня еще была мысль при коннекте к нежелательному IP-адресу отменить IRP-пакет, там же создать и отослать новый пакет в другом, нужном направлении
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).
Теперь свежесозданный запрос следует отправить вниз вместо исходного, при этом нам следует дождаться его окончания и только после этого завершить оригинальный запрос:
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
// автоматически.
//
AP>Как вариант, у меня еще была мысль при коннекте к нежелательному IP-адресу отменить IRP-пакет, там же создать и отослать новый пакет в другом, нужном направлении
Пожалуйста, уважайте коллег и не допускайте излишнего цитирования. Для неуважающих напомню, что есть правила форума и ресурса + санкции за несоблюдение оных. Модератор
Использую этот метод:
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)
где может быть проблема?
Коллега, на первый раз убрал Ваш оверквотинг и выписал предупредительный 1-day бан — уважайте коллег и не допускайте излишнего цитирования. Для неуважающих напомню, что есть правила форума и ресурса + санкции за несоблюдение оных. Модератор
PS по проблеме см ссылку ниже и используйте пожалуйста поиск по ключевым словам — PFN_LIST_CORRUPT в данном случае.
D>и постоянно получаю синий экран: D>PFN_LIST_CORRUPT (4e) D>где может быть проблема? Re[3]: Маппирование Irp->UserBuffer
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.
Здравствуйте, 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 на пару для заведомо нейтрального сайта ?
D>А в TDI_CONNECT перед требуется менять значения удаленной пары IP:Port на пару для заведомо нейтрального сайта ?
Вопрос, честно говоря, не очень понял, но вообще если тебе нужно сделать редирект, то подмена непосредственно адреса, это само собой разумеющееся. Короче говоря, для корректного редиректа, как минимум, потребуется подменить IP-адрес, порт и поле Host в Http-заголовках (ну и плюс ещё имя объекта в самом начале заголовка по желанию).
Здравствуйте, x64, Вы писали:
x64>Короче говоря, для корректного редиректа, как минимум, потребуется подменить IP-адрес, порт и поле Host в Http-заголовках (ну и плюс ещё имя объекта в самом начале заголовка по желанию).
D>А где подменять IP-адрес и порт? в TDI_CONNECT, при составлении карты соединений?
Угу. Т.е. тебе для редиректа нужно заранее иметь карту "запрещенных" IP-адресов, что при установлении ссвязи с ними твой редиректор автоматов изменял target-IP на твой. Сложность редиректа с TDI-уровня именно в этом и заключается.
Здравствуйте, Аноним, Вы писали:
А>Угу. Т.е. тебе для редиректа нужно заранее иметь карту "запрещенных" IP-адресов, что при установлении ссвязи с ними твой редиректор автоматов изменял target-IP на твой. Сложность редиректа с TDI-уровня именно в этом и заключается.
сделал тестовый пример: редирект с mail.ru на rambler.ru. В TDI_CONNECT учел, что mail.ru имеет несколько Ip.
Страница rambler отображается почтя вся, только 2 маленькие картинки подвисают.
Очень благодарен за понятные ответы, долго так пытался и наконец получилось
Хочу сделать соответствие GET запросу и html странице, которую получаю в ответ на этот запрос. Предполагаю, что если я сохраню FileObject при TDI_SEND (содержит GET запрос) то html страницу в ответ на этот GET я буду получать при TDI_RECEIVE с таким же FileObject. Это верно?