[iOS翻译]如何用Xcode 9.3构建一个iOS命令行工具

743 阅读5分钟

本文由 简悦SimpRead 转码,原文地址 bazad.github.io

在开发漏洞或在越狱的设备上工作时,建立命令行来......,往往很有用。

在进行一个即将到来的项目时,我发现我需要创建一个独立的Mach-O可执行文件,与一个通过Xcode管理的iOS应用程序共享代码(可能有很多代码)。该iOS应用程序将利用一个系统漏洞,提升权限,然后生成独立的二进制文件作为有效载荷。然而,我不想在命令行上构建有效载荷二进制文件:这不仅会给构建流程带来摩擦,而且还意味着在两个独立的构建系统中管理相同的共享代码文件。因此,我决定搞清楚如何在Xcode中添加一个独立的iOS可执行目标。

我在网上找不到关于如何让Xcode为iOS构建一个独立的Mach-O可执行文件的最新参考资料,所以在想出方法后,我决定写这篇指南。在这篇文章中,我们将创建一个Xcode项目,模仿macOS命令行工具项目类型。你也可以用我的ios-command-line-tool项目作为一个例子。

Xcode项目和产品类型

当您创建一个新的项目时,Xcode提供了一些模板来让您开始。对于macOS,"命令行工具 "模板将设置一个项目,构建一个独立的可执行二进制。不幸的是,Xcode并没有为iOS提供一个命令行工具模板。

Xcode项目的设置位于.xcodeproj目录下一个名为project.pbxproj的文件中。我的第一个想法是创建一个macOS命令行工具项目,然后编辑项目文件,将所有对macOS的引用(例如macOS SDK,x86-64架构)改为其iOS的对应物。不幸的是,这不起作用,失败的原因是 "目标指定产品类型为'com.apple.product-type.tool',但'iphoneos'平台没有这种产品类型 "的错误。很明显,我需要了解更多关于Xcode项目的信息。

Xcode管理构建设置的方式是通过一系列可以相互继承的构建规范。这些规范被定义在Xcode.app应用包内的.xcspec文件(实际上是plists)中。com.apple.product-type.tool "标识符是指一个特定的产品类型规范,它可以正确配置Xcode来构建一个命令行工具。在Xcode 9.3中,这个产品类型被定义在文件MacOSX Product Types.xcspec中。

下面是完整的 "com.apple.product-type.tool "定义。

// Tool (normal Unix command-line executable)
{   Type = ProductType;
    Identifier = com.apple.product-type.tool;
    Class = PBXToolProductType;
    Name = "Command-line Tool";
    Description = "Standalone command-line tool";
    IconNamePrefix = "TargetExecutable";
    DefaultTargetName = "Command-line Tool";
    DefaultBuildProperties = {
        FULL_PRODUCT_NAME = "$(EXECUTABLE_NAME)";
        MACH_O_TYPE = "mh_execute";
        EXECUTABLE_PREFIX = "";
        EXECUTABLE_SUFFIX = "";
        REZ_EXECUTABLE = YES;
        INSTALL_PATH = "/usr/local/bin";
        FRAMEWORK_FLAG_PREFIX = "-framework";
        LIBRARY_FLAG_PREFIX = "-l";
        LIBRARY_FLAG_NOSPACE = YES;
        GCC_DYNAMIC_NO_PIC = NO;
        GCC_SYMBOLS_PRIVATE_EXTERN = YES;
        GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
        STRIP_STYLE = "all";
        CODE_SIGNING_ALLOWED = YES;
    };
    PackageTypes = (
        com.apple.package-type.mach-o-executable   // default
    );
},

默认的构建属性(在DefaultBuildProperties字典中)可以在project.pbxproj文件中被覆盖。然而,其他的设置是不能被覆盖的。因此,如果我们要构建一个iOS命令行工具,我们需要找到一个不同的产品类型,在不可重写的属性中尽可能地接近这个产品。

我们可以在Embedded-Device.xcspecEmbedded-Shared.xcspec文件中查看iOS上的产品类型。与 "com.apple.product-type.tool "最相似的iOS产品类型是 "com.apple.product-type.library.dynamic"。

{
    Class = PBXDynamicLibraryProductType;
    DefaultBuildProperties = {
...
    };
    DefaultTargetName = "Dynamic Library";
    Description = "Dynamic library";
    IconNamePrefix = "TargetLibrary";
    Identifier = "com.apple.product-type.library.dynamic";
    Name = "Dynamic Library";
    PackageTypes = (
        com.apple.package-type.mach-o-dylib
    );
    Type = ProductType;
}

这表明我们应该能够通过从动态库产品类型开始,然后编辑构建设置以匹配真正的命令行工具,来构建一个iOS的命令行工具。

唯一的问题是,Xcode在用户界面中没有提供为iOS创建动态库的选项。最接近的选项是 "Cocoa Touch Framework "和 "Cocoa Touch Static Library"。其中,静态库最接近于动态库(框架有额外的包装,我们不需要)。因此,我们将在Xcode中把我们的命令行工具项目作为一个静态库开始。

创建一个iOS命令行工具Xcode项目的步骤

首先,创建一个新的Xcode项目。作为项目模板,选择 "Cocoa Touch Static Library"。

image.png (使用 "Cocoa Touch Static Library "iOS模板创建一个Xcode项目。)

一旦你打开这个项目,Xcode应该看起来像这样。

image.png (创建一个iOS静态库项目后的Xcode。)

现在,关闭Xcode并手工编辑project.pbxproj文件。将目标的productType属性从com.apple.product-type.library.static改为com.apple.product-type.library.dynamic

重新打开Xcode并导航到目标设置的 "常规 "标签。选择一个签署团队,然后构建项目。构建应该成功完成。你现在有一个iOS动态库项目。

(在将项目转换为动态库后,Xcode将需要一个签名团队。)

现在我们将开始把动态库项目转换为一个命令行工具。在目标的Xcode设置中,导航到 "Build Phases "并删除 "Copy Files "阶段。删除自动生成的头文件,编辑.m文件,添加一个备用的main函数。

image.png (删除头文件,在.m文件中添加一个主函数。)

接下来我们将在目标的构建配置中添加以下设置。

DYLIB_COMPATIBILITY_VERSION = "";
DYLIB_CURRENT_VERSION = "";
EXECUTABLE_PREFIX = "";
EXECUTABLE_SUFFIX = "";
MACH_O_TYPE = mh_execute;

导航到目标的 "构建设置 "标签。在 "链接 "下,清除 "兼容版本 "和 "当前版本",并将 "Mach-O类型 "设置为 "可执行"。在 "打包 "下,确保 "可执行前缀 "是清楚的。最后,点击顶部的加号按钮,添加一个新的用户定义的设置。将设置的名称设为 "EXECUTABLE_SUFFIX",并将其值留空。

image.png (配置目标设置以构建可执行文件而不是dylib。)

就这样吧! 构建该项目,最后你应该得到一个用Xcode构建的独立的iOS命令行工具。

(现在Xcode将构建一个独立的iOS可执行文件。)

作为参考,我在GitHub上包含了这个ios-command-line-tool项目。请自由使用它作为一个模板。我把这个项目放在了公共领域。


www.deepl.com 翻译