目录

  • Theos 简介
  • Theos 安装
  • Theos 的目录结构
  • Tweak 工程的目录结构
  • Tweak 插件加载图片资源
  • Tweak 插件的实现原理
  • 补充:Q & A
  • 其他注意点

Theos 简介

  • 相关链接

    Theos(iphonedev)

    Theos GitHub Wiki - Installation - Installation macOS

    Theos GitHub Wiki - Technical - Structure

  • 什么是 Theos?

    Theos 是一个跨平台的开发工具套件,用于在不使用 Xcode 的情况下管理、开发和部署 iOS 软件。它是人们为越狱的 iOS 构建插件(Tweak)的重要工具,大多数插件开发人员使用 Theos

    Theos 工具套件由以下 3 个重要的组件组成:

    1. 一个项目模板系统 ( NIC ),它为不同的目的创建可立即构建的空项目
    2. 一个由 GNU Make 驱动的强大构建系统,能够直接创建 .deb 包以在 Cydia 中分发
    3. Logos,一个内置的基于预处理器的指令库,旨在使 MobileSubstrate 插件的开发变得容易

    Theos 主要用于以越狱为中心的 iOS 开发(例如:MobileSubstrate Extension、PreferenceLoader Bundle、打算在 Cydia 中分发的应用程序),但也可用于其他类型的项目。这对于希望在不使用 macOS 或者 Xcode 的情况下开发基于 iPhone SDK 的应用程序的人很有帮助,因为 Theos 也可以在 Linux 和 iOS 上使用

    Theos 还包括 Logify.pl,它接收类的头文件(.h 文件)作为输入,并生成 Cydia Substrate 插件(.xm 文件)作为输出,生成的 Cydia Substrate 插件(.xm 文件)会 hook 该类头文件中声明的所有方法,并在这些方法被调用时打印日志消息。这有助于越狱开发者查看在使用目标 App 的过程中何时调用了某些方法

