Модераторы: Snowy, bartram, MetalFan, bems, Poseidon, Riply

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Драйверы Windows режима ядра, как это делается в Delphi 
:(
    Опции темы
ama_kid
Дата 22.8.2008, 12:00 (ссылка) |    (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



1. Введение
Данная статья является, по своей сути, продолжением известной статьи Геннадия Порева  "Создание драйверов режима ядра в среде Borland Delphi". Все моё дальнейшее повествование будет предполагать, что читатель по крайней мере ознакомился с представленным в ней материалом, поэтому если это еще не сделано - то сейчас самое время. 
К статье прилагается архив со всеми исходными текстами, описанными и разобранными здесь, при этом хотелось бы упомянуть, что некоторые вещи, которые можно найти в исходниках - здесь не описаны, например логирование действий драйвера в файл на диске, которая мало чем отличается от работы с логами в user-mode, или найденная мной в интернете ручная приближенная реализация cdecl-формата вызова функции DbgPrint, которая на языке С++ имеет переменное число параметров с возможностью вставлять управляющие символы для вывода значений переменных (Delphi этого не умеет). Не описано это просто потому, что нет желания останавливаться на этом, при необходимости  - это можно самому посмотреть и понять. Также, при цитировании кода в тексте статьи я буду опускать те участки или параллельные вызовы функций (в основном, конечно, отладочный вывод), которые не нужны для понимания той части, о которой говорю в данный момент, поэтому не стоит удивляться, если обнаружите, что исходном коде наличествует еще какой-то "мусор" по сравнению со статьей. 
2. Инструментарий 
Прежде всего, хочу немного времени уделить описанию инструментария, который я использую у себя. В принципе, в изначальной статье он был достаточно подробно описан, так что я не буду повторяться, просто на всякий случай пошагово опишу его применение. Средой разработки является любой текстовый редактор (кстати, почитателям компоненто-на-форму-кидательства дальнейшее не рекомендуется к прочтению). Лично я пользуюсь встроенным редактором Far'а, но можно использовать любой удобный и привычный инструмент. В принципе, впоследствии я нашел на просторах интернета "Delphi Driver Development Kit" (где вроде как используется компилятор от 7-й версии дельфи), но к тому моменту уже привык к изначальной среде окружения, поэтому не стал его особо изучать (кроме того на него тявкает мой антивирус, а после замены инфицированных компилятора и линкера на рабочие варианты - мне так и не удалось заставить его работать). Кому надо - может погуглить:
Как правило, для разработки драйвера я копирую целиком  папку, содержащую нижеописанные файлы и работаю уже в ней:
а) Файл ntoskrnl.lib - самый главный файл, содержащий все импортируемые ядром Windows функции. Данный файл не только жизненно необходим для работы линковщика, но также может помочь узнать формат вызова конкретной необходимой функции, о чем я расскажу ниже.
б) dcc32.exe, rlink32.dll, system.dcu и sysinit.dcu - собственно компилятор Delphi3 с сопутствующими файлами.
в) link.exe, mspdb71.dll - линковщик от мелкомягких с необходимой для его работы библиотекой
г) DrvInst.exe - инсталлятор\деинсталлятор драйвера. Исходник данного приложения на языке Delphi можно найти в прилагающемся архиве и в нем мной реализованы два способа установки драйвера: моментальный (по умолчанию) и с перезагрузкой (во многих случаях может потребоваться и это). Второй способ в исходнике закомментирован, но рекомендуется его проверить для того, чтобы понимать, как это делается.
д) Пакетные файлы: _clear.bat (очистка папки), _copy.bat (копирование драйвера в системную папку), _Inst.bat (запуск драйвера), Make.bat и _make.bat (компилирование и сборка драйвера) и _Uninst.bat (очистка системы от следов драйвера) - собственно представляют из себя управление циклом жизни драйвера. Каждый из них вызывает соответствующие команды и исполняемые файлы для реализации своей задачи. Для корректной работы перед началом использования надо в них заменить все названия драйвера на своё собственное (заменив, естесственно, и соответствующие названия файлов). Затем, при наличии в этой же папке файла-исходника драйвера, запускать надо в следующем порядке: 
- _make.bat (если все прошло успешно - в папке появится sys-файл драйвера)
- _copy.bat (этот файл скопируется в системную папку)
- (в данном месте я обычно запускаю утилиту DbgView.exe)
- _Inst.bat (драйвер запустится и начнет свою работу)
- _UnInst.bat (драйвер выгрузится и удалится из системы и текущей папки)
е) Ну и не могу не упомянуть скрытых игроков нашей команды - это предустановленный DDK, MSDN и интернет. 99% информации - оттуда.
Естественно, этот способ организации процесса не является истиной в последней инстанции и можно его оптимизировать, но для того, чтобы дальнейшие описания были понятны и, самое главное, вопроизводимы - примем такую среду за отправную точку.

3. Первые шаги
В данной главе я постараюсь показать, как можно работать в kernel-mode и что делать с доставшимся правом "делать все, что захочется". Исходный код тестового драйвера, рассмотренный здесь, а также user-mode'ного приложения для его тестирования, доступен в прилагающемся архиве в папке под именем MyDrv. 

3.1 Перевод функций DDK
Итак, автор первоначальной статьи  оставил нас наедине с "голым" драйвером  tiny, который может только загружаться и выгружаться. Куда двигаться дальше? Надо сказать, что несмотря на использовании компилятора дельфи, все его RTTL-вкусности типа функций trunc, sqrt, random, равно как классов, объектов и других полезностей в ООП, остаются за бортом. Да что там функции - даже банальное сложение двух строковых переменных недопустимо, потому что для этой несложной с точки зрения программиста операции компилятору приходится генерировать вызовы нескольких функций, которых просто нет в режиме ядра! Ну и особую грусть вызывает невозможность использования стандартных обработчиков исключений try...except...finally... по той же самой причине. Поэтому необходимо посмотреть в сторону того, что разрешено. Здесь на помощь приходят файлы ntddk.h, ntdef.h (файлы описаний из WinDDK) и ntoskrnl.lib (описанный выше). Этих файлов конечно недостаточно для того, чтобы написать какой-то навороченный драйвер файловой системы низкого уровня, но для написания более-менее полезного драйвера в учебных целях их хватит (при этом  h-файлы используются только как справочный материал по объявлениям констант и необходимых нам функций). Для начала работы необходимо явно указать компилятору, какие функции режима ядра можно использовать - делается это путем статической линковки функций файла ntoskrnl.lib. Для тех, кто не очень четко представляет себе этот механизм - поясню сказанное: возьмем, к примеру, функцию IoCreateDevice, которая впоследствие нам еще пригодится. Объявление этой функции в файле ntddk.h выглядит следующим образом:
Код
NTKERNELAPI
NTSTATUS
IoCreateDevice(
    IN PDRIVER_OBJECT DriverObject,
    IN ULONG DeviceExtensionSize,
    IN PUNICODE_STRING DeviceName OPTIONAL,
    IN DEVICE_TYPE DeviceType,
    IN ULONG DeviceCharacteristics,
    IN BOOLEAN Exclusive,
    OUT PDEVICE_OBJECT *DeviceObject);
Все типы, используемые в объявлении этой и других функций можно посмотреть в заголовочных файлах WinDDK, в частности уже упомянутых мной ntdef.h и ntddk.h. Это только выглядят они так страшно, на самом деле большинство из них являются дефайнами для простейших типов byte, integer, word, pointer, а также типа record. NTKERNELAPI - это дефайн для формата вызова функции stdcall, а NTSTATUS - это тип возвращаемого значения (integer). Для того, чтобы перевести объявление данной функции - необходимо узнать "формат" её вызова. Я для этого открываю файл ntoskrnl.lib любым просмотрщиком бинарных файлов (в основном использую встроенный в far), и делаю поиск по наименованию требуемой мне функции. В итоге практически сразу нахожу такой фрагмент:
Цитата
IoCreateStreamFileObjectLite@8__IoCreateStreamFileObjectEx@12 ___imp__IoCreateStreamFileObjectEx@12__IoCreateStreamFileObject@8 ___imp__IoCreateStreamFileObject@8__IoCreateNotificationEvent@8 ___imp__IoCreateNotificationEvent@8__IoCreateFileSpecifyDeviceObjectHint@60 ___imp__IoCreateFileSpecifyDeviceObjectHint@60__IoCreateFile@56___imp__IoCreateFile@56 __IoCreateDriver@8___imp__IoCreateDriver@8__IoCreateDisk@8___imp__IoCreateDisk@8 __IoCreateDevice@28___imp__IoCreateDevice@28__IoCreateController@4 ___imp__IoCreateController@4__IoConnectInterrupt@44 ___imp__IoConnectInterrupt@44__IoCompleteRequest@8___imp__IoCompleteRequest@8 __IoCheckShareAccess@20___imp__IoCheckShareAccess@20 __IoCheckQuotaBufferValidity@12___imp__IoCheckQuotaBufferValidity@12 __IoCheckQuerySetVolumeInformation@12___imp__IoCheckQuerySetVolumeInformation@12 __IoCheckQuerySetFileInformation@12_
Выделенный фрагмент и есть описание имени, по которому она экспортируется из библиотеки ntoskrnl.lib (предшествующий ему символ подчеркивания учитывать следует только в единственном эксземпляре, второй - является непечатным символом и вставляется редактором при наборе статьи автоматически). Поэтому конечное описание этой функции на языке Delphi будет выглядеть следующим образом:
Код
function IoCreateDevice(
                        const DriverObject : PDRIVER_OBJECT;
                        const DeviceExtensionSize : ULong;
                        const DeviceName : PUNICODE_STRING;
                        const DeviceType : DEVICE_TYPE;
                        const DeviceCharacteristics : ULong;
                        const Exclusive : DDKBoolean;
                        var   DeviceObject : PDEVICE_OBJECT
                        ) : NTSTATUS; stdcall; external 'ntoskrnl.lib' name '_IoCreateDevice@28';
Здесь я использовал уже переведенные описания типов, которые можно найти в прилагающемся к статье архиве. Все остальные функции импортируются по образу и подобию...

3.2. Создание объекта "устройство"
Сам по себе драйвер, который умеет только загружаться и выгружаться - бесполезен. Как правило, все драйверы работают с какими-либо устройствами (но не обязательно, и впоследствии я покажу пример этого), поэтому самое первое, что необходимо сделать для того, чтобы труд не был напрасным - создать объект "устройство", к которому можно будет впоследствии обращаться из приложения. Создание устройства в принципе может происходить на любом этапе работы драйвера, но как правило это делают при начальной инициализации в функции DriverEnry:
Код
const
 DEVICE_NAME_STRING = 'TestDriver';

 DOSNameBuffer = '\DosDevices\'+DEVICE_NAME_STRING;
 NameBuffer    = '\Device\'+DEVICE_NAME_STRING;
...
function DriverEntry;
var
 Status        : NTSTATUS;
 DeviceObject  : PDEVICE_OBJECT;
 uniDosString  : UNICODE_STRING;
 uniNameString : UNICODE_STRING;
begin
...
 RtlInitUnicodeString(uniNameString,NameBuffer);
 RtlInitUnicodeString(uniDosString,DOSNameBuffer);
 Status:=IoCreateDevice(DriverObject,0,@uniNameString,
                        FILE_DEVICE_UNKNOWN,0,False,DeviceObject);
 if NT_SUCCESS(Status) Then
   Status:=IoCreateSymbolicLink(@uniDOSString,@uniNameString);
...
end;
В приведенным куске кода производится инициализация объектов-строк с помощью функции RtlInitUnicodeString, далее функцией IoCreateDevice создается устройство, к которому можно обращаться, а затем при помощи функции IoCreateSymbolicLink создается символьная связь между именем этого устройства и именем, находящимся в видимой области для user-mode, для того, чтобы приложение могло получить доступ к этому устройству. Естественно, при выгрузке драйвера необходимо выполнить аналогичные действия, но в обратной последовательности:
Код
procedure ADriverUnload(const DriverObject : PDRIVER_OBJECT); stdcall;
var
 uniDosString  : UNICODE_STRING;
begin
...
 RtlInitUnicodeString(uniDosString,DOSNameBuffer);
 IoDeleteSymbolicLink(@uniDosString);
 IoDeleteDevice(DriverObject.DeviceObject);
...
end;
Устройство создано и  теперь с ним можно работать. Присоединение\отсоединение к  устройству осуществляется обычными функциями CreateFile\CloseHandle. В функцию CreateFile передается параметром наш DEVICE_NAME_STRING, но в определенном формате:
Код
const
 DeviceName     = 'TestDriver';   
