以编程方式确定在另一个进程中加载​​了哪些模块? (OS X)

时间:2010-11-29 23:23:13

标签: objective-c xcode macos gdb

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

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

通过GDB获取此信息非常简单。您只需连接到该流程,然后键入“info shared”。这是我想要的确切信息类型。如:

  

Num Basename
  输入地址原因| |资源   | |
  | | | | | |
  1 Adium
   - 0x1000 exec Y Y /Applications/Adium.app/Contents/MacOS/Adium   (偏移0x0)2 dyld
   - 0x8fe00000 dyld Y Y / usr / lib / dyld,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)

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

4 个答案:

答案 0 :(得分:10)

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

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

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;
}

此地址将指向内存中的struct 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),找到第一个看起来像它的区域(检查MH_DYLINKER作为文件类型;请参阅mach-o文件格式以获取更多信息)。最后我记得检查,gdb和/或lldb也有这样做的功能。解析mach头也是判断进程是32位还是64位的一种可能方法。

检索完所有dyld图像信息条目后,您可能还希望按地址对它们进行排序。

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

答案 1 :(得分:2)

我建议您下载gdb as used by the Development Tools的来源。

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

在任何情况下,您都希望使用各种mach API来执行此操作。特别是,API可以在/usr/include/mach/*.h中找到。具体来说,您需要从task_for_pid()开始,然后逐步了解所需的信息。

请注意task_for_pid()(以及用于grub通过其他任务的任何其他机制)需要管理员访问权限或计算机上development组的成员资格。

答案 2 :(得分:2)

您可以从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的实现相当简单。

答案 3 :(得分:1)

感谢@Zorg提供了很好的解释。基于@Zorg的澄清,我编写了一个简单的代码片段,它实现了内核内存复制部分所需的功能。请看一看。

#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;
}