在两个进程之间共享内存(C、Windows)
Posted
技术标签:
【中文标题】在两个进程之间共享内存(C、Windows)【英文标题】:Sharing memory between two processes (C, Windows) 【发布时间】:2010-11-15 02:51:38 【问题描述】:由于我还没有找到答案 to the question asked previously here 我正在尝试不同的方法。
有没有办法在两个进程之间共享内存?
第二个进程从注入中获取信息,因为它是一个不再受支持的遗留程序。
我的想法是在那里注入一些代码,在我传递给注入程序的结构中,将地址(或其他)传递给我需要运行的数据所在的共享内存。获得数据后,我将在注入的线程中填充我自己的变量。
这可能吗? 怎么样?
感谢代码。
编辑:
我认为不清楚,所以我会澄清一下。我知道怎么注射。我已经在做。这里的问题是将动态数据传递给注入。
【问题讨论】:
什么样的程序? Windows、GUI、控制台? 所有这些。我可以从服务、GUI 或控制台运行 对于 POCO 项目中的内存映射文件,有一个很好用的 C++ 包装器。 pocoproject.org/download/index.html 我在反复尝试使用 Boost 的东西后发现它,其他人可能会觉得它很容易使用,但我发现它很难正确使用。 【参考方案1】:虽然 windows 通过其file mapping API 支持共享内存,但您不能轻易地将共享内存映射直接注入另一个进程,因为MapViewOfFileEx 不接受进程参数。
但是,您可以通过使用VirtualAllocEx 和WriteProcessMemory 在另一个进程中分配内存来注入一些数据。如果您要使用DuplicateHandle 复制句柄,然后注入一个调用MapViewOfFileEx 的存根,则可以在另一个进程中建立共享内存映射。因为听起来你无论如何都会注入代码,所以这应该很适合你。
总而言之,您需要:
通过调用 CreateFileMapping 创建匿名共享内存段句柄,其中 hFile 为 INVALID_HANDLE_VALUE,lpName 为 NULL。 将此句柄复制到带有DuplicateHandle的目标进程中 使用VirtualAllocEx 为代码分配一些内存,其中flAllocationType = MEM_COMMIT | MEM_RESERVE 和 flProtect = PAGE_EXECUTE_READWRITE 使用WriteProcessMemory 将存根代码写入此内存。这个存根可能需要用汇编程序编写。将 DuplicateHandle 中的 HANDLE 写在此处某处。 使用CreateRemoteThread 执行您的存根。然后存根必须使用它获得的 HANDLE 来调用MapViewOfFileEx。然后,这些进程将拥有一个公共共享内存段。如果你的存根加载一个外部库,你可能会发现它更容易一些——也就是说,让它简单地调用 LoadLibrary(查找 LoadLibrary 的地址留给读者作为练习)并从库的 dllmain 条目中完成你的工作观点。在这种情况下,使用命名共享内存可能比使用 DuplicateHandle 更简单。有关详细信息,请参阅 CreateFileMapping 上的 MSDN 文章,但本质上,为 hFile 传递 INVALID_HANDLE_VALUE 为 lpName 传递一个名称。
编辑:由于您的问题是传递数据而不是实际的代码注入,因此这里有几个选项。
-
使用可变大小的共享内存。您的存根获取共享内存的大小和名称或句柄。如果您只需要交换一次数据,这是合适的。 请注意,共享内存段的大小在创建后不能轻易更改。
使用named pipe。您的存根获取管道的名称或句柄。然后,您可以使用适当的协议来交换可变大小的块 - 例如,为长度写入 size_t,然后是实际消息。或者使用 PIPE_TYPE_MESSAGE 和 PIPE_READMODE_MESSAGE,并观察 ERROR_MORE_DATA 以确定消息的结束位置。如果您需要多次交换数据,这是合适的。
编辑 2:以下是如何为存根实现句柄或指针存储的草图:
.db B8 ;; mov eax, imm32
.dl handle_value ;; fill this in (located at the start of the image + one byte)
;; handle value is now in eax, do with it as you will
;; more code follows...
您也可以只使用固定名称,这可能更简单。
【讨论】:
作为一个对这里发生的事情只有一个概念的非 Windows 人,我应该有点害怕这可能吗?是否有类似的 unix 流程可以做到这一点? 是的,这可以在 unix 中完成。使用 PTRACE_POKEUSER 来阻止匿名 mmap 调用的寄存器。调用完成后,使用 PTRACE_POKEDATA 或 mmapping /proc/pid/mem 写入代码。然后让代码执行任何操作。 澄清一下,以上是针对 Linux 的。其他 Unix 可能会有类似的东西。如果您想要自己的线程,请从注入的存根中调用 libpthread。 这听起来是个不错的解决方案。我知道如何注入并且我已经有了工作代码。您能否发布一个有关如何使用整个事物的示例(您刚刚描述的内容)。谢谢! 恐怕现在不行。不过,我相信您可以弄清楚——所有这一切中最困难的部分是为代码注入编写存根。 :)【参考方案2】:你可以试试memory-mapped file。
This 提供了更多的分步细节。
【讨论】:
如果您不想一直使用 Raw Win32 调用,我建议您使用 POCO 项目的预构建测试包装类。 pocoproject.org/download/index.html 比 Boost 更轻量级和可读性。 你能做一个内存映射文件,但该文件从不接触文件系统,而是一个仅支持内存的文件吗?【参考方案3】:你可以使用共享内存
http://msdn.microsoft.com/en-us/library/aa366551(VS.85).aspx【讨论】:
共享内存是内存映射文件。你想做什么? 对不起,我指的是管道。让我删除那条评论【参考方案4】:您是否尝试使用管道(用于内存)甚至序列化(用于您的对象)?您可以使用文件来管理进程之间的内存。套接字也很适合在进程之间进行通信。
【讨论】:
【参考方案5】:内存映射是要走的路,你甚至不需要创建一个永久的内存空间,当所有共享它的进程都关闭时,内存扇区就会超出范围。还有其他方法。将数据从一个 C 应用程序传递到另一个应用程序的一种快速而肮脏的方式就是使用操作系统。在命令行输入app1 | app2
。这会导致 app2 成为 app1 的输出目的地,否则来自 app1 的 printf 命令会将其发送到 app2(这称为管道)。
【讨论】:
【参考方案6】:您可以尝试使用Boost.Interprocess 在两个进程之间进行通信。但是要将代码注入到以前存在的不受支持的软件中,您可能必须使用@bdonlan 的方式使用WriteProcessMemory。
【讨论】:
谢谢。你能详细说明吗?我知道如何注射,这不是问题。 我正在使用 writeprocessmemory。我只需要传递有时会改变大小的信息【参考方案7】:如果您谈论的是 Windows,主要障碍是每个进程都存在于自己的虚拟地址空间中。不幸的是,您无法将正常的内存地址从一个进程传递到另一个进程并获得您期望的结果。 (另一方面,线程都生活在相同的地址空间中,这就是为什么线程可以以相同的方式查看内存。)
但是,Windows 确实有一个共享内存空间,您必须非常小心才能正确管理它。在共享内存空间中分配空间的任何进程都负责显式释放该内存。这与本地内存形成对比,本地内存在进程终止时或多或少会消失。
请参阅this MSDN sample article,了解有关如何使用共享内存空间接管世界的一些想法。呃,与遗留软件的接口。或者随便什么:) 祝你好运!
【讨论】:
感谢 cmets。他们进一步澄清了情况。不过,那篇文章并没有真正说明要使用哪种 API 或方法。 全局堆在 win32 中是进程本地的。至少,将它用于共享内存不会在任何基于 NT 的(XP 和朋友)上工作。 @bdonlan - ??也许我们在这里谈论的不是同一件事。全局堆存在于全局内存中,在所有进程之间共享。所有分配都是基于句柄的,而不是基于指针的,等等。如果我们在谈论 .NET,我认为你可能是对的——我不记得托管 CLR 术语是否具有“全局”含义。 bdonlan,你能扩大你的评论吗?我想确保我明白我在说什么:) "Windows内存管理没有提供单独的本地堆和全局堆。因此,GlobalAlloc和LocalAlloc函数本质上是一样的。" msdn.microsoft.com/en-us/library/aa366574%28VS.85%29.aspx 啊!现在我明白我的困惑了。我完全错误地说“堆”。我在想共享内存空间。那不是一堆。 :) thx bdonlan!以上是关于在两个进程之间共享内存(C、Windows)的主要内容,如果未能解决你的问题,请参考以下文章