32/64位ReadProcessMemory问题

时间:2015-09-23 17:55:45

标签: delphi x86 64-bit readprocessmemory

我总是在x86机器上使用这个代码没有问题:

PIDHandle:= OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_OPERATION or
                                          PROCESS_VM_READ, false, Struct.th32ProcessID);
if (PIDHandle <> 0)
 ScanMemory(PIDHandle, Struct.szExeFile);

procedure TForm1.ScanMemory(PIDHandle: THandle; const ProcessName: string);
var
  MemStart, ReceivedBytes: SIZE_T;
  MemInfo: MEMORY_BASIC_INFORMATION;
begin
  MemStart:= 0;
  while (VirtualQueryEx(PIDHandle, Pointer(MemStart), MemInfo, SizeOf(MemInfo)) <> 0) do
  begin
    if ((MemInfo.State = MEM_COMMIT) and (not (MemInfo.Protect = PAGE_GUARD)
    or (MemInfo.Protect = PAGE_NOACCESS)) and (MemInfo.Protect = PAGE_READWRITE)) then
    begin
      SetLength(Buff, MemInfo.RegionSize);
      if (ReadProcessMemory(PIDHandle, MemInfo.BaseAddress, Buff,
          MemInfo.RegionSize, ReceivedBytes)) then
        begin
          //do particular stuff with memory
        end; //if readprocessmemory
    end; //if mempages
    MemStart:= MemStart + MemInfo.RegionSize;
  end; 
end; 

现在我编译了一个x64二进制文件,它在一些随机进程中停止响应,我不确定它们是否是x86 / x64 ...

从x64 bin运行ReadProcessMemory到x86进程是否有任何已知问题? 是否有必要保持x86 bin读取其他x86进程的内存和x64 bin以读取x64进程?或者有任何解决方法?

1 个答案:

答案 0 :(得分:1)

问题不在于ReadProcessMemory(),而在于VirtualQueryEx()

MEMORY_BASIC_INFORMATION文档说:

  

要使调试器能够调试在不同体系结构(32位与64位)上运行的目标,请使用此结构的一种显式形式。

     
typedef struct _MEMORY_BASIC_INFORMATION32 {
    DWORD BaseAddress;
    DWORD AllocationBase;
    DWORD AllocationProtect;
    DWORD RegionSize;
    DWORD State;
    DWORD Protect;
    DWORD Type;
} MEMORY_BASIC_INFORMATION32, *PMEMORY_BASIC_INFORMATION32;

typedef struct DECLSPEC_ALIGN(16) _MEMORY_BASIC_INFORMATION64 {
    ULONGLONG BaseAddress;
    ULONGLONG AllocationBase;
    DWORD     AllocationProtect;
    DWORD     __alignment1;
    ULONGLONG RegionSize;
    DWORD     State;
    DWORD     Protect;
    DWORD     Type;
    DWORD     __alignment2;
} MEMORY_BASIC_INFORMATION64, *PMEMORY_BASIC_INFORMATION64;

因此,在查询32位进程时使用MEMORY_BASIC_INFORMATION32,在查询64位进程时使用MEMORY_BASIC_INFORMATION64。但是,Delphi不会声明这些记录类型,因此您必须在代码中手动执行此操作。

Delphi在MEMORY_BASIC_INFORMATION单元中的Windows声明是以旧的32位版本为模型的:

