Как получить размер буфера, требуемый NtQueryInformationProcess? - PullRequest
1 голос
/ 10 марта 2019

Параметр ReturnLength в NtQueryInformationProcess задокументирован следующим образом:

ReturnLength

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

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

Я хотел, чтобы функция возвращала размер буфера, необходимый для определенного информационного класса.

Я попробовал следующее:

 // for static linking

function NtQueryInformationProcess(ProcessHandle        : THANDLE;
                               ProcessInformationClass  : DWORD;
                               ProcessInformation       : PPEB;
                               ProcessInformationLength : DWORD;
                               ReturnLength             : PDWORD)
     : NTSTATUS; stdcall; external ntdll;

 // for dynamic linking

 type
   TNtQueryInformationProcess
     = function (ProcessHandle       : THANDLE;
            ProcessInformationClass  : DWORD;
            ProcessInformation       : PPEB;
            ProcessInformationLength : DWORD;
            ReturnLength             : PDWORD) : NTSTATUS; stdcall;

var
  ProcessHandle : THANDLE;

  Peb           : TPEB;
  BufferSize    : DWORD;
  ReturnLength  : DWORD;
  NtResult      : NTSTATUS;
  NtdllHandle   : HMODULE;
  NtQueryInformationProcessPtr : TNtQueryInformationProcess;

begin
 ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS,
                              FALSE,
                              GetCurrentProcessId());

 ZeroMemory(@Peb, sizeof(Peb));
 BufferSize   := sizeof(PROCESS_BASIC_INFORMATION);
 ReturnLength := 0;

 NtResult := NtQueryInformationProcess(ProcessHandle,
                                       ProcessBasicInformation,
                                    @Peb,
                                    BufferSize,
                                   @ReturnLength);

 writeln('NTSTATUS     : ', IntToHex(NtResult, 0));
 writeln('ReturnLength : ', ReturnLength);

 // try calling the function by address

 NtdllHandle := LoadLibrary('ntdll.dll');

 pointer(NtQueryInformationProcessPtr) := GetProcAddress(NtdllHandle,
                                             'NtQueryInformationProcess');

 // reinitialize just to be safe

 ZeroMemory(@Peb, sizeof(Peb));
 BufferSize   := sizeof(PROCESS_BASIC_INFORMATION);
 ReturnLength := 0;

 NtResult := NtQueryInformationProcessPtr(ProcessHandle,
                                          ProcessBasicInformation,
                                       @Peb,
                                       BufferSize,
                                       @ReturnLength);

 writeln('NTSTATUS     : ', IntToHex(NtResult, 0));
 writeln('ReturnLength : ', ReturnLength);

 writeln('program end.');
 readln;
end.

когда я устанавливаю размер буфера на правильный размер для ProcessBasicInformation, все работает как положено, но, если я устанавливаю BufferSize на ноль (например), желая, чтобы функция возвращала необходимый размер буфера в переменной ReturnLength, я получаю возврат 0xC0000004 (несоответствие размера), как и ожидалось, НО переменная ReturnLength не установлена ​​на размер, необходимый для успешного получения ProcessBasicInformation.

Учитывая описание параметра ReturnLength, я ожидал, что API установит ReturnLength на любой размер, требуемый запрошенным классом информации.

Мой вопрос: есть ли ошибка в коде или API не работает как задокументировано? или я неправильно понимаю описание параметра ReturnLength?

Спасибо за вашу помощь.

1 Ответ

3 голосов
/ 10 марта 2019

Многие Win32 API предназначены для того, чтобы сообщать вам правильный размер буфера, вызывая его с нулевым указателем и задавая 0 для ReturnLength. Nt API обычно работают немного по-другому:

Вы просто передаете ему буфер произвольного размера, и он сообщит вам, если он хочет большего, возвращая STATUS_INFO_LENGTH_MISMATCH.

Я также рекомендую использовать библиотеку Jedi Api вместо того, чтобы переводить функции / заголовки (Nt Api) самостоятельно.

EDIT : Похоже, вы пытаетесь прочитать класс ProcessBasicInformation, который имеет фиксированный размер и не возвращает указатель на PEB, но указатель на PROCESS_BASIC_INFORMATION struct / record.

Вот пример того, что вы пытаетесь сделать:

// uses JwaNative

function TDebugThread.ReadPEB: Boolean;
var
  nts: NTSTATUS;
  pbi: PROCESS_BASIC_INFORMATION;
  dwBytes: DWORD;
begin
  Result := False;
  nts := NtQueryInformationProcess(pi.hProcess,
    ProcessBasicInformation, @pbi, SizeOf(pbi), @dwBytes);

  if nts <> STATUS_SUCCESS then
    Exit;

  New(PEB);
  Result := ReadProcessMemory(pi.hProcess, pbi.PebBaseAddress, PEB, SizeOf(PEB^),
    @dwBytes);

end;

Ниже приведен пример использования NtQuerySystemInformation (полный пример можно найти в моем GitHub хранилище ).

{ TProcessList }
constructor TProcessList.Create(const AOwnsObjects: Boolean = True);
var
  Current: PSystemProcesses;
  SystemProcesses : PSystemProcesses;
  dwSize: DWORD;
  nts: NTSTATUS;
