otool
Вы можете использовать otool (1), чтобы выгрузить содержимое раздела, содержащего внедренный plist:
otool -s __TEXT __info_plist /path/to/executable
, а затем направить его вывод в xxd (1) в следующем порядке:чтобы получить соответствующее представление ASCII:
otool -X -s __TEXT __info_plist /path/to/executable | xxd -r
Однако otool доступен только на машинах, на которых установлен Xcode .
NSBundle
ДляВ случаях, когда программе необходимо прочитать свой собственный встроенный список, можно использовать NSBundle:
id someValue = [[NSBundle mainBundle] objectForInfoDictionaryKey:someKey];
Mach-O
Для случаев, когда программе необходимо прочитать встроенный списокПроизвольный файл, не прибегая к otool, программа может проанализировать информацию Mach-O в файле и извлечь его встроенный plist следующим образом:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <mach-o/loader.h>
#include <sys/mman.h>
#include <sys/stat.h>
#import <Foundation/Foundation.h>
id embeddedPlist(NSURL *executableURL) {
id plist = nil;
int fd;
struct stat stat_buf;
size_t size;
char *addr = NULL;
char *start_addr = NULL;
struct mach_header_64 *mh = NULL;
struct load_command *lc = NULL;
struct segment_command_64 *sc = NULL;
struct section_64 *sect = NULL;
// Open the file and get its size
fd = open([[executableURL path] UTF8String], O_RDONLY);
if (fd == -1) goto END_FUNCTION;
if (fstat(fd, &stat_buf) == -1) goto END_FILE;
size = stat_buf.st_size;
// Map the file to memory
addr = start_addr = mmap(0, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) goto END_FILE;
// The first bytes are the Mach-O header
mh = (struct mach_header_64 *)addr;
// Load commands follow the header
addr += sizeof(struct mach_header_64);
for (int icmd = 0; icmd < mh->ncmds; icmd++) {
lc = (struct load_command *)addr;
if (lc->cmd != LC_SEGMENT_64) {
addr += lc->cmdsize;
continue;
}
if (lc->cmdsize == 0) continue;
// It's a 64-bit segment
sc = (struct segment_command_64 *)addr;
if (strcmp("__TEXT", sc->segname) != 0 || sc->nsects == 0) {
addr += lc->cmdsize;
continue;
}
// It's the __TEXT segment and it has at least one section
// Section data follows segment data
addr += sizeof(struct segment_command_64);
for (int isect = 0; isect < sc->nsects; isect++) {
sect = (struct section_64 *)addr;
addr += sizeof(struct section_64);
if (strcmp("__info_plist", sect->sectname) != 0) continue;
// It's the __TEXT __info_plist section
NSData *data = [NSData dataWithBytes:(start_addr + sect->offset)
length:sect->size];
plist = [NSPropertyListSerialization propertyListWithData:data
options:NSPropertyListImmutable
format:NULL
error:NULL];
goto END_MMAP;
}
}
END_MMAP:
munmap(addr, size);
END_FILE:
close(fd);
END_FUNCTION:
return plist;
}
и:
NSURL *url = [NSURL fileURLWithPath:@"/path/to/some/file"];
id plist = embeddedPlist(url);
if ([plist isKindOfClass:[NSDictionary class]]) {
NSDictionary *info = plist;
id someValue = [info objectForKey:someKey];
}
Обратите внимание, что embeddedPlist()
имеет некоторые ограничения: он ожидает, что файл будет тонким файлом Mach-O (т. Е. Он будет аварийно завершать работу с файлами, не относящимися к Mach-O, и не будет работать с толстыми файлами, содержащими, например, i386 и x86_64 Mach).-О данных);работает только с файлами x86_64;он не сообщает об ошибках.
Я выпустил BVPlistExtractor под лицензией MIT.Он определяет, является ли файл действительно тонким файлом Mach-O или толстым / универсальным файлом, и работает как с i386, так и с x86_64.