...
hDevice := CreateFile(pChar('\\.\'+DeviceName),
                       GENERIC_READ or GENERIC_WRITE,0,PSECURITY_DESCRIPTOR(nil),
                       OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
...
Если необходимо выполнять в драйвере какие-либо действия по событиям присоединения\отсоединения от устройства - то для этого необходимо назначить свои функции-диспетчеры:
Код
function CreateDispatch(const DeviceObject : PDEVICE_OBJECT;
         const Irp : PIRP) : NTSTATUS; stdcall;
begin
 IRP.IoStatus.Status:= STATUS_SUCCESS;
 IRP.IoStatus.Information:=0;
 IoCompleteRequest(IRP,IO_NO_INCREMENT);
 Result:=STATUS_SUCCESS;
end;

function CloseDispatch(const DeviceObject : PDEVICE_OBJECT;
                const Irp : PIRP) : NTSTATUS; stdcall;
begin
 IRP.IoStatus.Status:=STATUS_SUCCESS;
 IRP.IoStatus.Information:=0;
 IoCompleteRequest(IRP,IO_NO_INCREMENT);
 Result:=STATUS_SUCCESS;
end;

function DriverEntry;
begin
...
 DriverObject^.DriverUnload:=ADriverUnload;
 DriverObject^.MajorFunction[IRP_MJ_CREATE]:=CreateDispatch;
 DriverObject^.MajorFunction[IRP_MJ_CLOSE]:=CloseDispatch;
...
end;
После этого, при вызове, допустим, CreateFile для нашего устройства, управление будет передано в нашу функцию-диспетчер CreateDispatch. В данном случае показан просто код, осуществляющий действие по умолчанию.

3.3. Общение с приложением user-mode

Но просто создать\удалить устройство - это еще полдела. Зачастую требуется получить некоторую информацию от процесса, исполняющегося в режиме ядра или передать туда порцию своей информации. Для начала рассмотрим самый простой способ - организацию функций-диспетчеров запросов IRP_MJ_READ ($03) и IRP_MJ_WRITE ($04), которые приходят драйверу при выполнении команд ReadFile и WriteFile соответственно. Для начала - необходимо назначить эти самые диспетчеры в функции DriverEntry:
Код
function DriverEntry;
begin
...
 DriverObject^.MajorFunction[IRP_MJ_READ]:=ReadWriteDispatch;
 DriverObject^.MajorFunction[IRP_MJ_WRITE]:=ReadWriteDispatch;
...
end;
...
function ReadWriteDispatch(
         const DeviceObject : PDEVICE_OBJECT;
         const Irp : PIRP) : NTSTATUS; stdcall;
var
 pIrp : PIO_STACK_LOCATION;
begin
...
 pIrp := Irp^.Tail.Overlay.IRPStruct2.IRPUnion2.CurrentStackLocation;
 case pIrp^.MajorFunction of 
   IRP_MJ_READ:  Result:=ReadDispatch(DeviceObject,Irp);
   IRP_MJ_WRITE: Result:=WriteDispatch(DeviceObject,Irp);
   else begin 
         IoCompleteRequest(IRP,IO_NO_INCREMENT);
         Result:=STATUS_SUCCESS;
        end;  
 end;
end;
...
function ReadDispatch(
         const DeviceObject : PDEVICE_OBJECT;
         const Irp : PIRP) : NTSTATUS; stdcall;
begin
...
 IRP.IoStatus.Status:=STATUS_SUCCESS;
 IRP.IoStatus.Information:=ulSize;
 IoCompleteRequest(IRP,IO_NO_INCREMENT);
 Result:=STATUS_SUCCESS;
end;

function WriteDispatch(
         const DeviceObject : PDEVICE_OBJECT;
         const Irp : PIRP) : NTSTATUS; stdcall;
begin
...
 IRP.IoStatus.Status:=STATUS_SUCCESS;
 IRP.IoStatus.Information:=ulSize;
 IoCompleteRequest(IRP,IO_NO_INCREMENT);
 Result:=STATUS_SUCCESS;
end;
Как вариант, можно назначить каждому типу запроса сразу свой диспетчер, в итоге будет равноценно. 
Теперь рассмотрим методы приема\передачи буфера данных. При создании устройства можно выбрать тип метода передачи этого буфера: буферизированный (DO_BUFFERED_IO = $00000004) или прямой (DO_DIRECT_IO = $00000010) путем установки поля Flags объекта-устройств:
Код
DeviceObject^.Flags := DeviceObject^.Flags or (DO_BUFFERED_IO);
Если  флаги не были установлены - то используется третий, "никакой" (neither) метод передачи буфера. Сразу скажу, что я лично практически во всех примерах использую именно такой (neither) метод несмотря на то, что он является относительно опасным по сравнению с остальными (из-за того, что пользовательское приложение может передать заведомо некорректный указатель, а т.к. диспетчер ввода\вывода не в этом случае проверяет валидность адресов, то эта проблема ложится на разработчика). Останавливаться на этом подробно не буду, но понимать различия между способами передачи буфера нужно обязательно. Основное отличие - это его расположение при обработке запросов чтения-записи (т.е. откуда брать полученную информацию и куда её складывать, а также её размеры). Подробно обо всём этом можно почитать в любой книжке по драйверам или в интернете (в частности, MSDN или WASM.RU). Приведу пример обработки запросов чтения\записи:
а)  Запрос чтения (ReadFile):
Сам запрос от приложения-инициатора выглядит следующим образом:
Код
...
ReadFile(hDevice,buf,STRSIZE,actualread,nil);
...
Где buf: array[1..STRSIZE]of byte; - буфер для принятых данных, STRSIZE - его максимальный размер, actualread - переменная для получения количества считанных данных.
Теперь рассмотрим обработку этого запроса в нашем тестовом драйвере:
Код
function ReadDispatch(
         const DeviceObject : PDEVICE_OBJECT;
         const Irp : PIRP) : NTSTATUS; stdcall;
const ReadBuffer='Buffer For ReadFile routine';
var
 ulSize:ULong;
 pBuffer:Pointer;
 pIrp : PIO_STACK_LOCATION;
 ans:ANSI_STRING;
begin
...
 pIrp := Irp^.Tail.Overlay.IRPStruct2.IRPUnion2.CurrentStackLocation;
 ulSize := pIrp^.Parameters.Read.Length;
 if ulSize>=sizeof(ReadBuffer) then ulSize:=sizeof(ReadBuffer)-1;
 ans:=BCAnsiStringImply(ReadBuffer);
 pBuffer := Irp^.UserBuffer;
 if pBuffer<>nil then RtlMoveMemory(pBuffer,ans.Buffer,ulSize);

 IRP.IoStatus.Status:=STATUS_SUCCESS;
 IRP.IoStatus.Information:=ulSize;
 IoCompleteRequest(IRP,IO_NO_INCREMENT);
 Result:=STATUS_SUCCESS;
end;
Здесь стоит отметить следующие моменты: максимальная длина буфера, переданная нам в запросе (STR_SIZE) считывается из pIrp^.Parameters.Read.Length, Irp^.UserBuffer  указывает напрямую на область памяти виртуального адресного пространства процесса-инициатора запроса (нашего приложения), в которую мы копируем данные, а количество действительно обработанных данных указывается в IRP.IoStatus.Information. Ну а при получении STATUS_SUCCESS (const STATUS_SUCCESS = $00000000) менеджер запросов автоматически завершит за нас остальную работу (напоминаю, что при использовании буферизированного или прямого способа ввода-вывода будет изменяться расположение указателя на буфер данных, к примеру при использовании буферизированного метода - он будет располагаться не в Irp^.UserBuffer, а в Irp^.AssociatedIrp.SystemBuffer, который указывает уже не на область памяти процесса-инициатора, а на промежуточный системный буфер, организованный менежером запросов внутри ядра системы и который по завершению запроса будет скопирован в буфер вызывающего процесса).
б)  Запрос записи (WriteFile):
Запрос от приложения-инициатора:
Код
...
WriteFile(hDevice,buf,Len,NumWritten,nil);
...
Где buf: array[1..STRSIZE]of byte; - буфер для принятых данных, Len - его размер, NumWritten - переменная для получения количества реально записанных данных.
В драйвере:
Код
function WriteDispatch(
         const DeviceObject : PDEVICE_OBJECT;
         const Irp : PIRP) : NTSTATUS; stdcall;
var
 ulSize:ULong;
 pIrp : PIO_STACK_LOCATION;
 pBuffer:Pointer;
 ans:ANSI_STRING;
begin
...
 pIrp := Irp^.Tail.Overlay.IRPStruct2.IRPUnion2.CurrentStackLocation;
 ulSize := pIrp^.Parameters.Write.Length;
 pBuffer := Irp^.UserBuffer;
 if pBuffer<>nil then 
  begin 
   ans:=BCAnsiStringAllocate(ulSize);
   RtlMoveMemory(ans.Buffer,pBuffer,ulSize);
   ...
   BCAnsiStringFree(ans);
  end;
 IRP.IoStatus.Status:=STATUS_SUCCESS;
 IRP.IoStatus.Information:=ulSize;
 IoCompleteRequest(IRP,IO_NO_INCREMENT);
 Result:=STATUS_SUCCESS;
end;
Здесь все аналогично предыдущему случаю, за исключением направления записи данных, думаю, особого пояснения ничего не требует. Функции BCAnsiStringImply и BCAnsiStringAllocate - это просто вспомогательные функции-обертки над аналогичными функциями ядра, которые можно найти в исходниках драйвера.

Это сообщение отредактировал(а) ama_kid - 8.9.2008, 22:24