Theos 安装

  • 在 macOS 上安装 Theos

    本指南将帮助您在 macOS 设备上安装 Theos

    安装平台:macOS
    安装平台最低操作系统版本:Mavericks 10.9
    支持的目标平台:macOS、iOS、watchOS、tvOS、simulators

    以下说明中展示的所有命令均应以 user 用户身份运行,而不是以 root 用户身份运行。同样地,Theos 也应该以普通用户的身份运行,而不是以根用户的身份运行

    1. 安装以下前置条件:

      Homebrew(macOS 软件包管理工具)

      Xcode (必须安装),命令行工具包不足以让 Theos 工作。Xcode 包含针对所有苹果平台的 Toolchain。需要 Xcode 5.0 或者更新的版本。虽然支持 Xcode 4.4,但是仅在为 ARMv6 构建时(第一和第二代 iPhone 与 iPod touch)

      brew install ldid xz
      
    2. 设置 THEOS 环境变量:

      echo "export THEOS=~/theos" >> ~/.zprofile
      

      如果您使用的是 macOS 10.14 或者更早的版本,请将 ~/.zprofile 更改为 ~/.profile。因为在 macOS 10.15 中,默认的 shell 已经从 bash 更改为 zsh。您可以通过运行 echo $SHELL 来检查您使用的是哪一种 shell

      要使此更改生效,您必须重新启动 shell。打开一个新的 shell 界面并执行 echo $THEOS 以检查更改是否生效

    3. 将 Theos 克隆到您的设备:

      git clone --recursive https://github.com/theos/theos.git $THEOS
      
    4. 获取 Toolchain:

      Xcode 包含 Toolchain

    5. 获取 iOS SDK:

      虽然 Xcode 始终提供最新的 iOS SDK,但是从 Xcode 7.3 开始,它不再包含您可以链接的私有 Framework。这在开发 Tweak 时,可能是一个问题。您可以从 我们的 SDK 存储库 中获取修补过的 SDK

      curl -LO https://github.com/theos/sdks/archive/master.zip
      TMP=$(mktemp -d)
      unzip master.zip -d $TMP
      mv $TMP/sdks-master/*.sdk $THEOS/sdks
      rm -r master.zip $TMP
      
  • 关于在安装 Theos 时,子模块克隆失败的问题

    在安装 Theos 时,执行第 ③ 步:将 Theos 克隆到您的设备,正常情况下 git 的输出如下所示:

    ~ > git clone --recursive https://github.com/theos/theos.git $THEOS
    正克隆到 '/Users/Airths/theos'...
    remote: Enumerating objects: 9798, done.
    remote: Counting objects: 100% (451/451), done.
    remote: Compressing objects: 100% (242/242), done.
    remote: Total 9798 (delta 282), reused 343 (delta 206), pack-reused 9347
    接收对象中: 100% (9798/9798), 2.42 MiB | 111.00 KiB/s, 完成.
    处理 delta 中: 100% (6174/6174), 完成.
    子模组 'vendor/dm.pl'(https://github.com/theos/dm.pl.git)已对路径 'vendor/dm.pl' 注册
    子模组 'vendor/include'(https://github.com/theos/headers.git)已对路径 'vendor/include' 注册
    子模组 'vendor/lib'(https://github.com/theos/lib.git)已对路径 'vendor/lib' 注册
    子模组 'vendor/logos'(https://github.com/theos/logos.git)已对路径 'vendor/logos' 注册
    子模组 'vendor/nic'(https://github.com/theos/nic.git)已对路径 'vendor/nic' 注册
    子模组 'vendor/templates'(https://github.com/theos/templates.git)已对路径 'vendor/templates' 注册
    正克隆到 '/Users/Airths/theos/vendor/dm.pl'...
    remote: Enumerating objects: 142, done.
    remote: Total 142 (delta 0), reused 0 (delta 0), pack-reused 142
    接收对象中: 100% (142/142), 54.20 KiB | 75.00 KiB/s, 完成.
    处理 delta 中: 100% (72/72), 完成.
    正克隆到 '/Users/Airths/theos/vendor/include'...
    remote: Enumerating objects: 3253, done.
    remote: Counting objects: 100% (401/401), done.
    remote: Compressing objects: 100% (326/326), done.
    remote: Total 3253 (delta 105), reused 239 (delta 71), pack-reused 2852
    接收对象中: 100% (3253/3253), 1.05 MiB | 29.00 KiB/s, 完成.
    处理 delta 中: 100% (1330/1330), 完成.
    正克隆到 '/Users/Airths/theos/vendor/lib'...
    remote: Enumerating objects: 833, done.
    remote: Counting objects: 100% (329/329), done.
    remote: Compressing objects: 100% (206/206), done.
    remote: Total 833 (delta 162), reused 263 (delta 113), pack-reused 504
    接收对象中: 100% (833/833), 632.52 KiB | 26.00 KiB/s, 完成.
    处理 delta 中: 100% (391/391), 完成.
    正克隆到 '/Users/Airths/theos/vendor/logos'...
    remote: Enumerating objects: 2619, done.
    remote: Counting objects: 100% (137/137), done.
    remote: Compressing objects: 100% (80/80), done.
    remote: Total 2619 (delta 63), reused 121 (delta 57), pack-reused 2482
    接收对象中: 100% (2619/2619), 516.99 KiB | 21.00 KiB/s, 完成.
    处理 delta 中: 100% (1029/1029), 完成.
    正克隆到 '/Users/Airths/theos/vendor/nic'...
    remote: Enumerating objects: 529, done.
    remote: Counting objects: 100% (5/5), done.
    remote: Compressing objects: 100% (5/5), done.
    remote: Total 529 (delta 0), reused 2 (delta 0), pack-reused 524
    接收对象中: 100% (529/529), 107.89 KiB | 29.00 KiB/s, 完成.
    处理 delta 中: 100% (155/155), 完成.
    正克隆到 '/Users/Airths/theos/vendor/templates'...
    remote: Enumerating objects: 1056, done.
    remote: Counting objects: 100% (260/260), done.
    remote: Compressing objects: 100% (69/69), done.
    remote: Total 1056 (delta 215), reused 208 (delta 184), pack-reused 796
    接收对象中: 100% (1056/1056), 283.62 KiB | 222.00 KiB/s, 完成.
    处理 delta 中: 100% (618/618), 完成.
    子模组路径 'vendor/dm.pl':检出 '9723deb8b99cd7aa071ec1fad310df2ff80d1cf2'
    子模组路径 'vendor/include':检出 '30aabe179bb275646086a6249953a9f617395dcb'
    子模组路径 'vendor/lib':检出 '8b7f487ee82d6a1357432ab2599f617ce8e5a82e'
    子模组路径 'vendor/logos':检出 'a733c99539ed9ef2a3914528d8c5522dc2396789'
    子模组路径 'vendor/nic':检出 '1f2e89d4acb805a81503ac3ea4c7115ca55f75a1'
    子模组路径 'vendor/templates':检出 '8af2906bf6f6dd13d97302405316bd8a5419a265'
    

    但是因为 GitHub 的服务器在国外,网络连接不稳定,所以在克隆 theos/vendor 目录下的子模块时,很容易失败。子模块克隆失败时,git 会出现以下报错信息(以 vendor/dm.pl 子模块克隆失败为例):

    fatal: 无法访问 'https://github.com/theos/dm.pl.git/':LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443
    fatal: 无法克隆 'https://github.com/theos/dm.pl.git' 到子模组路径 '/Users/Airths/theos/vendor/dm.pl'
    克隆 'vendor/dm.pl' 失败。按计划重试
    

    耐心地多尝试几次就好了,一定要确保 vendor/ 目录下的所有子模块都有被克隆下来并成功检出

  • Theos 备用下载地址

    因为将 Theos 克隆到本地设备的过程确实不容易成功,所以我上传了一份已经克隆完整的 Theos 供开发者下载:Theos 备用下载地址

Theos 的目录结构

关于 Theos 内部目录结构的各部分的信息:

  • bin/:包含 Theos 内部使用和用户外部使用的各种脚本

  • include/:提供给您放置您自己的头文件

  • lib/:提供给您放置您自己的库(Library)和框架(Framework)

  • makefiles/:makefiles 构成了 Theos 本身的大部分内容

    1. install/:关于在不同的平台上安装软件包的规则。这些命令将在安装和卸载软件包时执行

    2. instance/:在构建单个实例(项目)时,包含在 sub-make 中的 Makefile。这包括对源代码的编译

    3. master/:Makefile 包括从 master make 调用

    4. package/:关于为各种打包格式构建软件包的规则。这些命令将在构建软件包时执行

    5. platform/:根据当前操作系统平台进行 Makefile。这些都为平台设置了适当的 Theos 环境

    6. targets/:根据当前操作系统平台和目标操作系统平台进行 Makefile。这些都为平台设置了适合构建的 Theos 环境

  • mod/:提供给您放置模块(module)。Theos 会自动包含来自此目录的各种文件

  • sdks/:提供给您放置 SDK

  • templates/:提供给您放置您自己的 NIC 模板

  • toolchain/:提供给您放置 Toolchain。如 Installation 中所述

  • vendor/:Theos 包含的子模块组件

    1. dm.pl/:用于构建 deb 包的 脚本文件,无需 dpkg-deb

    2. include/:对大多数项目来说,可能有用也可能没有用的,内置头文件(Built-in headers)

    3. lib/:对大多数项目来说,可能有用也可能没有用的,内置库(Built-in library)

    4. logos/:Logos 的文件

    5. nic/:NIC 的文件

    6. templates/:可用于使用 NIC 搭建新项目的 内置模板(Built-in templates)

  • Prefix.pch:导入到所有基于 C 语言的编译过程中的前缀头文件(prefix header)。为旧(legacy)项目提供宏和框架导入

Tweak 工程的目录结构

当开发者创建好 Tweak 工程后,会在工程目录中看到以下 4 个文件(假设创建的工程名称为 Demo):

  1. control
  2. Demo.plist工程名称.plist
  3. Makefile
  4. Tweak.x

接来下分别介绍这 4 个文件的作用:

  • ① control 文件

    该文件记录了 Tweak 工程的基本信息,在 make package 时,会被打包进 .deb 包中,该文件的默认内容如下所示:

    # Package 字段用于描述此 deb 包的名称,和 BundleID 一样采用域名反写的命名方式,可以按需更改
    Package: com.hcg.demo# Name字段用于描述此 Tweak 工程的名称,可以按需更改
    Name: Demo# Version 字段用于描述此 deb 包的版本号,可以按需更改
    Version: 0.0.1# Architecture 字段用于描述此 deb 包安装的目标设备的 CPU 架构,不要更改
    Architecture: iphoneos-arm# Description 字段用于对此 deb 包的功能做简单的介绍,可以按需更改
    Description: An awesome MobileSubstrate tweak!# Maintainer 字段用于描述此 deb 包的维护者,即此 deb 包的制作者而非 tweak 的作者,可以按需更改
    Maintainer: Airths LionHeart <994923259@qq.com># Author 字段用于描述 tweak 的作者,而非此 deb 包的维护者,可以按需更改
    Author: Airths LionHeart <994923259@qq.com># Section 字段用于描述此 deb 包所属的程序类别,不要更改
    Section: Tweaks# Depends 字段用于描述此 deb 包的"依赖"。"依赖"指的是此 Tweak 插件运行的基本条件,可以按需更改
    # 可以填写固件版本或者其他程序,如果当前的 iOS 不满足"依赖"中所定义的条件,则此 Tweak 插件无法正常工作
    # 例如,以下键值表示:目标设备的 iOS 版本必须在 6.0 以上,并且安装的 mobilesubstrate 的版本必须在 0.9.5000 以上,此 Tweak 插件才能正常工作
    Depends: mobilesubstrate (>= 0.9.5000), firmware (>=6.0)
    

    需要注意的是,Theos 在生成 .deb 包时,会对 control 文件做进一步的处理。比如:

    1. 更改 Version 字段为 0.0.1-N,用于标识 Theos 的打包次数,以方便管理
    2. 增加 Installed-Size 字段,用于描述 .deb 包安装后的估算大小,与实际安装大小可能会有偏差,不要更改

    control 文件中可以自定义的字段还有很多,一般上面介绍的字段就已经足够了。关于 control 文件中其他字段的详细信息,可以查看 Debian 官方文档中对于 control 文件及其字段的介绍

  • ② Demo.plist(工程名称.plist)文件

    该文件记录了 Tweak 工程的配置信息,描述了 Tweak 插件的作用范围,该文件的默认内容如下所示:

    Filter 下是一系列数组,可以分为以下 3 类:

    1. Bundles:用于指定若干个 Bundle 为 Tweak 插件的作用对象。如:com.apple.springboard
    2. Classes:用于指定若干个 Class 为 Tweak 插件的作用对象。如:NSString
    3. Executables:用于指定若干个可执行文件为 Tweak 插件的作用对象。如:callservicesd

    这 3 种类型的数组可以混合使用,但是当 Filter 下有不同类型的数组时,需要添加一个 Mode: Any 键值对。当 Filter 下只有一种类型的数组时,不需要添加 Mode: Any 键值对

    有关过滤器 Filter 更详细的介绍,请参阅 Cydia Substrate(iOS)- Cydia Substrate 简介 - MobileLoader 之 Filter(过滤器)

  • ③ Makefile 文件

    该文件用来指定 Tweak 工程(编译、链接、打包、安装)时要用到的文件、框架、库等信息,将 Tweak 工程整个(编译、链接、打包、安装)的过程自动化,该文件的默认内容如下所示:

    # TARGET 字段用于标识 Tweak 插件的部署目标所需具备的条件
    # 部署的目标设备:iPhone,使用的编译器:clang,使用的 SDk 版本:最新,部署的目标 iOS 版本 >= 7.0
    TARGET := iphone:clang:latest:7.0# INSTALL_TARGET_PROCESSES 字段用于标识在安装 Tweak 插件后要杀死的进程列表
    INSTALL_TARGET_PROCESSES = SpringBoard# 此条 include 指令用于导入 common Makefile,固定写法,不要更改
    include $(THEOS)/makefiles/common.mk# TWEAK_NAME 字段用于标识:nic.pl 在创建 Tweak 工程时开发者所输入的工程名称(Project Name)
    # 与 control 文件中的 "Name" 字段对应,不要更改
    TWEAK_NAME = Demo# Demo_FILES 字段(Demo 是工程名称)用于标识 Tweak 工程中要参与编译的源文件,可以按需更改
    # 如果 Tweak 工程中需要用到多个源文件,则需要用空格将各个文件名分开。也可以用通配符,但是需要指定源文件具体的存放路径
    # 比如:src 目录中有好几十个 .xm 文件,一个一个输入文件名称太繁琐且容易出错,可以写成
    # Demo_FILES = Tweak.x src/*.m
    Demo_FILES = Tweak.x# Demo_CFLAGS 字段(Demo 是工程名称)用于标识 Tweak 工程的编译器标志
    Demo_CFLAGS = -fobjc-arc# 此条 include 指令用于导入 tweak Makefile
    # 因为这里新建的是 Tweak 工程,所以需要导入 tweak Makefile,对应的值是 tweak.mk
    # Theos 在创建不同类型的工程时,会填入不同的值,以导入不同的 Makefile,例如:application.mk、tool.mk、... ...
    include $(THEOS_MAKE_PATH)/tweak.mk
    

    除了上述 Theos 在 Makefile 文件中提供的默认字段外,Theos 还为 Makefile 文件准备了许多其他的字段

    开发者可以通过在 Tweak 工程的 Makefile 文件中设置这些字段,或者将这些字段导出作为环境变量(以影响所有 Tweak 工程)来配置 Tweak 工程

    在 Tweak 工程的 Makefile 文件中的某个字段之前追加 export 关键字,会导致此字段影响该 Tweak 工程的所有子工程。这对于某些字段非常有用,例如 SYSROOTTARGET

    以下是 Makefile 文件中常用字段的列表:

    变量名:THEOS
    说明:安装在开发者机器上的 Theos 的目录,它是平台相关的
    默认值:
    备注:变量名:PACKAGE_VERSION
    说明:当前的构建版本号
    默认值:$(THEOS_PACKAGE_BASE_VERSION)-$(VERSION.INC_BUILD_NUMBER)
    备注:PACKAGE_VERSION=$(THEOS_PACKAGE_BASE_VERSION) 可用于禁用调试版本号变量名:ARCHS
    说明:要构建的目标的 CPU 架构列表。可能的值: i386、x86_64、armv6、armv7、armv7s、armv7f、armv7v、arm64、arm64e
    默认值:armv7 arm64 arm64e
    备注:arm64e 设备在大多数情况下可以运行 arm64 二进制文件。但是,PreferenceLoader 通常无法在 arm64e 设备上加载 arm64 的 Bundle变量名:TARGET
    说明:构建目标的说明书。可以排除某些值。格式:platform:compiler:sdk_version:deployment_version
    默认值:
    备注:变量名:INSTALL_TARGET_PROCESSES
    说明:指定要在安装时重新加载的进程列表。例子:INSTALL_TARGET_PROCESSES = SpringBoard Spotlight
    默认值:
    备注:变量名:SYSROOT
    说明:要用于构建此项目的 SDK 的根目录。例如:$(THEOS)/sdks/iPhoneOS11.2.sdk。只有在需要针对特定的 SDK 构建项目时才需要使用此值
    默认值:$(THEOS)/SDKs/iPhoneOS<sdk_version>.sdk
    备注:变量名:TWEAK_NAME
    说明:在 _Tweak_ 项目中,此项目的名称。Theos 将在您的项目目录中查找 <TWEAK_NAME>.plist,因此请确保 plist 文件共享此处定义的名称
    默认值:
    备注:变量名:$(TWEAK_NAME)_FILES
    说明:要为项目编译的文件列表。.x* 格式的文件将在编译前通过 Logos 进行预处理。示例值:Tweak.xm AnotherFileWithHooks.x MyCoolObjcClass.m
    默认值:
    备注:shellscript 宏可用于包含大量文件,而无需手动键入所有文件变量名:$(TWEAK_NAME)_GENERATOR
    说明:为 Logos 指定 hook 生成器。可能的值是区分大小写的 MobileSubstrate 和 internal。例子:$(TWEAK_NAME)_GENERATOR = internal
    默认值:MobileSubstrate
    备注:变量名:THEOS_DEVICE_IP
    说明:用于将软件包安装到远程设备的 IP
    默认值:
    备注:(也可以是 ~/.ssh/config 中定义的主机名)变量名:THEOS_DEVICE_PORT
    说明:用于将软件包安装到远程设备的端口
    默认值:
    备注:变量名:THEOS_BUILD_DIR
    说明:包将放置在指定的目录中
    默认值: .
    备注:变量名:DEBUG
    说明:设置为 1 以启用调试模式
    默认值:undefined
    备注:变量名:$(TWEAK_NAME)_FRAMEWORKS
    说明:用于指定要导入的框架
    默认值:
    备注:变量名:$(TWEAK_NAME)_PRIVATE_FRAMEWORKS
    说明:用于指定要导入的私有框架
    默认值:
    备注:
    
  • ④ Tweak.x 文件

    该文件用来实现 Tweak 插件的具体功能(在进行逆向开发时,编写代码的地方),支持 LogosCC++ 语法,该文件的默认内容如下所示:

    /* How to Hook with Logos
    Hooks are written with syntax similar to that of an Objective-C @implementation.
    You don't need to #include <substrate.h>, it will be done automatically, as will
    the generation of a class list and an automatic constructor.%hook ClassName// Hooking a class method
    +(id)sharedInstance {return %orig;
    }// Hooking an instance method with an argument.
    -(void)messageName:(int)argument {%log; // Write a message about this call, including its class, name and arguments, to the system log.%orig; // Call through to the original function with its original arguments.%orig(nil); // Call through to the original function with a custom argument.// If you use %orig(), you MUST supply all arguments (except for self and _cmd, the automatically generated ones.)
    }// Hooking an instance method with no arguments.
    -(id)noArguments {%log;id awesome = %orig;[awesome doSomethingElse];return awesome;
    }// Always make sure you clean up after yourself; Not doing so could have grave consequences!
    %end
    */
    

    有关 Logos 语法更详细的介绍,请参阅 Theos(四):Logos && logify.pl

