在OS X中读取其他进程的内存?

时间:2020-03-05 18:39:33  来源:igfitidea点击:

我一直在尝试了解如何在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打开。