NtQuerySystemInformation возвращает 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), В чем проблема?

3 ответа

Решение

NtQuerySystemInformation если SystemInformationLength слишком мала для ошибки возврата информации удержания STATUS_INFO_LENGTH_MISMATCH, (RtlNtStatusToDosError(STATUS_INFO_LENGTH_MISMATCH)==ERROR_BAD_LENGTH)

нужно понимать, что некоторые SystemInformationClass возвращают хорошо известные данные фиксированного размера. как пример SystemBasicInformation

поэтому вам нужно сделать следующее для этого класса информации фиксированного размера:

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

но некоторый информационный класс возвращает данные переменной длины. требуемая длина неизвестна в начале и STATUS_INFO_LENGTH_MISMATCH здесь абсолютная нормальная ошибка (не смертельная). Информационный класс переменной длины, который вы всегда должны запрашивать в цикле и проверять возвращаемый статус STATUS_INFO_LENGTH_MISMATCH в качестве условного продолжения цикла:

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

почему в петле? потому что требуемая длина может измениться после возврата системой размера буфера, необходимого для получения запрошенной информации, и до того, как вы позвоните ZwQuerySystemInformation снова с этим размером буфера.

яркий пример этого SystemProcessInformation который получил информацию обо всех процессах и потоках, запущенных в настоящее время в системе. после возврата системой необходимого размера буфера - в системе могут запускаться новые потоки или процессы - в результате может потребоваться больший буфер.

мы можем запросить эту информацию следующим образом:

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

Кажется, что нет проблем - ошибка должна ожидаться для звонков с нулем SystemInformationLength параметр.

MSDN говорит о NtQuerySystemInformation:

ReturnLength [out, необязательно] - 4-й параметр

Необязательный указатель на местоположение, где функция записывает фактический размер запрашиваемой информации. Если этот размер меньше или равен параметру SystemInformationLength, функция копирует информацию в буфер SystemInformation; в противном случае он возвращает код ошибки NTSTATUS и возвращает в ReturnLength размер буфера, необходимый для получения запрошенной информации.

Так что проверьте, если DWORD MemorySize действительно содержит ненулевой размер.

Просто удали

if (NT_SUCCESS(Status)) {

И заменить его на:

if(MemorySize){
Другие вопросы по тегам