【问题标题】:Determining programmatically what modules are loaded in another process? (OS X)以编程方式确定在另一个进程中加载​​了哪些模块? (OS X)
【发布时间】:2011-05-17 14:01:45
【问题描述】:

我想做的事情我觉得很简单,我只是不确定该怎么做。

具体来说,我只想获取在另一个进程中加载​​的模块(共享/动态库)列表。以及获取该模块在给定进程中的起始地址。

使用 GDB 获取这些信息非常简单。您只需连接到该进程,然后键入“信息共享”。这正是我想获得的信息类型。如:

Num Basename
类型地址原因 | |资源 | |
| | | | | |
1 亚丁
- 0x1000 执行 Y Y /Applications/Adium.app/Contents/MacOS/Adium (偏移量 0x0)2 dyld
- 0x8fe00000 dyld Y Y /usr/lib/dyld at 0x8fe00000(偏移量 0x0) 前缀为“__dyld_” 3 WebCore F 0x95b6a000 dyld Y Y /System/Library/Frameworks/WebKit.framework/Versions/A/Frameworks/WebCore.framework/Versions/A/WebCore 在 0x95b6a000(偏移量 0x95b6a000)

有人知道如何以编程方式执行此操作吗?显然模块加载的位置是动态的,所以我需要确定它的位置。

【问题讨论】:

  • 您是否只对在 OS X 上执行此操作感兴趣?

标签: objective-c xcode macos gdb


【解决方案1】:

首先使用task_for_pid()获取任务端口。

然后使用task_info找到“dyld所有图片信息地址”:

struct task_dyld_info dyld_info;
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
if (task_info(task, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count) == KERN_SUCCESS)
{
    // retrieve dyld_info.all_image_info_addr;
}

这个地址会指向内存中的一个结构体dyld_all_image_infos:

struct dyld_all_image_infos {
    uint32_t version;
    uint32_t infoArrayCount;
    const struct dyld_image_info* infoArray;
    // ...
}

infoArrayCount 和 infoArray 条目在这里很重要。您必须检索这些值(使用 mach_vm_read)并遍历 infoArray。每个条目都是一个 struct dyld_image_info:

struct dyld_image_info {
    const struct mach_header* imageLoadAddress;
    const char* imageFilePath;
    uintptr_t imageFileModDate;
};

在这个结构中,您有兴趣将值检索到 imageLoadAddress(内存中库的地址)和 imageFilePath(内存中以 NULL 结尾的文件路径的地址)。

重要提示:以上结构中标记为指针或 uintptr_t 的字段具有不同的字节大小,具体取决于正在运行的进程是 32 位还是 64 位。您可以通过查看 dyld_info.all_image_info_format 是 TASK_DYLD_ALL_IMAGE_INFO_32 还是 TASK_DYLD_ALL_IMAGE_INFO_64 来确定指针大小(应该可以,但我自己没有测试过)。

最后,这仍然不包括动态链接器本身的条目。要检索它,我发现的一种方法是遍历 vm 区域(即 mach_vm_region),并找到第一个看起来像是 mach dylinker 的区域(检查 MH_DYLINKER 作为文件类型;参见 mach-o 文件格式了解更多信息)。最后我记得检查,gdb 和/或 lldb 也有这样做的功能。解析 mach 标头也是判断进程是 32 位还是 64 位的一种可能方法。

在您检索到所有 dyld 图像信息条目后,您可能还想按地址对其进行排序。

我建议不要查看 newosxbook 的 vmmap 实现代码。它已经过时了(因为它仍然使用 DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET),并且它做了一些不必要的暴力破解。

【讨论】:

  • 对此我无法表达我的感激之情。它会为我节省很多时间。
  • 我在获取 dyld_image_info 结构时遇到了麻烦。每当我从内存中提取它时,我都会得到 NULL。我将字节大小计算为 infoSize = sizeof(*imageInfo) * imageInfos->infoArrayCount;这可能与 64/32 位计数有关吗?
  • @mattvv 如果您的进程的指针大小与目标的指针大小不同,那么计算看起来不会起作用 - 因此您也不应该直接使用 struct dyld_image_info 。我在上面编辑了我的帖子,展示了另一种确定目标指针大小的可能方法。
【解决方案2】:

我建议你可以去下载gdb as used by the Development Tools的源代码。

但是,好吧,我已经阅读该来源,但我不确定告诉任何人去阅读它是一个有效的建议。

