Добрый день. При перехвате TDI_SEND нужно изменить содержимое mdl буфера. Достаточно просто в конец буфера дописать несколько байт. Каким способом это можно сделать? Насколько я понял для этого можно использовать функцию: PMDL IoAllocateMdl( __in_opt PVOID VirtualAddress, __in ULONG Length, __in BOOLEAN SecondaryBuffer, __in BOOLEAN ChargeQuota, __inout_opt PIRP Irp ); В первом параметре я указываю адрес буфера, который хочу дописать к mdl. Во втором параметре - его длину. В последнем - текущий irp. В третьем параметре, если указываю TRUE, то mdl буфер остается неизменным, мои данные туда не записываются. если указываю FALSE, то получаю BSOD, видимо по причине изменения размера mdl буфера. Тогда вопрос, где указать новый размер mdl буфера? Прошу помочь разобраться с IoAllocateMdl или посоветовать другой способ редактирования mdl буфера.
Там ничего заумного нет, как кажется на первый взгляд. 1) Получить буфер, где будет содержаться MDL - MmGetSystemAddressForMdlSafe или MmGetSystemAddressForMdl. 2) По указателю пишешь то, что тебе нужно. При этом при записи нужно помнить, что MDL могут быть несколько (см. Mdl->Next), хотя в твоем случае это вряд ли встретиться Как-то так
maxxx777 Дык он и будет отредактирован уже Получи адрес MDL и пиши туда, что хочешь, в пределах размера MDL - все просто, не надо ничего выдумывать
Нет, я сразу не совсем правильно сформулировал вопрос. Данные mdl буфера мне нужно оставить как есть, нетронутыми. А в конец дописать несколько байт. Длина буфера измениться. Как это отразить в irp, чтобы он без проблем пошел дальше с измененным mdl буфером, с чуть большей длиной?
maxxx777 Думаю, можно сделать так: выделить свой буфер (NonPaged), скопировать в него данные, дописать свои, построить для буфера MDL, освободить старую MDL, ее буфер, и перезаписать указатель Irp ->MdlAddress на свежепостроенную MDL. Но это чисто теоретически, сам не пробовал.
Вообще, если для твоей задачи содержимое заголовков пакета не столь критично, то можно просто поэкспериментировать с самими заголовками, т.е. попробовать записать поверх какого-от маловажного заголовка нужные тебе байты, не забыв про "\r\n\" в конце. Я так выкручивался, у меня работало. Веб-сервера иногда нормально на это реагируют, однако не всегда, нужно будет смотреть в каждом конкретном случае.
И если у тот кто создавал MDL, в тот момент когда будет его освобождать, будет брать указатель не из IRP, а из своей переменной будет BSOD. Кажется наиболее безопасно будет создать свой IRP со своими данными и послать его, а оригинальный либо сразу вернуть, либо в тот момент, когда завершится свой.
100gold Я думаю, что так вряд ли кто-то делает, т.к. при вызове IoCallDriver вызывающий не должен делать каких-либо предположений насчет IRP -- она может быть даже завершена и освобождена к моменту возврата из IoCallDriver. Остается, правда, completion routine еще. Но построить новую IRP, наверное, лучше.
Трудонсть будет с complete и IoCancelIrp. Читать переводить: http://msdn.microsoft.com/en-us/library/ff554457%28v=VS.85%29.aspx
Пожалуй я не буду менять размер буфера, а запишу нужные мне данные поверх существующих. Кажется так проще и быстрее. Потом, при необходимости, сделаю что-нибудь более грамотное. Всем спасибо за помощь!
Значит так, рассказываю. Если размер новых данных не превышает размер оригинального буфера, то ничего дополнительно делать не нужно, заменили данные, указали их размер и отправили запрос дальше. Единственное, что тут возможно ещё потребуется сделать, это поставить функцию завершения, в которой в I/O-статусе, в случае успеха, указать кол-во переданных байт равное длине оригинального буфера, чтобы не сбить логику клиента. Я лично так и сделал, но, думаю, это нужно не для всех клиентов, в общем, пробуйте, экспериментируйте, как я в своё время. Если же новых данных больше, то есть два варианта: либо разбить оригинальный запрос на два подзапроса, либо создавать новый буфер. В первом случае пишем данных сколько можно в оригинальный буфер, ставим функцию завершения, отправляем дальше, а в функции завершения, если первый запрос прошёл удачно, то создаём и отправляем дальше второй запрос с остатком новых данных, при этом оригинальный запрос не отдаём пока ядру, это мы сделаем в функции завершения второго запроса. Мне этот способ не нравится по нескольким причинами, но это не суть. Второй способ чуть проще: создаём структуру, в которую кладём информацию об оригинальных данных (адрес буфера, длина и т.д.), создаём новый буфер с новыми данными, записываем на место старого, ставим функцию завершения и ту самую структуру к ней контекстом и отправляем дальше. В функции завершения освобождаем свой буфер, ставим на место старый и возвращаем управление I/O-менеджеру. Техника, вообще говоря, стандартная, поэтому не понимаю, почему она вызывает столько вопросов. Тут вполне достаточно понимания принципов работы I/O-менеджера и вопрос отпадёт сам собой. Хотя, конечно, соглашусь, что процесс завершения I/O-запросов освещён в документации не так чётко, как хотелось бы, тем не менее, используя поиск (в т.ч. и по ресурсам Microsoft), а также сэмпловые исходники, можно составить полную картину.