Ідея написання драйвера псевдопристроїв виникла не відразу. На самому початку шляху для мене на першому плані стояло питання - а чи стане в нагоді така програма взагалі? Затребується пересічним програмістом, а тим більше користувачем, механізм блокування дискових квот або, скажімо, запис в пам'ять з атрибутом «немає доступу»? Тепер, коли отримані перші результати випробувань в операційній системі Windows NT, сумнівів більше не залишилося. На мою думку найбільш корисною ця програма виявиться для експертів в області інформаційної безпеки, що використовують ОС WindowsNT, тобто дана програма допоможе розробникам ефективних додатків для обеспченіе інформаційної безпеки WindoewsNT.
Бажано також мати уявлення про архітектурну моделі драйверів пристроїв. Особливий акцент зроблений на взаємодію програмного середовища з системним менеджером вводу / виводу.
2. Режим ядра і користувальницький режим
Мікропроцесор Pentium має чотири рівні привілеїв (privilege levels), відомих також як кільця (rings), які керують, наприклад, доступом до пам'яті, можливістю використовувати деякі критичні команди процесора (такі як команди, пов'язані з захистом) і т.д. Кожен потік виконується на одному з цих рівнів привілеїв. Кільце 0 - найбільш привілейований рівень, з повним доступом до всієї пам'яті і до всіх команд процесора. Кільце 3 - найменш привілейований рівень.
Для забезпечення сумісності з системами на базі процесорів, відмінних від тих, що випускає компанія Intel, Windows NT підтримує тільки два рівня привілеїв - кільця 0 і 3. Якщо потік працює в кільці 0, кажуть, що він виконується в режимі ядра (kernel mode) . У літературі режим ядра іноді також називають режимом супервізора. Якщо потік виконується в кільці 3, кажуть, що він працює в режимі користувача (user mode). У призначеному для користувача режимі працюють додатки і підсистеми забезпечення середовища (Win32, OS / 2 і POSIX). Графічна система, драйвери графічних пристроїв, драйвери принтерів і диспетчер управління вікнами виконуються в режимі ядра (див. Схему «Архітектура Windows NT в спрощеному вигляді»). Режим виконання user mode значно надійніший, але вимагає додаткових витрат, які знижують загальну продуктивність процесу. Здійснюється в призначеному для користувача режимі код не може порушити цілісності виконує системи Windows NT, ядра і драйверів пристроїв. Цікаво, що драйвери пристроїв працюють в режимі ядра. Ця обставина має два наслідки. По-перше, на відміну від неправильно виконується додатки неправильно працюючий драйвер може зашкодити нормальному роботу всієї системи, так як він має доступ і до всього системного коду і до всієї пам'яті. По-друге, прикладний програміст може отримати доступ до захищених ресурсів, написавши драйвер псевдопристроїв (fake device), хоч це і не легке завдання.
#ifdef _X86_ // структура відноситься до процесорної моделі x86
typedef struct _CONTEXT
// набір бітових прапорів, що визначає вміст структури
DWORD ContextFlags;
// Налагодження регістри
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
// регістри з плаваючою точкою
FLOATING_SAVE_AREA FloatSave;
// Сегментні регістри
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
// Цілочисельні регістри
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
// Регістри управління
DWORD Ebp;
DWORD Eip;
DWORD SegCs;
DWORD EFlags;
DWORD Esp;
DWORD SegSs;
// Додаткові регістри
BYTE ExtendedRegisters [MAXIMUM_SUPPORTED_EXTENSION];
#endif
// ALPHA, _ MIPS_, _AMD64_, _M_IA64 і ін.
д) Лістинг 2
/ * ++
Ім'я модуля:
spurp.h
опис:
Визначає відносяться до SPURP тип пристрою,
функцію обробки і код IOCTL для додатків і драйвера
--* /
#ifndef SPURP_H
#define SPURP_H
// макрос, що визначає код нового IOCTL
#define IOCTL_SWITCH_CONTEXTS CTL_CODE (FILE_DEVICE_UNKNOWN, 0x802, \
METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA)
#define SPURP_DEVICE_NAME_U L "\\ Device \\ SPURP"
#define SPURP_WIN_DEVICE_NAME_U L "\\ DosDevices \\ SPURP"
NTSTATUS
DriverEntry (
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING registryPath
);
NTSTATUS
SwitchStackDispatchIoctl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp);
VOID
SpurpUnload (
IN PDRIVER_OBJECT DriverObject
);
#endif
е) Лістинг 3
/ * ++
Ім'я модуля:
spurp.c
опис:
драйвер, який здійснює виконання функцій користувача
з переданими аргументами в режимі ядра
--* /
#include
#include "spurp.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, SwitchStackDispatchIoctl)
#pragma alloc_text (PAGE, SpurpUnload)
#endif
NTSTATUS
DriverEntry (
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING unicodeDeviceName;
UNICODE_STRING unicodeWinDeviceName;
PDEVICE_OBJECT deviceObject;
status = IoCreateDevice (
DriverObject,
0,
unicodeDeviceName,
FILE_DEVICE_UNKNOWN,
0,
(BOOLEAN) TRUE,
deviceObject
);
if (! NT_SUCCESS (status))
return status;
>
//
// Виділяємо пам'ять і инициализируем UNICODE - рядок з ім'ям
// нашого пристрою, видимим в підсистемі Win32
//
(Void) RtlInitUnicodeString ( unicodeWinDeviceName, SPURP_WIN_DEVICE_NAME_U);
status = IoCreateSymbolicLink (
(PUNICODE_STRING) unicodeWinDeviceName,
(PUNICODE_STRING) unicodeDeviceName
);
if (! NT_SUCCESS (status))
IoDeleteDevice (deviceObject);
return status;
>
DriverObject-> MajorFunction [IRP_MJ_DEVICE_CONTROL] = SwitchStackDispatchIoctl;
DriverObject-> DriverUnload = SpurpUnload;
// ++
// SwitchStackDispatchIoctl
//
// This is the dispatch routine which processes
// Device I / O Control functions sent to this device
//
// Параметри:
// DeviceObject - покажчик а об'єкт пристрою
// Irp - покажчик на пакет даних введення / виводу (I / O Request Packet)
//
// Значення, що повертається:
// NSTATUS статус завершення IRP
//
// -
NTSTATUS SwitchStackDispatchIoctl (PDEVICE_OBJECT DeviceObject, PIRP Irp)
PIO_STACK_LOCATION Io_s;
NTSTATUS Status;
//
// Отримати покажчик на поточний стан стека вводу / виводу (I / O Stack)
//
Io_s = IoGetCurrentIrpStackLocation (Irp);
//
// Make sure this is a valid IOCTL for us.
//
if (Io_s-> Parameters.DeviceIoControl.IoControlCode! = IOCTL_SWITCH_CONTEXTS)
Status = STATUS_INVALID_PARAMETER;
>
else
//
// Отримати покажчик на функцію, що викликається.
//
VOID (* UserFunctToCall) (PULONG) = Irp-> UserBuffer;
//
// і аргумент для передачі
//
PVOID UserArg;
UserArg = Io_s-> Parameters.DeviceIoControl.Type3InputBuffer;
//
// Викликати функцію користувача з переданими параметрами
//
(VOID) (* UserFunctToCall) ((UserArg));
Status = STATUS_SUCCESS;
>
Irp-> IoStatus.Status = Status;
Irp-> IoStatus.Information = 0;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return (Status);
>
// ++
// SpurpUnload
//
// Ця функція звільняє зайняті драйвером
// ресурси і видаляє його об'єкт пристрою (device object).
//
// Параметри:
// DeviceObject - покажчик на об'єкт пристрою
//
// Значення, що повертається:
// VOID (функція припиняє роботу драйвера)
//
// -
VOID SpurpUnload (IN PDRIVER_OBJECT DriverObject)
UNICODE_STRING uniWin32NameString;
//
// Видалити видиме користувачеві ім'я пристрою
//
RtlInitUnicodeString ( uniWin32NameString, SPURP_WIN_DEVICE_NAME_U);
IoDeleteSymbolicLink ( uniWin32NameString);
//
// Видалити устойство
//
IoDeleteDevice (DriverObject-> DeviceObject);
ж) Лістинг 4
; Ім'я модуля:
; spurp.inf
; Опис:
; дані установки для драйвера spurp.sys
[DestinationDirs]
SPURP.Files.x86_12 = 12
[SPURP_Inst.ntx86.Services]
AddService = spurp, 0x00000002, SPURP_Service_Instx86,
[SPURP_Service_Instx86]
ServiceType =% SERVICE_KERNEL_DRIVER%
StartType =% SERVICE_SYSTEM_START%
ErrorControl =% SERVICE_ERROR_NORMAL%
ServiceBinary =% 12% \ spurp.sys
[SPURP_EventLog_Inst]
AddReg = SPURP_EventLog_Inst.AddReg
[SPURP_EventLog_Inst.AddReg]
HKR ,, EventMessageFile,% REG_EXPAND_SZ%, "%% SystemRoot %% \ System32 \ IoLogMsg.dll"
HKR ,, TypesSupported,% REG_DWORD%, 7
; ******* Non Localizable Strings *******
SERVICE_BOOT_START = 0x0
SERVICE_SYSTEM_START = 0x1
SERVICE_AUTO_START = 0x2
SERVICE_DEMAND_START = 0x3
SERVICE_DISABLED = 0x4
SERVICE_KERNEL_DRIVER = 0x1
SERVICE_ERROR_IGNORE = 0x0
SERVICE_ERROR_NORMAL = 0x1
SERVICE_ERROR_SEVERE = 0x2
SERVICE_ERROR_CRITICAL = 0x3
Власне, це моє дослідження, в процесі якого я написав драйвер псевдопристроїв.
Сенс цієї розробки обговорений в тексті дослідження, а про можливості, думаю, і так всім зрозуміло (виконання команд на рівні ядра з режиму користувача, що істотно розширює можливості використання системи).