Присоединённый файл ( Кол-во скачиваний: 452 )
Присоединённый файл  _Release.part01.rar 781,25 Kb


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
ama_kid
Дата 22.8.2008, 12:13 (ссылка) |    (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



3.4 Продвинутое общение
Рассмотренный нами в предыдущей главе метод общения драйвера с внешним миром, конечно, прост и понятен, но что делать, если задача требует не просто считать\записать данные, а произвести несколько более сложные действия, например, передать управляющие команды, обеспечить диалог драйвер-приложение или выполнить что-либо ещё специфическое для данного драйвера? Можно, конечно, при желании все организовать через ReadFile\WriteFile, но есть более универсальное решение -  функция DeviceIoControl. Для примера, рассмотрим следующие функции:
Код
Procedure CapsLockOn;assembler;
asm
 mov ax,0edh
 out 60h,ax
 mov ax,4
 out 60h,ax
end;

function InPort(port:word):byte;assembler;stdcall;
asm
 mov dx,port
 in al,dx
 mov Result,al
end;
Эти функции еще со времен DOS позволяли зажечь индикатор CapsLock на клавиатуре и считать последние значения клавиатурного порта 60h соответственно. Но в современной реальности, если попробовать вызвать их напрямую из приложения Delphi - мы получим исключение "Privileged Instruction" благодаря тому, что в системе NT давным-давно обычным приложениям запрещен доступ к портам ввода-вывода. С помощью тестового драйвера я покажу, как можно обойти ограничение системы на такие функции (написанные, напоминаю, в приложении режима user-mode и располагающиеся в его виртуальном адресном пространстве). Усилий для этого требуется совсем минимум - надо всего лишь передать в драйвер адреса этих функций и выполнить их непосредственно в нём. Сделаем это с помощью управляющих кодов и упомянутой нами функции DeviceIoControl:
Код
const
 IOCTRL_CODE_MESSAGE_RUN_ROUTINE_1 = (1 shl 3) or METHOD_NEITHER;
 IOCTRL_CODE_MESSAGE_RUN_ROUTINE_2 = (2 shl 3) or METHOD_NEITHER;
...
procedure IoControlDriver_CapsOn;
var
 bytesret:cardinal;
 p:pointer;
 i:integer;
begin
 p:=@i;
 Integer(p^):=Integer(@CapsLockOn);
 DeviceIoControl(hDevice,IOCTRL_CODE_MESSAGE_RUN_ROUTINE_1,
                 p,4,
                 nil,0,bytesret,nil);
...
end;

procedure IoControlDriver_KbPortIn;
var
 bytesret:cardinal;
 p:pointer;
 i:integer;
 w:integer;
begin
 p:=@i;
 Integer(p^):=Integer(@InPort);
 w:=0;
 DeviceIoControl(hDevice,IOCTRL_CODE_MESSAGE_RUN_ROUTINE_2,
                 p,4,
                 @w,4,bytesret,nil);
 // После этого вызова bytesret содержит количество возвращенных байт, 
 // w - возвращенное значение:
...
end;
Здесь приложение передает в драйвер адреса требуемых функций для того, чтобы он их исполнил, и во втором случае - получает некоторый ответ. Особых пояснений, пожалуй, требует только формат объявлений констант IOCTRL_CODE_MESSAGE_RUN_ROUTINE_1 и IOCTRL_CODE_MESSAGE_RUN_ROUTINE_2. Дело в том, что, как я уже упоминал, для обработки функций ReadFile и WriteFile способ передачи буфера (BUFFERED_IO|DIRECT_IO|NEITHER) - устанавливается путем формирования поля Flags объекта DeviceObject. Для обработки же ввода-вывода с помощью функции DeviceIoControl способ передачи буфера указывается индивидуально для каждого вызова путем специального формирования управляющего двойного  слова, передаваемого вторым параметром. Его формат представлен на картинке
user posted image
Расшифровка полей управляющего слова следующая:
1.    Поле DeviceType определяет тип объекта-устройства, которому предназначен запрос. Это тот самый тип устройства, который передается функции IoCreateDevice()
при создании устройства.
2.    Поле Function идентифицирует конкретные действия, которые должно предпринять устройство при получении запроса. Значения поля Function должны быть уникальны внутри устройства.
3.    Поле Method указывает метод передачи буферов данных. Для задания этого поля можно использовать константы:
      METHOD_BUFFERED         = 0;
      METHOD_IN_DIRECT         = 1;
      METHOD_OUT_DIRECT     = 2;
      METHOD_NEITHER            = 3;
4.    Поле Access указывает тип доступа, который должен был быть запрошен (и предоставлен) при открытии объекта-файла, для которого передается данный код Управления вводом/выводом. Возможные значения: FILE_ANY_ACCESS = $0000, FILE_READ_ACCESS = $0001 и  FILE_WRITE_ACCESS = $0002.
Для нас самое важное значение имеют биты 0-2 (в которых указывается метод передачи буфера) и 3-12 (в которых указывается передаваемый код управления драйвером). Остальные байты можно формировать по желанию и необходимости, в нашем случае такой необходимости нет. 
Теперь рассмотрим обработку этого запроса со стороны драйвера:
Для начала необходимо назначить функцию-обработчик данного запроса. Это выполняется, как обычно, в DriverEntry:
Код
...
DriverObject^.MajorFunction[IRP_MJ_DEVICE_CONTROL]:=DeviceIOControlDispatch;
...
Ну а сам обработчик выглядит следующим образом:
Код
const
 IOCTRL_RUN_ROUTINE_1 = (1 shl 3) or 3;
 IOCTRL_RUN_ROUTINE_2 = (2 shl 3) or 3;
type
 TUserRoutine1 = procedure;
 TUserRoutine2 = function (port:word):byte;stdcall;
...
function DeviceIOControlDispatch(
         const DeviceObject : PDEVICE_OBJECT;
         const Irp : PIRP) : NTSTATUS; stdcall;
var
 pIrp : PIO_STACK_LOCATION;
 iBuffer:pointer;
 inputBufLength:ULONG;
 outputBufLength:ULONG;
 FunctionCode:ULONG; 
 RetStr:ANSI_STRING;
 RetCode:integer;
begin
 pIrp := Irp^.Tail.Overlay.IRPStruct2.IRPUnion2.CurrentStackLocation;
 inputBufLength  := pIrp^.Parameters.DeviceIoControl.InputBufferLength;
 outputBufLength := pIrp^.Parameters.DeviceIoControl.OutputBufferLength;

 iBuffer:=pIrp^.Parameters.DeviceIoControl.Type3InputBuffer;

 FunctionCode := pIrp^.Parameters.DeviceIoControl.IoControlCode;
     
 case FunctionCode of 
   IOCTRL_RUN_ROUTINE_1:  
            begin
             ...
             TUserRoutine1(iBuffer^);
            end;
   IOCTRL_RUN_ROUTINE_2: 
            begin
             ...
             RetCode:=TUserRoutine2(iBuffer^)($60);
             RtlMoveMemory(Irp^.UserBuffer,@RetCode,4);
             ...
            end;
 end;
 IRP.IoStatus.Status:=STATUS_SUCCESS;
 IRP.IoStatus.Information:=4;
 IoCompleteRequest(IRP,IO_NO_INCREMENT);
 Result:=STATUS_SUCCESS;
end;
Как видно, здесь в зависимости от передаваемого управляющего кода - выполняется приведение переданного указателя к соответствующему типу-функции и последующее его исполнение. В итоге запрещенный код уровня пользователя благополучно выполнится. Но на такие функции, естесственно, накладывается ограничение - нельзя использовать функции, которых нет в режиме ядра, да и вообще следует быть осторожным. Я не стал здесь делать проверку типа передачи буфера (хотя, конечно, надо, потому что если драйвер начнет читать или писать в буфер, предполагая один тип передачи данных, а на самом деле окажется другой - то естесственным продолжением окажется BSOD). При желании - это можно сделать самостоятельно. Следует отметить, что расположение указателей буферов ввода-вывода для DeviceIoControl отличается от расположения буферов для обработки ReadFile\WriteFile - на этот раз они находятся в структуре Parameters.DeviceIoControl текущего IRP-пакета, но самого принципа это не меняет - знать расположение буфера в зависимости от типа передачи нужно обязательно.

4. Из жизни
Предыдущая глава была посвящена решению проблем, выдуманных нами фактически на пустом месте. Это не сильно интересно, поэтому в данной главе я хотел бы разобрать пару примеров, основа которых была мной написана для реальных проектов. Надеюсь, они помогут в более быстром освоении темы. Оба примера будут рассмотрены, естественно, на языке Delphi, но если кому интересно - то в соответствующих папках в архиве можно найти и C++ - варианты этих драйверов, которые собираются из-под DDKXP.

4.1 Пример №1: Перехват клавиатуры
Рассмотрим частный пример распространенной проблемы глобального перехвата клавиатуры - отлов сочетания CAD. В операционных системах семейства NT это сочетание является единственным, которое не отлавливается из user-mode (исключение составляет способ отлова через MSGINA.dll, но он обладает своими недостатками), поэтому я и взял его для примера, но в принципе данный подход применим для отлова любого требуемого сочетания клавиш. Основной идеей является установка драйвера-фильтра клавиатуры верхнего уровня. Что такое драйвер-фильтр и чем верхний уровень отличается от нижнего я объяснять не буду, об этом можно почитать в соответствующих источниках, но суть сводится к тому, что мы "посадим" наш драйвер поверх устройства основного драйвера клавиатуры, оставляя всю рутинную работу по работе с портами ввода-вывода и первичной обработке результатов ему, а сами будем только фильтровать запросы, проходящие между режимом пользователя и этим драйвером. Данный пример был написан мною по мотивам и на основе широко известного примера Ctrl2Cap, но с некоторыми изменениями, на которых я остановлюсь подробнее. Исходный код драйвера находится в прилагающемся архиве в папке под названием KbdIntcpt.
Итак, рассмотрим функцию DriverEntry: первым делом мы создаем объект "устройство", как было показано ранее, с помощью функций IoCreateDevice и IoCreateSymbolicLink. 
Код
...
DriverObject^.DriverUnload := DriverUnload;
 
RtlInitUnicodeString(deviceNameUnicodeString, NT_DEVICE_NAME);
Status := IoCreateDevice(DriverObject,sizeof (DEVICE_EXTENSION), 
    @deviceNameUnicodeString,FILE_DEVICE_UNKNOWN,
                     0,false,deviceObject);
RtlInitUnicodeString(deviceLinkUnicodeString, DOS_DEVICE_NAME);
Status:=IoCreateSymbolicLink(@deviceLinkUnicodeString,@deviceNameUnicodeString);
...
Создание символьной связи, в данном случае достаточно бесполезно, потому что я не осуществляю взаимодействия с этим драйвером, но если кому-нибудь понадобится - в предыдущей главе я описал как это можно сделать. Далее идет важный код - осуществляется присоединения фильтра в стек клавиатуры:
Код
extension := PDEVICE_EXTENSION(deviceObject^.DeviceExtension);
 extension^.DriverObject := DriverObject;

 RtlInitUnicodeString(kbdname,'\Device\KeyboardClass0');
 Status := IoAttachDevice(deviceObject, @kbdname,extension^.DeviceObject);
 if (extension^.DeviceObject<>nil)and(NT_SUCCESS(Status)) then
   begin
    deviceObject^.Flags := deviceObject^.Flags or DO_BUFFERED_IO;
    deviceObject^.Flags := deviceObject^.Flags and (not DO_DEVICE_INITIALIZING);
   end;
Поясню немного: сначала мы получаем указатель на область "расширений" нашего устройства, а затем с помощью функции IoAttachDevice помещаем наше устройство в стек устройств, которые по цепочке будут получать IRP-пакеты от драйвера клавиатуры. '\Device\KeyboardClass0' - это соответственно имя основного устройства, которое и является целью для нашего драйвера. Обратите внимание, что тип передачи данных указывается DO_BUFFERED_IO.
Затем идёт кусок кода по созданию объекта-евента:
Код
const
 EVENT_NAME        = '\BaseNamedObjects\'+DRIVER_NAME+'Event'; 
var
 UnloadEvent        : PKEVENT  = nil;    
 UnloadEventHandle    : THandle   = 0;    
 EventName        : UNICODE_STRING;    
...
RtlInitUnicodeString(EventName, EVENT_NAME);
UnloadEvent := IoCreateNotificationEvent(@EventName, @UnloadEventHandle);
Назначение этого кода и объекта я объясню чуть ниже, сам процесс создания и работы с евентами в режиме ядра мало чем отличается от аналогичных действий в режиме user-mode. Ну и в конце необходимо назначить обработчики-диспетчеры для запросов:
Код
for i:=0 to IRP_MJ_MAXIMUM_FUNCTION do 
   DriverObject^.MajorFunction[i]         := DriverDispatchGeneral;
 DriverObject^.MajorFunction[IRP_MJ_READ]   := DriverRead;
 DriverObject^.MajorFunction[IRP_MJ_CREATE] := DriverCreate;
 DriverObject^.MajorFunction[IRP_MJ_CLOSE]  := DriverClose;
В конце работы драйвера необходимо выполнить обратные действия:
Код
procedure DriverUnload(const DriverObject : PDRIVER_OBJECT); stdcall;
var
 deviceLinkUnicodeString    : UNICODE_STRING;
 extension            : PDEVICE_EXTENSION;
begin
 UnloadProcess:=true;
 extension := DriverObject.DeviceObject^.DeviceExtension;
 ...
 KeWaitForSingleObject(UnloadEvent,Executive,KernelMode,false,nil);
 ZwClose(UnloadEventHandle);
 ...
 RtlInitUnicodeString(deviceLinkUnicodeString, DOS_DEVICE_NAME);
 IoDeleteSymbolicLink(@deviceLinkUnicodeString);
 IoDetachDevice(extension^.DeviceObject);
 IoDeleteDevice(DriverObject.DeviceObject);
 ...
end;
Основным, притягивающим наше внимание, обработчиком будет  DriverRead, потому что к нему будут поступать все IRP-пакеты от драйвера клавиатуры. Остальные обработчики - просто заглушки, их описание я опускаю. DriverRead будет вызываться при любом событии, возникающем с клавиатурой, в частности, при нажатии и отпускании клавиши. Разберем его исходный код:
Код
Var
    UnloadProcess        : boolean = false;
...

function ReadCompleteCallback(const DeviceObject : PDEVICE_OBJECT;
                              const Irp : PIRP;
                              const Context : PVoid) : NTSTATUS; stdcall;
var
...
begin
 ...
 // Помечаем IRP как ожидающий завершения, если требуется
 if (Irp^.PendingReturned) then IoMarkIrpPending(Irp);
 KeSetEvent(UnloadEvent, 0, false);
 Result:=Irp^.IoStatus.Status;
end; 

function DriverRead(const DeviceObject : PDEVICE_OBJECT;
              const Irp : PIRP) : NTSTATUS; stdcall;
var
 current_irp_stack      : PIO_STACK_LOCATION;
 next_irp_stack        : PIO_STACK_LOCATION;
begin
 ...
 current_irp_stack := IoGetCurrentIrpStackLocation(Irp);
 next_irp_stack := IoGetNextIrpStackLocation(Irp);    
 next_irp_stack^ := current_irp_stack^;
 KeClearEvent(UnloadEvent);
 if not UnloadProcess then IoSetCompletionRoutine(Irp, ReadCompleteCallback, DeviceObject, true, true, true);
 Result:=IoCallDriver(PDEVICE_EXTENSION(DeviceObject^.DeviceExtension)^.DeviceObject, 
 Irp);
end;
Самым важным моментом здесь является DDK-шный макрос (я перевел его в функцию) IoSetCompletionRoutine. Спускаясь по цепочке фильтров к драйверу, IRP попадает в наш обработчик, где ему устанавливается т.н. "функция завершения запроса" ReadCompleteCallback. Таким образом, когда драйвер клавиатуры обработает запрос - он вызовет нашу функцию и мы сможем обработать результат по нашему усмотрению. Здесь возникает несколько тонких моментов, требующих разъяснения:
а) Дело в том, что в обработке драйвера клавиатуры всегда находится как минимум один IRP-пакет, поэтому когда мы присоединяемся в цепочку фильтров - то неизбежно пропустим тот пакет, который уже сидит в обработке, потому что на него еще не была установлена наша функция возврата. Фактически это означает что первое после загрузки драйвера нажатие клавиши не будет отловлено.
б) Опять же, благодаря тому, что в обработке всегда наличествует как минимум один пакет, в момент выгрузки драйвера в этом пакете сидит привязанная к нему наша callback-функция ReadCompleteCallback (если, конечно, между загрузкой и выгрузкой драйвера происходили нажатия клавиш, что должно было привести к вызову IoSetCompletionRoutine) и если мы выгрузимся до того, как драйвер клавиатуры вызовет эту функцию, мы неизбежно получим BSOD. Для того, чтобы этого избежать - я и использовал евент, сигнализирующий о получении очередного пакета и не дающий выгрузиться драйверу, если он не сработал. Этой же цели служит булевская переменная UnloadProcess, которая не дает установить процедуру завершения, если пошел процесс выгрузки драйвера. Но все это приводит к небольшому неудобству - при выгрузке драйвера необходимо нажать клавишу. Упомянутый мной пример Ctrl2Cap этот  момент обходит другим способом - он для своей установки\выгрузки требует перезагрузки компьютера, кому интересно - может погуглить и посмотреть, как это реализовано.
в) Ну и еще один достаточно тонкий момент, который не относится именно к данному драйверу, но который я впервые испытал на своей шкуре именно в этом месте, поэтому упомяну здесь. Дело в том, что callback-функция ReadCompleteCallback вызывается на уровне DISPATCH_LEVEL, в то время как остальной код драйвера по умолчанию работает на самом низкоприоритетном уровне PASSIVE_LEVEL. Это различие аукнулось тем, что попытка записи в лог-файл внутри этой функции приводила к BSOD, потому что работа с файлами производится тоже на уровне PASSIVE_LEVEL. Поэтому при разработке механизмов взаимодействия рекомендую не забывать о таких вещах, как приоритет выполнения.
Ну а что же собственно выполняет функция ReadCompleteCallback? Её код выглядит следующим образом:
Код
Const
// Коды отлавливаемых клавиш
 VK_CONTROL        = $1D;
 VK_ALT            = $38;
 VK_DELETE        = $53;
 // Константа для работы с KEYBOARD_INPUT_DATA
 KEY_BREAK        = 1;
var
 CtrlAlt        : array [0..1] of boolean = (false,false);
...

function IsKeyDown(pKeyData:Pointer;const Key:byte):boolean;
begin
 Result:=(KEYBOARD_INPUT_DATA(pKeyData^).MakeCode = Key) and 
     ((KEYBOARD_INPUT_DATA(pKeyData^).Flags and KEY_BREAK) = 0);
end;                                                           

function IsKeyUp(pKeyData:Pointer;const Key:byte):boolean;
begin
 Result:=(KEYBOARD_INPUT_DATA(pKeyData^).MakeCode = Key) and 
     ((KEYBOARD_INPUT_DATA(pKeyData^).Flags and KEY_BREAK) > 0);
end;

function ReadCompleteCallback(const DeviceObject : PDEVICE_OBJECT;
                              const Irp : PIRP;
                              const Context : PVoid) : NTSTATUS; stdcall;
var
 KeyData        : PKEYBOARD_INPUT_DATA;
 num_keys, i        : integer; 
begin
 ...
 if NT_SUCCESS(Irp^.IoStatus.Status) then
  begin
   KeyData:=Irp^.AssociatedIrp.SystemBuffer;
   num_keys := integer((Irp^.IoStatus.Information div sizeof(KEYBOARD_INPUT_DATA)));
   for i:=0 to num_keys-1 do
    begin
     if IsKeyDown   (Pointer(Cardinal(KeyData)+i*SizeOf(KEYBOARD_INPUT_DATA)),VK_CONTROL) then CtrlAlt[0]:=true
     else if IsKeyUp(Pointer(Cardinal(KeyData)+i*SizeOf(KEYBOARD_INPUT_DATA)),VK_CONTROL) then CtrlAlt[0]:=false; 
     if IsKeyDown   (Pointer(Cardinal(KeyData)+i*SizeOf(KEYBOARD_INPUT_DATA)),VK_ALT)     then CtrlAlt[1]:=true
     else if IsKeyUp(Pointer(Cardinal(KeyData)+i*SizeOf(KEYBOARD_INPUT_DATA)),VK_ALT)     then CtrlAlt[1]:=false; 

     if (IsKeyDown(Pointer(Cardinal(KeyData)+i*SizeOf(KEYBOARD_INPUT_DATA)),VK_DELETE) or
         IsKeyUp  (Pointer(Cardinal(KeyData)+i*SizeOf(KEYBOARD_INPUT_DATA)),VK_DELETE)) and (CtrlAlt[0] and CtrlAlt[1]) then
    begin
     RtlZeroMemory(Pointer(Cardinal(KeyData)+i*SizeOf(KEYBOARD_INPUT_DATA)),sizeof(KEYBOARD_INPUT_DATA));
{$IFDEF __DEBUG__}
         DbgPrint(DRIVER_NAME+' : ' + 'Ctrl+Alt+Delete Key Sequence Is Blocked!!!');
{$ENDIF}
    end;    
    end;
  end;

 // Помечаем IRP как ожидающий завершения, если требуется
 if (Irp^.PendingReturned) then IoMarkIrpPending(Irp);
 KeSetEvent(UnloadEvent, 0, false);
 Result:=Irp^.IoStatus.Status;
