通过 -sectcreate __TEXT 读取链接到可执行文件的数据(嵌入式 plist)

发布于 2024-12-09 22:30:15 字数 251 浏览 0 评论 0原文

我使用 -sectcreate __TEXT 链接器标志将可执行文件与 plist 链接。这样做的原因主要是使用了SMJobBless()方法。但我需要阅读从另一个应用程序链接的 plist。这只是因为我需要在 10.5 系统上安装相同的特权应用程序,并且无法在 10.5 上使用 SMJobBless()。

如何使用 Objective-C 读取此链接的 plist,以便我可以将其复制到 /Library/LaunchDaemons/ 自己?

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 linked from another application. This is only because I need to install the same privileged application on a 10.5 system and I can’t use SMJobBless() on 10.5.

How do I read this linked plist using Objective-C so I can copy it to /Library/LaunchDaemons/ myself?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

猥琐帝 2024-12-16 22:30:15

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

对于程序需要读取自己嵌入的 plist 的情况,可以使用 NSBundle:

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

Mach-O

对于程序需要读取任意文件的嵌入 plist 而不借助 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-O 数据的胖文件);它仅适用于 x86_64 文件;它不报告错误。

我继续在 MIT 许可下发布了 BVPlistExtractor。它检测文件是否确实是瘦 Mach-O 文件还是胖/通用文件,并且适用于 i386 和 x86_64。

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

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.

时光沙漏 2024-12-16 22:30:15

有一个 CoreFoundation 函数可以实现此目的:CFBundleCopyInfoDictionaryForURL()。从文档中:

对于目录 URL,这相当于 CFBundleCopyInfoDictionaryInDirectory。对于表示非捆绑应用程序的纯文件 URL,此函数将尝试从文件的 (__TEXT, __info_plist) 部分(对于 Mach- O 文件)或来自 plst 资源。

它可在 Mac OS X v10.2 及更高版本上使用。如果您在 Cocoa 中使用,则可以执行此操作(前提是您有该捆绑包的 (NSURL*)url):

NSDictionary* infoPlist = [ (NSDictionary*) CFBundleCopyInfoDictionaryForURL( (CFURLRef) url ) autorelease];

There's a CoreFoundation function for that: CFBundleCopyInfoDictionaryForURL(). From the documentation:

For a directory URL, this is equivalent to CFBundleCopyInfoDictionaryInDirectory. For a plain file URL representing an unbundled application, this function will attempt to read an information dictionary either from the (__TEXT, __info_plist) section of the file (for a Mach-O file) or from a plst resource.

It's available on Mac OS X v10.2 and later. If you use in Cocoa you can do this (provided you have an (NSURL*)url for the bundle):

NSDictionary* infoPlist = [ (NSDictionary*) CFBundleCopyInfoDictionaryForURL( (CFURLRef) url ) autorelease];
醉梦枕江山 2024-12-16 22:30:15

用户的计算机可能没有安装otool,我也遇到了同样的问题。解决方案是使用 launchctl,它保证在任何现代 Mac 上都存在。

它有一个 plist 子命令,它执行以下操作:

Prints the the property list embedded in the __TEXT,__info_plist segment/section
of the target Mach-O or the specified segment/section.

如果您未指定该部分,则默认情况下它将打印 __TEXT。提供的唯一参数是可执行文件的路径:

launchctl plist /Library/PrivilegedHelperTools/com.sparklabs.ViscosityHelper

如果您提供了路径,输出可能如下所示:

{
    "CFBundleIdentifier" = "com.sparklabs.ViscosityHelper";
    "SMAuthorizedClients" = (
        "anchor apple generic and identifier "com.viscosityvpn.Viscosity" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = "34XR7GXFPX")";
    );
    "CFBundleName" = "ViscosityHelper";
    "CFBundleVersion" = "548";
    "CFBundleInfoDictionaryVersion" = "6.0";
};

它可以在命令中使用-line 以及通过 NSTask (swift 中的 Process)类的代码。

User's computer would not probably have otool installed, and I had the same problem. The solution was to use launchctl, which is guaranteed to be present at any modern Mac.

It has a plist subcommand which does the following:

Prints the the property list embedded in the __TEXT,__info_plist segment/section
of the target Mach-O or the specified segment/section.

If you do not specify the section, it prints __TEXT by default. The only argument to provide is the path to the executable:

launchctl plist /Library/PrivilegedHelperTools/com.sparklabs.ViscosityHelper

If you have given path, output might be something like this:

{
    "CFBundleIdentifier" = "com.sparklabs.ViscosityHelper";
    "SMAuthorizedClients" = (
        "anchor apple generic and identifier "com.viscosityvpn.Viscosity" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = "34XR7GXFPX")";
    );
    "CFBundleName" = "ViscosityHelper";
    "CFBundleVersion" = "548";
    "CFBundleInfoDictionaryVersion" = "6.0";
};

It can be used in command-line as well as from code via NSTask (Process in swift) class.

全部不再 2024-12-16 22:30:15

一种更简单的方法:

#include <mach-o/getsect.h>

unsigned long *len;
char *data = getsectdata("__TEXT", "__info_plist");

man getectdata。有很多关于如何访问各个部分(当前可执行文件、任意可执行文件、框架等)的示例。

A much simpler approach:

#include <mach-o/getsect.h>

unsigned long *len;
char *data = getsectdata("__TEXT", "__info_plist");

man getsectdata. There's lots of examples on how to access various pieces (current executable, arbitrary executable, framework, etc).

青萝楚歌 2024-12-16 22:30:15

由于接受的答案中的 otool 命令似乎不再起作用(在 macOS Monterey 上产生乱码),因此您仍然可以通过命令行上的 mach-o 读取嵌入的 Info.plist:

otool -P ./binary

从手册页:

 -P     Print the info plist section, (__TEXT,__info_plist), as strings.

Since the otool command from the accepted answer does not seem to work anymore (produces gibberish on macOS Monterey), here is how you can still read an embedded Info.plist from a mach-o on the command line:

otool -P ./binary

From the man pages:

 -P     Print the info plist section, (__TEXT,__info_plist), as strings.
长不大的小祸害 2024-12-16 22:30:15

与最初的问题类似,我需要使用 -sectcreate __TEXT 读取嵌入在可执行文件中的信息和 launchd 属性列表。现在是 2021 年,我正在使用 Swift,但找不到任何现有的东西可以满足我的需求。我花了一些时间才弄清楚如何实现,所以我决定将其打包:EmbeddedPropertyList

如果您在使用时遇到任何问题,请在 Github 上打开问题时在此处回复。

Similarly to the original question I needed to read info and launchd property lists embedded in an executable with -sectcreate __TEXT. As it's 2021 I'm using Swift, but couldn't find anything existing to meet my needs. It took me a bit to figure out how, so I decided to package it up: EmbeddedPropertyList.

If you run into any issues using it, either reply here on open an issue on Github.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文