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){