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