Tweak 插件加载图片资源

  • iOS 中常用的 2 种加载图片资源的方式

    // 1.根据指定的图片名称从 App 的 Main Bundle 中加载图片
    NSString* imageName = @"hcgImage0.png";
    UIImage* image0 = [UIImage imageNamed:imageName];// 2.根据指定的图片路径从 iOS 系统中的特定位置加载图片
    //   在非越狱的 iOS 设备上受沙盒机制的限制,在越狱的 iOS 设备上不受沙盒机制的限制
    NSString* docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString* imagePath = [docPath stringByAppendingPathComponent:@"hcgImage1.png"];
    UIImage* image1 = [[UIImage alloc] initWithContentsOfFile:imagePath];
    

    因为使用 Tweak 单独开发的插件无法从宿主 App 的 Main Bundle 中读取图片(而且也无法将图片放置到宿主 App 的 Main Bundle 中)
    所以 Tweak 插件需要使用第二种方式 -[UIImage initWithContentsOfFile:],根据指定的图片路径从 iOS 系统中的特定位置加载图片

  • Tweak 工程的 layout 目录

    虽然我们已经知道了 Tweak 插件可以使用 -[UIImage initWithContentsOfFile:] 方法加载图片资源
    但是我们依然面临着 3 个问题:

    1. 在编写 Tweak 工程时,我们应该将图片资源放在工程目录的哪个位置?
    2. 在安装 Tweak 插件时,插件中所包含的图片资源会被放置到 iOS 系统的哪个位置?
    3. 在运行 Tweak 插件时,我们应该去哪里加载这些图片资源?

    为了解决这些问题,Theos 在 Tweak 工程下提供了一个 layout 目录:layout 目录用于充当目标 iOS 设备的根目录。在安装 Tweak 插件到目标 iOS 设备时,layout 目录会被映射为目标 iOS 设备的根目录,layout 目录中的资源会被拷贝到目标 iOS 设备的根目录中

    将图片资源放置到 Tweak 工程的 layout 目录下,就相当于将图片资源放置到了目标 iOS 设备的根目录下,虽然这时候我们可以在 Tweak 插件中引用到该图片资源了,但是直接将图片资源放置到目标 iOS 设备的根路径下显然是不合适的,因为如果 Tweak 插件的图片资源较多的话,会使目标 iOS 设备的根目录很乱

    因此,我们需要将所有 Tweak 插件中用到的图片资源统一放置到目标 iOS 设备特定的目录中,也就是说,需要在目标 iOS 设备上指定一个用于存放所有 Tweak 插件图片资源的资源库。在 iOS 的逆向开发中,一般使用 /Library/PreferenceLoader/Preferences/ 作为存放 Tweak 插件图片资源的资源库。当然,这只是个小小的建议,并不是强制的要求

    比如,我们的 Tweak 工程名称为 WeChatTweak,则我们可以在该 Tweak 工程下新建如下的目录:

    之后,我们在调用图片资源时,只需要填写图片资源的全路径即可:

    NSString* imagePath = @"/Library/PreferenceLoader/Preferences/WeChatTweak/hcgImage1.png";
    UIImage* image1 = [[UIImage alloc] initWithContentsOfFile:imagePath];
    

    当然,如果 Tweak 插件中用到很多图片资源的话,我们可以把用于存放该 Tweak 工程图片资源的目录抽取成一个宏,不用每次都写那么一大串:

    // 1.在宏定义中,a (空格) b 的含义是 a/b
    // 2.在宏定义中,# 是字符串操作符,用于将参数序列化成一个字符串。#abc 的含义为 @"abc"
    #define IMAGE_PATH(IMG_NAME) @"/Library/PreferenceLoader/Preferences/WeChatTweak" #IMG_NAME
    

    我们下次再调用图片资源就很简洁了:

    // 注意:直接写图片名称 hcgImage1.png,不用加 @""
    UIImage* image1 = [[UIImage alloc] initWithContentsOfFile:IMAGE_PATH(hcgImage1.png)];
    

