Alter.Org.UA
 << Back Home UK uk   Donate Donate

Creating NT OS-version independent drivers

Why ?

Because this improves popularity and make product support easier. I personally like programms those can be installed and work anywhere. And vice versa - I hate those which require some specific ServicePack, have separate setup for each OS, etc. And, inspite of widespread opinion, it is not too difficult to make universal driver.

How ?

  • Determine where we are. (example 1)
  • Using knowledge about OS version make
    • tricky unions for structures those definitions are changed across OS versions. (example 2)
    • several if/else branches for version-specific parts of code, e.g. Legacy vs WDM style. (example 3)
  • If you can - do not use "new" API functions
  • If you cannot or if you really want it -

Examples

Some parts of the code is taken from real projects, e.g. from DbgPrint Dump. See Cross-Nt library which was a part of SDK, and now it is available standalone. Some other part of code is gained by DeathSoft from newest Windows. There is an idea to put all these to static and dynamic libraries, it is partially implemented in Cross-Nt library.

Example 1

Checking OS version in DriverEntry() and getting ready to use GetModuleHandle()/GetProcAddress(). If you need not NT 3.51 support, if is more safe to use NtQuerySystemInformation() for locating modules in memory. This is because scanning of LDR_DATA_TABLE_ENTRY chain when some module is being unloaded may cause sustem crash.

    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");
    }

Implementations for "old" OSes
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
    }
}

See also:

<< Back designed by Alter aka Alexander A. Telyatnikov powered by Apache+PHP under FBSD © 2002-2024