Post

iOS 逆向工程学习

iOS 逆向工程学习

1. iOS 逆向工程的常见手法

1.1 静态分析(Static Analysis)

静态分析是指在不运行应用的情况下,对应用的二进制文件进行分析。常见的静态分析工具有:

  • class-dump:用于导出 Objective-C 类、方法和属性。
  • HopperIDA Pro:用于反汇编和反编译 Mach-O 可执行文件。
  • otool:用于分析应用的动态库依赖关系。

防御措施:

  • 启用 -fvisibility=hidden 限制符号暴露。
  • 使用 strip 命令删除调试符号。
  • 对关键代码进行混淆(如使用 LLVM Pass 插件)。

1.2 动态分析(Dynamic Analysis)

动态分析是指在应用运行时对其行为进行监测和篡改。常见工具有:

  • Cycript:可以动态修改 Objective-C 运行时对象。
  • Frida:强大的跨平台动态分析工具。
  • gdb/lldb:用于调试和修改应用的运行状态。

防御措施:

  • 检测调试器 (ptrace 阻止调试)。
  • 运行时完整性校验(哈希校验关键代码段)。

1.3 Dylib 注入(Dynamic Library Injection)

Dylib 注入是一种常见的逆向手法,攻击者可以利用 DYLD_INSERT_LIBRARIES 环境变量加载恶意动态库来篡改应用行为。

防御措施:

  • 运行时检测 DYLD_INSERT_LIBRARIES
  • 使用 __RESTRICT 防止动态库注入。
  • 代码签名完整性校验(如 codesign -vv)。

1.4 Method Swizzling

Method Swizzling 是 Objective-C 运行时的一种特性,允许开发者在运行时交换方法的实现。攻击者可以利用它来劫持关键 API,例如 UIApplication sendAction: 以监听用户输入。

防御措施:

  • 运行时检测方法的 IMP 是否被篡改。
  • 使用 @final 限制类的扩展。
  • +load 方法中检测方法实现地址是否被修改。

2. 如何防止 Dylib 注入?

2.1 检测环境变量 DYLD_INSERT_LIBRARIES

main() 入口处检测 DYLD_INSERT_LIBRARIES 是否存在:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import <dlfcn.h>
#import <sys/sysctl.h>

BOOL isInjectedDylib() {
    char *env = getenv("DYLD_INSERT_LIBRARIES");
    return env != NULL;
}

int main(int argc, char * argv[]) {
    if (isInjectedDylib()) {
        exit(1); // 终止应用
    }
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

2.2 运行时检测已加载的 Dylib

可以使用 dyld API 检测是否有非官方 Dylib 被注入。

1
2
3
4
5
6
7
8
9
10
11
#import <mach-o/dyld.h>

BOOL isInjectedDylibDetected() {
    for (uint32_t i = 0; i < _dyld_image_count(); i++) {
        const char *image_name = _dyld_get_image_name(i);
        if (strstr(image_name, "MobileSubstrate") || strstr(image_name, "Frida")) {
            return YES;
        }
    }
    return NO;
}

2.3 启用 __RESTRICT 保护

Info.plist 中添加 UIRequiresFullScreen,防止 DYLD_INSERT_LIBRARIES 注入。

1
2
<key>UIRequiresFullScreen</key>
<true/>

3. 如何防止 Method Swizzling?

3.1 检测方法实现是否被替换

可以通过 method_getImplementation 检测某个方法的实现是否发生变化。

1
2
3
4
5
6
#import <objc/runtime.h>

BOOL isMethodSwizzled(Class cls, SEL selector, IMP originalImp) {
    Method method = class_getInstanceMethod(cls, selector);
    return method_getImplementation(method) != originalImp;
}

3.2 在 +load 方法中锁定方法实现

+load 方法会在类被加载时执行,可以在这里缓存原始方法实现,并在运行时比对。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@implementation MyClass

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method originalMethod = class_getInstanceMethod(self, @selector(targetMethod));
        originalIMP = method_getImplementation(originalMethod);
    });
}

- (void)targetMethod {
    if (isMethodSwizzled([self class], @selector(targetMethod), originalIMP)) {
        exit(0); // 终止应用
    }
    // 继续执行原始逻辑
}
@end

4. 其他安全防护措施

4.1 防止调试(反调试检测)

1
2
3
4
5
6
7
8
9
10
11
12
#include <sys/types.h>
#include <sys/sysctl.h>

BOOL isDebuggerAttached() {
    struct kinfo_proc info;
    size_t size = sizeof(info);
    int name[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
    if (sysctl(name, 4, &info, &size, NULL, 0) == -1) {
        return NO;
    }
    return (info.kp_proc.p_flag & P_TRACED) != 0;
}

4.2 完整性校验(Hash 校验关键代码段)

可以计算代码段的哈希值,并在运行时比对。

1
2
3
4
5
6
7
#import <CommonCrypto/CommonDigest.h>

NSData *calculateSHA256(NSData *data) {
    unsigned char hash[CC_SHA256_DIGEST_LENGTH];
    CC_SHA256(data.bytes, (CC_LONG)data.length, hash);
    return [NSData dataWithBytes:hash length:CC_SHA256_DIGEST_LENGTH];
}
This post is licensed under CC BY 4.0 by the author.