无论如何,您都需要使用各种mach API 来执行此操作。具体来说,API 位于/usr/include/mach/*.h。具体来说,您需要从 task_for_pid() 开始,然后逐步找到您需要的信息。

请注意,task_for_pid()(以及用于挖掘其他任务内部的任何其他机制)需要管理员访问权限或机器上 development 组的成员身份。

【讨论】:

  • 感谢您的建议,我实际上是从那条路线开始的,但无法了解哪些功能实际上提供了有关共享模块的信息:/ 但是是的,我启动了具有 root 访问权限的应用程序并使用task_for_pid 和 vm_read_overwrite 用于内存访问等...在 GDB 中我应该更具体地查看的任何想法?当我第一次尝试但找不到它的位置时,实际上花了 2 个小时:/
  • 不——我已经很久很久没有研究过 gdb 代码了。这是一个伤人的地方。
  • 我认为您不需要读取目标应用程序的内存,而是读取mach-o或walk vm区域信息。如果能找到 vmmap 的源代码,它会给出一个更简洁的例子。
  • mach API 不会让你走得太远,dyld 是一个用户态进程。
【解决方案3】:

您可以从 Breakpad 项目中获取一些现有的 BSD 许可代码,这些代码正是这样做的:

dyld 为 GDB 提供了一些钩子,特别是一个众所周知的函数符号,gdb 可以使用它来访问包含此信息的结构。 见http://www.opensource.apple.com/source/dyld/dyld-132.13/include/mach-o/dyld_images.h 你可以在这里看到 GDB 是如何做到的: http://www.opensource.apple.com/source/gdb/gdb-1344/src/gdb/macosx/macosx-nat-dyld.c(查找“macosx_init_addresses”)。 lookup_minimal_symbol 的内部结构太可怕了,无法谈论,但 Breakpad 的实现相当简单。

【讨论】:

    【解决方案4】:

    感谢@Zorg 的精彩解释。根据@Zorg 的说明,我编写了一个简单的 sn-p 来实现所需的功能以及内核内存复制部分。请看一看。

    #include <stdio.h>
    #include <stdlib.h>
    #include <mach-o/dyld_images.h>
    #include <mach/vm_map.h>
    
    #define PATH_MAX 2048
    
    
    // to build.
    // cc  -o test_mach test_mach.c
    
    // Helper function to read process memory (a la Win32 API of same name) To make
    // it easier for inclusion elsewhere, it takes a pid, and does the task_for_pid
    // by itself. Given that iOS invalidates task ports after use, it's actually a
    // good idea, since we'd need to reget anyway
    
    unsigned char *
    readProcessMemory (int pid,
            mach_vm_address_t addr,
            mach_msg_type_number_t* size) {
        task_t t;
        task_for_pid(mach_task_self(), pid, &t);
        mach_msg_type_number_t  dataCnt = (mach_msg_type_number_t) *size;
        vm_offset_t readMem;
    
        // Use vm_read, rather than mach_vm_read, since the latter is different in
        // iOS.
    
            kern_return_t kr = vm_read(t,           // vm_map_t target_task,
                         addr,                      // mach_vm_address_t address,
                         *size,                     // mach_vm_size_t size
                         &readMem,                  //vm_offset_t *data,
                         &dataCnt);                 // mach_msg_type_number_t *dataCnt
    
            if (kr) {
                fprintf (stderr, "Unable to read target task's memory @%p - kr 0x%x\n" ,
                        (void *) addr, kr);
                 return NULL;
            }
    
        return ( (unsigned char *) readMem);
    }
    
    
    int main(int argc, char* argv[]) {
    
        if (argc != 2) {
            fprintf(stderr, "Invalid usage %s\n", argv[0]);
            exit(0);
        }
    
        int pid = atoi(argv[1]);
    
        task_t task;
        task_for_pid(mach_task_self(),pid, &task);
    
        struct task_dyld_info dyld_info;
        mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
    
        if (task_info(task, TASK_DYLD_INFO, (task_info_t) &dyld_info, &count)
                == KERN_SUCCESS) {
            mach_msg_type_number_t size = sizeof(struct dyld_all_image_infos);
    
            uint8_t* data =
                readProcessMemory(pid, dyld_info.all_image_info_addr, &size);
            struct dyld_all_image_infos* infos = (struct dyld_all_image_infos *) data;
    
            mach_msg_type_number_t size2 =
                sizeof(struct dyld_image_info) * infos->infoArrayCount;
            uint8_t* info_addr =
                readProcessMemory(pid, (mach_vm_address_t) infos->infoArray, &size2);
            struct dyld_image_info* info = (struct dyld_image_info*) info_addr;
    
            for (int i=0; i < infos->infoArrayCount; i++) {
                mach_msg_type_number_t size3 = PATH_MAX;
    
                uint8_t* fpath_addr = readProcessMemory(pid,
                        (mach_vm_address_t) info[i].imageFilePath, &size3);
                if (fpath_addr)
                    printf("path: %s %d\n",fpath_addr , size3);
            }
        }
        return 0;
    }
    

    【讨论】:

    • vm_offset_t readMem;的目的是什么?我找不到关于类型 vm_offset_t 的任何文档,而且似乎 readMem 只是某种虚拟变量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-25
    • 1970-01-01
    • 2013-08-09
    • 1970-01-01
    • 2013-02-17
    • 2011-02-13
    • 2016-09-17
    相关资源
    最近更新 更多