NtQuerySystemInformation returns 24 (ERROR_BAD_LENGTH)

2.7k views Asked by At

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?

3

There are 3 answers

0
RbMm On BEST ANSWER

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;
}
1
bukkojot On

Just remove

if (NT_SUCCESS(Status)) {

And replace it with:

if(MemorySize){
4
zx485 On

It seems that there is no problem - the error is to be expected for calls with a zero SystemInformationLength parameter.

MSDN says about 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.

So check if DWORD MemorySize does contain a non-zero size.