Escritura de controladores para diferentes versiones de Windows

Al crear un proyecto de controlador, se especifica el sistema operativo de destino mínimo, que es la versión mínima de Windows en la que se ejecutará el controlador. Por ejemplo, podría especificar que Windows 7 es el sistema operativo de destino mínimo. En ese caso, el controlador se ejecutaría en Windows 7 y versiones posteriores de Windows.

Nota:

Si desarrolla un controlador para una versión mínima determinada de Windows y desea que el controlador funcione en versiones posteriores de Windows, no debe usar ninguna función no documentada y no debe usar funciones documentadas de ninguna manera distinta de cómo se describe en la documentación. De lo contrario, es posible que el controlador no se ejecute en las versiones posteriores de Windows. Aunque tenga cuidado de usar solo las funciones documentadas, debe probar el controlador en la nueva versión de Windows cada vez que se publique una.

Escritura de un controlador de varias versiones con solo características comunes

Al diseñar un controlador que se ejecutará en varias versiones de Windows, el enfoque más sencillo es permitir que el controlador use solo las funciones y estructuras DDI que son comunes a todas las versiones de Windows en las que se ejecutará el controlador. En esta situación, establece el sistema operativo de destino mínimo en la versión más antigua de Windows que admitirá el controlador.

Por ejemplo, para admitir todas las versiones de Windows, a partir de Windows 7, debe:

  1. Diseñe e implemente el controlador para que use solo las características que están presentes en Windows 7.

  2. Al compilar el controlador, especifique Windows 7 como sistema operativo de destino mínimo.

Aunque este proceso es sencillo, puede restringir el controlador para que use solo un subconjunto de la funcionalidad que está disponible en versiones posteriores de Windows. En muchos casos, querrá usar la funcionalidad más reciente del sistema operativo cuando esté disponible para mejorar la seguridad, mejorar la confiabilidad o habilitar características más recientes.

Escritura de un controlador de varias versiones que usa características dependientes de la versión

Un controlador en modo kernel puede determinar dinámicamente si una API proporcionada por el sistema operativo está disponible o en qué versión de Windows se ejecuta el controlador y decide usar características que están disponibles en ese entorno en tiempo de ejecución. Por ejemplo, un controlador que debe admitir todas las versiones de Windows, a partir de Windows 7, puede determinar, en tiempo de ejecución, la versión de Windows en la que se ejecuta. Si el controlador se ejecuta en Windows 7, solo debe usar las funciones DDI que admite Windows 7. Sin embargo, el mismo controlador puede usar funciones DDI adicionales exclusivas de Windows 8, por ejemplo, cuando su comprobación en tiempo de ejecución determina que esas API están presentes actualmente o determina que se ejecuta en Windows 8.

Nota:

Se recomienda comprobar la disponibilidad de características o API siempre que sea posible en lugar de intentar comprobar si el controlador se está ejecutando en una versión de sistema operativo determinada o posterior.

Llamar condicionalmente a funciones dependientes de la versión de Windows

Un controlador en modo kernel puede usar las funciones MmGetSystemRoutineAddress o MmGetSystemRoutineAddressEx para comprobar dinámicamente si una API determinada que quiere usar está disponible en el entorno en tiempo de ejecución actual y para obtener un puntero de función que se usará para llamar a esa API.

Nota:

Para ayudar a conservar la comprobación de tipos y evitar errores accidentales, debe crear una definición de tipo que refleje el tipo de función original.

Ejemplo: Determinar la disponibilidad de la API y llamar condicionalmente a la API

typedef
NTSTATUS
(*PFN_IoOpenDriverRegistryKey)(
    PDRIVER_OBJECT     DriverObject,
    DRIVER_REGKEY_TYPE RegKeyType,
    ACCESS_MASK        DesiredAccess,
    ULONG              Flags,
    PHANDLE            DriverRegKey
    );

VOID ExampleFunction(VOID) {
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    HANDLE persistentStateKey = NULL;
    PFN_IoOpenDriverRegistryKey pfnIoOpenDriverRegistryKey = NULL;
    UNICODE_STRING functionName = {0};

    RtlInitUnicodeString(&functionName, L"IoOpenDriverRegistryKey");
    pfnIoOpenDriverRegistryKey = (PFN_IoOpenDriverRegistryKey)MmGetSystemRoutineAddress(&functionName);

    if (pfnIoOpenDriverRegistryKey != NULL) {
        // Open a key to where state can be stored under the driver service
        status = pfnIoOpenDriverRegistryKey(g_GlobalStructure.DriverObject,
                                            DriverRegKeyPersistentState,
                                            KEY_WRITE,
                                            0,
                                            &persistentStateKey);
    } else {
        // Fall back to opening up a different location to store state in
    }

    // Use the opened registry key
}

Determinación de la versión de Windows

Un controlador en modo kernel puede usar la función RtlVerifyVersionInfo para comprobar dinámicamente en qué versión de Windows se está ejecutando actualmente.

Nota:

Se recomienda comprobar la disponibilidad de características o API siempre que sea posible en lugar de intentar comprobar si el controlador se está ejecutando en una versión de sistema operativo determinada o posterior.

Ejemplo: Determinar la versión de Windows

En el ejemplo siguiente se detecta si la versión del sistema operativo actualmente en ejecución es mayor o igual que la versión 10.0 y detecta si el número de compilación es mayor o igual que la compilación 22000 (Windows 11, versión 21H2).

...

NTSTATUS Status = STATUS_SUCCESS;
RTL_OSVERSIONINFOEXW VersionInfo = {0};
ULONG TypeMask = 0;
ULONGLONG ConditionMask = 0;

VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);
VersionInfo.dwMajorVersion = 10;
VersionInfo.dwMinorVersion = 0;
VersionInfo.dwBuildNumber = 22000;

TypeMask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER;
VER_SET_CONDITION(ConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
VER_SET_CONDITION(ConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
VER_SET_CONDITION(ConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);

Status = RtlVerifyVersionInfo(&VersionInfo,
                              TypeMask,
                              ConditionMask);

if (NT_SUCCESS(Status)) {

    //
    // The call to RtlVerifyVersionInfo succeeded, so the running OS
    // version and build number is greater than or equal to the value
    // specified. Do appropriate action for newer OS versions.
    //

} else if (Status == STATUS_REVISION_MISMATCH) {

    //
    // The running OS version is less than the value specified. Do
    // appropriate action for older OS versions.
    //

} else {

    //
    // There was an error comparing to the running OS version. Do
    // appropriate action for when the OS version is not known.
    //

}
...