NtQuerySystemInformation返回24(ERROR_BAD_LENGTH)

问题描述:

这里是我的功能: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)。哪里有问题?

+1

请不要破坏你自己的帖子。 – EJoshuaS

+0

更糟糕的是,你似乎得到了多个有用的答案。 – EJoshuaS

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

只是删除

if (NT_SUCCESS(Status)) { 

,取而代之的是:

if(MemorySize){ 
+0

这是错误。所有有条件的'if'必须仅基于返回状态 – RbMm

似乎是没有问题的 - 错误是可以预期以零SystemInformationLength参数调用。

MSDN说,大约NtQuerySystemInformation

ReturnLength [出,可选] - 第四个参数

一个可选的指针,它指向函数写请求的信息的实际大小的位置。如果该大小小于或等于SystemInformationLength参数,则该函数将信息复制到SystemInformation缓冲区; 否则,它将返回一个NTSTATUS错误代码,并在ReturnLength中返回接收请求信息所需的缓冲区大小。

因此检查DWORD MemorySize确实包含非零大小。

+0

Oooh。好,非常感谢你!对于不注意文档,我太傻了! – johnsonlee

+0

现在在同一个函数中,我得到'0xC0000005:访问冲突读取位置0x4865764A'在对Allocate()的调用之后,有什么明显的陈述这个背后的推理? – johnsonlee

+0

我只能猜测,因为我不知道你正在使用什么分配器以及错误发生在哪里。你不能使用调试器吗? – zx485