-
防止抓包:
-
使用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 obfuscated256;
for (int i = 0; i < strlen(string); i++) {
obfuscatedi = stringi ^ 0x55;
}
obfuscatedstrlen(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 name4;
struct kinfo_proc info;
size_t info_size = sizeof(info);
name0 = CTL_KERN;
name1 = KERN_PROC;
name2 = KERN_PROC_PID;
name3 = 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 <CommonCrypto/CommonHMAC.h>
@implementation RequestSigner
- (NSString *)signRequestWithParams:(NSDictionary *)params
timestamp:(NSTimeInterval)timestamp {
// 1. 参数排序并拼接
NSArray *sortedKeys = \[params allKeys sortedArrayUsingSelector:@selector(compare:)];
NSMutableString *signString = NSMutableString string;
for (NSString *key in sortedKeys) {
signString appendFormat:@"%@=%@\&", key, params\[key];
}
// 2. 添加时间戳和随机数
NSString *nonce = self generateNonce;
signString appendFormat:@"timestamp=%f\&nonce=%@", timestamp, nonce;
// 3. 添加动态密钥(定期更新)
NSString *dynamicKey = self getDynamicKey;
signString appendFormat:@"\&dynamic_key=%@", dynamicKey;
// 4. HMAC签名
NSString *signature = [self hmacSHA256:signString
secret:self getAppSecret];
return signature;
}
- (NSString *)generateNonce {
// 生成随机字符串
NSString *uuid = \[NSUUID UUID UUIDString];
return \[uuid stringByReplacingOccurrencesOfString:@"-" withString:@""
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 cHMACCC_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 keyPtrkCCKeySizeAES256 + 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 resultCC_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++) {
decryptedi = encryptedDatai ^ 0x55; // 简单XOR,可以使用更复杂算法
}
decryptedlength = '\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 name4;
name0 = CTL_KERN;
name1 = KERN_PROC;
name2 = KERN_PROC_PID;
name3 = 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
六、使用建议
-
分层防护:不要依赖单一防护措施
-
动态更新:定期更新加密算法和密钥
-
服务端配合:与服务端协同实现完整防护
-
性能平衡:在安全和性能间取得平衡
-
持续更新:跟踪最新的逆向技术并更新防护
七、注意事项
-
审核风险:部分技术可能违反App Store审核条款
-
用户体验:避免过度防护影响正常使用
-
维护成本:安全方案需要持续维护和更新
-
法律合规:确保符合相关法律法规