首先使用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;
}
该地址将指向内存中的一个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),并找到第一个看起来像 mach dylinker 的区域(检查 MH_DYLINKER 作为文件类型;请参阅 mach-o 文件格式了解更多信息)。最后我记得检查过,gdb 和/或 lldb 也有一个执行此操作的函数。解析 mach 头也是判断进程是 32 位还是 64 位的一种可能方法。
检索所有 dyld 图像信息条目后,您可能还想按地址对它们进行排序。
我建议不要查看 newosxbook 的 vmmap 实现代码。它已经过时了(因为它仍然使用 DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET),并且它做了一些不必要的暴力破解。