Objective c 以编程方式确定在另一个进程中加载哪些模块?(操作系统X)

Objective c 以编程方式确定在另一个进程中加载哪些模块?(操作系统X),objective-c,xcode,macos,gdb,Objective C,Xcode,Macos,Gdb,我觉得我想做的很简单,只是不知道该怎么做 具体来说,我只想获得在另一个进程中加载的模块(共享/动态库)列表。以及获取该模块在给定进程中的起始地址 使用GDB获取这些信息非常简单。您只需连接到流程,然后键入“信息共享”。这正是我想要获取的信息类型。例如: Num Basename 键入地址原因| |源 || || | | | | 1个Adium -0x1000 exec Y Y/Applications/Adium.app/Contents/MacOS/Adium (偏移量0x0)2 dyld -

我觉得我想做的很简单,只是不知道该怎么做

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

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

Num Basename
键入地址原因| |源 ||
|| | | | |
1个Adium
-0x1000 exec Y Y/Applications/Adium.app/Contents/MacOS/Adium (偏移量0x0)2 dyld
-0x8fe00000 DYY Y/usr/lib/dyld在0x8fe00000处(偏移量 0x0),前缀为“\uuudyld\u3”网络核心 F 0x95b6a000动态Y /System/Library/Frameworks/WebKit.framework/Versions/A/Frameworks/WebCore.framework/Versions/A/WebCore 在0x95b6a000处(偏移量0x95b6a000)


有人知道如何通过编程实现这一点吗?显然,模块加载的位置是动态的,所以我需要确定它位于何处。

我建议您可以下载源代码进行下载

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

在任何情况下,您都需要使用各种
mach
api来实现这一点。特别是,API可以在
/usr/include/mach/*.h
中找到。具体来说,您需要从
task\u for_pid()
开始,然后逐步获得所需的信息


请注意,
task\u for_pid()
(以及用于从内部搜索其他任务的任何其他机制)需要管理员访问权限或机器上的
开发组的成员身份。

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

dyld为GDB提供了一些钩子,特别是一个众所周知的函数符号,GDB可以使用它来访问包含此信息的结构。 看见 您可以在这里看到GDB是如何做到这一点的: (查找“macosx_初始地址”)。lookup_minimal_symbol的内部结构太可怕了,无法谈论,但是Breakpad的实现相当简单。

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

然后使用任务信息查找“dyld all images info address”:

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 {
    uint32_t version;
    uint32_t infoArrayCount;
    const struct dyld_image_info* infoArray;
    // ...
}
infoArrayCount和infoArray条目在这里很重要。您必须检索这些值(使用mach_vm_read)并遍历infoArray。每个条目都是一个结构动态图像信息:

struct dyld_image_info {
    const struct mach_header* imageLoadAddress;
    const char* imageFilePath;
    uintptr_t imageFileModDate;
};
在此结构中,您感兴趣的是检索imageLoadAddress(内存中库的地址)和imageFilePath(内存中以NULL结尾的文件路径的地址)的值

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

最后,这仍然不包括动态链接器本身的条目。要检索它,我找到的一种方法是遍历vm区域(即mach_vm_区域),并找到第一个看起来像mach dylinker的区域(检查文件类型是否为MH_dylinker;有关更多信息,请参阅mach-o文件格式)。上一次我回忆起检查,gdb和/或lldb也有一个用于执行此操作的函数。解析马赫头也是判断进程是32位还是64位的一种可能方法

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


我建议不要查看newosxbook的vmmap实现代码。它已经过时了(因为它仍然使用DYLD\u ALL\u IMAGE\u INFOS\u OFFSET\u OFFSET),并且它会执行一些不必要的暴力强制。

感谢@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;
}
#包括
#包括
#包括
#包括
#定义路径_MAX 2048
//建造。
//cc-o试验马赫数试验马赫数c
//Helper函数读取进程内存(一个同名的la Win32 API)以使
//它更容易包含在其他地方,它需要一个pid,并为pid执行任务
//它本身。考虑到iOS在使用后会使任务端口失效,它实际上是一个
//好主意,反正我们得注册
无符号字符*
readProcessMemory(int pid,
机器虚拟机地址地址,
马赫数信息类型编号尺寸){
任务;;
用于pid的任务(马赫任务、pid和t);
马赫数/型号/数据量=(马赫数/型号/型号/型号)*尺寸;
vm_offset_t readMem;
//使用vm_read,而不是mach_vm_read,因为后者在
//iOS。
kern\u return\u t kr=vm\u read(t,//vm\u map\u t target\u task,
地址,//mach\u vm\u地址\u t地址,
*大小,//mach\u vm\u size\t大小
&readMem,//vm\u offset\u t*数据,
&dataCnt);//马赫数msg类型数dataCnt
if(kr){
fprintf(stderr,“无法读取目标任务的内存@%p-kr 0x%x\n”,
(无效*)地址,kr);
返回NULL;
}
返回((无符号字符*)readMem);
}
int main(int argc,char*argv[]){
如果(argc!=2){
fprintf(stderr,“无效用法%s\n”,argv[0]);
出口(0);
}
int-pid=atoi(argv[1]);
任务任务;
用于pid的任务(马赫任务、pid和任务);
结构任务动态信息动态信息;
马赫数