Reading data (embedded plist) linked to executable through -sectcreate __TEXT

后端 未结 4 1760
礼貌的吻别
礼貌的吻别 2021-01-31 09:55

I am linking a executable with a plist using -sectcreate __TEXT linker flags. Reason for this is mainly to use the SMJobBless() method. But I need to read plist lin

4条回答
  •  逝去的感伤
    2021-01-31 10:51

    otool

    You can use otool(1) to dump the contents of the section containing the embedded plist:

    otool -s __TEXT __info_plist /path/to/executable
    

    and then pipe its output to xxd(1) in order to obtain the corresponding ASCII representation:

    otool -X -s __TEXT __info_plist /path/to/executable | xxd -r
    

    However, otool is only available in machines where Xcode has been installed.

    NSBundle

    For the cases where a program needs to read its own embedded plist, NSBundle can be used:

    id someValue = [[NSBundle mainBundle] objectForInfoDictionaryKey:someKey];
    

    Mach-O

    For the cases where a program needs to read the embedded plist of an arbitrary file without resorting to otool, the program can parse the Mach-O information in the file and extract its embedded plist as follows:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #import 
    
    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;
    }
    

    and:

    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];
    }
    

    Note that embeddedPlist() has some limitations: it expects the file to be a thin Mach-O file (i.e., it will crash with non-Mach-O files and it won’t work with fat files containing, for example, both i386 and x86_64 Mach-O data); it only works with x86_64 files; it doesn’t report errors.

    I went ahead and released BVPlistExtractor under the MIT licence. It detects whether the file is indeed a thin Mach-O file or a fat/universal file, and works with both i386 and x86_64.

提交回复
热议问题