有时候我看到有人会使用 ReadProcessMemory 这个 API 来实现进程间通信,老实说吧,我觉得这不是一个明智的选择,原因有如下几条。
首先,你不能使用 ReadProcessMemory 来跨越安全上下文 (Security Contexts),至少你需要做一些额外的工作才能做到。如果用户通过 “runas” 来使用另一个用户身份来运行程序,则这两个进程将不能通过 ReadProcessMemory 来进行进程间数据的传输。
我们可以通过调整进程的权限来实现这个功能,具体来说,就是对正在与之通信的进程的所有者授予 PROCESS_VM_READ 权限,但这样会将安全的大门开的太大。使用该用户标识运行的任何进程都会读取到共享的数据,而不仅仅是要与之通信的进程。如果你正在与较低特权的进程进行通信,则只是将数据暴露给你感兴趣的进程以外的较低特权的进程。
更重要的是,一旦授予了 PROCESS_VM_READ 权限,你就会将其授予您的整个进程。该进程不仅可以读取你尝试共享的数据,还可以读取映射到你的地址空间的任何其他内容。它可以读取所有全局变量,可以读取堆,可以从堆栈中读取变量。它甚至可以损坏堆栈的数据结构!
什么?仅仅是授予读取访问权限就损坏堆栈?
如果进程将其堆栈增长到堆栈保护页中,则未经处理的异常筛选器将捕获保护异常并扩展堆栈。但是,当它发生在私有的“捕获所有异常”处理程序(例如 IsBadReadPtr 函数使用的处理程序)中时,它将私下处理,并且不会到达未处理的异常筛选器。结果,堆栈不会增长,不会创建新的堆栈保护页面。当堆栈正常增长到并超过过早提交的保护页的点时,通常的堆栈保护异常现在是访问冲突,导致线程死亡,并可能扩散到随之而来的进程。
你可能认为,可以捕获堆栈访问冲突并尝试干净地关闭线程,但由于多种原因,这是不可能的。首先,结构化异常处理在遇到异常的线程堆栈上执行。如果该线程具有损坏的堆栈,则无法调度该异常,因为异常筛选器想要在其上运行的堆栈不再可行。
即使你可以以某种方式在某种“紧急堆栈”上运行这些异常过滤器,但仍然无法解决问题。在异常点,线程可能处于任何中间d的代码流。也许它是在堆管理器内部,堆锁持有,堆数据结构处于不断变化的状态。为了使进程保持活动状态,需要使堆数据结构保持一致并释放堆锁。但是你不知道该怎么做。
还有许多其他进程间通信机制可供使用。其中之一是匿名共享内存,我几年前讨论过。匿名共享内存仍然存在一个问题,即在与你与之通信的同一令牌下运行的任何进程都可以读取共享内存块,但至少公开的范围仅限于你希望明确想要共享的数据。
(从某种意义上说,没有比这更好的了。你正在与之通信的进程一旦从你那里获得数据,就可以对数据做任何它想做的事情。即使你以某种方式安排了只有目标进程可以访问内存,也没有什么能阻止该目标进程将其复制到共享内存块之外的某个地方,此时任何运行相同令牌的人都可以从目标进程中读取数据。)
总结
目前小僧仅仅使用到了共享内存,惭愧惭愧。
什么邮槽管道剪贴板,我是一个都没有听说过。
最后
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《ReadProcessMemory is not a preferred IPC mechanism》