Tweak 插件的实现原理

  • 在 Tweak 工程目录下运行:make、make package、make install

    有 Tweak 工程 WeChatTweak

    在 Tweak 工程 WeChatTweak 的根目录运行 make 指令,将生成隐藏目录 .theos,并将 Tweak 代码编译为动态库:

    ~/Desktop/TheosDemo/wechattweak > make
    # 提示并行构建(即并行编译)不可用,构建速度可能会比较慢。建议升级 GNU Make
    ==> Notice: Build may be slow as Theos isn’t using all available CPU cores on this computer. Consider upgrading GNU Make: https://github.com/theos/theos/wiki/Parallel-Building
    # 提示开始构建项目
    ==> Making all for tweak WeChatTweak…
    # 2.1构建 armv7 架构
    ==> Preprocessing Tweak.x…
    ==> Compiling Tweak.x (armv7)…
    ==> Linking tweak WeChatTweak (armv7)…
    ==> Generating debug symbols for WeChatTweak…
    warning: no debug symbols in executable (-arch armv7)
    rm /Users/Airths/Desktop/TheosDemo/wechattweak/.theos/obj/debug/armv7/Tweak.x.m
    # 2.2构建 arm64 架构
    ==> Preprocessing Tweak.x…
    ==> Compiling Tweak.x (arm64)…
    ==> Linking tweak WeChatTweak (arm64)…
    ==> Generating debug symbols for WeChatTweak…
    warning: no debug symbols in executable (-arch arm64)
    rm /Users/Airths/Desktop/TheosDemo/wechattweak/.theos/obj/debug/arm64/Tweak.x.m
    # 2.3合并以上的 CPU 架构,并进行签名
    ==> Merging tweak WeChatTweak…
    ==> Signing WeChatTweak…
    
    # 生成的成隐藏目录 .theos 如下所示:
    ~/Desktop/TheosDemo/wechattweak/.theos > tree
    .
    ├── build_session
    └── obj└── debug├── WeChatTweak.dylib├── arm64│   ├── Tweak.x.04c5eecd.Td│   ├── Tweak.x.04c5eecd.o│   ├── WeChatTweak.dylib│   └── WeChatTweak.dylib.dSYM│       └── Contents│           ├── Info.plist│           └── Resources│               └── DWARF│                   └── WeChatTweak.dylib└── armv7├── Tweak.x.186d1d90.Td├── Tweak.x.186d1d90.o├── WeChatTweak.dylib└── WeChatTweak.dylib.dSYM└── Contents├── Info.plist└── Resources└── DWARF└── WeChatTweak.dylib12 directories, 12 files
    

    在 Tweak 工程 WeChatTweak 的根目录运行 make package 指令,将生成 packages 目录,并将(WeChatTweak.dylib + WeChatTweak.plist + 图片资源)打包成 .deb 文件:

    ~/Desktop/TheosDemo/wechattweak > make package
    # 提示并行构建(即并行编译)不可用,构建速度可能会比较慢。建议升级 GNU Make
    ==> Notice: Build may be slow as Theos isn’t using all available CPU cores on this computer. Consider upgrading GNU Make: https://github.com/theos/theos/wiki/Parallel-Building
    # 提示开始构建项目(项目已经编译完成,并且没有对代码进行任何改动)
    # 也就是说,在执行 make package 指令时,会再次对 Tweak 工程进行编译
    ==> Making all for tweak WeChatTweak…
    make[2]: Nothing to be done for `internal-library-compile'.
    # 提示开始打包项目
    ==> Making stage for tweak WeChatTweak…
    dm.pl: building package `com.hcg.wechattweak:iphoneos-arm' in `./packages/com.hcg.wechattweak_0.0.1-1+debug_iphoneos-arm.deb'
    
    # 在打包 Tweak 工程时,.theos 目录下会新增出一些打包过程中用到的文件和目录:
    ~/Desktop/TheosDemo/wechattweak/.theos > tree
    .
    ├── _
    │   ├── DEBIAN
    │   │   └── control
    │   └── Library
    │       ├── MobileSubstrate
    │       │   └── DynamicLibraries
    │       │       ├── WeChatTweak.dylib
    │       │       └── WeChatTweak.plist
    │       └── PreferenceLoader
    │           └── Preferences
    │               └── WeChatTweak
    │                   └── hcgImage1.png
    ├── build_session
    ├── fakeroot
    ├── last_package
    ├── obj
    │   └── debug
    │       ├── WeChatTweak.dylib
    │       ├── arm64
    │       │   ├── Tweak.x.04c5eecd.Td
    │       │   ├── Tweak.x.04c5eecd.o
    │       │   ├── WeChatTweak.dylib
    │       │   └── WeChatTweak.dylib.dSYM
    │       │       └── Contents
    │       │           ├── Info.plist
    │       │           └── Resources
    │       │               └── DWARF
    │       │                   └── WeChatTweak.dylib
    │       └── armv7
    │           ├── Tweak.x.186d1d90.Td
    │           ├── Tweak.x.186d1d90.o
    │           ├── WeChatTweak.dylib
    │           └── WeChatTweak.dylib.dSYM
    │               └── Contents
    │                   ├── Info.plist
    │                   └── Resources
    │                       └── DWARF
    │                           └── WeChatTweak.dylib
    └── packages└── com.hcg.wechattweak-0.0.121 directories, 19 files
    
    # 在打包完 Tweak 工程后,生成的 .deb 包会被放置在 packages 目录下
    ~/Desktop/TheosDemo/wechattweak/packages > tree
    .
    └── com.hcg.wechattweak_0.0.1-1+debug_iphoneos-arm.deb0 directories, 1 file
    

    在工程根目录运行 make install 指令,将 .deb 包传输到 iOS 设备,并通过 Cydia 安装该 .deb 包:

  • Tweak 插件的实现原理

    ① 在 Tweak.x 文件中编写 Tweak 代码

    ② 使用 make 命令将 Tweak.x 文件编译为 .dylib 动态库

    ③ 使用 make package 命令将 .dylib 动态库以及同名的 .plist 文件打包为 .deb 文件

    ④ 使用 make install 命令将 .deb 文件传输到 iOS 设备,并通过 Cydia 安装 .deb 文件

    ⑤ Tweak 插件将会被安装在 /Library/MobileSubstrate/DynamicLibraries 目录中,其中:

    1. .dylib 动态库存放着编译后的 Tweak 代码
    2. .plist 文件存放着编译后的 Tweak 代码的作用范围(需要 hook 的 AppID、可执行文件名称、类名)

    ⑥ 在 App 启动时,Cydia Substrate 的 MobileLoader 首先会使用 dyld 的环境变量 DYLD_INSERT_LIBRARIES 将自己加载到正在运行的 App 进程中,然后它将查找目录 /Library/MobileSubstrate/DynamicLibraries/ 中的所有动态库,根据与 .dylib 同名的 .plist 文件所指定的作用范围(即过滤条件),有选择地在 App 进程中通过 dlopen 函数加载符合条件的动态库(动态库里面存放着编译后的 Tweak 代码)

    因此:
    通过 Theos 开发的 Tweak 插件其实只是修改内存中 App 的某些的方法的代码实现
    通过 Theos 开发的 Tweak 插件其实没有修改硬盘中 App 的 MachO 可执行文件