end;
Передаваемый с IRP буфер (ввиду того, что мы указали буферизированный метод передачи - то буфер содержится в Irp^.AssociatedIrp.SystemBuffer) содержит массив из элементов KEYBOARD_INPUT_DATA, каждый из которых содержит информацию об очередной нажатой клавише. Больше одного элемента в этом массиве, я конечно не видел, но обрабатывать как массив все равно надо, поэтому и организован цикл по количеству элементов. В этом цикле производится проверка нажатия сооветствующих клавиш. Нажатие и отжатие клавиш CTRL и ALT - приводит соответственно к установке\сбросу соответствующих внутренних флагов, а нажатие DELETE с установленными флагами CTRL и ALT - к блокировке нажатия путем очистки буфера, о чем сигнализирует вывод в отладочную консоль. В конце функции - сбрасываем евент для возможности выгрузки драйвера. 

4.2 Пример №2: Перехват системных функций ядра
Ну и еще один пример, который я хотел бы рассмотреть - это пример осуществления перехвата функций ядра на примере функции ZwQueryDirectoryFile для обеспечения "невидимости" требуемого файла\папки. Делается это путем модификации системной таблицы служебных дескрипторов (SSDT). Данный метод зачастую называют "грязным", но иногда приходится прибегать и к таким способам, благо из режима ядра это разрешено. Этот пример тоже был написан под большим влиянием найденного в сети драйвера, осуществляющего какую-то работу с таблицей дескрипторов, но, к сожалению, я уже не помню что это был за драйвер, поэтому авторство идеи оставляю за "неизвестным героем". Исходный код находится в прилагающемся архиве в папке под названием FileHider и полный код я приводить здесь не буду, просто поясню основные моменты.
Итак, хук устанавливается в функции DriverEntry с помощью экспортируемой недокументированной глобальной переменной KeServiceDescriptorTable (со скачиваемыми примерами рекомендую проявлять осторожность - антивирус ругается на скомпилированные файлы, принимая rootkit-технологии за зверьё). Тонким моментом здесь является использование бита WP системного регистра cr0 при установке\снятии хука. Этот бит управляет защитой от записи системных страниц памяти. Если у вас на машине стоит физической памяти больше 256 Мб (WinXP) или 128 Мб (Win2k), тогда данный код никак не влияет на функциональность (т.е. если его убрать - никаких отличий не найти). Но! Если на машине стоит памяти меньше чем указано - тогда система осуществляет защиту системных страниц памяти от записи и при попытке что-то записать в область системного кода будет генерироваться исключение и это вызовет BSOD (подробнее об этом можно прочитать в журнале Хакер, глава "Типы атак на ядро"). Именно для того, чтобы это учесть, и сделан сброс бита WP перед записью в область системного кода - тогда системные страницы с любом случае будут доступны для модификации. 
После установки хука - остальное дело техники. Все запросы, благодаря наличию в SSDT адреса нашей функции, перенаправляются ей, затем она тут же вызывает оригинальную системную процедуру по сохраненному указателю, а возвращаемый результат анализирует на предмет соответствия требуемым условиям. В примере я сделал проверку по соответствии имени файла\папки определенному шаблону, а также закомментировал проверку по атрибутам файла. Анализ осуществляется путем перебора всего списка элементов типа FILE_BOTH_DIRECTORY_INFORMATION, который возвращается оригнальной функцией ZwQueryDirectoryFile. В случае выполнения условий проверки  - проверяемый элемент удаляется из списка путем перенаправления указателя предыдущего элемента списка на следующий. При завершении работы драйвера - в функции DriverUnload адрес оригинальной процедуры возвращается на место : 
Конечно, для того, чтобы полностью "спрятать" требуемый файл - просто перехвата ZwQueryDirectoryFile недостаточно, даже если спрятать папку таким образом - в неё все равно можно будет зайти путем ввода команды "cd". Но это уже вопрос наращивания функциональности.

5. Заключение
Данная статья не охватывает многие и многие аспекты темы разработки драйверов. Например, не были рассмотрены вопросы обмена с реальными устройствами, не приведено примеров работы с файловой системой на низком уровне, даже не рассмотрен вопрос построения inf-файлов, необходимых для полноценной установки устройства в систему, или других узкоспециализированных моментов. Но и не в этом её цель. Целью является дать дополнительный импульс для тех, кто хочет осилить эту необъятную тему, с тем, чтобы дальше можно было двигаться самостоятельно, дать начальные познания при помощи привычной "среды", а также дать своеобразные "направляющие" для правильного составления конкретных вопросов на соответствующих форумах. Если не получилось  - прошу строго не судить

P.S. Выражаю огромную благодарность Snowy за помощь в подготовке и оформлении статьи.

Это сообщение отредактировал(а) ama_kid - 8.9.2008, 22:11

Присоединённый файл ( Кол-во скачиваний: 482 )
Присоединённый файл  _Release.part02.rar 436,36 Kb


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
THandle
Дата 22.8.2008, 12:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Хранитель Клуба
Group Icon
Награды: 1



Профиль
Группа: Админ
Сообщений: 3639
Регистрация: 31.7.2007
Где: Moscow, Dubai

Репутация: 7
Всего: 372



ama_kid,  smile 


Цитата(ama_kid @  22.8.2008,  13:18 Найти цитируемый пост)
фу, блин... склейка сообщений все мозги вынесла...



Ага. Бываетsmile

+ тебе за статью. Когда нибудь точно пригодится smile 
PM   Вверх
MetalFan
  Дата 22.8.2008, 12:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 16
Всего: 128



ama_kid, сам написал?! ну ты smile! очень познавательно! и с меня +


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
ama_kid
Дата 22.8.2008, 12:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



THandleMetalFan, спасибо smile

Это сообщение отредактировал(а) ama_kid - 5.12.2008, 23:26


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Riply
Дата 22.8.2008, 21:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



ama_kid
Спасибо.
Когда при решении задач мне стало довольно сильно мешать
мое неумение писать драйвера, я решила обратиться к С,
считая что в родной и любимой Delphi это будет "не естественно"  smile 
Еще раз спасибо, за избавление от столь пагубного заблуждения  smile 
PM MAIL   Вверх
ne0n
  Дата 22.8.2008, 21:56 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


PlayBoy
**


Профиль
Группа: Участник
Сообщений: 733
Регистрация: 5.8.2005
Где: Н.Новгород

Репутация: 4
Всего: 11



Riply,  ну на самом деле - это не такое пагубное заблуждение...переводить ddk на паскаль, никто не будет...а самому эту задачу не осилить, хотя может и найдуться интузиасты smile 

ничего нового не узнал, но за стотью в любом случаи респек и + 


PM MAIL ICQ   Вверх
Riply
Дата 8.9.2008, 19:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



ama_kid
Наконец-то выдалось свободное время и можно заняться всякими вкусностями smile
Начала смотреть код (Delphi версия) и у меня сразу появилась куча вопросов:
1. STRSIZE        = 115;
   Что это за число (115) и откуда оно взялось ?
2. NTSTATUS спецально объявлен как ULong ?
   Если да, то почему ?
3. Вытекает из второго
   Реализация ф-ии NT_Success ( Result := (NTS=STATUS_SUCCESS) )
   обусловлена именно таким объявлением NTSTATUS ?
4. Почему, после вызовов BCAnsiStringAllocate не всегда
   вызывается BCAnsiStringFree (например в WriteDispatch) ?
5. Rtl-кие функции для работы со строками валидны в нашем случае ?
   (Имеется ввиду совместимость способов выделения и освобождения памяти 
    Rtl-кими функциями и BC-функциями)  ?

P.S.
 При ответах, просьба учесть нулевой уровень вопрошающей в данной области  smile  

PM MAIL   Вверх
ama_kid
Дата 8.9.2008, 22:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



Цитата(Riply @  8.9.2008,  20:06 Найти цитируемый пост)
1. STRSIZE        = 115;
   Что это за число (115) и откуда оно взялось ?
115 - это "магическое число" и взялось оно мной практически с потолка, дабы ограничить размер статического массива  smile 
Цитата(Riply @  8.9.2008,  20:06 Найти цитируемый пост)
2. NTSTATUS спецально объявлен как ULong ?
   Если да, то почему ?
3. Вытекает из второго
   Реализация ф-ии NT_Success ( Result := (NTS=STATUS_SUCCESS) )
   обусловлена именно таким объявлением NTSTATUS ?
Я не совсем понял вопрос. А как он должен быть объявлен? В принципе, в DDK он объявлен знаковым (long), но в изначальной статье (на которую я ссылаюсь вначале) он был объявлен как cardinal (и в принципе, я нигде не видел использования знаковых особенностей этого дефайна), поэтому перенес все один в один.  smile 

Цитата(Riply @  8.9.2008,  20:06 Найти цитируемый пост)
4. Почему, после вызовов BCAnsiStringAllocate не всегда
   вызывается BCAnsiStringFree (например в WriteDispatch) ?
Мда... Это, надо сказать, ты хорошо заметила, молодец smile Конечно, обязано вызываться, моя ошибка... Поправил, попутно найдя ещё аналогичную ошибку в DeviceIOControlDispatch smile

Цитата(Riply @  8.9.2008,  20:06 Найти цитируемый пост)
5. Rtl-кие функции для работы со строками валидны в нашем случае ?
   (Имеется ввиду совместимость способов выделения и освобождения памяти 
    Rtl-кими функциями и BC-функциями)  ?
Не понял, что ты имеешь ввиду под выделением\освобождением памяти Rtl-кими функциями? Память выделяется\освобождается функциями MmAllocateNonCachedMemory\MmFreeNonCachedMemory, а Rtl-кие функции как раз и предназначены для работы со строками (ну и для многого другого). BC-функции, как я говорил, это просто обертки над ними (посмотри их код в модуле BCore.inc - там они как раз и вызывают Rtl-функции)


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Riply
Дата 8.9.2008, 23:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(ama_kid @  8.9.2008,  22:14 Найти цитируемый пост)
115 - это "магическое число" и взялось оно мной практически с потолка, дабы ограничить размер статического массива  


Я только начала разбираться, но мне кажется, что можно работать с блоками
размером до MAXWORD shr 1. Я пыталась исходить из размерности полей.



Цитата(ama_kid @  8.9.2008,  22:14 Найти цитируемый пост)
Я не совсем понял вопрос. А как он должен быть объявлен? В принципе, в DDK он объявлен знаковым (long), но в изначальной статье (на которую я ссылаюсь вначале) он был объявлен как cardinal (и в принципе, я нигде не видел использования знаковых особенностей этого дефайна), поэтому перенес все один в один.


К сожалению не знаю как обстоят дела в мире драйверов, но в обычном приложении,
работающем с Nt-функциями знаковость NTSTATUS очень важна. 
А NT_SUCCESS должна иметь такой вид:
Код

function NT_SUCCESS(const Status: NTSTATUS): Boolean; inline;
begin
 Result := Status >= 0;
end;

Дело в том, что некоторые (если не многие) функции могут в случае успеха возвращать не STATUS_SUCCESS
а "некий код", содержащий STATUS_SEVERITY_INFORMATIONAL.
В этом случае фунция выполнена успешно, а в ее результате дается дополнительная информация.
Как правило это используется в Query функциях (например STATUS_MORE_ENTRIES говорит, что все удачно записано,
но меня можно вызвать еще раз для дозаписи).
Но встречается и в другин. Например я сталкивалась с STATUS_PREDEFINED_HANDLE, STATUS_EVENT_PENDING
или при работе с секциями (мутантами) STATUS_OBJECT_NAME_EXISTS а вот здесь это становится довольно важно.

Цитата(ama_kid @  8.9.2008,  22:14 Найти цитируемый пост)
Не понял, что ты имеешь ввиду под выделением\освобождением памяти Rtl-кими функциями?

Ой. Прошу маленький тай-аут. Еще чуть поразбираюсь и сформулирую вопрос более точно.
Но пока мне не понятно, чем обусловлен выбор MmAllocateNonCachedMemory вместо Rtl - Heap - функций ?
(Они кстати "совместимы" с такими как RtlFreeUnicodeString)


PM MAIL   Вверх
ama_kid
Дата 9.9.2008, 09:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



Цитата(Riply @  9.9.2008,  00:48 Найти цитируемый пост)
Я только начала разбираться, но мне кажется, что можно работать с блоками размером до MAXWORD shr 1. Я пыталась исходить из размерности полей.
Это - ради бога, как удобно так и работай, я просто не хотел гонять большой блок данных smile

