-
防止抓包:
-
使用SSL Pinning(证书绑定):在客户端内置服务器证书或公钥,在建立SSL/TLS连接时进行验证,防止中间人攻击。
-
避免使用明文传输:所有网络请求使用HTTPS,并且确保正确的证书验证。
-
-
代码混淆:
- 使用工具对Objective-C代码进行混淆,增加逆向难度。例如,类名、方法名、属性名使用无意义的字符串替换。
-
反调试:
- 使用系统API检测是否被调试,如果被调试则采取相应措施(如退出应用)。
-
完整性检查:
- 检查应用是否被重签名或篡改,可以通过比较嵌入的签名或检查文件完整性来实现。
-
字符串加密:
- 对敏感字符串进行加密,防止在二进制文件中直接显示。
-
使用C代码:
- 将敏感逻辑用C/C++实现,因为C/C++编译后的代码比Objective-C更难逆向。
-
动态加载代码:
- 从服务器下载加密的代码,在运行时解密执行,增加静态分析的难度。
-
使用安全框架:
- 使用第三方安全框架,如Obfuscator、Themis等。
下面是一些具体的实现示例:
1. SSL Pinning
在iOS中,可以使用NSURLSession或AFNetworking等网络库来实现SSL Pinning。
使用NSURLSession的示例:
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
// 加载内置的证书
NSString *certPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:certPath];
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
// 设置锚点证书
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)@[(__bridge id)cert]);
SecTrustResultType result;
SecTrustEvaluate(serverTrust, &result);
if (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
} else {
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
CFRelease(cert);
} else {
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
}
2. 代码混淆
可以使用脚本在编译阶段对类名、方法名进行替换。例如,使用一个宏定义来替换方法名:
// 定义一个混淆宏,在预编译阶段将方法名替换为随机字符串
#define OBFUSCATE(string) [NSString stringWithUTF8String:obfuscateCString(string)]
// 随机字符串生成函数
const char* obfuscateCString(const char* string) {
// 实现一个简单的混淆算法,例如异或操作
// 注意:这里只是示例,实际使用时需要更复杂的算法
static char obfuscated[256];
for (int i = 0; i < strlen(string); i++) {
obfuscated[i] = string[i] ^ 0x55;
}
obfuscated[strlen(string)] = '\0';
return obfuscated;
}
// 使用示例
NSString *secretKey = OBFUSCATE("mySecretKey");
但是,这种方法需要谨慎使用,因为可能会引起运行时错误。更常见的做法是使用第三方工具,如Obfuscator-iOS。
3. 反调试
通过ptrace系统调用可以防止调试器附加。但是,ptrace是私有API,使用它可能会导致App Store审核被拒。因此,在发布版本中不建议使用。
#import <sys/types.h>
#import <sys/sysctl.h>
#import <unistd.h>
// 检查是否被调试
static int checkDebugger() {
int name[4];
struct kinfo_proc info;
size_t info_size = sizeof(info);
name[0] = CTL_KERN;
name[1] = KERN_PROC;
name[2] = KERN_PROC_PID;
name[3] = getpid();
if (sysctl(name, 4, &info, &info_size, NULL, 0) == -1) {
NSLog(@"sysctl failed");
return -1;
}
return (info.kp_proc.p_flag & P_TRACED) ? 1 : 0;
}
// 在应用启动时调用
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 如果检测到调试器,可以采取相应措施,如退出应用
if (checkDebugger()) {
exit(0);
}
return YES;
}
4. 完整性检查
检查签名是否匹配:
- (BOOL)isValidSignature {
// 获取嵌入的签名
NSString *embeddedSignature = @"your_embedded_signature"; // 从服务器获取或内置
// 计算当前应用的签名(这里简化,实际需要获取代码签名信息)
// 可以使用SecCodeCopySigningInformation等函数
// 比较两个签名是否一致
return [embeddedSignature isEqualToString:currentSignature];
}
5. 字符串加密
对敏感字符串进行加密,运行时解密:
// 加密字符串(示例:Base64编码,实际可以使用更复杂的加密)
#define ENCRYPT(string) [[string dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0]
#define DECRYPT(string) [[NSString alloc] initWithData:[[NSData alloc] initWithBase64EncodedString:string options:0] encoding:NSUTF8StringEncoding]
// 使用
NSString *encrypted = ENCRYPT(@"secret string");
NSString *decrypted = DECRYPT(encrypted);
6. 使用C代码
将敏感算法用C实现,并编译成静态库,增加逆向难度。
7. 动态加载代码
从服务器下载加密的代码(如JavaScript代码,通过JavaScriptCore执行),或者使用动态库(但iOS限制较多)。
8. 使用安全框架
集成第三方安全框架,如Themis(提供安全存储、加密通信等功能)。
请注意,没有任何方法可以完全防止逆向工程,但通过多种措施组合可以增加逆向的难度。同时,要平衡安全性和用户体验,以及避免使用私有API导致审核被拒。
iOS逆向安全防抓包需要从多个层面进行防护,以下是Objective-C实现的关键方案:
一、网络层防护
1. SSL/TLS证书绑定
// SSLPinningManager.h
@interface SSLPinningManager : NSObject
- (instancetype)sharedManager;
- (void)setupSSLPinning;
@end
// SSLPinningManager.m
@implementation SSLPinningManager
- (void)setupSSLPinning {
// 方法1:公钥绑定
self setupPublicKeyPinning\]; // 方法2:证书绑定 \[self setupCertificatePinning\]; } - (void)setupPublicKeyPinning { // 使用AFNetworking或原生NSURLSession实现 AFSecurityPolicy \*policy = \[AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey\]; policy.allowInvalidCertificates = NO; policy.validatesDomainName = YES; // 设置公钥哈希数组 NSArray \*pinnedKeys = @\[ @"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // 替换为实际公钥哈希 \]; policy.pinnedCertificates = \[NSSet setWithArray:pinnedKeys\]; \[AFHTTPSessionManager manager\].securityPolicy = policy; } - (void)setupCertificatePinning { // 原生NSURLSession实现 NSURLSessionConfiguration \*config = \[NSURLSessionConfiguration defaultSessionConfiguration\]; config.TLSMinimumSupportedProtocol = kTLSProtocol12; // 添加自定义SSL验证 \[NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil\]; } #pragma mark - NSURLSessionDelegate - (void)URLSession:(NSURLSession \*)session didReceiveChallenge:(NSURLAuthenticationChallenge \*)challenge completionHandler:(void (\^)(NSURLSessionAuthChallengeDisposition, NSURLCredential \*))completionHandler { if (\[challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust\]) { SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; // 1. 设置策略 SecPolicyRef policy = SecPolicyCreateSSL(true, (__bridge CFStringRef)challenge.protectionSpace.host); SecTrustSetPolicies(serverTrust, policy); // 2. 验证证书链 SecTrustResultType result; SecTrustEvaluate(serverTrust, \&result); // 3. 自定义验证:检查是否包含预置证书 BOOL isTrusted = \[self validateCertificate:serverTrust\]; if (isTrusted) { NSURLCredential \*credential = \[NSURLCredential credentialForTrust:serverTrust\]; completionHandler(NSURLSessionAuthChallengeUseCredential, credential); } else { completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); } } } - (BOOL)validateCertificate:(SecTrustRef)trust { // 获取证书链 CFIndex certificateCount = SecTrustGetCertificateCount(trust); // 加载预置证书 NSString \*certPath = \[\[NSBundle mainBundle\] pathForResource:@"your_certificate" ofType:@"der"\]; NSData \*certData = \[NSData dataWithContentsOfFile:certPath\]; SecCertificateRef embeddedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData); for (CFIndex i = 0; i \< certificateCount; i++) { SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, i); // 比较证书 if (CFEqual(certificate, embeddedCertificate)) { CFRelease(embeddedCertificate); return YES; } } CFRelease(embeddedCertificate); return NO; } @end #### 2. 双向认证 (mTLS) // MutualTLSAuthManager.h @interface MutualTLSAuthManager : NSObject - (void)setupClientCertificate; @end // MutualTLSAuthManager.m @implementation MutualTLSAuthManager { NSData \*_clientCertificate; SecIdentityRef _identity; } - (void)setupClientCertificate { // 从Keychain或加密存储中加载客户端证书 NSString \*encryptedCertPath = \[\[NSBundle mainBundle\] pathForResource:@"client_cert_encrypted" ofType:@"p12"\]; NSData \*encryptedData = \[NSData dataWithContentsOfFile:encryptedCertPath\]; // 解密证书 NSData \*decryptedData = \[self decryptData:encryptedData\]; // 导入证书到Keychain \[self importPKCS12Data:decryptedData\]; } - (void)importPKCS12Data:(NSData \*)pkcs12Data { CFArrayRef items = NULL; NSDictionary \*options = @{ (__bridge id)kSecImportExportPassphrase: \[self getDecryptionKey
};
OSStatus status = SecPKCS12Import((__bridge CFDataRef)pkcs12Data,
(__bridge CFDictionaryRef)options,
&items);
if (status == errSecSuccess) {
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
_identity = (SecIdentityRef)CFDictionaryGetValue(identityDict,
kSecImportItemIdentity);
CFRetain(_identity);
}
if (items) CFRelease(items);
}
// 在NSURLSessionDelegate中使用
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler {
if (challenge.previousFailureCount == 0 && _identity) {
SecCertificateRef certificate = NULL;
SecIdentityCopyCertificate(_identity, &certificate);
NSArray *certs = @[(__bridge id)certificate];
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:_identity
certificates:certs
persistence:NSURLCredentialPersistenceForSession];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
if (certificate) CFRelease(certificate);
} else {
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}
二、防代理检测
// ProxyDetectionManager.h
@interface ProxyDetectionManager : NSObject
-
(BOOL)isProxyEnabled;
-
(BOOL)isVPNConnected;
@end
// ProxyDetectionManager.m
#import <SystemConfiguration/SystemConfiguration.h>
@implementation ProxyDetectionManager
- (BOOL)isProxyEnabled {
CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
NSDictionary *settings = (__bridge NSDictionary *)proxySettings;
BOOL enabled = NO;
// 检查HTTP代理
NSString *httpProxy = settings[(NSString *)kCFNetworkProxiesHTTPProxy];
NSString *httpsProxy = settings[(NSString *)kCFNetworkProxiesHTTPSProxy];
if (httpProxy || httpsProxy) {
enabled = YES;
}
// 检查PAC代理
NSString *pacURL = settings[(NSString *)kCFNetworkProxiesProxyAutoConfigURLString];
if (pacURL) {
enabled = YES;
}
CFRelease(proxySettings);
// 额外检查:Charles等工具常用端口
if (enabled) {
enabled = [self checkSpecificProxyPorts];
}
return enabled;
}
- (BOOL)checkSpecificProxyPorts {
// 常见抓包工具端口检测
NSArray *commonProxyPorts = @[@8888, @8889, @8080, @8081, @9999];
for (NSNumber *port in commonProxyPorts) {
int socketFD = socket(AF_INET, SOCK_STREAM, 0);
if (socketFD == -1) continue;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons([port intValue]);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(socketFD, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
close(socketFD);
return YES;
}
close(socketFD);
}
return NO;
}
- (BOOL)isVPNConnected {
// 检查VPN连接状态
CFDictionaryRef cfDict = CFNetworkCopySystemProxySettings();
NSDictionary *dict = (__bridge NSDictionary *)cfDict;
NSArray *keys = [dict[@"SCOPED"] allKeys];
for (NSString *key in keys) {
if ([key containsString:@"tap"] ||
key containsString:@"tun"\] \|\|
\[key containsString:@"ppp"\] \|\|
\[key containsString:@"ipsec"\]) {
CFRelease(cfDict);
return YES;
}
}
CFRelease(cfDict);
return NO;
}
@end
### 三、请求保护
#### 1. 参数签名
// RequestSigner.h
@interface RequestSigner : NSObject
+ (NSString \*)signRequestWithParams:(NSDictionary \*)params
timestamp:(NSTimeInterval)timestamp;
+ (NSDictionary \*)signedHeadersForRequest:(NSURLRequest \*)request;
@end
// RequestSigner.m
#import \
substringToIndex:16];
}
- (NSString *)getDynamicKey {
// 从服务器获取或本地生成的动态密钥
// 可以定期更新,增加破解难度
NSTimeInterval interval = [[NSDate date] timeIntervalSince1970];
NSInteger hour = (NSInteger)(interval / 3600);
// 基于时间的动态密钥
NSString *baseKey = @"base_secret_key";
NSString *timeKey = [NSString stringWithFormat:@"%ld", (long)hour];
return [self hmacSHA256:timeKey secret:baseKey];
}
- (NSString *)hmacSHA256:(NSString *)data secret:(NSString *)secret {
const char *cData = [data cStringUsingEncoding:NSUTF8StringEncoding];
const char *cSecret = [secret cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cSecret, strlen(cSecret), cData, strlen(cData), cHMAC);
NSData *hmacData = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
return [hmacData base64EncodedStringWithOptions:0];
}
- (NSDictionary *)signedHeadersForRequest:(NSURLRequest *)request {
NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
NSString *nonce = [self generateNonce];
// 获取请求体
NSData *bodyData = request.HTTPBody;
NSString *bodyString = [[NSString alloc] initWithData:bodyData encoding:NSUTF8StringEncoding];
// 生成签名
NSString *signature = [self signRequestWithBody:bodyString
timestamp:timestamp
nonce:nonce];
return @{
@"X-Timestamp": @(timestamp).stringValue,
@"X-Nonce": nonce,
@"X-Signature": signature,
@"X-App-Version": [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"],
@"X-Device-ID": [self getDeviceUniqueID]
};
}
@end
2. 请求加密
// RequestEncryptor.h
@interface RequestEncryptor : NSObject
-
(NSData *)encryptRequestData:(NSDictionary *)params;
-
(NSDictionary *)decryptResponseData:(NSData *)data;
@end
// RequestEncryptor.m
#import <CommonCrypto/CommonCryptor.h>
@implementation RequestEncryptor
- (NSData *)encryptRequestData:(NSDictionary *)params {
// 1. JSON序列化
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params
options:0
error:&error];
// 2. AES加密
NSData *encryptedData = [self aes256Encrypt:jsonData];
// 3. Base64编码
NSString *base64String = [encryptedData base64EncodedStringWithOptions:0];
// 4. 添加混淆(可选)
base64String = [self obfuscateString:base64String];
return [base64String dataUsingEncoding:NSUTF8StringEncoding];
}
- (NSData *)aes256Encrypt:(NSData *)data {
char keyPtr[kCCKeySizeAES256 + 1];
bzero(keyPtr, sizeof(keyPtr));
// 动态密钥生成
NSString *dynamicKey = [self generateDynamicKey];
dynamicKey getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding\]; NSUInteger dataLength = \[data length\]; size_t bufferSize = dataLength + kCCBlockSizeAES128; void \*buffer = malloc(bufferSize); size_t numBytesEncrypted = 0; CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, keyPtr, kCCKeySizeAES256, NULL, \[data bytes\], dataLength, buffer, bufferSize, \&numBytesEncrypted); if (cryptStatus == kCCSuccess) { return \[NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted\]; } free(buffer); return nil; } + (NSString \*)generateDynamicKey { // 基于设备指纹和应用状态的动态密钥 NSString \*deviceID = \[self getDeviceID\]; NSString \*appVersion = \[\[NSBundle mainBundle\] objectForInfoDictionaryKey:@"CFBundleVersion"\]; NSTimeInterval time = floor(\[\[NSDate date\] timeIntervalSince1970\] / 3600); NSString \*baseString = \[NSString stringWithFormat:@"%@\|%@\|%.0f", deviceID, appVersion, time\]; // SHA256哈希作为密钥 const char \*cStr = \[baseString UTF8String\]; unsigned char result\[CC_SHA256_DIGEST_LENGTH\]; CC_SHA256(cStr, (CC_LONG)strlen(cStr), result); NSData \*hashData = \[NSData dataWithBytes:result length:CC_SHA256_DIGEST_LENGTH\]; return \[hashData base64EncodedStringWithOptions:0\]; } + (NSString \*)obfuscateString:(NSString \*)string { // 简单混淆:交换字符位置 NSMutableString \*obfuscated = \[NSMutableString stringWithString:string\]; NSInteger length = obfuscated.length; for (int i = 0; i \< length / 2; i++) { unichar temp = \[obfuscated characterAtIndex:i\]; \[obfuscated replaceCharactersInRange:NSMakeRange(i, 1) withString:\[NSString stringWithFormat:@"%c", \[obfuscated characterAtIndex:length - i - 1\]\]\]; \[obfuscated replaceCharactersInRange:NSMakeRange(length - i - 1, 1) withString:\[NSString stringWithFormat:@"%c", temp\]\]; } return obfuscated; } @end ### 四、代码混淆与保护 #### 1. 字符串加密 // StringObfuscator.h @interface StringObfuscator : NSObject + (NSString \*)decryptString:(const unsigned char \*)encryptedData length:(size_t)length; @end // StringObfuscator.m @implementation StringObfuscator // 加密字符串的宏(在编译前执行) #define OBFUSCATE(key) \[StringObfuscator decryptString:obf_##key length:sizeof(obf_##key)
// 加密后的字符串数据(示例)
static const unsigned char obf_api_key[] = {0xAA, 0xBB, 0xCC, ...};
static const unsigned char obf_secret[] = {0xDD, 0xEE, 0xFF, ...};
- (NSString *)decryptString:(const unsigned char *)encryptedData
length:(size_t)length {
// XOR解密
unsigned char *decrypted = malloc(length + 1);
for (int i = 0; i < length; i++) {
decrypted[i] = encryptedData[i] ^ 0x55; // 简单XOR,可以使用更复杂算法
}
decrypted[length] = '\0';
NSString *result = [NSString stringWithUTF8String:(const char *)decrypted];
free(decrypted);
return result;
}
// 使用示例
- (void)makeRequest {
NSString *apiKey = OBFUSCATE(api_key);
NSString *secret = OBFUSCATE(secret);
// 使用解密后的字符串
}
@end
2. 防Hook检测
// HookDetectionManager.h
@interface HookDetectionManager : NSObject
-
(BOOL)isMethodHooked:(Class)className selector:(SEL)selector;
-
(void)addAntiHookProtection;
@end
// HookDetectionManager.m
#import <objc/runtime.h>
#import <mach-o/dyld.h>
@implementation HookDetectionManager
- (BOOL)isMethodHooked:(Class)className selector:(SEL)selector {
Method originalMethod = class_getInstanceMethod(className, selector);
// 获取方法实现地址
IMP originalIMP = method_getImplementation(originalMethod);
// 检查IMP是否指向预期代码段
Dl_info info;
if (dladdr((void *)originalIMP, &info)) {
// 检查是否在系统库中
const char *imageName = info.dli_fname;
NSString *imagePath = [NSString stringWithUTF8String:imageName];
// 如果不在主程序镜像中,可能被Hook
if (![imagePath containsString:[[NSBundle mainBundle] bundlePath]]) {
return YES;
}
}
return NO;
}
- (void)addAntiHookProtection {
// 1. 检查关键方法是否被Hook
self checkCriticalMethods\]; // 2. 添加反Hook代码 \[self installTrampolines\]; // 3. 检测Cydia Substrate等框架 \[self detectInjectionFrameworks\]; } + (void)checkCriticalMethods { NSArray \*criticalSelectors = @\[ @"encryptData:", @"decryptData:", @"signRequest:", @"getApiKey" \]; for (NSString \*selectorName in criticalSelectors) { SEL selector = NSSelectorFromString(selectorName); if (\[self isMethodHooked:\[self class\] selector:selector\]) { \[self takeProtectiveAction\]; } } } + (void)installTrampolines { // 使用汇编保护关键函数 __attribute__((always_inline)) void protectedFunction() { // 内联汇编保护 #ifdef __arm64__ __asm__ volatile( "mov x0, #0\\n" "ret\\n" ); #endif } } + (void)detectInjectionFrameworks { // 检查动态库注入 uint32_t count = _dyld_image_count(); for (uint32_t i = 0; i \< count; i++) { const char \*imageName = _dyld_get_image_name(i); NSString \*imagePath = \[NSString stringWithUTF8String:imageName\]; // 检测常见注入框架 NSArray \*injectionKeywords = @\[ @"MobileSubstrate", @"CydiaSubstrate", @"cynject", @"libhooker", @"ElleKit", @"Substitute" \]; for (NSString \*keyword in injectionKeywords) { if (\[imagePath containsString:keyword\]) { \[self takeProtectiveAction\]; break; } } } } + (void)takeProtectiveAction { // 触发保护机制 \[self clearSensitiveData\]; \[self crashRandomly\]; // 或执行其他混淆行为 exit(0); // 最后退出 } @end ### 五、综合防护方案 // SecurityManager.h @interface SecurityManager : NSObject + (void)initializeSecurity; + (BOOL)isEnvironmentSafe; @end // SecurityManager.m @implementation SecurityManager + (void)initializeSecurity { static dispatch_once_t onceToken; dispatch_once(\&onceToken, \^{ // 1. 检查运行环境 if (!\[self isEnvironmentSafe\]) { \[self handleUnsafeEnvironment\]; return; } // 2. 设置网络防护 \[\[SSLPinningManager sharedManager\] setupSSLPinning\]; \[\[MutualTLSAuthManager sharedManager\] setupClientCertificate\]; // 3. 检测代理 if (\[ProxyDetectionManager isProxyEnabled\] \|\| \[ProxyDetectionManager isVPNConnected\]) { \[self handleProxyDetected\]; } // 4. 防Hook保护 \[HookDetectionManager addAntiHookProtection\]; // 5. 定时检查 \[self scheduleSecurityChecks\]; }); } + (BOOL)isEnvironmentSafe { // 越狱检测 if (\[self isJailbroken\]) return NO; // 调试器检测 if (\[self isDebuggerAttached\]) return NO; // 重签名检测 if (\[self isResigned\]) return NO; // 模拟器检测 #if TARGET_IPHONE_SIMULATOR return NO; #endif return YES; } + (BOOL)isJailbroken { // 常见越狱文件检测 NSArray \*jailbreakPaths = @\[ @"/Applications/Cydia.app", @"/Library/MobileSubstrate/MobileSubstrate.dylib", @"/bin/bash", @"/usr/sbin/sshd", @"/etc/apt" \]; for (NSString \*path in jailbreakPaths) { if (\[\[NSFileManager defaultManager\] fileExistsAtPath:path\]) { return YES; } } // 尝试写入系统目录 NSString \*testPath = @"/private/test.txt"; NSError \*error; \[@"test" writeToFile:testPath atomically:YES encoding:NSUTF8StringEncoding error:\&error\]; \[\[NSFileManager defaultManager\] removeItemAtPath:testPath error:nil\]; return (error == nil); } + (BOOL)isDebuggerAttached { // ptrace反调试 #ifndef PT_DENY_ATTACH #define PT_DENY_ATTACH 31 #endif // 方法1:ptrace typedef int (\*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data); ptrace_ptr_t ptrace_ptr = (ptrace_ptr_t)dlsym(RTLD_DEFAULT, "ptrace"); if (ptrace_ptr) { ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0); } // 方法2:syscall syscall(26, 31, 0, 0, 0); // 方法3:检测父进程 struct kinfo_proc info; size_t size = sizeof(info); int name\[4\]; name\[0\] = CTL_KERN; name\[1\] = KERN_PROC; name\[2\] = KERN_PROC_PID; name\[3\] = getppid(); if (sysctl(name, 4, \&info, \&size, NULL, 0) != -1) { if (strcmp(info.kp_proc.p_comm, "debugserver") == 0 \|\| strcmp(info.kp_proc.p_comm, "lldb") == 0) { return YES; } } return NO; } + (void)scheduleSecurityChecks { // 定时执行安全检查 dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 30 \* NSEC_PER_SEC, 1 \* NSEC_PER_SEC); dispatch_source_set_event_handler(timer, \^{ if (!\[self isEnvironmentSafe\]) { \[self handleUnsafeEnvironment\]; } if (\[ProxyDetectionManager isProxyEnabled\]) { \[self handleProxyDetected\]; } }); dispatch_resume(timer); } + (void)handleUnsafeEnvironment { // 清理敏感数据 \[self clearSensitiveData\]; // 上报安全事件 \[self reportSecurityEvent:@"unsafe_environment"\]; // 混淆行为或优雅退出 dispatch_async(dispatch_get_main_queue(), \^{ UIAlertController \*alert = \[UIAlertController alertControllerWithTitle:@"安全警告" message:@"检测到不安全环境,应用即将退出" preferredStyle:UIAlertControllerStyleAlert\]; \[\[UIApplication sharedApplication\].keyWindow.rootViewController presentViewController:alert animated:YES completion:\^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 \* NSEC_PER_SEC), dispatch_get_main_queue(), \^{ exit(0); }); }\]; }); } @end ### 六、使用建议 1. **分层防护**:不要依赖单一防护措施 2. **动态更新**:定期更新加密算法和密钥 3. **服务端配合**:与服务端协同实现完整防护 4. **性能平衡**:在安全和性能间取得平衡 5. **持续更新**:跟踪最新的逆向技术并更新防护 ### 七、注意事项 1. **审核风险**:部分技术可能违反App Store审核条款 2. **用户体验**:避免过度防护影响正常使用 3. **维护成本**:安全方案需要持续维护和更新 4. **法律合规**:确保符合相关法律法规