为何需要进行形实转换
内核模式驱动程序必须验证从用户模式应用程序传入的任何 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 个字节) |