iOS 逆向

Theos Tweak 开发实战:从环境搭建到 UI 注入

2025-07-28 wangjun 18 min read
0x

Tweak 是什么?

Tweak 是运行在越狱设备上的注入式插件:它把自定义代码注入目标 App 进程,在运行时修改其行为。上一篇文章讲的 Method Swizzling 是底层原理,而 Theos 则是把这种能力工程化的成熟框架。

环境准备

# 1. 安装 Theos
$ git clone --recursive https://github.com/theos/theos /opt/theos
$ export THEOS=/opt/theos

# 2. 指定 SDK(可复用 Xcode 的 iOS SDK)
$ export THEOS_SDK=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk

# 3. 确认已越狱设备可 SSH 连接
$ ssh root@192.168.1.100

还需要一台已越狱的设备(checkra1n / unc0ver / palera1n 均可),设备上要有 dpkgldid 等基础工具。

Tweak 工程结构

MyTweak/
├── control          # 包信息:包名、版本、依赖、作者
├── Makefile         # 构建配置:目标系统、架构
├── Tweak.xm         # Logos 源码(核心逻辑)
└── layout/          # 随包安装的额外文件(可选)
# Makefile 示例
export TARGET = iphone:clang:latest:15.0
export ARCHS = arm64

include $(THEOS)/makefiles/common.mk

TWEAK_NAME = MyTweak
MyTweak_FILES = Tweak.xm
MyTweak_CFLAGS = -fobjc-arc

include $(THEOS_MAKE_PATH)/tweak.mk

Logos 语法速查

Logos 是 Theos 提供的一组预处理器指令,编译时会被自动展开成 Objective-C 运行时代码:

  • %hook ClassName:开始 Hook 一个类
  • %orig:调用被 Hook 方法的原始实现
  • %new:为类新增一个方法
  • %property:为类动态添加属性
  • %end:结束 Hook
%hook UIViewController

- (void)viewDidLoad {
  %orig; // 先执行原始 viewDidLoad
  NSLog(@"[Tweak] %@ did load", self);
}

%end

实战:给导航栏注入一个按钮

%hook MainViewController

- (void)viewDidLoad {
  %orig;
  UIBarButtonItem *btn = [[UIBarButtonItem alloc]
      initWithTitle:@"注入" style:UIBarButtonItemStylePlain
      target:self action:@selector(myAction:)];
  self.navigationItem.rightBarButtonItem = btn;
}

- (void)myAction:(id)sender {
  UIAlertController *alert = [UIAlertController
      alertControllerWithTitle:@"Tweak"
      message:@"注入成功" preferredStyle:UIAlertControllerStyleAlert];
  [alert addAction:[UIAlertAction actionWithTitle:@"OK"
      style:UIAlertActionStyleDefault handler:nil]];
  [self presentViewController:alert animated:YES completion:nil];
}

%end

构建、安装与排错

$ make package        # 打包成 .deb
$ make install        # 通过 SSH 安装到设备
$ sbreload            # respring 重启 SpringBoard 生效
  • 注入不生效:确认 TARGET 版本覆盖设备系统版本,并重启目标 App
  • 闪退:用 frida -U -n App 附加查看堆栈,或直接看 syslog
  • 方法没被 Hook:%hook 的类名/方法签名必须与实际一致,用 class-dump 核对
Tweak 开发的本质是"在别人的进程里优雅地生活" -- 理解运行时,尊重运行时,才能把 Hook 写稳。