检查内存是否可读,或者为什么不捕获异常



我有这样的代码,它是从外部进程的注入DLL中调用的。它确实读取了一些内存范围,但有时我会在DataBuffer := TCharPointer(Address + CharOffset)^;行遇到分段错误。那么,有没有什么方法可以检查内存是否可读呢?

function GetCurrentData(Address: Pointer): PChar;
var
  DataBuffer: Char;
  CharArray: Array of Char;
  CharOffset: Integer;
  ReadBytes: longword;
begin
  CharOffset := 0;
  SetLength(CharArray, 0);
  repeat
    DataBuffer := TCharPointer(Address + CharOffset)^;
    CharOffset := CharOffset + 1;
    SetLength(CharArray, CharOffset);
    CharArray[CharOffset - 1] := DataBuffer;
  until (Ord(DataBuffer) = 0);
  Result := PChar(@CharArray[0]);
end;

我也试图捕捉到异常,但由于某种原因,这不起作用。主机程序仍然崩溃。

unit UnitEventBridgeExports;
{$mode objfpc}{$H+}
interface
uses
  Classes, SysUtils, Windows, ShellAPI, JwaTlHelp32, SimpleIPC;
type
  TCharPointer = ^Char;
const
  WOWEXE = 'TestProgramm.exe';
var
  IPCClient: TSimpleIPCClient;
  PID: DWord;
  Process: THandle;
procedure EventCalled;
procedure InitializeWoWEventBridge; stdcall;
implementation

function GetProcessIDByName(Exename: String): DWord;
var
  hProcSnap: THandle;
  pe32: TProcessEntry32;
begin
  Result := 0;
  hProcSnap := CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS, 0);
  if hProcSnap <> INVALID_HANDLE_VALUE then
  begin
    pe32.dwSize := SizeOf(ProcessEntry32);
    if Process32First(hProcSnap, pe32) = True then
    begin
      while Process32Next(hProcSnap, pe32) = True do
      begin
        if pos(Exename, pe32.szExeFile) <> 0 then
          Result := pe32.th32ProcessID;
      end;
    end;
    CloseHandle(hProcSnap);
  end;
end;

procedure InitializeEventBridge; stdcall;
begin
  IPCClient := TSimpleIPCClient.Create(nil);
  IPCClient.ServerID := 'EventBridgeServer';
  IPCClient.Active := True;
  IPCClient.SendStringMessage('init');
  PID := GetProcessIDByName(EXE);
  Process := OpenProcess(PROCESS_ALL_ACCESS, False, PID);
end;

function GetCurrentData(Address: Pointer): PChar;
var
  DataBuffer: Char;
  CharArray: Array of Char;
  CharOffset: Integer;
  ReadBytes: longword;
  CharPointer: TCharPointer;
  BreakLoop: Boolean;
begin
  CharOffset := 0;
  SetLength(CharArray, 0);
  BreakLoop := False;
  repeat
    try
      CharPointer := TCharPointer(Address + CharOffset);
      DataBuffer := CharPointer^;
      CharOffset := CharOffset + 1;
      SetLength(CharArray, CharOffset);
      CharArray[CharOffset - 1] := DataBuffer;
    except
      BreakLoop := True;
    end;
  until (Ord(DataBuffer) = 0) or BreakLoop;
  Result := PChar(@CharArray[0]);
end;

procedure EventCalled;
var
  TmpAddress: Pointer;
  StringData: PChar;
begin
  {$ASMMODE intel}
  asm
    mov [TmpAddress], edi
  end;
  StringData := GetCurrentData(TmpAddress);
  IPCClient.SendStringMessage('update:' + StringData);
  //IPCClient.SendStringMessage('update');
end;
end.

当函数退出时,GetCurrentData()实现返回一个指向超出范围的本地数组的指针,然后EventCalled()在该指针不再有效后尝试使用该指针。试试这个:

function GetCurrentData(Address: Pointer): AnsiString; 
var 
  Offset: Integer; 
begin 
  Result := '';
  Offset := 0; 
  repeat 
    try 
      if PByte(Longint(Address) + Offset)^ = #0 then Break;
      Inc(Offset); 
    except 
      Break; 
    end; 
  until False; 
  SetString(Result, PAnsiChar(Address), Offset); 
end; 
procedure EventCalled; 
var 
  TmpAddress: Pointer; 
  StringData: AnsiString; 
begin 
  {$ASMMODE intel} 
  asm 
    mov [TmpAddress], edi 
  end; 
  StringData := GetCurrentData(TmpAddress); 
  IPCClient.SendStringMessage('update:' + StringData); 
  //IPCClient.SendStringMessage('update'); 
end; 

IsBadReadPtr API提供帮助。你给出地址和大小,就可以恢复可读性。陈建议永远不要使用它。

除此之外,VirtualQuery应该向您提供有关有问题的地址的信息,以说明其可读性。

由于Ken在下面的评论中再次警告了IsBadReadPtr的危险,我提出了不要错过的答案。一定要阅读评论和Raymdond博客的链接。请务必查看:

  • IsBadReadPtr的最有效替代品
  • 如何检查指针是否有效

最新更新