为何需要进行形实转换

内核模式驱动程序必须验证从用户模式应用程序传入的任何 I/O 缓冲区的大小。 如果 32 位应用程序将包含指针精度数据类型的缓冲区传递给 64 位驱动程序,并且不发生砰砰声,则驱动程序预期缓冲区会大于实际大小。 这是因为指针精度在 32 位 Microsoft Windows 上为 32 位,在 64 位 Windows 上为 64 位。 例如,请考虑以下结构定义:

typedef struct _DRIVER_DATA
{
    HANDLE           Event;
    UNICODE_STRING   ObjectName;
} DRIVER_DATA;

在 32 位 Windows 上,DRIVER_DATA 结构的大小为 12 个字节。 下表显示了 DRIVER_DATA 结构的 Event 成员和 ObjectName 成员的大小:

事件 ObjectName (USHORT 长度) ObjectName (USHORT 最大长度) ObjectName (PWSTR 缓冲区)
32 位 16 位 16 位 32 位
(4 个字节) (2 个字节) (2 个字节) (4 个字节)

在 64 位 Windows 上,DRIVER_DATA结构的大小为 24 个字节。 (需要 4 个字节的结构填充,以便 缓冲区 成员可以在 8 字节边界上对齐。)

事件 ObjectName (USHORT 长度) ObjectName (USHORT 最大长度) 空 (结构填充) ObjectName (PWSTR 缓冲区)
64 位 16 位 16 位 32 位 64 位
(8 个字节) (2 个字节) (2 个字节) (4 个字节) (8 个字节)

如果 64 位驱动程序在预期 24 个字节时收到 12 个字节的DRIVER_DATA,则大小验证将失败。 若要防止这种情况,驱动程序必须检测DRIVER_DATA结构是否由 32 位应用程序发送,如果是,请在执行验证之前将其适当地发送。

例如,上述DRIVER_DATA结构的 thunked 版本可以定义如下:

typedef struct _DRIVER_DATA32
{
    VOID *POINTER_32   Event;
    UNICODE_STRING32   ObjectName;
} DRIVER_DATA32;

由于它仅包含固定精度数据类型,因此此新结构在 32 位 Windows 和 64 位 Windows 上的大小相同。

事件 ObjectName (USHORT 长度) ObjectName (USHORT 最大长度) ULONG 缓冲区
32 位 16 位 16 位 32 位
(4 个字节) (2 个字节) (2 个字节) (4 个字节)