otool
Bạn có thể sử dụng otool (1) để đổ nội dung của phần này có chứa các plist nhúng:
otool -s __TEXT __info_plist /path/to/executable
và sau đó ống đầu ra của nó để xxd (1) để có được đại diện ASCII tương ứng:
otool -X -s __TEXT __info_plist /path/to/executable | xxd -r
Tuy nhiên, otool is only available in machines where Xcode has been installed.
NSBundle
Đối với trường hợp một chương trình cần phải đọc plist nhúng riêng của mình, NSBundle thể được sử dụng:
id someValue = [[NSBundle mainBundle] objectForInfoDictionaryKey:someKey];
Mach-O
Đối với trường hợp một chương trình cần đọc plist nhúng của một tập tin tùy ý mà không cần đến otool, chương trình có thể phân tích cú pháp thông tin Mach-O trong tệp và trích xuất plist nhúng của nó như sau:
#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;
}
và:
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];
}
Lưu ý rằng embeddedPlist()
có một số hạn chế: nó sẽ yêu cầu tập tin là một tập tin mỏng Mach-O (tức là, nó sẽ sụp đổ với các tập tin không Mach-O và nó sẽ không làm việc với các tệp chất béo có chứa, ví dụ, cả dữ liệu Mach-O i386 và x86_64); nó chỉ hoạt động với các tệp x86_64; nó không báo cáo lỗi.
Tôi đã tiếp tục và phát hành BVPlistExtractor theo giấy phép MIT. Nó phát hiện xem tệp thực sự là một tệp Mach-O mỏng hay một tệp chất béo/phổ dụng và hoạt động với cả i386 và x86_64.
Trong trường hợp bạn chưa thấy câu trả lời cập nhật, tôi đã phát hành [BVPlistExtractor] (https://github.com/bavarious/BVPlistExtractor). –
@Bavarious Thanks! BVPlistExtractor là hoàn hảo. – ETroll