delphihandles

About SystemHandleInformation on 64 bits application


I need know how enumarate handles on 64 bits applicatio, i made it on32 bits and works perfectly, but the same code compiled as 64 bits only show some handles. i already changed variables to longword for example but without success. i read about SystemHandleInformation on x64 should be another value instead $10 (16 dec) but tried without success.

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Windows,
  Classes,
  PsApi;

const
  SystemHandleInformation       = $10;
  STATUS_SUCCESS                = $00000000;
  STATUS_BUFFER_OVERFLOW        = $80000005;
  STATUS_INFO_LENGTH_MISMATCH   = $C0000004;
  //
  type
  NTSTATUS = Cardinal;
  OBJECT_INFORMATION_CLASS = (ObjectBasicInformation, ObjectNameInformation,
                              ObjectTypeInformation, ObjectAllTypesInformation, ObjectHandleInformation);
  //
  SYSTEM_HANDLE = packed record
    UniqueProcessId       : USHORT;
    CreatorBackTraceIndex : USHORT;
    ObjectTypeIndex       : UCHAR;
    HandleAttributes      : UCHAR;
    HandleValue           : USHORT;
    HObject               : PVOID;
    GrantedAccess         : ULONG;
  end;
  PSYSTEM_HANDLE       = ^SYSTEM_HANDLE;
  SYSTEM_HANDLE_ARRAY  = Array[0..0] of SYSTEM_HANDLE;
  PSYSTEM_HANDLE_ARRAY = ^SYSTEM_HANDLE_ARRAY;
  //
  SYSTEM_HANDLE_INFORMATION = packed record
    uCount   : ULONG;
    Handles  : SYSTEM_HANDLE_ARRAY;
  end;
  PSYSTEM_HANDLE_INFORMATION = ^SYSTEM_HANDLE_INFORMATION;
  //
  TNtQuerySystemInformation  = function (SystemInformationClass:DWORD; SystemInformation:pointer; SystemInformationLength:DWORD;  ReturnLength:PDWORD):THandle; stdcall;
  TNtQueryObject             = function (ObjectHandle:cardinal; ObjectInformationClass:OBJECT_INFORMATION_CLASS; ObjectInformation:pointer; Length:ULONG;ResultLength:PDWORD):THandle;stdcall;

  var
  NTQueryObject             : TNtQueryObject;
  NTQuerySystemInformation  : TNtQuerySystemInformation;

Procedure EnumerateOpenFiles();
const
  HANDLE_BUFFER_INCREASE_CHUNK = 5000 * 1024;
var
  sDummy      : string;
  hProcess    : THandle;
  hObject     : THandle;
  ResultLength: DWORD;
  aBufferSize : DWORD;
  aIndex      : LONG;//Integer;
  pHandleInfo : PSYSTEM_HANDLE_INFORMATION;
  HDummy      : THandle;

  lpszProcess : PWideChar;
begin
  AbufferSize      := HANDLE_BUFFER_INCREASE_CHUNK;
  pHandleInfo      := AllocMem(AbufferSize);
  HDummy           := NTQuerySystemInformation(DWORD(SystemHandleInformation), pHandleInfo, AbufferSize, @ResultLength);  //Get the list of handles

  if(HDummy = STATUS_SUCCESS) then
  begin
    for aIndex:=0 to pHandleInfo^.uCount-1 do
    begin
      hProcess := OpenProcess(PROCESS_DUP_HANDLE or PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, FALSE, pHandleInfo.Handles[aIndex].UniqueProcessId);  //open the process to get aditional info
      if(hProcess <> INVALID_HANDLE_VALUE) then
      begin
        hObject := 0;

        if DuplicateHandle(hProcess, pHandleInfo.Handles[aIndex].HandleValue, GetCurrentProcess(), @hObject, STANDARD_RIGHTS_REQUIRED, FALSE, 0) then  //Get  a copy of the original handle
        begin
          lpszProcess := AllocMem(MAX_PATH);
          if GetModuleFileNameEx(hProcess, 0,lpszProcess, MAX_PATH) <> 0 then
            sDummy:=lpszProcess
          else
            sDummy:= 'System Process';

            WriteLn(Format('PID [%d] Process [%s]',  [pHandleInfo.Handles[aIndex].UniqueProcessId, sDummy]));

          FreeMem(lpszProcess);
          CloseHandle(hObject);
        end;

        CloseHandle(hProcess);
      end;
    end;
  end;

  WriteLn('Finish');
  FreeMem(pHandleInfo);
end;

begin
  NTQueryObject            := GetProcAddress(GetModuleHandle('NTDLL.DLL'), 'NtQueryObject');
  NTQuerySystemInformation := GetProcAddress(GetModuleHandle('NTDLL.DLL'), 'NtQuerySystemInformation');
  if (@NTQuerySystemInformation <> nil) and (@NTQuerySystemInformation <> nil) then EnumerateOpenFiles() else WriteLn('falhou no inicio');
  ReadLn;
end.

That works perfectly on x86 application, but when i change to x64 he don't show the same results as x86, anyone know why?


Solution

  • Local variable names and two unremoved comments suggest that this is a variation on code posted by RRUZ at 2009 here. At that time there was no 64 bit Delphi version so it was not possible for him to test the code on 64 bits. Anyway, I was able to test this with XE2 on W7x64 using "jwanative.pas" for the missing NtQuerySystemInformation from your sample. You also have one end too many, you need to remove the end that comes before FreeMem(lpszProcess);. Otherwise the code will not compile - probably a copy/paste error on your part.

    The error is mis-packing the SYSTEM_HANDLE and SYSTEM_HANDLE_INFORMATION records, their layouts are messed up for 64 bit when packed. This page by Geoff Chappell (have to acknowledge according to the site's terms) suggests that

    The SYSTEM_HANDLE_INFORMATION is 0x14 and 0x20 bytes in 32-bit and 64-bit Windows, respectively.

    Unpack it to have 32 bytes in x64 instead of 28 while packed.

    Similarly, this page suggests:

    The SYSTEM_HANDLE_TABLE_ENTRY_INFO structure is 0x10 or 0x18 bytes in 32-bit and 64-bit Windows, respectively.

    Unpack your record and it will be 24 bytes on x64 instead of 20 while packed. Although the members slightly differ, you'll be able to see it runs about the same as on x32.


    Note that the code may or may not run on later/future versions of OS. Microsoft not only does not fully document system information retrieval but also warn that

    The NtQuerySystemInformation function and the structures that it returns are internal to the operating system and subject to change from one release of Windows to another.