调用ReadProcessMemory时导致引用被设置为null的奇怪行为

本文关键字:null 设置 ReadProcessMemory 引用 调用 | 更新日期: 2023-09-27 17:54:34

我在c#中通过这个p/Invoke签名调用ReadProcessMemory时遇到了一些非常奇怪的行为:

[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;

代码非常相似,但由于某种原因,对buffer的引用丢失了,并且buffer被设置为null。如果它不是外部调用,我会100%确定这是一个bug,因为缓冲区不是作为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 Framework 4.0
  • Visual Studio Professional 2012 (Version 11.0.51106.01 Update 1)
  • 安装。net Framework 4.5.50709
  • 以管理员身份运行
  • 出现在发布和调试版本中,在visual studio主机可执行文件和常规构建可执行文件中。
  • Windows 7 64位
  • 进程从32位读取内存
  • 构建配置:平台:任意CPU

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

Edit2:我添加了我所遵循的简单步骤来解决问题,作为可以在这里找到的答案。

调用ReadProcessMemory时导致引用被设置为null的奇怪行为

可能因为你是一个64位的应用程序,你的lpNumberOfBytesRead应该是"长",所以调用ReadProcessMemory覆盖(你的一部分)缓冲区指针返回。

在ReadProcessMemory导入中声明的byte[] lpBufferOutAttribute(这是_Out_ LPVOID lpBuffer的正确移植)指定数据应该从被调用方封送回调用方…所以你的字节数组可能被调用者自己(Kernel32)引用为空。

由于Hans Passant和500 - Internal Server Error(我标记为可接受的答案)的提示,我设法解决了这个问题。

以下是我所采取的步骤:

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

谢谢你的帮助

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

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