Цитата(Riply @  9.9.2008,  00:48 Найти цитируемый пост)
знаковость NTSTATUS очень важна
Вот как это дело расписано в DDK:
Цитата
//
//  Status values are 32 bit values layed out as follows:
//
//   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
//   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
//  +---+-+-------------------------+-------------------------------+
//  |Sev|C|       Facility          |               Code            |
//  +---+-+-------------------------+-------------------------------+
//
//  where
//
//      Sev - is the severity code
//
//          00 - Success
//          01 - Informational
//          10 - Warning
//          11 - Error
//
//      C - is the Customer code flag
//
//      Facility - is the facility code
//
//      Code - is the facility's status code
//
Знаковый бит обозначает либо Warning, либо Error. Т.е. тут важна не знаковость сама по себе, а наличие последнего бита в "severity code" (фактически это одно и то же, но под другим соусом smile ).
Цитата(Riply @  9.9.2008,  00:48 Найти цитируемый пост)
NT_SUCCESS должна иметь такой вид:
Я наконец понял, что ты имеешь ввиду. smile Формально ты, конечно, права и я с тобой тут спорить не буду. Да и DDK говорит о том же:
Код
//
// Generic test for success on any status value (non-negative numbers
// indicate success).
//
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
//
// Generic test for information on any status value.
//
#define NT_INFORMATION(Status) ((ULONG)(Status) >> 30 == 1)
//
// Generic test for warning on any status value.
//
#define NT_WARNING(Status) ((ULONG)(Status) >> 30 == 2)
//
// Generic test for error on any status value.
//
#define NT_ERROR(Status) ((ULONG)(Status) >> 30 == 3)
проверку именно на неравенство
Код
const STATUS_SUCCESS                    = NTStatus( $00000000 );
я использую просто для удобства, чтобы  получить гарантированный STATUS_SUCCESS.  Если хочешь - можешь переделать под свой вкус в соответствие с канонами DDK smile
Цитата(Riply @  9.9.2008,  00:48 Найти цитируемый пост)
Но пока мне не понятно, чем обусловлен выбор MmAllocateNonCachedMemory вместо Rtl - Heap - функций ?
Да практически ничем за исключением того с ней меньше геморроя (чтобы использовать RtlAllocateHeap, надо предварительно вызвать RtlCreateHeap с кучей параметров), ну и благодаря тому, что блок памяти выделяется в некэшируемой области - его можно использовать на любом уровне приоритета выполнения (хотя в данном примере это и не сильно надо, все равно весь код исполняется на PASSIVE_LEVEL).

Цитата(Riply @  9.9.2008,  00:48 Найти цитируемый пост)
Они кстати "совместимы" с такими как RtlFreeUnicodeString
Цитата(MSDN)
The RtlFreeUnicodeString routine releases storage that was allocated by RtlAnsiStringToUnicodeString or RtlUpcaseUnicodeString.



--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Riply
Дата 9.9.2008, 17:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(ama_kid @  9.9.2008,  09:26 Найти цитируемый пост)
чтобы  получить гарантированный STATUS_SUCCESS


IMHO, довольно опасный способ проверки. Например при создании секции такая проверка может показать неудачу,
в то время как все успешно и мы получили на руки открытый Handle. Который еще и закрыть надо smile


Цитата(ama_kid @  9.9.2008,  09:26 Найти цитируемый пост)
Да практически ничем за исключением того с ней меньше геморроя 


Я почему спрашиваю. Мы ведь в наших Dispatch функциях предполагаем многократное выделение/освобождение.
Соответственно меня и интересует какой из способов работы с памятью лучше всего подходит
для этого случая. С MmAllocateNonCachedMemory я еще не окончательно разобралась, но
меня сильно смущает следующая фраза из MSDN:
 "A device driver that must use noncached memory should allocate only what it needs during 
driver initialization because nonpaged pool is likely to become fragmented as the system runs."


Насчет же функций типа RtlAnsiStringToUnicodeString (а они предназначены для частого использования) , 
мое копательство в них показало, что они работают именно через Rtl-Heap. 
Поэтому я и говорила о "совместимости". Если бы BC функции тоже работали через Heap,
то их можно было бы использовать "вперемежку" с Rtl-скими (проверяла в юзер-моде).
P.S.
Я ни в коем случае не спорю, а просто пытаюсь понять что к чему и с чем едят.
Соответственно многие мои утверждения приведены не как контр-аргументы, 
а для того, чтобы мне показали ошибку в моих рассуждениях и пояснили "как надо на самом деле" smile
Где еще найдешь бесплатное образование, кроме как не на таких форумах smile




Это сообщение отредактировал(а) Riply - 9.9.2008, 17:26
PM MAIL   Вверх
ama_kid
Дата 9.9.2008, 18:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



Цитата(Riply @  9.9.2008,  18:24 Найти цитируемый пост)
IMHO, довольно опасный способ проверки. Например при создании секции такая проверка может показать неудачу, в то время как все успешно и мы получили на руки открытый Handle. Который еще и закрыть надо
Ну вот сначала закрываем, а потом отлаживаем и разбираемся, что же мы получили smile
Цитата(Riply @  9.9.2008,  18:24 Найти цитируемый пост)
какой из способов работы с памятью лучше всего подходит
для этого случая
Конкретно для этого случая мне было удобнее использовать то, что я использовал smile
Но вообще, режим ядра предоставляет гораздо большие возможности для работы с памятью, чем user-mode и на эту тему есть интересные статьи на Wasm.ru (главы "Работа с памятью"). Можешь почитать, если интересно...



--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Riply
Дата 9.9.2008, 19:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(ama_kid @  9.9.2008,  18:54 Найти цитируемый пост)
на эту тему есть интересные статьи на Wasm.ru (главы "Работа с памятью"). 


Спасибо.


Цитата(ama_kid @  9.9.2008,  18:54 Найти цитируемый пост)
если интересно...


Издеваетесь ?   smile 
PM MAIL   Вверх
ama_kid
Дата 10.9.2008, 15:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



Цитата(Riply @  9.9.2008,  20:47 Найти цитируемый пост)
Издеваетесь ?   smile  
ни в коем разе smile



--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Riply
Дата 11.9.2008, 10:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



ama_kid, никак не могу понять:
Вот пишем мы лог из Dispatch функций.
А на чем основана наша уверенность в том, что это потоко-безопасно ?
PM MAIL   Вверх
ama_kid
Дата 11.9.2008, 14:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



Цитата(Riply @  11.9.2008,  11:19 Найти цитируемый пост)
на чем основана наша уверенность в том, что это потоко-безопасно ?
Хм... Для начала два встречных вопроса:
1) Я где-нибудь это утверждал?
2) Я где-нибудь использовал потоки?
Естественно, разработка многопоточных приложений может наложить свой отпечаток, в том числе и в режиме ядра, но все-таки это не является предметом рассмотрения в даной статье... Ну а ответом на поставленный вопрос, думаю, может служить тот факт, что посылка запросов IRP_MJ_READ\WRITE\DEVICE_CONTROL осуществляется синхронно, т.е. системный менеджер запросов не пошлет драйверу следующий IRP-пакет до тех пор, пока им не будет обработан предыдущий. Поэтому, допустим, вызов ReadFile из одного потока при обработке ReadFile из другого потока вызовет просто задержку на исполнение первого вызова. Отчасти поэтому и не рекомендуется задерживать обработку IRP-пакетов (а на высоких уровнях приоритета - вообще нельзя) - т.к. это приводит к performance reducing...


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Riply
Дата 11.9.2008, 14:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(ama_kid @  11.9.2008,  14:01 Найти цитируемый пост)
1) Я где-нибудь это утверждал?


Это смотря что считать утверждением  smile 
Я посчитала, что раз ты не побеспокоился о потоко-безопасности,
значит считаешь, что это излишне. Вот я и пыталась понять на чем это заключение основано  smile 

Цитата(ama_kid @  11.9.2008,  14:01 Найти цитируемый пост)
Ну а ответом на поставленный вопрос, думаю, может служить тот факт, что посылка запросов IRP_MJ_READ\WRITE\DEVICE_CONTROL осуществляется синхронно, т.е. системный менеджер запросов не пошлет драйверу следующий IRP-пакет до тех пор, пока им не будет обработан предыдущий.


Спасибо. Именно это меня и интересовало.
Т.е. сколько бы приложений одновременно не открыло наше устройство для связи с драйвером
и с какой интенсивностью они бы ни читали(писали) мы будем получать только по одному запросу
и пока его не обработаем, следующий нам никто не даст.
Я правильно поняла ?

PM MAIL   Вверх
ama_kid
Дата 11.9.2008, 15:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



Цитата(Riply @  11.9.2008,  15:20 Найти цитируемый пост)
ты не побеспокоился о потоко-безопасности
Да на таком уровне и беспокоиться не надо - BSOD быстренько бы обо всём напомнил  smile 
Цитата(Riply @  11.9.2008,  15:20 Найти цитируемый пост)
Я правильно поняла ?
вполне  smile





--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Riply
Дата 12.9.2008, 04:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(ama_kid @  9.9.2008,  18:54 Найти цитируемый пост)
Цитата(Riply @  9.9.2008,  18:24 )
какой из способов работы с памятью лучше всего подходит
для этого случая

Конкретно для этого случая мне было удобнее использовать то, что я использовал 


И очень похоже, что ты оказался прав в своем выборе smile

Вот что гласит MSDN:
To allocate I/O buffer space, the best memory allocation routines to use are MmAllocateNonCachedMemory, MmAllocateContiguousMemorySpecifyCache, AllocateCommonBuffer (if the driver's device uses bus-master DMA or a system DMA controller's auto-initialize mode), or ExAllocatePoolWithTag. 

Nonpaged pool typically becomes fragmented as the system runs, so a driver's DriverEntry routine should call these routines to set up any long-term I/O buffers the driver needs. Each of these routines, except ExAllocatePoolWithTag, allocates memory that is aligned on a processor-specific boundary (determined by the processor's data-cache-line size) to provide best performance. 




Это сообщение отредактировал(а) Riply - 12.9.2008, 04:29
PM MAIL   Вверх
Riply
Дата 16.9.2008, 18:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



