在OS X中读取其他进程的内存?
我一直在尝试了解如何在Mac OS X上读取其他进程的内存,但是运气并不好。我在网上看到了很多将ptrace和PEEKDATA结合使用的示例,但是在BSD [man ptrace]中没有该选项。
int pid = fork(); if (pid > 0) { // mess around with child-process's memory }
在Mac OS X上如何读写另一个进程的内存?
解决方案
回答
Matasano Chargen在将一些调试代码移植到OS X上有一段不错的帖子,其中包括学习如何在另一个进程中读写内存(以及其他事情)。
它必须工作,否则GDB不会:
It turns out Apple, in their infinite wisdom, had gutted ptrace(). The OS X man page lists the following request codes: PT_ATTACH — to pick a process to debug PT_DENY_ATTACH — so processes can stop themselves from being debugged [...] No mention of reading or writing memory or registers. Which would have been discouraging if the man page had not also mentioned PT_GETREGS, PT_SETREGS, PT_GETFPREGS, and PT_SETFPREGS in the error codes section. So, I checked ptrace.h. There I found: PT_READ_I — to read instruction words PT_READ_D — to read data words PT_READ_U — to read U area data if you’re old enough to remember what the U area is [...] There’s one problem solved. I can read and write memory for breakpoints. But I still can’t get access to registers, and I need to be able to mess with EIP.
回答
操纵过程在其背后的记忆是一件坏事,并且充满了危险。这就是Mac OS X(像任何Unix系统)保护内存并保持进程彼此隔离的原因。
当然可以做到:在明确协作的进程之间存在共享内存的功能。只要操作具有明确的操作权限(由安全框架授予),也可以使用其他方法来操作其他进程的地址空间。但这就是编写调试工具以供使用的人员的地方。对于Mac OS X上的绝大多数开发来说,这都不应该是正常的甚至是罕见的事情。
回答
使用task_for_pid()
或者其他方法来获取目标进程任务端口。此后,我们可以使用vm_read(),vm_write()等直接操作进程地址空间。
回答
如果我们希望能够在进程之间共享内存块,则应检出shm_open(2)和mmap(2)。在一个进程中分配一块内存,然后将路径(用于shm_open)传递给另一个进程,这很容易,然后两者都可能在一起发疯。正如克里斯·汉森(Chris Hanson)所提到的,这比在另一个进程的地址空间中四处浏览要安全得多。当然,如果我们不能同时控制这两个过程,那将不会带来太大的好处。
(请注意,shm_open的最大路径长度似乎为26个字节,尽管似乎在任何地方都没有记录。)
// Create shared memory block void* sharedMemory = NULL; size_t shmemSize = 123456; const char* shmName = "mySharedMemPath"; int shFD = shm_open(shmName, (O_CREAT | O_EXCL | O_RDWR), 0600); if (shFD >= 0) { if (ftruncate(shFD, shmemSize) == 0) { sharedMemory = mmap(NULL, shmemSize, (PROT_READ | PROT_WRITE), MAP_SHARED, shFD, 0); if (sharedMemory != MAP_FAILED) { // Initialize shared memory if needed // Send 'shmemSize' & 'shmemSize' to other process(es) } else handle error } else handle error close(shFD); // Note: sharedMemory still valid until munmap() called } else handle error ... Do stuff with shared memory ... // Tear down shared memory if (sharedMemory != NULL) munmap(sharedMemory, shmemSize); if (shFD >= 0) shm_unlink(shmName); // Get the shared memory block from another process void* sharedMemory = NULL; size_t shmemSize = 123456; // Or fetched via some other form of IPC const char* shmName = "mySharedMemPath";// Or fetched via some other form of IPC int shFD = shm_open(shmName, (O_RDONLY), 0600); // Can be R/W if you want if (shFD >= 0) { data = mmap(NULL, shmemSize, PROT_READ, MAP_SHARED, shFD, 0); if (data != MAP_FAILED) { // Check shared memory for validity } else handle error close(shFD); // Note: sharedMemory still valid until munmap() called } else handle error ... Do stuff with shared memory ... // Tear down shared memory if (sharedMemory != NULL) munmap(sharedMemory, shmemSize); // Only the creator should shm_unlink()
回答
通常,我建议我们使用常规的open()打开一个临时文件。一旦在两个进程中都将其打开,则可以将其从文件系统中取消link(),并且将像使用shm_open一样进行设置。该过程与Scott Marcy为shm_open指定的过程极为相似。
这种方法的缺点是,如果将要执行unlink()的进程崩溃,则最终将得到一个未使用的文件,并且没有进程负责清理该文件。与shm_open共享此缺点,因为如果没有shm_unlinks给定名称,该名称将保留在共享内存空间中,可供以后的进程使用shm_open打开。