begin
  inherited Create(AOwnsObjects);

  dwSize := 200000;
  SystemProcesses := AllocMem(dwSize);

  nts := NtQuerySystemInformation(SystemProcessesAndThreadsInformation,
      SystemProcesses, dwSize, @dwSize);

  while nts = STATUS_INFO_LENGTH_MISMATCH do
  begin
    ReAllocMem(SystemProcesses, dwSize);
    nts := NtQuerySystemInformation(SystemProcessesAndThreadsInformation,
      SystemProcesses, dwSize, @dwSize);
  end;

  if nts = STATUS_SUCCESS then
  begin
    Current := SystemProcesses;
    while True do
    begin
      Self.Add(TProcess.Create(Current^));
      if Current^.NextEntryDelta = 0 then
        Break;

      Current := PSYSTEM_PROCESSES(DWORD_PTR(Current) + Current^.NextEntryDelta);
    end;
  end;

  FreeMem(SystemProcesses);
end;

Редактировать : чтобы ответить на комментарий @ScienceAmateur, следующий тестовый код всегда возвращает 0 для ReturnLength:

uses
  Windows,
  System.SysUtils,
  Rtti,
  JwaNative,
  JwaWinType,
  JwaNtSecApi,
  JwaNtStatus;


function NtStatusErrorMessage(const nts: NTSTATUS): String;
begin
  Result := SysErrorMessage(LsaNtStatusToWinError(nts));
end;

var
  nts: NTSTATUS;
  pic: PROCESS_INFORMATION_CLASS;
  Buffer: Pointer;
  pil: DWORD;
  ReturnLength: DWORD;
begin
  for pic := Low(PROCESS_INFORMATION_CLASS) to High(PROCESS_INFORMATION_CLASS) do
  begin
    Buffer := nil;
    pil := 0;
    ReturnLength := 0;
    nts := NtQueryInformationProcess(GetCurrentProcess,
      ProcessBasicInformation, Buffer, pil, @ReturnLength);
    WriteLn(Format('%s: returned 0x%.8x and ReturnLength: %d', [TRttiEnumerationType.GetName(pic), nts, ReturnLength]));
  end;

  WriteLn('Finished.');

  if DebugHook <> 0 then
    ReadLn;

Выход:

ProcessBasicInformation: returned 0xC0000004 and ReturnLength: 0
ProcessQuotaLimits: returned 0xC0000004 and ReturnLength: 0
ProcessIoCounters: returned 0xC0000004 and ReturnLength: 0
ProcessVmCounters: returned 0xC0000004 and ReturnLength: 0
ProcessTimes: returned 0xC0000004 and ReturnLength: 0
ProcessBasePriority: returned 0xC0000004 and ReturnLength: 0
ProcessRaisePriority: returned 0xC0000004 and ReturnLength: 0
ProcessDebugPort: returned 0xC0000004 and ReturnLength: 0
ProcessExceptionPort: returned 0xC0000004 and ReturnLength: 0
ProcessAccessToken: returned 0xC0000004 and ReturnLength: 0
ProcessLdtInformation: returned 0xC0000004 and ReturnLength: 0
ProcessLdtSize: returned 0xC0000004 and ReturnLength: 0
ProcessDefaultHardErrorMode: returned 0xC0000004 and ReturnLength: 0
ProcessIoPortHandlers: returned 0xC0000004 and ReturnLength: 0
ProcessPooledUsageAndLimits: returned 0xC0000004 and ReturnLength: 0
ProcessWorkingSetWatch: returned 0xC0000004 and ReturnLength: 0
ProcessUserModeIOPL: returned 0xC0000004 and ReturnLength: 0
ProcessEnableAlignmentFaultFixup: returned 0xC0000004 and ReturnLength: 0
ProcessPriorityClass: returned 0xC0000004 and ReturnLength: 0
ProcessWx86Information: returned 0xC0000004 and ReturnLength: 0
ProcessHandleCount: returned 0xC0000004 and ReturnLength: 0
ProcessAffinityMask: returned 0xC0000004 and ReturnLength: 0
ProcessPriorityBoost: returned 0xC0000004 and ReturnLength: 0
ProcessDeviceMap: returned 0xC0000004 and ReturnLength: 0
ProcessSessionInformation: returned 0xC0000004 and ReturnLength: 0
ProcessForegroundInformation: returned 0xC0000004 and ReturnLength: 0
ProcessWow64Information: returned 0xC0000004 and ReturnLength: 0
ProcessImageFileName: returned 0xC0000004 and ReturnLength: 0
ProcessLUIDDeviceMapsEnabled: returned 0xC0000004 and ReturnLength: 0
ProcessBreakOnTermination: returned 0xC0000004 and ReturnLength: 0
ProcessDebugObjectHandle: returned 0xC0000004 and ReturnLength: 0
ProcessDebugFlags: returned 0xC0000004 and ReturnLength: 0
ProcessHandleTracing: returned 0xC0000004 and ReturnLength: 0
MaxProcessInfoClass: returned 0xC0000004 and ReturnLength: 0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...