3

ReadProcessMemory通过此 P/Invoke 签名调用 C# 时,我遇到了一些非常奇怪的行为:

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ReadProcessMemory(
    IntPtr hProcess,
    IntPtr lpBaseAddress,
    [Out] byte[] lpBuffer,
    int dwSize,
    out int lpNumberOfBytesRead
    );

在我的应用程序中,我正在扫描具有读写访问权限的内存区域的整个内存(并且应用了更多过滤器,不过这是另一部分)。

扫描部分的代码是这样的:

int numberOfBytes;
if (!NativeMethods.ReadProcessMemory(handle, region.StartAddress,
    buffer, (int)region.RegionSize, out numberOfBytes))
// The handle, region (custom struct containing some fields from the
// MEMORY_BASIC_INFORMATION struct), and buffer come from parameters.

并且代码完美运行。它扫描整个内存以查找字节序列。那里没有问题。


在我的程序流程中更进一步,我有这个代码:
注意:它使用与前面的代码相同的句柄 IntPtr(检查它)并且它在同一个线程中运行

int bytesRead;
byte[] buffer = new byte[128]; // In my real app this is some calculated value
                            // however that irrelevant. It's calculated 128.
if (!NativeMethods.ReadProcessMemory(handle, location.Location,
    buffer, buffer.Length, out bytesRead))
    continue; // Error while reading
// At this point buffer == null, so the next line causes an exception
if (bytesRead != buffer.Length) continue;

代码非常相似,但由于某种原因,对缓冲区的引用丢失并且缓冲区设置为空。如果它不是外部调用,我会 100% 确定这是一个错误,因为缓冲区没有作为refout参数传递。但是我知道 .NET 在外部调用(例如编组)时会做一些伏都教的事情。

使情况更加奇怪的是,当我将该代码替换为:

int bytesRead;
byte[] buffer = new byte[128];
byte[] bufferRef = buffer;
if (!NativeMethods.ReadProcessMemory(handle, location.Location,
    buffer, buffer.Length, out bytesRead))
    continue; // Error while reading
buffer = bufferRef;
if (bytesRead != buffer.Length) continue;

代码很简单。内存读取和所有!所以发生的所有事情都是由于某种原因buffer变量失去了对实际缓冲区的引用。它把我搞糊涂了。


这种行为是由于我做错了什么(例如错误的 P/Invoke)造成的,它是否危险(泄漏内存?),并且可以解释?


我的配置:

  • .NET 框架 4.0
  • Visual Studio Professional 2012(版本 11.0.51106.01 更新 1)
  • 已安装 .NET Framework 4.5.50709
  • 以管理员身份运行
  • 发生在发布和调试版本中,在 Visual Studio 主机可执行文件和常规构建可执行文件中。
  • Windows 7 64 位
  • 我正在读取内存的进程是 32 位的
  • 构建配置:平台:任何 CPU

编辑:我正在使用的完整 NativeMethods 类可以在这里找到:http: //paste2.org/p/2770271

Edit2:我添加了解决问题的简单步骤,可以在此处找到答案。

4

4 回答 4

2

可能因为您是 64 位应用程序,所以您的 lpNumberOfBytesRead 应该是“长”的,因此对 ReadProcessMemory 的调用会在返回时覆盖(您的一部分)缓冲区指针。

于 2013-01-19T01:13:57.670 回答
0

由于Hans Passant500 - Internal Server Error(我将其标记为已接受的答案)的提示,我设法解决了这个问题。

这些是我采取的步骤:

  1. 我选择使用 32 位而不是 Any CPU。(主要是为了向后兼容。)
  2. 然后我使用 MSDN 页面更新了 P/Invoke 签名的功能和这个关于 windows 数据类型的页面。并选择了签名的 32 位变体。
  3. 我再次运行代码,缓冲区引用没有被清除。

谢谢您的帮助。

于 2013-01-19T02:06:37.963 回答
0

在您的 ReadProcessMemory 导入OutAttribute中声明的of (这是 的正确移植)指定数据应该从被调用者封送回调用者......因此您的字节数组可能被被调用者本身(Kernel32)引用为空。byte[] lpBuffer_Out_ LPVOID lpBuffer

于 2013-01-19T01:02:53.523 回答
-2

不要将 标记byte[] buffer[Out]参数。它更类似于refthan out,因为它是一个字节数组,所以已经暗示了这一点。由于整数(int这里)是一个值类型,它需要out参数 for numberOfBytesRead。那是唯一应该用 标记的out

当某些东西被标记为out参数时,Marshal该类期望被调用者(ReadProcessMemory此处)返回一个值。字节数组只是指向内存中包含字节的位置的指针(地址)。您不希望被调用者写入此指针。

于 2013-01-19T01:45:21.623 回答