IosBX's Blog

IosbX's Blog
记录自己的每一次进步
  1. 首页
  2. iOS
  3. 正文

iOS逆向-Hook Swift 技术详解与实战

2026年 1月 27日 213点热度 3人点赞 0条评论

一. 引言

受历史包袱影响,当前大厂主流 App 仍以 Objective-C (OC) 为核心,但自 Swift ABI 稳定以来,大厂对 Swift 的投入已进入增长期。在逆向工程和插件开发中,面对越来越多的 Swift 代码,掌握 Swift Hook 技术变得至关重要。

二. Swift 与 OC Hook 的本质区别

Objective-C 的方法调用基于 Runtime 消息发送机制 (objc_msgSend),这使得 Method Swizzling 变得非常简单且强大。而 Swift 为了性能,引入了更复杂的派发机制:

  • 静态派发 (Static Dispatch): 编译期确定函数地址,直接内联或跳转,无法通过 Runtime 替换。常见于 final 修饰的方法、struct/enum 的方法以及 extension 中的默认实现。
  • 动态派发 (Dynamic Dispatch):
    • 基于 VTable (虚函数表): 纯 Swift 类默认使用这种方式。
    • 基于 OC Runtime (消息发送): 继承自 NSObject 或被 @objc dynamic 修饰的方法,依旧走 OC 消息转发流程。

三. 方案一:利用 OC Runtime (最推荐)

这是最简单且最稳定的方式,但前提是目标 Swift 方法必须暴露给 Objective-C Runtime。

3.1 适用场景

  • 类继承自 NSObject。
  • 方法或属性被 @objc 或 @objc dynamic 修饰。

3.2 Logos 实战技巧

由于 Swift 类使用了 Name Mangling (名称修饰),类名通常不是简单的字符串(如 MyClass),而是像 _TtC5MyApp7MyClass 这种形式。在 Logos (Theos) 中,直接 Hook 往往会失败。我们可以使用 动态获取类 + 显式初始化 的技巧。

// 1. 定义 Hook 组和占位符类名
%group MySwiftHook
%hook MySwiftClass // 这里的名字可以随便取,只是个占位符

- (NSString *)displayName {
    NSString *originalName = %orig;
    NSLog(@"[Hook] 获取用户名: %@", originalName);
    return [NSString stringWithFormat:@"[VIP] %@", originalName];
}

%end
%end

%ctor {
    // 2. 动态查找 Swift 类
    // Swift 类名通常格式为: ModuleName.ClassName
    NSString *className = @"MyAppModule.User";
    Class cls = NSClassFromString(className);
    
    // 3. 如果找到了类,手动初始化 Hook 组,将占位符绑定到真实类上
    if (cls) {
        %init(MySwiftHook, MySwiftClass = cls);
    } else {
        NSLog(@"[Error] 未找到类: %@", className);
    }
}
提示: 如何确定 className?可以使用 nm 命令查看二进制符号,或者在运行时通过 objc_copyClassNamesForImage 打印所有类名。

四. 方案二:基于 Name Mangling 的符号 Hook

对于静态派发的 Swift 函数,或者 C 函数,我们可以利用 Fishhook (iOS) 或 Dobby 等 Inline Hook 框架,针对符号地址进行替换。

4.1 获取 Mangled Name

Swift 函数名在编译后会被重整。例如 func calculate(x: Int) -> Int 可能变成 $s5MyApp9calculate1xS2i_tF。

  • 使用 nm -g path/to/binary | grep "functionName" 查找符号。
  • 使用 swift demangle 还原符号含义。

4.2 Dobby Hook 示例 (伪代码)

// 声明原函数指针
int (*orig_calculate)(int);

// 新函数
int my_calculate(int x) {
    return orig_calculate(x * 2); // 修改参数
}

// 注入
void* addr = DobbySymbolResolver(NULL, "$s5MyApp9calculate1xS2i_tF");
DobbyHook(addr, (void*)my_calculate, (void**)&orig_calculate);

五.实战

创建一个 Swift 语言的 App 项目测试,

写一个继承 NSObject 的类,使用 @objc dynamic 修饰变量

import Foundation
import UIKit

class User: NSObject {
    @objc dynamic var name: String
    @objc dynamic var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
        super.init()
    }
    
    @objc dynamic func exampleName() -> String {
        return name
    }
    
    @objc dynamic func exampleAge() -> Int {
        return age
    }
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        // 测试调用
        let user = User(name: "IosBX", age: 17)
        print("用户名字: \(user.exampleName())")
        print("年龄检查: \(user.exampleAge())")
        
    }
}

用 Theos 创建一个 Tweak,


#import <Foundation/Foundation.h>

%group SwiftHook

%hook User

- (NSString *)exampleName {
    NSString *originalName = %orig;
    return [NSString stringWithFormat:@"[Hacked] %@", originalName];
}

- (NSInteger)exampleAge {
    NSInteger originalAge = %orig;
    return 999;
}

%end

%end

%ctor {
    
    // 动态查找 Swift 类
    // 格式通常为: ModuleName.ClassName
    NSString *className = @"SwiftApp.User";
    Class cls = NSClassFromString(className);
    
    if (cls) {
        %init(SwiftHook, User = cls);
    }
}

用 NSClassFromString 获取 Swift 类,用 %init 将 Hook 逻辑应用到该类上。

原始输出:

Hook 之后输出:

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2026年 1月 27日

IosBX

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2026 IosBX's Blog. ALL RIGHTS RESERVED.