typedef struct _MEMORY_BASIC_INFORMATION {
  PVOID  BaseAddress;
  PVOID  AllocationBase;
  DWORD  AllocationProtect;
  SIZE_T RegionSize;
  DWORD  State;
  DWORD  Protect;
  DWORD  Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
PMemoryBasicInformation = ^TMemoryBasicInformation;
_MEMORY_BASIC_INFORMATION = record
  BaseAddress : Pointer;
  AllocationBase : Pointer;
  AllocationProtect : DWORD;
  RegionSize : SIZE_T;
  State : DWORD;
  Protect : DWORD;
  Type_9 : DWORD;
end;
{$EXTERNALSYM _MEMORY_BASIC_INFORMATION}
TMemoryBasicInformation = _MEMORY_BASIC_INFORMATION;
MEMORY_BASIC_INFORMATION = _MEMORY_BASIC_INFORMATION;
{$EXTERNALSYM MEMORY_BASIC_INFORMATION}

这仅适用于32位调用进程,而不是在64位调用进程中使用时。

使用IsWow64Process()检查PIDHandle是否是32位或64位进程的句柄,然后使用适当的记录类型扫描其内存,例如:

// 32-bit and 64-bit processes can scan the full address space of a 32-bit process,
// but a 32-bit process cannot scan the full address space of a 64-bit process

type
  PMEMORY_BASIC_INFORMATION32 = ^MEMORY_BASIC_INFORMATION32;
  MEMORY_BASIC_INFORMATION32 = record
    BaseAddress: DWORD;
    AllocationBase: DWORD;
    AllocationProtect: DWORD;
    RegionSize: DWORD;
    State: DWORD;
    Protect: DWORD;
    _Type: DWORD;
  end;

  {$IFDEF WIN64}
  PMEMORY_BASIC_INFORMATION64 = ^MEMORY_BASIC_INFORMATION64;
  {$ALIGN 16}
  MEMORY_BASIC_INFORMATION64 = record
    BaseAddress: ULONGLONG;
    AllocationBase: ULONGLONG;
    AllocationProtect: DWORD;
    _alignment1: DWORD;
    RegionSize: ULONGLONG;
    State: DWORD;
    Protect: DWORD;
    _Type: DWORD;
    _alignment2: DWORD;
  end;
  {$ENDIF}

function VirtualQueryEx32(hProcess: THandle; lpAddress: Pointer; var lpBuffer: MEMORY_BASIC_INFORMATION32; dwLength: SIZE_T): SIZE_T; stdcall; external 'kernel32' name 'VirtualQueryEx';

{$IFDEF WIN64}
function VirtualQueryEx64(hProcess: THandle; lpAddress: Pointer; var lpBuffer: MEMORY_BASIC_INFORMATION64; dwLength: SIZE_T): SIZE_T; stdcall; external 'kernel32' name 'VirtualQueryEx';
{$ENDIF}

procedure TForm1.DoSomethingWithBuff(const ProcessName: string);
begin
  // do particular stuff with Buff
end;

procedure TForm1.ScanMemory32(PIDHandle: THandle; const ProcessName: string);
var
  MemStart: DWORD;
  ReceivedBytes: SIZE_T;
  MemInfo: MEMORY_BASIC_INFORMATION32;
begin
  MemStart := 0;
  while (VirtualQueryEx32(PIDHandle, Pointer(MemStart), MemInfo, SizeOf(MemInfo)) <> 0) do
  begin
    if ((MemInfo.State = MEM_COMMIT) and (not (MemInfo.Protect = PAGE_GUARD)
    or (MemInfo.Protect = PAGE_NOACCESS)) and (MemInfo.Protect = PAGE_READWRITE)) then
    begin
      SetLength(Buff, MemInfo.RegionSize);
      if (ReadProcessMemory(PIDHandle, Pointer(MemInfo.BaseAddress), Buff,
          MemInfo.RegionSize, ReceivedBytes)) then
      begin
        DoSomethingWithBuff(ProcessName);
      end;
    end;
    Inc(MemStart, MemInfo.RegionSize);
  end; 
end;

{$IFDEF WIN64}
procedure TForm1.ScanMemory64(PIDHandle: THandle; const ProcessName: string);
var
  MemStart: ULONGLONG;
  ReceivedBytes: SIZE_T;
  MemInfo: MEMORY_BASIC_INFORMATION64;
begin
  MemStart := 0;
  while (VirtualQueryEx64(PIDHandle, Pointer(MemStart), MemInfo, SizeOf(MemInfo)) <> 0) do
  begin
    if ((MemInfo.State = MEM_COMMIT) and (not (MemInfo.Protect = PAGE_GUARD)
    or (MemInfo.Protect = PAGE_NOACCESS)) and (MemInfo.Protect = PAGE_READWRITE)) then
    begin
      SetLength(Buff, MemInfo.RegionSize);
      if (ReadProcessMemory(PIDHandle, Pointer(MemInfo.BaseAddress), Buff,
          MemInfo.RegionSize, ReceivedBytes)) then
      begin
        DoSomethingWithBuff(ProcessName);
      end;
    end;
    Inc(MemStart, MemInfo.RegionSize);
  end; 
end;
{$ENDIF}

procedure TForm1.ScanMemory(PIDHandle: THandle; const ProcessName: string);
var
  Is32Bit: BOOL;
begin
  if not IsWow64Process(PIDHandle, @Is32Bit) then
    RaiseLastOSError;

  if Is32Bit then begin
    ScanMemory32(PIDHandle, ProcessName);
  end
  {$IFDEF WIN64}
  else begin
    ScanMemory64(PIDHandle, ProcessName);
  end
  {$ENDIF};
end;