Alter.Org.UA
 << Back Home EN en   Donate Donate

Как написать драйвер, работающий во всех 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
    }
}

См. также

<< Back Автор: Alter (Александр А. Телятников) Сервер: Apache+PHP под FBSD © 2002-2024