补充:Q & A

  • Question:make 命令主要做些了什么?

    将 Tweak 工程的代码编译为动态库(.dylib

  • Question:make package 命令主要做了些什么?

    .dylid 文件和 .plist 文件打包成 .deb 文件

  • Question:make install 命令主要做了些什么?

    .deb 文件传输到已越狱的 iOS 设备上,并通过 Cydia 安装 .deb 文件

  • 总结: make + make package + make install = make package install

  • Question:Tweak 插件被安装在已越狱的 iOS 设备的哪个位置上?

    Tweak 插件被安装在已越狱的 iOS 设备的 /Libray/MobileSubstrate/DynamicLibraries 目录中:
    *.dylib:编译后的 Tweak 代码
    *.plist:插件的作用范围

  • Question:未脱壳的 App 是否支持注入 Tweak 插件?

    支持。因为,Tweak 插件其实只是修改内存中 App 的某些的方法的代码实现,并没有修改硬盘中 App 的 MachO 可执行文件

  • Question:Tweak 插件的效果是永久的吗?

    视情况而定。如果更新了 App,新版的 App 中没有了被 hook 的类或者没有了被 hook 的方法,那么 Tweak 插件将会失效

  • Question:未越狱的 iOS 设备是否支持 Tweak 插件?

    不支持。Cydia Substrate 是 Tweak 插件安装、加载、运行的基础。未越狱的 iOS 设备无法安装 Cydia Substrate,也就不支持 Tweak 插件的安装、加载、运行

  • Question:能否为 Swift 方法编写 Tweak 插件?

    虽然理论上可以实现,但是目前还不支持。因为(Swift 和 Objective-C)ABI 之间的差异,以及 Logos 预处理器仅支持 Objective-C 的事实,所以目前无法在 Swift 中或者为 Swift 编写 Tweak。然而,很有可能在未来有办法制作 Swift Tweak,因为现在 Swift 是 ABI 稳定的

  • Question:能否为游戏项目编写 Tweak 插件?

    虽然理论上可以实现,但是实际上很难。因为很多游戏都是用 C#、C++ 或者其他脚本语言编写的,在编译成 MachO 文件的过程中大多经过了中间层(比如:解释器、虚拟机)的转换,而且游戏项目一般都会有代码混淆,所以很难为游戏项目编写 Tweak 插件

  • Question:Tweak 工程如何生成 Release 包?

    通过 make package 命令,默认生成的是 Debug 包。如果想生成 Release 包,则需要使用 make package debug=0
    Debug 包一般用在开发测试,Release 包一般用在生产环境。一般况下 Release 包会比 Debug 包小一点(因为少了符号调试信息等)

  • Question:如何编写多文件的 Tweak 工程?

    在编写 Tweak 插件时,不一定只能在 Tweak.x 文件中编写代码。可以创建多个文件来进行开发,这样会使功能模块化、组织清晰化
    而且,文件的格式并不一定限于.x,也可以是 .h.m.xm 等等
    但是要注意:记得在 Makefile 中加入要参与编译的文件,引入其他文件时要写全路径

  • Question:如何在越狱 iOS 设备上卸载已安装的 Tweak 插件?

    在越狱 iOS 设备上卸载已安装的 Tweak 插件有 2 种方式:

    1. 直接删除(不推荐这种方式,因为可能会卸载不干净)
      直接在 /Library/MobileSubstrate/DynamicLibraries 目录下,删除 Tweak 插件对应的 .dylib 文件和 .plist 文件
    2. 通过 Cydia 卸载(推荐这种方式,因为卸载得比较彻底)
      Cydia - Installed - 选中要卸载的 Tweak 插件 - Modify - Remove
  • Question:Makefile 文件的作用是什么?

    该文件用来指定 Tweak 工程(编译、链接、打包、安装)时要用到的文件、框架、库等信息,将 Tweak 工程整个(编译、链接、打包、安装)的过程自动化

  • Question:control 文件的作用是什么?

    该文件记录了 Tweak 工程的基本信息,在 make package 时,会被打包进 .deb 包中。需要注意的是,Theos 在生成 .deb 包时会对 control 文件做进一步的处理。比如:

    1. 更改 Version 字段为 0.0.1-N,用于标识 Theos 的打包次数,以方便管理
    2. 增加 Installed-Size 字段,用于描述 .deb 包安装后的估算大小,与实际安装大小可能会有偏差,不要更改

其他注意点

  • 解决新版 Theos 将插件安装在目标 iOS 设备的根目录下的问题

    之前版本的 Theos 默认将插件安装在 /Library/MobileSubstrate/DynamicLibraries/ 目录下面
    最近发现,新版的 Theos 将插件直接安装在目标 iOS 设备的根目录下面了

    解决办法:

    1. 安装 dpkg

      brew install dpkg
      
    2. 修改 ~/theos/makefiles/package/deb.mk 文件的内容

      $(ECHO_NOTHING)COPYFILE_DISABLE=1 $(FAKEROOT) -r $(_THEOS_PLATFORM_DPKG_DEB) -Z$(_THEOS_PLATFORM_DPKG_DEB_COMPRESSION) -z$(THEOS_PLATFORM_DEB_COMPRESSION_LEVEL) -b "$(THEOS_STAGING_DIR)" "$(_THEOS_DEB_PACKAGE_FILENAME)"$(ECHO_END)
      # 将上面这一句代码修改为下面这一句代码就可以搞定
      $(ECHO_NOTHING)COPYFILE_DISABLE=1 $(FAKEROOT) -r dpkg-deb -Zgzip -b "$(THEOS_STAGING_DIR)" "$(_THEOS_DEB_PACKAGE_FILENAME)" $(STDERR_NULL_REDIRECT)$(ECHO_END)
      

Theos(一):简介 安装相关推荐

  1. Python 3 mysql 简介安装

    Python 3 mysql 简介安装 一.数据库是什么 1.  什么是数据库(DataBase,简称DB) 数据库(database,DB)是指长期存储在计算机内的,有组织,可共享的数据的集合.数据 ...

  2. Ambari简介安装

    文章目录 简介 安装(使用本地源) 安装ambari server 修改主机名称和host文件 关闭防火墙和selinux ssh免密 安装JDK和Mysql 安装JDK 安装mysql 时钟同步 安 ...

  3. Tomcat简介 安装 配置 示例

    Tomcat简介 & 安装 & 配置 & 示例 1.Tomcat简介 2.Tomcat安装 1)RPM包安装 2)二进制安装 3.配置 1)server.xml组件类别 2)s ...

  4. hbase 数据库简介安装与常用命令的使用

    一:hbase 简介与架构功能 二:hbase 安装与配置 三:hbase 常见shell 命令操作 一:hbase 简介与架构功能 1.1 为什么要使用hbase 数据库 传统的RDBMS关系型数据 ...

  5. anaconda的python使用教程-Python,Anaconda简介安装使用教程

    ① 首先,自己也是闲来无趣刚刚开始接触Python这门语言,收集了一些关于一些初级的学前准备资料仅供大家参考 Python简介: Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚 ...

  6. 【Flutter】shared_preferences 本地存储 ( 简介 | 安装 shared_preferences 插件 | 使用 shared_preferences 流程 )

    文章目录 一.shared_preferences 本地存储插件简介 二.安装 shared_preferences 插件 三.使用 shared_preferences 流程 四.完整代码示例 五. ...

  7. faiss(1):简介 安装 与 原理

    1. 简介 Faiss是Facebook AI团队开源的针对聚类和相似性搜索库,为稠密向量提供高效相似度搜索和聚类,支持十亿级别向量的搜索,是目前最为成熟的近似近邻搜索库.它包含多种搜索任意大小向量集 ...

  8. SVN入门:流程简介 安装配置 项目库配置 客户端 上线方案

    查看警告:show warnings:  备份库结构:mysqldump 库 -add-drop-table  >  路径 添加用户:grant select,insert on 库名 to ' ...

  9. 【Pytorch神经网络理论篇】 27 图神经网络DGL库:简介+安装+卸载+数据集+PYG库+NetWorkx库

    DGL库是由纽约大学和亚马逊联手推出的图神经网络框架,支持对异构图的处理,开源相关异构图神经网络的代码,在GCMC.RGCN等业内知名的模型实现上也取得了很好的效果. 1 DGL库 1.1 DGL库的 ...

