[macOS翻译]Mach-O二进制文件的静态代码注入

369 阅读2分钟

本文由 简悦 SimpRead转码, 原文地址 jon-gabilondo-angulo-7635.medium.com

在《如何在 Mach-O 应用程序中注入代码》系列中,我广泛介绍了动态编码的不同方法......。

如何在Mach-O应用程序中注入代码系列中,我广泛介绍了不同的动态代码注入方法,其中最简单易行的方法是使用动态链接器 dyld 作为工具,在加载时注入dylib。dyld 被编程为注入在环境变量 DYLD_INSERT_LIBRARIES 中定义的库,而无需对应用程序进行任何修改。

Dynamic Code Injection(动态代码注入)的三部分系列文章中,我们介绍了注入成功的不同情况,深入探讨了 SIPAMFIentitlementsprovision profiles 等问题,这些问题有时会导致无法注入代码。

在本文中,我想讨论另一种众所周知的技术--静态代码注入(Static Code Injection),由于它需要修改 Mach-O 二进制文件,因此复杂程度大大提高。这种操作需要一些工具来修改二进制文件,而这些工具确实存在,其中之一就是我的 macho-inject

github.com/JonGabilond…

这项任务的主要动机是能够注入 Organismo iOS/Mac 仪器库。Organismo 是编程学生和用户界面爱好者学习应用程序内部结构的好工具。在学习过程中,我们还能了解到苹果公司的一些优秀安全机制,它们能让我们的电脑更加安全。

What is Static Code Injection

一般来说,"代码注入 "是指对二进制文件进行扩充,从而将非事先定义的代码加载到其进程内存地址中,以扩展其行为/功能,而无需经过代码创建者的意图或批准。

这可以通过两种方式实现、

  1. Dynamic。正如我们在使用 dyld 的三部分系列文章中所看到的,使用环境变量可以实现。

$ DYLD_INSERT_LIBRARIES=/path_to/Organismo-mac.framework/Versions/A/Organismo-mac /Applications/Calculator.app/Contents/MacOS/Calculator

  1. Static。修改二进制文件,使其承载新代码或 "新加载代码 "命令,以加载动态链接库。我们将重点关注后者。我们要修改二进制文件,添加一条新的 dylib 加载命令,将 "注入 "代码加载到 dylib 中。

What to Modify in the Mach-O Binary

Mach-O 的二进制文件非常丰富,可以在许多苹果文档和开发人员文章中找到。正是在这些文章中,我找到了了解 Mach-O 二进制结构的文档,以便能够修改它。因此,我将专门介绍向 Mach-O 二进制文件中注入库所必需的内容。

加载命令部分中的 LC_LOAD_DYLIB

二进制文件必须加载的 dylibs 定义在 "Load Commands(加载命令)"部分,描述为命令 LC_LOAD_DYLIB。我们要做的就是在该部分添加一个新的 LC_LOAD_DYLIB。最好是在 LC_CODE_SGINATURE 之前。

请参见下面的 LC_LOAD_DYLIB 命令:

计算器应用程序的 Mach-O-View 截图。显示 mach 头文件和加载命令。

二进制修补程序实现

macho-inject 工具是基于 C 代码实现的,它打开二进制文件并到达添加定义 LC_LOAD_DYLIB 的新结构的位置。

允许我们浏览和修改 Mach-O 内容的结构定义在文件 macho-headers.h 中。

这些结构用于创建 LC_LOAD_DYLIB 命令的数据:

struct dylib {    
    union lc_str  name;   /* library's path name */    
    uint32_t timestamp;   /* library's build time stamp */    
    uint32_t current_version;  /* library's current version number */
    uint32_t compatibility_version; /* library's compatibility version number*/
};
struct dylib_command {    
    uint32_t cmd;  /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB,                             LC_REEXPORT_DYLIB */    
    uint32_t cmdsize; /* includes pathname string */    
    struct dylib dylib;  /* the library identification */
};

注入代码在 injection.cpp 中:

在二进制文件中添加 LC_LOAD_DYLIB 命令:

    struct dylib_command dyld;
    fread(&dyld, sizeof(struct dylib_command), 1, binaryFile);
    
    ORGLOG_V("Attaching the library");
    
    dyld.cmd = LC_LOAD_DYLIB;
    dyld.cmdsize = load_dylib_size;
    dyld.dylib.compatibility_version = DYLIB_COMPATIBILITY_VERSION;
    dyld.dylib.current_version = DYLIB_CURRENT_VER;
    dyld.dylib.timestamp = 2;
    dyld.dylib.name.offset = sizeof(struct dylib_command);
    fseek(binaryFile, -sizeof(struct dylib_command), SEEK_CUR);
    fwrite(&dyld, sizeof(struct dylib_command), 1, binaryFile);
    
    size_t bytes = fwrite(dylibPath.string().data(), dylibPath.string().length(), 1, binaryFile); // write the dylib load string
    if (bytes==0) {
        ORGLOG_V("ERROR writing the loading path to the loading commands. In inject_dylib.");
    }
    
    // 7. Pad position. Maybe dylibPath is not exactly falling into size of long.
    if (dylibPath.string().length() % sizeof(long)) {
        fseek(binaryFile, sizeof(long) - (dylibPath.string().length() % sizeof(long)), SEEK_CUR);
    }

The Code Signature Will Be Broken

这种二进制修补的结果是,二进制文件(或应用程序)的代码签名将失效(如果它已被签名)。

正如我们在前几篇文章中所看到的,macho-inject 是一个强大的应用程序筛选工具,可以指示它重签名整个应用程序。重签名有很多陷阱,并不总是导致应用程序有效。请阅读之前的文章以了解其局限性。

Examples

$ macho_inject -s 'Apple Development: XXX (XXXX)' --hardened -v -f /pathto/Organismo.framework -o /pathto/AppSigning/Out /pathTo/WhatsApp.app

祝您在 macho-inject 中玩得开心!

谢谢 !