iOS 逆向工程学习
iOS 逆向工程学习
1. iOS 逆向工程的常见手法
1.1 静态分析(Static Analysis)
静态分析是指在不运行应用的情况下,对应用的二进制文件进行分析。常见的静态分析工具有:
class-dump
:用于导出 Objective-C 类、方法和属性。Hopper
、IDA 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.