最新文章

  1. 用source命令执行脚本和用sh执行脚本之间的区别
  2. yolov3为什么对大目标检测不好_基于改进Yolov3的目标检测的研究
  3. 利用zxing读写PDF417码制的二维码
  4. ASP.NET - 演练:创建网页以显示 XML 数据
  5. 19.并发容器之BlockingQueue
  6. js 利用事件委托解决mousedown中的click
  7. linux usr local权限,【linux】sudo chown -R $(whoami) /usr/local提示操作被拒绝
  8. pre和code的区别
  9. Modelsim下载,亲测有效
  10. 【MUI框架】学习笔记整理 Day 2
  11. C# winform 界面美化技巧(扁平化设计)
  12. IDEA插件下载地址
  13. [树形DP]贪吃的九头龙
  14. m4a怎么转换成mp3,m4a转mp3方法
  15. vue项目-后台管理系统
  16. VMware虚拟机去虚拟化完整版教程|永久过强壳VMP、SE壳、GK盾、TMD教程|VMware去虚拟化吾爱汇编论坛教程完整版
  17. 如何在电脑上进行PDF压缩?
  18. python 操作excel2007
  19. 二次元博客系统Halo
  20. 光线追踪渲染实战:蒙特卡洛路径追踪及其c++实现

热门文章

  1. 本博客已转至蚂蚁笔记
  2. web of science 中批量导出文件ciw到endnote时,没反应怎么操作
  3. 历时 6 年发展,GAN 领域当下最热门的“弄潮儿”都有哪些?
  4. 混合开发框架|Flutter多引擎dart多入口设计实现
  5. WebService之Axis2系列教程(一)Axis2的下载、安装和使用
  6. 1、eclipse安装Axis2插件
  7. pytest装饰器实现批量造数
  8. 【feign】OpenFeign设置header的5种方式
  9. 为什么计算机二级打不开,电脑二级网页打不开是什么回事
  10. ffmpeg将视频编码为H264格式