NtQuerySystemInformation returns 24 (ERROR_BAD_LENGTH)

NtQuerySystemInformation returns 24 (ERROR_BAD_LENGTH)

这是我的函数:

PVOID QuerySystemInformation(SYSTEMINFOCLASS SystemEnum) {
        DWORD MemorySize = NULL;
        NTSTATUS Status = NtQuerySystemInformation(SystemEnum, NULL, 0, &MemorySize);
        if (NT_SUCCESS(Status)) {
            PVOID Memory = PVOID(Allocate(MemorySize));
            if (Memory != ERROR) {
                Status = NtQuerySystemInformation(SystemEnum, Memory, MemorySize, &MemorySize);
                if (NT_SUCCESS(Status)) {
                    return Memory;
                }
                Free(Memory);
            }
        }
        return ERROR;
    }

我将 SystemBasicInformation 传递给函数。第一次调用 NtQuerySystemInformation 后,出现错误。 RtlNtStatusToDosError(Status) 的结果是 24 (ERROR_BAD_LENGTH)。问题出在哪里?

只需删除

if (NT_SUCCESS(Status)) {

并将其替换为:

if(MemorySize){

似乎没有问题 - 对于 SystemInformationLength 参数为零的调用,错误是正常的。

MSDN 说 NtQuerySystemInformation:

ReturnLength [out, optional] - 4th parameter

An optional pointer to a location where the function writes the actual size of the information requested. If that size is less than or equal to the SystemInformationLength parameter, the function copies the information into the SystemInformation buffer; otherwise, it returns an NTSTATUS error code and returns in ReturnLength the size of buffer required to receive the requested information.

因此请检查 DWORD MemorySize 是否包含非零大小。

NtQuerySystemInformation 如果 SystemInformationLength 对于保持信息来说太小 return 错误 STATUS_INFO_LENGTH_MISMATCH。 (RtlNtStatusToDosError(STATUS_INFO_LENGTH_MISMATCH)==ERROR_BAD_LENGTH)

需要了解一些 SystemInformationClass return 众所周知的 fixed 大小数据。例如 SystemBasicInformation

所以你需要为此做下一步 固定 尺寸信息 Class :

SYSTEM_BASIC_INFORMATION sbi;
NTSTATUS status = ZwQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), 0);
if (0 <= status)
{
    // do something
}

但一些信息 Class return 可变长度数据。要求的长度在开始时是未知的,STATUS_INFO_LENGTH_MISMATCH 这里是绝对正常的错误(不是致命的)。可变长度信息 Class 您需要始终查询 in loop 并检查 returned status for STATUS_INFO_LENGTH_MISMATCH as conditional continue loop:

do 
{
    ...
    status = ZwQuerySystemInformation(...);
    ...
} while (status == STATUS_INFO_LENGTH_MISMATCH);

为什么在循环中?因为所需的长度可以在系统 return 之后更改为您接收请求信息所需的缓冲区大小,并且在您使用此缓冲区大小再次调用 ZwQuerySystemInformation 之前。

这个 SystemProcessInformation 的杰出示例,它获取了有关系统中当前 运行 所有进程和线程的信息。在系统 return 之后,您需要的缓冲区大小 - 新线程或进程可以在系统中启动 - 因此可能需要更大的缓冲区。

我们可以通过以下方式查询此信息:

NTSTATUS QueryProcessInformation()
{
    NTSTATUS status;

    ULONG cb = 0x10000;

    do 
    {
        status = STATUS_INSUFFICIENT_RESOURCES;

        if (void* buf = new BYTE[cb])
        {
            if (0 <= (status = ZwQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
            {
                union {
                    PVOID pv;
                    PBYTE pb;
                    PSYSTEM_PROCESS_INFORMATION pspi;
                };

                pv = buf;

                ULONG NextEntryOffset = 0;

                do 
                {
                    pb += NextEntryOffset;

                    DbgPrint("%p %wZ\n", pspi->UniqueProcessId, &pspi->ImageName);

                } while (NextEntryOffset = pspi->NextEntryOffset);
            }

            delete [] buf;
        }

    } while (status == STATUS_INFO_LENGTH_MISMATCH);

    return status;
}

或者我们可以在堆栈中使用累积分配(这仅适用于我们拥有巨大堆栈大小的用户模式)

NTSTATUS QueryProcessInformation2()
{
    NTSTATUS status;

    union {
        PVOID buf;
        PBYTE pb;
        PSYSTEM_PROCESS_INFORMATION pspi;
    };

    ULONG cb = 0, rcb = 0x10000;

    volatile UCHAR guz;

    PVOID stack = alloca(guz);

    do 
    {
        if (cb < rcb)
        {
            cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
        }

        if (0 <= (status = ZwQuerySystemInformation(SystemProcessInformation, buf, cb, &rcb)))
        {
            ULONG NextEntryOffset = 0;

            do 
            {
                pb += NextEntryOffset;

                DbgPrint("%p %wZ\n", pspi->UniqueProcessId, &pspi->ImageName);

            } while (NextEntryOffset = pspi->NextEntryOffset);
        }

    } while (status == STATUS_INFO_LENGTH_MISMATCH);

    return status;
}