Как написать драйвер, работающий во всех NT
Зачем ?
А затем, что это imho способствует популярности и облегчает поддержку продукта.
Лично мне очень нравятся программы,
которые ставятся и работают где угодно. И наоборот, очень раздражают те, что требуют определенного ServicePack,
имеют отдельный дистрибутив под каждую ОС и т.п. И, вопреки распространенному мнению, сделать драйвер
универсальным не так сложно.
Как ?
- Выяснить, где мы находимся. (пример 1)
- Пользуясь знанием версии ОС сделать
- хитрые union'ы для структур, которые по-разному объявлены в разных версиях. (пример 2)
- несколько веток if/else для специфичных кусков кода, например инициализация в
Legacy и WDM стиле. (пример 3)
- Где можно - обходиться без использования "новых" функций
- Где нельзя или очень хочется -
Примеры
Часть приведенного кода взята из реальных проектов, в частности из DbgPrint Dump,
а точнее - его библиотеки Cross-Nt (раньше было в SDK, теперь есть и отдельно),
другая часть героически добыта DeathSoft'ом
из новых виндов. Есть мысль собрать это все в статические и динамические библиотеки, уже частично реализована в
Cross-Nt library.
Example 1
Определяем в DriverEntry() версию ОС и готовимся к использованию
GetModuleHandle()/GetProcAddress(). Если поддержка NT 3.51 не планируется,
то лучше для поиска загруженых модулей пользоваться NtQuerySystemInformation(), т.к. сканирование
цепочки LDR_DATA_TABLE_ENTRY во время выгрузки какого-нибудь модуля может увалить систему.
if(!NT_SUCCESS(status = CrNtInit(SavedDriverObject, RegistryPath))) {
return status;
}
CrNtPsGetVersion(&MajorVersion, &MinorVersion, &BuildNumber, &SavedSPString);
NTSTATUS
CrNtInit(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
PLIST_ENTRY Next;
PLIST_ENTRY LoadOrderListHead;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry0;
__try
{
if(!g_LoadOrderListHead) {
LdrDataTableEntry0 = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
if(LdrDataTableEntry0) {
Next = LdrDataTableEntry0->LoadOrder.Blink;
while (TRUE) {
LdrDataTableEntry = CONTAINING_RECORD( Next,
LDR_DATA_TABLE_ENTRY,
LoadOrder
);
__try {
Next = Next->Blink;
} __except(EXCEPTION_EXECUTE_HANDLER) {
return STATUS_UNSUCCESSFUL;
}
if(!LdrDataTableEntry->ModuleName.Buffer) {
return STATUS_UNSUCCESSFUL;
}
__try {
if(RtlCompareUnicodeString(&LdrDataTableEntry->ModuleName, &g_NtoskrnlNameString, TRUE) == 0)
{
LoadOrderListHead = Next;
break;
}
} __except(EXCEPTION_EXECUTE_HANDLER) {
}
if(LdrDataTableEntry == LdrDataTableEntry0) {
return STATUS_UNSUCCESSFUL;
}
}
g_LoadOrderListHead = LoadOrderListHead;
} else {
// We are called from boot-driver, DriverSection is NULL :(
PVOID p;
// Locate NTOSKRNL.EXE
p = (PVOID)&NtBuildNumber;
g_hNtosKrnl = CrNtFindModuleBaseByPtr(p, "NtBuildNumber");
// Locate HAL.DLL
p = (PVOID)HalDisplayString;
p = CrNtSkipImportStub(p);
g_hHal = CrNtFindModuleBaseByPtr(p, "HalDisplayString");
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
if(!g_hNtosKrnl) {
g_hNtosKrnl = CrNtGetModuleBase("NTOSKRNL.EXE");
}
/*
Init pointers to required functions here.
Make them pointing either to native implementation or
to our own stub like this:
p = CrNtGetProcAddress(g_hNtosKrnl, "PsGetVersion");
CrNtPsGetVersion = p ? p : PsGetVersion_nt351_impl;
*/
g_CmCSDVersionString.Buffer = L"";
g_CmCSDVersionString.Length = 0;
g_CmCSDVersionString.MaximumLength = 0;
return STATUS_SUCCESS;
} // end CrNtInit()
Example 2
typedef struct _HW_INITIALIZATION_DATA_COMMON {
HW_INITIALIZATION_DATA comm;
HW_INITIALIZATION_DATA_2K w2k;
}HW_INITIALIZATION_DATA_COMMON, *PHW_INITIALIZATION_DATA_COMMON;
Example 3
// Set PnP-specific API
if(WinVer_Id() > WinVer_NT) {
hwInitializationData.w2k.HwAdapterControl = AtapiAdapterControl;
}
extern ULONG MajorVersion;
extern ULONG MinorVersion;
extern ULONG BuildNumber;
extern ULONG SPVersion;
#define WinVer_Is351 (MajorVersion==0x03)
#define WinVer_IsNT (MajorVersion==0x04)
#define WinVer_Is2k (MajorVersion==0x05 && MinorVersion==0x00)
#define WinVer_IsXP (MajorVersion==0x05 && MinorVersion==0x01)
#define WinVer_IsdNET (MajorVersion==0x05 && MinorVersion==0x02)
#define WinVer_Id() ((MajorVersion << 8) | MinorVersion)
#define WinVer_351 (0x0351)
#define WinVer_NT (0x0400)
#define WinVer_2k (0x0500)
#define WinVer_XP (0x0501)
#define WinVer_dNET (0x0502)
Example 4
p = CrNtGetProcAddress(g_hNtosKrnl, "PsGetVersion");
CrNtPsGetVersion = p ? p : PsGetVersion_nt351_impl;
if(WinVer_Id() > WinVer_NT) {
IoGetAttachedDeviceReference = (PIO_GET_ATTACHED_DEVICE_REFERENCE)
CrNtGetProcAddress(g_hNtosKrnl, "IoGetAttachedDeviceReference");
PoStartNextPowerIrp = (PPO_START_NEXT_POWER_IRP)
CrNtGetProcAddress(g_hNtosKrnl, "PoStartNextPowerIrp");
PoCallDriver = (PPO_CALL_DRIVER)CrNtGetProcAddress(g_hNtosKrnl, "PoCallDriver");
}
Код "новых" функций by DeathSoft
ULONG
inline
ObGetObjectPointerCount(
IN PVOID Object
)
{
POBJECT_HEADER Hdr=OBJECT_TO_OBJECT_HEADER(Object);
return Hdr->PointerCount;
} // end ObGetObjectPointerCount()
ULONG
inline
ObGetObjectHandleCount(
IN PVOID Object
)
{
POBJECT_HEADER Hdr=OBJECT_TO_OBJECT_HEADER(Object);
return Hdr->HandleCount;
} // end ObGetObjectHandleCount()
#define OBJECT_TO_OBJECT_HEADER(obj) \
CONTAINING_RECORD( (obj), OBJECT_HEADER, Body )
typedef struct _OBJECT_HEADER {
union {
struct {
LONG PointerCount;
LONG HandleCount;
};
LIST_ENTRY Entry;
};
POBJECT_TYPE Type;
UCHAR NameInfoOffset;
UCHAR HandleInfoOffset;
UCHAR QuotaInfoOffset;
UCHAR Flags;
union {
POBJECT_CREATE_INFORMATION ObjectCreateInfo;
PVOID QuotaBlockCharged;
};
PSECURITY_DESCRIPTOR SecurityDescriptor;
QUAD Body;
} OBJECT_HEADER, *POBJECT_HEADER;
#define OBJECT_HEADER_TO_NAME_INFO(ohdr) ((POBJECT_HEADER_NAME_INFO) \
((ohdr)->NameInfoOffset == 0 ? NULL : ((PCHAR)(ohdr) - (ohdr)->NameInfoOffset)))
BOOLEAN
__fastcall
KeTestSpinLock(
IN PKSPIN_LOCK SpinLock
)
{
if(!*SpinLock)
{
return TRUE;
}
__asm pause;
return FALSE;
} // end KeTestSpinLock()
__declspec (naked)
HANDLE
CrNtPsGetCurrentProcessId_impl()
{
_asm {
mov eax,fs:[124h]
mov eax,[eax+1e0h]
ret
}
}
__declspec (naked)
HANDLE
CrNtPsGetCurrentThreadId_impl()
{
_asm {
mov eax,fs:[124h]
mov eax,[eax+1e4h]
ret
}
}
См. также
|