Search code examples
cwinapint

NtQuerySystemInformation returns 24 (ERROR_BAD_LENGTH)


Here is my function:

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

I pass SystemBasicInformation to the function. After the first call to NtQuerySystemInformation, I get an error. The result of RtlNtStatusToDosError(Status) is 24 (ERROR_BAD_LENGTH). Where is the problem?


Solution

  • NtQuerySystemInformation if SystemInformationLength too small for hold info return error STATUS_INFO_LENGTH_MISMATCH. (RtlNtStatusToDosError(STATUS_INFO_LENGTH_MISMATCH)==ERROR_BAD_LENGTH)

    need understand that some SystemInformationClass return well known fixed size data. as example SystemBasicInformation

    so you need do next for this fixed size Information Class :

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

    but some Information Class return variable length data. required length is unknown at begin and STATUS_INFO_LENGTH_MISMATCH here absolute normal error (not fatal). the variable length Information Class you need always query in loop and check returned status for STATUS_INFO_LENGTH_MISMATCH as conditional continue loop:

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

    why in loop ? because the required length can change after system return to you size of buffer required to receive the requested information and before you call ZwQuerySystemInformation again with this buffer size.

    the bright example of this SystemProcessInformation which got information about all processes and threads running currently in system. after system return to you size of buffer required - new threads or processes can start in system - as result larger buffer can be need.

    we can query this info in next way:

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

    or alternatively we can use cumulative allocation in stack (this is only for user mode, where we have huge stack size)

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