ama_kid,  что-то мне не удается найти в ntoskrnl.lib ничего похожего
на KeGetCurrentIrql :(

Может она называется там совсем по-другому ?
Подскажи, пожалуйста, как ее импортировать ?




PM MAIL   Вверх
ama_kid
Дата 16.9.2008, 19:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



Цитата(Riply @  16.9.2008,  19:18 Найти цитируемый пост)
что-то мне не удается найти в ntoskrnl.lib ничего похожего
на KeGetCurrentIrql :(
Потому что она импортируется из wdm.lib. Смотри аттач (а по-хорошему, просто поставь себе DDK  smile )

Это сообщение отредактировал(а) ama_kid - 16.9.2008, 19:12

Присоединённый файл ( Кол-во скачиваний: 36 )
Присоединённый файл  wdm.lib 186,21 Kb


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Riply
Дата 16.9.2008, 19:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(ama_kid @  16.9.2008,  19:11 Найти цитируемый пост)
Потому что она импортируется из wdm.lib. Смотри аттач 


Спасибо. Скачала, буду смотреть.

Цитата(ama_kid @  16.9.2008,  19:11 Найти цитируемый пост)
а по-хорошему, просто поставь себе DDK  


С DDK мы еще не знакомы. Пока не разобралась с какой стороны к нему подходить smile

PM MAIL   Вверх
Riply
Дата 21.9.2008, 13:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



ama_kid

Хочу вернутся к вопросу потоко-безопасности.
Пару раз заметила, что при записи лога KbdIntcpt (правда чуть переделанного smile идет
искажение информации, причем на определенных сообщениях. Редко но бывает.
Тогда решила добавить в лог ThreadID (первый столбец во второй таблице) 
и посмотреть в каком порядке добавляются записи.

Выдержка из лога Dbgview:
00000034  11:43:50  DriverRead _ 0 0 0    
00000035  11:43:50  DriverRead IoCallDriver 103 0 0    
-->00000036  11:43:54  DriverUnload _ 0 0 0    
00000037  11:43:56  DRIVER_NAME : DriverReadCallBack    
00000038  11:43:56  DriverUnload KeWaitForSingleObject 0 0 0    
00000039  11:43:56  DriverUnload ZwClose 0 0 0    
00000040  11:43:56  DriverRead _ 0 0 0    
00000041  11:43:56  DriverUnload IoDeleteSymbolicLink 0 0 0    
00000042  11:43:56  DriverRead IoCallDriver 103 0 0    
00000043  11:43:56  DriverUnload IoDetachDevice 0 0 2262795872    
00000044  11:43:56  DriverUnload IoDeleteDevice 0 0 2240838328    
00000045  11:43:56  SnakeLog Finalizate 0 43 1024    

Соответствующие строчки из лога KbdIntcpt:
0000000748    21.09  07:43:50.500 DriverRead    IoCallDriver    Overlapped I/O operation is in progress ( $000003E5 )    0000000000    0000000000    0000000025    0000000046
-->0000000052    21.09  07:43:54.062 DriverUnload    _    The operation completed successfully    0000000000    0000000000    0000000026    0000000047
0000000052    21.09  07:43:56.593 DriverUnload    KeWaitForSingleObject    The operation completed successfully    0000000000    0000000000    0000000027    0000000048
0000000052    21.09  07:43:56.593 DriverUnload    ZwClose    The operation completed successfully    0000000000    0000000000    0000000028    0000000049
0000000748    21.09  07:43:56.593 DriverRead    IoCallDriver    Overlapped I/O operation is in progress ( $000003E5 )    0000000000    0000000000    0000000031    0000000051
0000000052    21.09  07:43:56.593 DriverUnload    IoDetachDevice    The operation completed successfully    0000000000    2262795872    0000000032    0000000052
0000000052    21.09  07:43:56.593 DriverUnload    IoDeleteDevice    The operation completed successfully    0000000000    2240838328    0000000033    0000000053
0000000052    21.09  07:43:56.593 SnakeLog    Finalizate    The operation completed successfully    0000000043    0000001024    0000000034    0000000054

"-->"    - помечен вход в процедуру DriverUnload
Видно, что паралельно с ней у нас отрабатывает и процедура DriverRead,
причем работают они "из разных потоков" (ID одного - 52, другого - 748).
Т.е. у нас получается следующая последовательность действий:
DriverUnload -->   KeWaitForSingleObject
DriverUnload -->   ZwClose
DriverRead --> входим в процедуру
DriverUnload -->   IoDeleteSymbolicLink
DriverRead --> IoCallDriver
DriverUnload -->   IoDetachDevice
DriverUnload -->   IoDeleteDevice

Вот такие пироги.

P.S.
 Очень надеюсь, что ты найдешь ошибку в рассуждениях и мне не придется
 разбираться еще и потоко-безопасностью,  вместо похода на дискотеку smile

P.P.S.
 У меня еще возникли вопросы по работе с памятью, но это после того как с этим разберемся smile

Добавлено через 5 минут и 1 секунду
Вот черт. У меня такие аккуратные таблички были, а тут все съехало.
Sorry. 
Надеюсь они стали не очень нечитабельны  smile 
PM MAIL   Вверх
Riply
Дата 23.9.2008, 07:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Если кому интересно...  smile

Попробовала чуть более детально разобраться с нашей потоко-безопасностью.
Вот что получилось:
Для синхронизации выбрала (после очень долгих и тяжких раздумий) объект ERESOURCE.
(Выбирала с учетом своих планов, так что кому-то может лучше подойдут объекты другого типа)
Эффект искажения данных в логе исчез сразу после заключения 
"логовых" функций в "критические обертки".
Для проверки, что одновременная запись в лог разными нитями мне не померещилась
в кошмарном сне smile, чуть изменила стандартный код вхождения в критический регион на нечто такое:

Код

function Test_Lock(const pLock: PVOID; const WaitResource: BOOLEAN): BOOLEAN; stdcall;
begin
 KeEnterCriticalRegion;
 Result := ExAcquireResourceExclusiveLite(pLock, WaitResource);
 if not Result then KeLeaveCriticalRegion;
end;

И в начале процедуры записи в лог:

var
 IsEmpty: Boolean;
begin
 IsEmpty := Test_Lock(pLockFile, False);
 if IsEmpty or Test_Lock(pLockFile, True) then
  try
 /// Если IsEmpty, к сообщению добавляем "Free" иначе "Locked(!)"

Вот выдержка из лога Dbgview:
 
00000043   07:48:45.484   DriverRead IoCallDriver 103 0 Free    
00000044   07:48:45.562   ReadCompleteCallback    
00000045   07:48:45.562   DriverRead _ 0 0 Free    
00000046   07:48:45.562   DriverRead IoCallDriver 103 0 Free    
00000047   07:48:48.718   DriverUnload _ 0 0 Free    
00000048   07:48:50.859   ReadCompleteCallback    
00000049   07:48:50.859   DriverUnload KeWaitForSingleObject 0 0 Free    
00000050   07:48:50.859   DriverUnload ZwClose 0 0 Free    
00000051   07:48:50.859   DriverRead _ 0 0 Locked(!)    
00000052   07:48:50.859   DriverUnload IoDeleteSymbolicLink 0 0 Locked(!)    
00000053   07:48:50.859   DriverRead IoCallDriver 103 0 Locked(!)    
00000054   07:48:50.859   DriverUnload IoDetachDevice 0 0 Free    
00000055   07:48:50.859   DriverUnload IoDeleteDevice 0 0 Free    
00000056   07:48:50.859   LatchFile LatchFile_Finalizate 0 38 1024


Как мне кажется, из нее (выдержки) видно как происходит борьба двух нитей за ресурс smile

P.S.
 То, что мне помогло использование ERESOURCE (это утверждение основано на отсутствии 
 BSOD в течении трех часов smile), вовсе не означает, что путь правильный.
 Требуется более тщательная проверка.    
PM MAIL   Вверх
ama_kid
Дата 29.9.2008, 10:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



Цитата(Riply @  21.9.2008,  14:10 Найти цитируемый пост)
Очень надеюсь, что ты найдешь ошибку в рассуждениях
Ой, что-то как-то я упустил свою тему из виду, извиняюсь, просто уже несколько дней не заглядывал на форум...  smile 
Имхо, нет в твоих рассуждениях ошибки, насколько я разобрался в той каше, которую ты вывалила smile
Насколько я понял, у тебя вызвал непонятки момент одновременной записи в лог из DriverRead и DriverUnload? Не забывай (или имей ввиду), что функции DriverEntry и DriverUnload - это не функции-диспетчеры, а функции, выполняющиеся в контексте одного из системных потоков (случайным образом выбирается один из потоков процесса SYSTEM). А функции-диспетчеры выполняются в контексте процесса, выполняющего запрос ввода-вывода . А раз так - то для синхронизации между функциями-диспетчерами и системным потоком конечно нужно использовать объекты синхронизации...

Кроме того, меня смутила твоя фраза про "немного переделанный вариант" KbdIntcpt. Насколько я понял, ты ввела Overlapped-режим? Если я правильно помню - то отложенные операции выполняются тоже в контексте системного потока (аналогично callback-функциям), поэтому между двумя системными потоками тоже неплохо бы вводить синхронизацию при использовании ими общих ресурсов  smile 


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
microxa
Дата 2.12.2008, 05:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 2
Регистрация: 2.12.2008

Репутация: 1
Всего: 1



hi, all smile
Относительно темы delphi драйверов...

Надо отметить что некоторым недостатком является  сборка драйверов из obj-ектников, или использованием сторонних тулз (omf2d, m$-link).

Вобщем куда удобней использовать собственный линкер delphi, устраняя некоторые неровности в производимых им PE dll-ках, таким вот напильником: 

Код

{$apptype console} {$H+}
program DrvConv; uses windows, imagehlp;
var 
    img: PLoadedImage; Section: PImageSectionHeader;
    dwHeaderSum, dwCorrectSum : dword;  
    str: string; i: integer; p0: ^integer; 

begin

    str:= ParamStr(1); if str = '' then exit;
    img:= ImageLoad(pchar(str), nil); if img := nil then exit;
    MapAndLoad(pchar(str), nil, img, true, false);

for i := 0 to img.NumberOfSections-1 do
begin
    Section:= pointer(integer(img.Sections)+
                          SizeOf(TImageSectionHeader)*i);
    str:= pchar(@Section.Name);

    if str = 'CODE' then Section.Characteristics := $68000020;
    if str = 'DATA' then Section.Characteristics := $C8000040;
    if str = 'BSS'  then Section.Characteristics := $C8000000;

    if str = '.text'  then Section.Characteristics := $68000020;
    if str = '.itext' then Section.Characteristics := $62000020;
    if str = '.data'  then Section.Characteristics := $C8000040;
    if str = '.bss'   then Section.Characteristics := $C8000000;
    if str = '.reloc' then Section.Characteristics := $42000040; 
    if str = '.rsrc'  then Section.Characteristics := $40000040; 
// фикс некорректного (0) начала таблицы импорта (PE имиджи D2-D7) 
    if str = '.idata' then begin 
    p0 := pointer(integer(img.MappedAddress)+Section.PointerToRawData);
    if p0^ = 0 then p0^ := pInteger(integer(p0)+$10)^;
    end;
end;
// Устан. атрибутов драйвера 
    img.FileHeader.OptionalHeader.Subsystem := IMAGE_SUBSYSTEM_NATIVE;
// Установить контрольную сумму (CRC) 
    CheckSumMappedFile (img.MappedAddress, img.SizeOfImage, 
                             @dwHeaderSum, @dwCorrectSum );

    img.FileHeader.OptionalHeader.CheckSum := dwCorrectSum;

    ImageUnload (img);  
    writeln ('DLL updated')
end.


Дабы компиль не напихал вредных зависимостей, разюмеется для сборки необходимы минимальные System/Sysinit

Код

// сборка: Dcc32 -b -y System.pas
unit System; 
{$H-,I-,S-,L-}
interface

type

  HRESULT = type LongWord;  
  THandle = LongWord;
  TVarRec = record
  end;
  PByte         = ^Byte;
  PCardinal     = ^Cardinal;
  PPointer      = ^Pointer;
  PGUID = ^TGUID;
  TGUID = record
  end;
  PWideString = ^WideString;
  PInitContext = ^TInitContext;
  TInitContext = record
  end;

procedure _Halt0;
procedure _InitLib;
procedure _DoneExcept;
procedure _HandleFinally;
procedure _HandleAnyException;

implementation
          uses sysinit;

procedure _InitLib;
asm pop eax
    leave // kill internal stack frame
    push eax
end;

procedure _HandleAnyException;
asm mov esp,[esp+8]       // restore stack ptr
    pop dword ptr fs:0    // restore prev SEH frame
    pop  ebp              // get callback address 
    xchg ebp,[esp]        // restore ebp
    add dword ptr [esp],5 // skip jmp, ret to EXCEPT block 
end;

procedure _DoneExcept; 
asm 
end;

procedure _HandleFinally;
asm jmp   _HandleAnyException
end;

procedure _Halt0; 
asm 
end;

end.


Код

unit SysInit;

interface
var
  TlsIndex:Integer;          
  TlsLast: Byte;
  PtrToNil:Pointer = nil;

implementation
end.


Особености реализации модулей позволяют использовать блоки try/except  (хотя от всех видов исключений (например GPF) - они не спасут). 

Ну и собственно как использовать:

Код

Library MyDrv;
...
function DriverEntry (DrvObj: PDRIVER_OBJECT; ...
...

asm jmp DriverEntry
end.

удачи smile

PM MAIL   Вверх
Pavia
Дата 6.12.2008, 19:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 418
Регистрация: 6.12.2008

Репутация: нет
Всего: 12



Использую DDDK0004 

Никак не пойму почему bsod. Пробую пообщаться с IO-APIC

Код

    p:=MmMapIoSpace(PHYSICAL_ADDRESS(IOAPIC),4*1024,MmNonCached );
    p[0]:=$10;
    d:=p[$10];
    irp.UserBuffer:=Pointer(d);
    MmUnmapIoSpace(p,4*1024);

вылитает на этих строчках. Может кто подскажет в чем дело?

А еще dbgprint не работает. 
PM MAIL   Вверх
Pavia
Дата 7.12.2008, 03:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 418
Регистрация: 6.12.2008

Репутация: нет
Всего: 12



Судя по всему срыв стека. Такое впечатление что неправильно экспортирую функции.
Код

type  PHYSICAL_ADDRESS = int64;

  DWord=Cardinal;
  PVOID = Pointer;
  {$EXTERNALSYM PVOID}
  PPVOID = ^PVOID;
  {$EXTERNALSYM PPVOID}
  PVOID64 = Pointer;
  {$EXTERNALSYM PVOID64}

  ULONG_PTR = Longword;
  {$EXTERNALSYM ULONG_PTR}

  SIZE_T = ULONG_PTR;
  {$EXTERNALSYM SIZE_T}
  PSIZE_T = ^SIZE_T;
  {$EXTERNALSYM PSIZE_T}

function MmMapIoSpace(PhysicalAddress:PHYSICAL_ADDRESS;NumberOfBytes:SIZE_T; CacheType:DWord):PVoid;stdcall; external NtKernel name 'MmMapIoSpace';
procedure MmUnMapIoSpace(BaseAddress:PVoid;NumberOfBytes:SIZE_T); stdcall; external NtKernel name 'MmUnmapIoSpace';

PM MAIL   Вверх
ReInit
Дата 7.12.2008, 11:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 14.11.2008

Репутация: 1
Всего: 1



ama_kid
Спасибо! Отличная инфа!

microxa
Т.е. ты собираешь ДЛЛ и используешь ее как драйвер?
Какую версию Делфи ты используешь?
Потому что слышал, если собирать МС линкером, то он понимает обьектники только 3-й Делфи, т.к. после 3-й пошел неправильный COFF формат... И спасибо за System/SysInit, я юзал несколько попроще, без поддержки исключений smile
PM MAIL   Вверх
ama_kid
Дата 7.12.2008, 12:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



Цитата(Pavia @  7.12.2008,  04:57 Найти цитируемый пост)
Такое впечатление что неправильно экспортирую функции
Есть подозрение, что экспортировать их надо следующим образом:
Цитата
... stdcall; external NtKernel name '_MmMapIoSpace@16';

Цитата(Pavia @  6.12.2008,  20:13 Найти цитируемый пост)
А еще dbgprint не работает.
Не сильно понятное описание ошибки, телепатировать сложно... smile 
Вообще, для тематических вопросов есть соответствующий раздел форума smile
Цитата(ReInit @  7.12.2008,  12:01 Найти цитируемый пост)
Спасибо! Отличная инфа!
Не за что smile
microxa, действительно, достаточно ценное замечание, использование try...except...end сильно упростит жизнь во многих случаях...


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Riply
Дата 7.12.2008, 18:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(microxa @  2.12.2008,  05:49 Найти цитируемый пост)
Вобщем куда удобней использовать собственный линкер delphi, устраняя некоторые неровности в производимых им PE dll-ках, таким вот напильником: 


А можно меня ткнуть носом в тот потолок, с которого взялись все  (без исключения) константы в напильнике ? smile

P.S.
 На всякий случай оговорюсь: я не утверждаю, что что-то "не так",
 просто не умею воспринимать любые магические числа в коде, акромя разве что нуля и единицы smile  



Это сообщение отредактировал(а) Riply - 7.12.2008, 18:15
PM MAIL   Вверх
bems
Дата 7.12.2008, 18:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 3400
Регистрация: 5.1.2006

Репутация: 21
Всего: 88



ну как минимум там добавляется not pageable

Добавлено через 5 минут и 9 секунд
и еще discardable 


--------------------
Обижено школьников: 8
PM MAIL   Вверх
ama_kid
Дата 7.12.2008, 18:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 19
Всего: 95



Цитата(bems @  7.12.2008,  19:37 Найти цитируемый пост)
ну как минимум там добавляется
Больше похоже что просто копируются значения, генерируемые мелкомягким линкером. Потому что если этот напильник натравить на нормальный драйвер - эти магические числа равны содержащимся в образе...


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Riply
Дата 7.12.2008, 20:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(ama_kid @  7.12.2008,  18:58 Найти цитируемый пост)
Больше похоже что просто копируются значения, генерируемые мелкомягким линкером. Потому что если этот напильник натравить на нормальный драйвер - эти магические числа равны содержащимся в образе... 


Если это так (просто копируются), то где гарантии, что и у другого драйвера в образе тоже самое  ?
Или под другим SP-ом или под другой версией Win  ?
PM MAIL   Вверх
ReInit
Дата 8.12.2008, 00:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 14.11.2008

Репутация: 1
Всего: 1



Riply
Если ты про характеристики секций, то это комбинации констант IMAGE_SCN_...

http://www.wasm.ru/article.php?article=green2red02
Цитата
DWORD   Characteristics;
Это поле содержит атрибуты секции. Атрибуты секции указывают на права доступа к ней, а также на некоторые особенности влияния на нее загрузчика. Флаги секций могут преобразовываться загрузчиком в атрибуты страниц и сегментов.

PM MAIL   Вверх
Riply
Дата 8.12.2008, 01:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(ReInit @  8.12.2008,  00:15 Найти цитируемый пост)
Если ты про характеристики секций


Да я про все, ибо если мне вместо SizeOf(BYTE) напишут 1, то я перестану понимать smile
Хотя, себе любимой, иногда такое и позволяю smile

Цитата(ReInit @  8.12.2008,  00:15 Найти цитируемый пост)
это комбинации констант IMAGE_SCN


Во. Уже что-то. Спасибо smile

PM MAIL   Вверх
microxa
Дата 8.12.2008, 02:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 2
Регистрация: 2.12.2008

Репутация: 1
Всего: 1



Приветствую адептов столь продвинутой "дельфийской магии" smile
Цитата(Ripli)

А можно меня ткнуть носом в тот потолок, с которого взялись все (без исключения) константы в напильнике ?

Насчет констант - мне просто не хотелось перегружать прогу километровыми PAGE_EXECUTE_READ or IMAGE_SCN_MEM_NOT_PAGED и тп. (имеються в модуле windows) 

Напильник пришлось немного доработать в области "фиксатора" import table:
Код

// fix incorrect data in the import table (PE images from D4-D7) 
    if str= '.idata' then begin 
    p0:= pointer(integer(img.MappedAddress)+Section.PointerToRawData);
// copy FirstThunk to OriginalFirstThunk 
   while (pInteger(integer(p0)+$10)^ <> 0) do
    begin
      p0^ := pInteger(integer(p0)+$10)^; 
      inc (p0,5); //process next descriptor
    end;
   end;

(гм.. FirstThunk OriginalFirstThunk взяты из описания структур PE формата из windows.h)

Переделаные таким образом DLL, работают (по моему опыту) ничуть не хуже слинкованых мc-линкером.

Ну и какбы в качестве примера..

Одним из возможных применений драйвера - вызов Ring0 процедур непосредственно в самой программе.  Понятное дело - трюк связан с определенным  риском, и расчитан на "железячников" более менее знакомых с архитектурой PC/х86 (или желающих изучать это дело).

В режиме ядра снимаються проблемы только с операциями in/out и доступом к привелигированиым регистрам и инструкциям CPU. Это в ряде случаев  может оказаться недостаточным, например для обращения к физическим адресам  памяти, что потребует вызова системных ф-кций. 

В противном случае  несогласование этого вопроса с системой, может приведет к bsod (и даже SEH не пикнет)))... 

Пример простого драйвера:
Код

library TestDrv;
                uses DDDK, drvobj;

function MmMapIoSpace (PhysicalAddress:int64; NumberOfBytes, 
                       CacheEnable:Cardinal):pointer; stdcall; external NtKernel;

procedure MmUnmapIoSpace(BaseAddress:pointer;NumberOfBytes:cardinal); 
                          stdcall; external NtKernel;

var 
    Drv:TDRV;

function DrvDeviceControl(DrvObj:PDRIVER_OBJECT;Irp:PIRP):integer;stdcall;
var
    Ring0Proc : procedure(Drv:pointer);
begin                  
    Ring0Proc := Irp.UserBuffer;
  try
    Ring0Proc(@Drv);
   except
 end;
    IoCompleteRequest(Irp, 0);
    Result := STATUS_SUCCESS;
end;

const
    DevStr  = '\Device\TestDrv';
    DosStr  = '\DosDevices\TestDrv';
var
    UdevStr : UNICODE_STRING = (Length:0);
    UdosStr : UNICODE_STRING = (Length:0);

function DrvCreateDispatch(DrvObj:PDRIVER_OBJECT;Irp:PIRP):integer;stdcall;
begin
    IoCompleteRequest(Irp,0);
    Result := STATUS_SUCCESS;
end;

procedure DrvUnload(DrvObj:PDRIVER_OBJECT);stdcall;
begin
    IoDeleteSymbolicLink(@UDosStr);
    IoDeleteDevice(DrvObj.DeviceObject);
end;

function DriverEntry(DrvObj: PDRIVER_OBJECT; RegistryPath :pointer):integer;stdcall;
var DevObj: PDeviceObject;
begin
    RTlInitUnicodeString(@UdevStr,devStr);
    RTlInitUnicodeString(@UdosStr,dosStr);

    IoCreateDevice(DrvObj,0,@UDevStr,FILE_DEVICE_UNKNOWN,0,false,DevObj);

    IoCreateSymbolicLink(@UDosStr,@UDevStr);
    DrvObj.DriverUnload := @DrvUnload;
    DrvObj.MajorFunction[IRP_MJ_CREATE] := @DrvCreateDispatch;
    DrvObj.MajorFunction[IRP_MJ_DEVICE_CONTROL] := @DrvDeviceControl;

    DRV.MmMapIoSpace := @MmMapIoSpace;
    DRV.MmUnmapIoSpace := @MmUnmapIoSpace;
    HalMakeBeep (1000);
    Result := STATUS_SUCCESS;
end;

asm jmp DriverEntry
end.

Собираеться в любой среде (IDE delphi/CMD32), главное в папку с проектом 
драйвера не забыть поместить облегченные версии RTL. 

Для упрощения, здесь адрес процедуры передаеться в драйвер в качестве параметра в  DeviceIOControl, а так как user-space программа,  экспортировать функции ядра  не может, между драйвером и программкой будет некоторый общий интерфейс:

Код

unit drvobj;
interface
type 
     TDRV = record 
     MmMapIoSpace : function(PhysicalAddress:int64;NumberOfBytes, 
                             CacheEnable:Cardinal):pointer; stdcall;
     MmUnmapIoSpace: procedure(BaseAddress:pointer;
                               NumberOfBytes:cardinal); stdcall; 
end; PDRV = ^TDRV;
implementation
end.


Код

{$apptype console}
program testDrv; uses windows, drvobj;

var
    str: pChar = 'Hello World from Kernel Mode!';

procedure Ring0(Drv:PDRV);
const
    size = $2000; 
var 
    i:integer; ptr: pointer;
begin
    ptr := Drv.MmMapIoSpace($B8000,size,0);

 for i := 0 to length(str) do
 begin
    pChar(integer(ptr)+i*2)^ := pChar(integer(str)+i)^; //put char
    pChar(integer(ptr)+(i*2)+1)^ := #$1F; //put attribute
 end;

 if ptr <> nil then Drv.MmUnMapIoSpace(ptr, size);
end;

var
    b:dword; hDrv:thandle; i:integer;
begin
    hDrv:=CreateFile('\\.\\TestDrv',$80000000,0,nil,3,$80,0);

    DeviceIoControl(hDrv,0,@b,4,@Ring0,0,b,nil);
    CloseHandle (hDrv);
    readln;
end.


Тестовый пример "хелловорлда" работает исключительно в режиме консольного fullscreen'а (80x25), поскольку маппит район $B8000 (видеопамять текстового режима, стандарт времен царя гороха))).
/added
Riply, спасибо! 
благодаря Вашему замечанию теперь я буду поступать так smile
Код

var
    b:dword; 
    f:file;
begin
     Assign (f, '\\.\\TestDrv');
     reset(f,1);
     DeviceIoControl(TFileRec(f).handle,0,@b,4,@Ring0,0,b,nil);
     Close(f);
     ReadLn;
end.

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

Шерлок Холмс был прав, насчет того что голова не чердак, и не стоит ее захламлять! smile

ммм... даже интересно стало прикрутить к драйверу  IRP_MJ_READ/ IRP_MJ_WRITE   

Это сообщение отредактировал(а) microxa - 8.12.2008, 06:47
PM MAIL   Вверх
Riply
Дата 8.12.2008, 03:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



microxa
Цитата(microxa @  8.12.2008,  02:42 Найти цитируемый пост)
Приветствую адептов столь продвинутой "дельфийской магии"


Здравствуйте smile

Цитата(microxa @  8.12.2008,  02:42 Найти цитируемый пост)
Насчет констант - мне просто не хотелось перегружать прогу километровыми PAGE_EXECUTE_READ or IMAGE_SCN_MEM_NOT_PAGED и тп. (имеються в модуле windows) 


Вот скажи-ка мне честно:
неужели, ты в следующей строчке сразу понимаешь, что и как создается ?
Цитата(microxa @  8.12.2008,  02:42 Найти цитируемый пост)
hDrv:=CreateFile('\\.\\TestDrv',$80000000,0,nil,3,$80,0);

Если да, то не кажется ли тебе, что уж лучше "перегружать прогу километровыми константами",
чем свой мозг ?  smile

P.S.
 Кстати, Шерлок Холмс сравнивал человеческий мозг с чердаком,
 на котором не так уж и много места smile


Это сообщение отредактировал(а) Riply - 8.12.2008, 03:32
PM MAIL   Вверх
ReInit
Дата 8.12.2008, 13:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 14.11.2008

Репутация: 1
Всего: 1



Riply
Цитата

 Кстати, Шерлок Холмс сравнивал человеческий мозг с чердаком,
 на котором не так уж и много места 

Шерлок Холмс (точнее Конан Дойль) заблуждался...
PM MAIL   Вверх
EagleXK
Дата 18.1.2009, 17:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 13
Регистрация: 11.9.2006
Где: Украина, Житомир

Репутация: нет
Всего: нет



Хмм... Странно, но Ctrl+Alt+Del не перехватился на XP SP2...
PM MAIL WWW ICQ   Вверх
Riply
Дата 21.2.2009, 18:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



День добрый !

ama_kid, а ты не мог бы дать файлы sysinit.pas и system.pas,
которые послужили "прототипом" для dcu-шек ?
Ну и командную строчку, с которой собирались  smile 
PM MAIL   Вверх
bems
Дата 21.2.2009, 22:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 3400
Регистрация: 5.1.2006

Репутация: 21
Всего: 88



Riply, в командной строке нужен ключ -Y
Это работало в 7ке. 


--------------------
Обижено школьников: 8
PM MAIL   Вверх
jungle
Дата 16.6.2009, 12:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аппаратный кодер



Профиль
Группа: Участник
Сообщений: 40
Регистрация: 17.1.2006
Где: Беларусь

Репутация: нет
Всего: нет



Добрый день!

Можно ли использовать 64-битовый линкер для сборки 64-разрядного драйвера? 
PM MAIL WWW   Вверх
Alexeis
Дата 16.6.2009, 12:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 16
Всего: 459



Цитата(jungle @  16.6.2009,  11:10 Найти цитируемый пост)
Можно ли использовать 64-битовый линкер для сборки 64-разрядного драйвера?  

  Разве он проглотит 32х разрядные бинарники? Кроме того как работать с указателями, ведь они 32х разрядные. Так что скорее всего нельзя.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
jungle
Дата 16.6.2009, 14:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аппаратный кодер



Профиль
Группа: Участник
Сообщений: 40
Регистрация: 17.1.2006
Где: Беларусь

Репутация: нет
Всего: нет



OK. И еще вопрос, если позволите, многоуваемый All. Автор статьи говорит об отсутствии возможности применить блок try-except-end, но вот в сырцах драйвера тов. microxa для считывания физ. памяти этот блок присутствует. Причем, microxa  заявляет о нормальной компиляции и т.д. И кто не прав?
PM MAIL WWW   Вверх
Alexeis
Дата 16.6.2009, 15:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 16
Всего: 459



  Есть такие соображения, что если код драйвера вызывается при обработке прерывания, то аппаратное исключение может не сработать ведь его приоритет может оказаться более низким, чем у кода обработчика прерывания, вот он и будет ждать окончания работы.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Riply
Дата 16.6.2009, 15:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(jungle @  16.6.2009,  14:59 Найти цитируемый пост)
но вот в сырцах драйвера тов. microxa для считывания физ. памяти этот блок присутствует


Присутствует - не означает работает.

Цитата(jungle @  16.6.2009,  14:59 Найти цитируемый пост)
Причем, microxa  заявляет о нормальной компиляции и т.д.


Надо у него спросить на чем основано его утверждение о "нормальной компиляции".
На том, что успешно создается sys - файл ?
Если так, то этого мало. 

Код ama-kid`а тоже будет успешно собираться и компилироваться с try/finally блоками.

Но вот если "перехватить" жалобные крики dcc32.exe в процессе компиляции, smile
то там можно увидеть примерно следующие строки:
Shd_QuerySys.obj: error LNK2001: uresolved external symbol @@HandleFinally$qqrv



PM MAIL   Вверх
Alexeis
Дата 16.6.2009, 15:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 16
Всего: 459



Цитата(Riply @  16.6.2009,  14:17 Найти цитируемый пост)
Shd_QuerySys.obj: error LNK2001: uresolved external symbol @@HandleFinally$qqrv

  Это уже ошибка линкера. Но ведь линкер используется другой, может он подставляет туда то что нужно из RTL.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Riply
Дата 16.6.2009, 15:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(Alexeis @  16.6.2009,  15:20 Найти цитируемый пост)
Это уже ошибка линкера


Угу. Перепутала. Sorry.


Цитата(Alexeis @  16.6.2009,  15:20 Найти цитируемый пост)
Но ведь линкер используется другой, может он подставляет туда то что нужно из RTL


Полностью согласна. 
Именно поэтому я и написала, что "Надо у него спросить на чем основано его утверждение" smile

А без его пояснений, нам остается только гадать на кофейной гуще smile
PM MAIL   Вверх
Акакий
Дата 6.7.2009, 13:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 6.7.2009

Репутация: нет
Всего: нет



Всем здравствуйте! Пишу (точнее - пытаюсь написать  smile ) свой первый драйвер на основе исходников уважаемого ama_kid.
Перехватываю ZwMapViewOfSection. Новая функция NewZwMapViewOfSection при выполнении некоторых условий должна изменять содержимое памяти, возвращенное "оригинальной" ZwMapViewOfSection. Код замещающей функции:
Код

Function NewZwMapViewOfSection(SectionHandle:dword;  ProcessHandle:dword;  Var BaseAddress:Pointer; ZeroBits,  CommitSize:dword; SectionOffset:pointer; ViewSize:pdword;                            InheritDisposition:dword; AllocationType,Protect:dword): NTSTATUS; stdcall;
  Var  pp :PVoid;
 Begin
//вызов "оригинальной" функции
Result:=ZwMapViewOfSectionOriginal(SectionHandle,ProcessHandle,BaseAddress,ZeroBits,CommitSize,SectionOffset,ViewSize,
                                       InheritDisposition,AllocationType,Protect);
    If (Result=STATUS_SUCCESS) and 
       (DWord(ViewSize^)>1) and (Byte(Pointer(DWord(BaseAddress)+1)^)=81) then
      begin
        pp:=MmAllocateNonCachedMemory(DWord(ViewSize^));
        RtlMoveMemory(pp,BaseAddress,DWord(ViewSize^));
        Byte(Pointer(DWord(pp)+1)^):=82;
        BaseAddress:=pp;
      end;
  end;



В данном примере изменение памяти происходит в случае, если ее 2-й байт равен 81 (буква "Q"). Открываю блокнотом текстовый файл, начинающийся с "QQQQQ....". Блокнот запускается после чего виндовоз уходит в перезагрузку. Если убрать присваивание BaseAddress:=pp, то перезагрузки не происходит. Я не делаю там Unmap(BaseAddress), т.к. это отладочный вариант. Попытка воспользоваться ZwAllocateVirtualMemory не помогла. Пожалуйста, объясните мне, как нужно правильно делать. 
P.S.
    Сильно подозреваю, что в моем коде, мягко выражаясь, много неправильного, а более точно - там вообще один бред :(  Но все-равно я верю в то, что найдется добрый человек и укажет мне на мои ошибки  smile 

Это сообщение отредактировал(а) Акакий - 6.7.2009, 16:29
PM MAIL   Вверх
Riply
Дата 6.7.2009, 18:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(Акакий @  6.7.2009,  13:31 Найти цитируемый пост)
В данном примере изменение памяти происходит в случае, если ее 2-й байт равен 81 (буква "Q"). 


Т.е. при любом вызове ZwMapViewOfSection (кем угодно !) она с вероятностью 1/256 возвращает бред,
а мы удивляемся почему падает система ?   smile

Цитата(Акакий @  6.7.2009,  13:31 Найти цитируемый пост)
Я не делаю там Unmap(BaseAddress), т.к. это отладочный вариант


Т.е. при отладке память бесконечна ?   smile

Цитата(Акакий @  6.7.2009,  13:31 Найти цитируемый пост)
 Сильно подозреваю, что в моем коде, мягко выражаясь, много неправильного, а более точно - там вообще один бред 


Осознание сего факта - большой шаг вперед (без смайлика)

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

У тебя этого понимания пока нет. И двумя, тремя словами его не дать.
Так что, садись за книги.
PM MAIL   Вверх
Акакий
Дата 6.7.2009, 20:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 6.7.2009

Репутация: нет
Всего: нет



Riply, благодарю за отзыв.

Цитата
 Т.е. при любом вызове ZwMapViewOfSection (кем угодно !) она с вероятностью 1/256 возвращает бред 

Да это я знаю, просто это "сиюминутный" отладочный вариант. Ну добавлю я анализ хендла на принадлежность его блокноту - это ничего не изменит. Я гружу драйвер, открываю блокнотом все файлы, начинающиеся не с "QQQQQ..." и все прекрасно открывается, но как только я открою "QQQQQ..." - сразу reboot. Сделал перехват ZwRead/WriteFile, все прекрасно работает(происходит расшифровка/шифрование "на лету"). А блокнот пользуется "маппингом" вот и встала задача перехвата ZwMapViewOfSection.

Цитата
 Т.е. при отладке память бесконечна ? 

См. выше  smile 

Цитата

Осознание сего факта - большой шаг вперед (без смайлика).....садись за книги

Ну читал я все эти книги (заранее принимаю аплодисменты, но все же не верю в свою безнадежность smile )

Перехватом я занимаюсь уже давно, только вот в ринг 3. В ринг 0 кодил, испрользуя Call Gate (шлюз вызова в GDT).

Это сообщение отредактировал(а) Акакий - 6.7.2009, 20:38
PM MAIL   Вверх
Riply
Дата 6.7.2009, 21:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(Акакий @  6.7.2009,  20:18 Найти цитируемый пост)
Да это я знаю, просто это "сиюминутный" отладочный вариант


Так. Давай с самого начала.
Ты спрашиваешь почему "виндовоз уходит в перезагрузку".

Я дала тебе (для начала) пару причин, по которым это может происходить.

1. При "заргузке блокнота" ZwMapViewOfSection может вызываться отнюдь не один раз и не только
    для чтения содержимого твоего файла.
    Откуда у тебя может быть уверенность, что при загрузке какой-нибудь библиотеки,
    ты не возвращаешь мусор вместо ее образа ?

2. ZwMapViewOfSection может быть вызвана не только блокнотом, а кем угодно (Система живет и работает).
    Откуда у тебя уверенность, что в течении твоего опыта она не вызвалась 567 раз
    и суммарный объем выделенной тобой памяти при этом не привысил максимально возможный ?
    (Кстати и здесь существует вероятность возврата мусора вместо образа)

Мне кажется, что сначала надо исключить возможность перезагрузки по любой из этих причин,
и только потом идти дальше.
PM MAIL   Вверх
Акакий
Дата 6.7.2009, 22:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 6.7.2009

Репутация: нет
Всего: нет



Цитата(Riply @ 6.7.2009,  21:22)

1. При "заргузке блокнота" ZwMapViewOfSection может вызываться отнюдь не один раз и не только
    для чтения содержимого твоего файла.

2. ZwMapViewOfSection может быть вызвана не только блокнотом, а кем угодно (Система живет и работает).


1. Сделал проверку на принадлежность хэндла процессу блокнота.
2. Заменил символ "Q" уникальной комбинацией из 100 символов (сильно сомневаюсь, что данная последовательность байтов может быть где-то еще).
Результат тот же.

Уточню свой вопрос: параметр BaseAddress функции ZwMapViewOfSection содержит адрес переменной, в которую ZwMapViewOfSection засылает адрес спроецированного объекта (в моем случае - файла) или ZwMapViewOfSection шлет этот адрес непосредственно в переменную BaseAddress (Var BaseAddress)? Мелкомягкие пишут, что BaseAddress - это "Pointer to a variable that receives the base address of the view". Значит, вроде как BaseAddress должен указывать на переменную с адресом. Да и у Рема в его NativeAPI.pas этот параметр идет без "var". Только вот я пробовал оба варианта - результат одинаковый - при попытке подменить BaseAddress, или записать по адресу BaseAddress новый адрес "спроецированной" памяти виндовоз уходит в ребут. 
PM MAIL   Вверх
Riply
Дата 6.7.2009, 23:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(Акакий @  6.7.2009,  22:09 Найти цитируемый пост)
Мелкомягкие пишут, что BaseAddress - это "Pointer to a variable that receives the base address of the view"


В данном вопросе я их слушаюсь и все работает smile

Цитата(Акакий @  6.7.2009,  22:09 Найти цитируемый пост)
1. Сделал проверку на принадлежность хэндла процессу блокнота.
2. Заменил символ "Q" уникальной комбинацией из 100 символов (сильно сомневаюсь, что данная последовательность байтов может быть где-то еще).


Устрани и остальные скользкие моменты:
Цитата(Акакий @  6.7.2009,  13:31 Найти цитируемый пост)
(DWord(ViewSize^)>1) and (Byte(Pointer(DWord(BaseAddress)+1)^)=81) then

у меня нет уверенности в том,  что при любом вызове, ViewSize <> nil, BaseAddress <> nil, BaseAddress^ <> nil.

Так же у меня нет уверенности, что ViewSize^ < максимальный размер памяти, который мы можем выделить.


Цитата(Акакий @  6.7.2009,  20:18 Найти цитируемый пост)
Перехватом я занимаюсь уже давно


А почему тогда сначала не оттестировать перехват в user mode ?
(Тем более, если нет уверенности в правильном понимании параметров ф-ии ZwMapViewOfSection)

PM MAIL   Вверх
Riply
Дата 7.7.2009, 00:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(Акакий @  6.7.2009,  22:09 Найти цитируемый пост)
при попытке подменить BaseAddress, или записать по адресу BaseAddress новый адрес "спроецированной" памяти виндовоз уходит в ребут


А на основании чего мы считаем, что адрес, полученный нами через MmAllocateNonCachedMemory,
может быть безболезненно передан NotePad`у ?
PM MAIL   Вверх
Акакий
Дата 7.7.2009, 06:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 6.7.2009

Репутация: нет
Всего: нет



Цитата

А почему тогда сначала не оттестировать перехват в user mode ?

Хорошая мысь, спасибо, нужно попробовать. В user mode я перехватывал MapViewOfFile/Ex, но блокнот ее не вызывает, что есть несколько странно(блокнот пользуется только функциями Zw?). Попробую перехватить ZwMapViewOfSection из ring3, но у меня странное предчуствие, что из блокнота она не будет вызываться.

Цитата

А на основании чего мы считаем, что адрес, полученный нами через MmAllocateNonCachedMemory,
может быть безболезненно передан NotePad`у ? 

Это да, при выделении памяти в драйвере я получаю память в сегменте данных нулевого кольца(селектор с RPL=0), а в блокноте, скорее всего, имеем 3-е кольцо (RPL=2) и может получаться некорректное смещение относительно базы сегмента данных блокнота. Пробовал делать
ZwAllocateVirtualMemory, но она не работает, по крайней мере при вызове из ring 3.


PM MAIL   Вверх
Акакий
Дата 7.7.2009, 10:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 6.7.2009

Репутация: нет
Всего: нет



Перехватил  ZwMapViewOfSection в ринг 3. Блокнот вызывает эту функцию, но использует ее только для вывода списка файлов, а сами файлы он читает с помощью чего-то другого. Интересно, при перехвате этой функции в ринг 0 она вызывается и при загрузке блокнотом файлов. Отсюда и происходят мои сомнения о корректности перехвата Zw функций в ринг 3. Кстати, на форуме rsdn, кто-то "жаловался", что от блокнота не приходит IRP_MJ_READ.
PM MAIL   Вверх
Акакий
Дата 7.7.2009, 13:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 6.7.2009

Репутация: нет
Всего: нет



Все, разобрался.  smile 
Код

Function NewZwMapViewOfSection(SectionHandle:dword; ProcessHandle:dword; BaseAddress:PPointer; 
                                                        ZeroBits, CommitSize:dword; SectionOffset:PInt64; ViewSize:pdword;
                                                      InheritDisposition:dword; AllocationType,Protect:dword):NTStatus; stdcall;
  Var  pp :Pointer;
         dd :DWord;
  Begin
 Result:=ZwMapViewOfSectionOriginal(SectionHandle,ProcessHandle,BaseAddress,ZeroBits,CommitSize,SectionOffset,ViewSize,
                                       InheritDisposition,AllocationType,Protect);
    If (Result=STATUS_SUCCESS) and (ViewSize<>Nil) and (BaseAddress<>Nil) and (Pointer(BaseAddress^)<>Nil) and
       (DWord(ViewSize^)>1) and (Byte(Pointer(BaseAddress^)^)=81) then
      begin
        pp:=Nil; dd:=DWord(ViewSize^);
        If ZwAllocateVirtualMemory(ProcessHandle,@pp,0,@dd,$1000,4)=STATUS_SUCCESS then
          begin
            RtlMoveMemory(pp,Pointer(BaseAddress^),dd);
            ZwUnmapViewOfSection(ProcessHandle,BaseAddress);
            Pointer(BaseAddress^):=pp;
            Byte(pp^):=82;
          end;
      end;
  end;


Riply, спасибо.

Это сообщение отредактировал(а) Акакий - 7.7.2009, 13:54
PM MAIL   Вверх
Акакий
Дата 20.7.2009, 15:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 6.7.2009

Репутация: нет
Всего: нет



Хочу выразить огромную благодарность ama_kid за то, что он бескорыстно делится с нами своими знаниями и своим трудом. Уважаемый ama_kid, если бы Вы еще написали статью о создании драйвера виртуального диска, то это была бы такая Вещь, переоценить которую было бы просто невозможно. Во всем нете нет ни одной подобной статьи. Все только для С++, а с этого, осмелюсь заявить, кривого языка, у меня не получается переводить на Delphi. Товарищи, да здравствует Delphi+Assembler!
PM MAIL   Вверх
Акакий
Дата 11.2.2010, 13:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 6.7.2009

Репутация: нет
Всего: нет



Ура! После долгих изысканий, изучений и мучений я все-таки сделал драйвер виртуального диска! Жалко только, что драйвер этот я сделал на С++. Итого, со всей ответственностью могу заявить, что сделать драйвер диска на Delphi в принципе невозможно, для этого, как минимум, прийдется переводить ПОЧТИ ВСЕ сишные библиотеки на Delphi. Точнее - теоретически написать драйвер на Delphi можно, но только теоретически(примерно так же, как и создать машину времени). 
   Конечно, статья, написанная ama_kid  может пригодиться для начального ознакомления "дельфистов" с драйверами. Но, к сожалению, в практическом плане статья эта абсолютно бесполезна. Все то, что могут делать "драйвера", приведенные в этой статье(и вообще, написанные на Delphi), я смогу сделать на Delphi через Call Gate безо всяких драйверов.

Модератор: Акакий сможет продолжить сообщать неправдивые сведения не раньше чем через два дня. 

Это сообщение отредактировал(а) bems - 11.2.2010, 14:58
PM MAIL   Вверх
FalseMaster
Дата 1.2.2011, 03:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 21
Регистрация: 18.11.2009

Репутация: нет
Всего: нет




Модератор: Сообщение скрыто.

PM MAIL   Вверх
birks
Дата 13.11.2011, 15:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 1
Регистрация: 13.11.2011

Репутация: нет
Всего: нет



А как с помощью данного примера отправить допустим нажатие клавиши f1 ?

PM MAIL   Вверх
FalseMaster
Дата 24.8.2013, 20:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 21
Регистрация: 18.11.2009

Репутация: нет
Всего: нет





Модератор: SDK не SDK, а тема не создана для ссылок


Это сообщение отредактировал(а) bems - 25.8.2013, 00:57
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: WinAPI и системное программирование"
Snowybartram
MetalFanbems
PoseidonRrader
Riply

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Delphi обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи
  • 99% ответов по WinAPI можно найти в MSDN Library, оставшиеся 1% здесь

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, bartram, MetalFan, bems, Poseidon, Rrader, Riply.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: WinAPI и системное программирование | Следующая тема »


 




[ Время генерации скрипта: 0.4753 ]   [ Использовано запросов: 23 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.