What I'm trying to do I feel is pretty straightforward, I'm just not sure exactly how to do it.
Specifically I just want to get a list of modules (shared/dynamic libraries) that are loaded in another process. As well as get the starting address of where that module is in the given process.
It's very straightforward to get this information with GDB. You simple connect to the process, and type "info shared." That is the exact type of information I would like to get to. Such as:
Num Basename
Type Address Reason | | Source | |
| | | | | |
1 Adium
- 0x1000 exec Y Y /Applications/Adium.app/Contents/MacOS/Adium (offset 0x0) 2 dyld
- 0x8fe00000 dyld Y Y /usr/lib/dyld at 0x8fe00000 (offset 0x0) with prefix "__dyld_" 3 WebCore F 0x95b6a000 dyld Y Y /System/Library/Frameworks/WebKit.framework/Versions/A/Frameworks/WebCore.framework/Versions/A/WebCore at 0x95b6a000 (offset 0x95b6a000)
Does anyone know how to do this programmatically? Obviously where modules load is dynamic so I need to determine where it's located.
First use task_for_pid() to obtain a task port.
Then find the "dyld all images info address" using 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;
}
This address will point to a struct dyld_all_image_infos in memory:
struct dyld_all_image_infos {
uint32_t version;
uint32_t infoArrayCount;
const struct dyld_image_info* infoArray;
// ...
}
The infoArrayCount and infoArray entries are important here. You have to retrieve these values (use mach_vm_read) and iterate through the infoArray. Each entry is a struct dyld_image_info:
struct dyld_image_info {
const struct mach_header* imageLoadAddress;
const char* imageFilePath;
uintptr_t imageFileModDate;
};
In this struct, you are interested in retrieving the values to imageLoadAddress (an address to the library in memory) and imageFilePath (an address to the NULL terminated file path in memory).
Important note: the fields that are marked as a pointer or as uintptr_t in the structs above have a different byte size depending on whether the running process is 32 or 64 bit. You may be able to determine pointer size by seeing if dyld_info.all_image_info_format is TASK_DYLD_ALL_IMAGE_INFO_32 or TASK_DYLD_ALL_IMAGE_INFO_64 (should work, but I have not tested this myself).
Lastly, this will still not include an entry to the dynamic linker itself. To retrieve that, one way I've found is to iterate through the vm regions (i.e, mach_vm_region), and find the first region that looks like it's a mach dylinker (check for MH_DYLINKER as the file type; see mach-o file format for more info). Last I recall checking, gdb and/or lldb have a function for doing this too. Parsing the mach header is also one possible way to tell if the process is 32 or 64 bit.
After you retrieve all the dyld image info entries, you may also want to sort them by address.
I recommend not looking at newosxbook's code for its vmmap implementation. It is outdated (since it still uses DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET), and it does some unnecessary brute-forcing.
I would suggest that you could go download the source for gdb as used by the Development Tools.
But, well, I've read that source and I'm not sure that telling anyone to go read it is a productive suggestion.
In any case, you will want to use the various mach
APIs to do this. In particular, the APIs are found in /usr/include/mach/*.h
. Specifically, you'll want to start with task_for_pid()
and work your way down to the info you need.
Note that task_for_pid()
(and any other mechanism used to grub through another tasks innards) requires either admin access or membership in the development
group on the machine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With