Objective c 以编程方式确定在另一个进程中加载哪些模块?(操作系统X)
我觉得我想做的很简单,只是不知道该怎么做 具体来说,我只想获得在另一个进程中加载的模块(共享/动态库)列表。以及获取该模块在给定进程中的起始地址 使用GDB获取这些信息非常简单。您只需连接到流程,然后键入“信息共享”。这正是我想要获取的信息类型。例如: Num BasenameObjective 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 -
键入地址原因| |源 ||
|| | | | |
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和任务);
结构任务动态信息动态信息;
马赫数