【问题标题】:Reading arbitrary memory locations? Possible?读取任意内存位置?可能的?
【发布时间】:2009-05-04 14:15:20
【问题描述】:

有没有办法(只读)访问任意内存位置而不会遇到访问冲突?我认为每个进程都有自己的虚拟地址空间,并且它可以读取所有可用的内存位置......似乎不是这样,因为如果我执行类似的操作,我的程序就会挂起

var
  IntPtr : PInteger;
  AnInteger : Integer;
...
IntPtr := $100;
AnInteger := IntPtr^;

我仍在尝试编写我的低级递归大小函数并尝试检测数据成员是否为对象引用。

谢谢!

【问题讨论】:

  • 我在你的代码中感觉到很多指针。指针是通往黑暗面的路径。指针会导致访问冲突,访问冲突会导致程序崩溃,程序崩溃会导致客户不满意。
  • 嗨!我通常不经常使用指针(至少在 Delphi 中)。但在这种情况下,我想检测一个 4 字节的对象是否是对象引用 - 为此我必须深入到指针级别......

标签: delphi memory pointers access-violation


【解决方案1】:

您只能通过指针访问您自己的进程的内存,即便如此,也只能是为您的进程映射的那些部分。有调试器钩子可以让你访问其他进程的内存;但他们很难做到正确。

所以如果你真的想遍历你的进程内存,你大概可以在这里找到你需要的函数:http://msdn.microsoft.com/en-us/library/ms878234.aspx

Windows 中的 AFAIR 也将一部分内核映射到您的进程内存空间(这就是您的进程无法使用所有 4G 的原因)。

【讨论】:

    【解决方案2】:

    您的应用程序挂起?那么你的应用程序有问题。 通常,会有一个简单的 AV。 AV 导致错误消息。就是这样。

    顺便说一句,你不应该害怕它 - 只是处理它。

    function IsValidObject(const AObj: Pointer { or TObject} ): Boolean;
    begin
      try
        ...
        // place your checking code there
        Result := ...;
      except
        on EAccessViolation do
          Result := False;
      end;
    end;
    

    唯一想到的这条规则的例外是,如果您正在编写某种异常处理程序并想要检测是否存在有效对象。在这种情况下,您可能不想在异常处理程序中生成异常;)

    如果这是您的情况 - 然后尝试使用此代码(这是一个示例):

    function GetReadableSize(const AAddress: Pointer; const ASize: Cardinal): Cardinal;
    const
      ReadAttributes = [PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE];
    var
      MemInfo: TMemoryBasicInformation;
      Tmp: Cardinal;
    begin
      Result := 0;
      if (VirtualQuery(AAddress, MemInfo, SizeOf(MemInfo)) = SizeOf(MemInfo)) and
         (MemInfo.State = MEM_COMMIT) and (MemInfo.Protect in ReadAttributes) then
      begin
        Result := (MemInfo.RegionSize - (Cardinal(AAddress) - Cardinal(MemInfo.BaseAddress)));
        if Result < ASize then
        begin
          repeat
            Tmp := GetReadableSize(Pointer(DWord(MemInfo.BaseAddress) + MemInfo.RegionSize), (ASize - Result));
            if (Tmp > 0) then
              Inc(Result, Tmp)
            else
              Result := 0;
          until (Result >= ASize) or (Tmp = 0);
        end;
      end;
    end;
    
    function IsValidBlockAddr(const AAddress: Pointer; const ASize: Cardinal): Boolean;
    begin
      Result := (GetReadableSize(AAddress, ASize) >= ASize);
    end;
    

    但通常你应该更喜欢第一种方法。

    【讨论】:

    • 我需要一些时间来仔细查看您的第二个代码示例,但请注意:我没有遇到访问冲突(您是对的),但是取消引用指针的操作需要永远.就像当我取消引用一个 nil 指针时......
    • 我认为你看到了别的东西 :( IntPtr^ 的挂起看起来很奇怪。你使用任何异常挂钩吗?可能是你的堆栈已损坏?
    【解决方案3】:

    如果您想安全地尝试读取任何内存地址而不大惊小怪,并在您尝试读取的内存不可访问时获得一个很好的错误代码而不是异常,那么您要使用的函数在 WinAPI 中:ReadProcessMemory.

    【讨论】:

      【解决方案4】:

      内存可能不会映射到所有地址。并且较低的 4kb 左右始终受到保护。

      但是,如果是针对 VM,如果您控制内存管理器,则可以建立一个列表,其中包含您的应用程序映射的所有内存范围。

      【讨论】:

        【解决方案5】:

        除非有一些我不知道的魔法方式,否则我很确定你不能这样做。 Windows 使用受保护的内存,这意味着您无法访问尚未专门分配给您的任何内容。

        有 DMA,但它是为驱动程序级软件保留的。

        【讨论】:

        • DMA 与此无关,它用于在外围设备和内存之间传输数据而不会使 CPU 紧张。
        【解决方案6】:

        在旧的 Windows、95、98、Me 中,你可以在你的函数中做一些内联汇编并读/写一些任意内存位置或硬件端口...

        函数 ReadPortByte : 字节; 变量 基础:字​​; 开始 基地 := FAddress; 汇编 mov DX,基地 在 AL、DX mov 结果, AL 结尾; 结束;

        您仍然可以通过使用设备驱动程序来做到这一点,但除非驱动程序为 Vista 及更高版本正确编译,否则 Vista 可能会给您带来一些问题。

        有几个免费的,值得一试。

        约翰

        【讨论】:

          猜你喜欢
          • 2012-10-02
          • 2011-01-17
          • 1970-01-01
          • 2011-10-01
          • 2015-05-10
          • 1970-01-01
          • 2015-10-06
          • 1970-01-01
          • 2013-09-26
          相关资源
          最近更新 更多