调用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,因为缓冲区不是作为ref
或out
参数传递的。然而,我知道。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:我添加了我所遵循的简单步骤来解决问题,作为可以在这里找到的答案。
可能因为你是一个64位的应用程序,你的lpNumberOfBytesRead应该是"长",所以调用ReadProcessMemory覆盖(你的一部分)缓冲区指针返回。
在ReadProcessMemory导入中声明的byte[] lpBuffer
的OutAttribute
(这是_Out_ LPVOID lpBuffer
的正确移植)指定数据应该从被调用方封送回调用方…所以你的字节数组可能被调用者自己(Kernel32)引用为空。
由于Hans Passant和500 - Internal Server Error(我标记为可接受的答案)的提示,我设法解决了这个问题。
以下是我所采取的步骤:
- 我选择使用32位而不是任意CPU。(主要是为了向后兼容)
- 然后我使用MSDN页面更新了函数的P/Invoke签名和这个关于windows数据类型的页面。并为签名的32位变体选择。
- 我再次运行代码,缓冲区引用未被清除。
谢谢你的帮助
不要将byte[] buffer
标记为[Out]
参数。它更类似于ref
而不是out
,因为它是一个字节数组,因此已经隐含了这一点。因为整数(这里的int
)是一种值类型,所以numberOfBytesRead
需要out
参数。这是唯一一个应该用out
标记的。
当某些东西被标记为out
参数时,Marshal
类期望被调用者(这里是ReadProcessMemory
)提供一个返回值。字节数组仅仅是指向内存中包含字节的位置的指针(地址)。您不希望被调用方写入此指针。