整体思路
- 代码混淆:通过打乱代码结构、重命名标识符等方式,增加逆向工程的难度,使攻击者难以理解和分析代码逻辑。
- 加密技术:对敏感数据(如密钥、配置信息等)进行加密存储和传输,确保即使数据被获取,没有解密密钥也无法使用。
- 运行时检测:在应用运行过程中,实时检测是否存在逆向工程行为,如越狱环境、调试器附着等,一旦检测到异常,采取相应的保护措施。
代码混淆
- 使用工具:
- Clang混淆:可以利用Clang编译器的一些特性来实现简单的混淆。例如,通过
-frename-all
选项可以对函数和变量进行重命名。不过,这种方式可能会影响代码可读性,在实际项目中需谨慎使用。
- 第三方工具:如obfuscator-llvm,它基于LLVM编译器框架,提供了更强大的混淆功能,包括控制流平坦化、指令替换等。
- 手动混淆技巧:
- 函数拆分:将一个复杂的函数拆分成多个小函数,打乱逻辑顺序,使逆向分析更困难。例如:
// 原始函数
- (void)originalFunction {
// 复杂逻辑
int result = 0;
for (int i = 0; i < 10; i++) {
result += i;
}
NSLog(@"Result: %d", result);
}
// 拆分后的函数
- (void)part1Function {
int result = 0;
return result;
}
- (void)part2Function:(int)result {
for (int i = 0; i < 10; i++) {
result += i;
}
return result;
}
- (void)part3Function:(int)result {
NSLog(@"Result: %d", result);
}
- (void)newFunction {
int result = [self part1Function];
result = [self part2Function:result];
[self part3Function:result];
}
- 字符串加密:对代码中的敏感字符串(如API密钥)进行加密处理,在运行时解密使用。可以使用对称加密算法(如AES)对字符串进行加密,然后在代码中解密:
#import <CommonCrypto/CommonCryptor.h>
// 加密函数
NSData *encryptString(NSString *plainText, NSString *key) {
NSData *data = [plainText dataUsingEncoding:NSUTF8StringEncoding];
size_t dataLength = [data length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
[key UTF8String], kCCKeySizeAES128,
NULL /* initialization vector (optional) */,
[data bytes], dataLength,
buffer, bufferSize,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer);
return nil;
}
// 解密函数
NSString *decryptString(NSData *cipherData, NSString *key) {
size_t dataLength = [cipherData length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
[key UTF8String], kCCKeySizeAES128,
NULL /* initialization vector (optional) */,
[cipherData bytes], dataLength,
buffer, bufferSize,
&numBytesDecrypted);
if (cryptStatus == kCCSuccess) {
return [[NSString alloc] initWithData:[NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted] encoding:NSUTF8StringEncoding];
}
free(buffer);
return nil;
}
加密技术
- 数据存储加密:
- 使用钥匙串(Keychain):iOS的钥匙串为存储敏感数据(如密码、密钥)提供了安全的方式。可以使用
Security.framework
来操作钥匙串。例如,存储一个密码:
#import <Security/Security.h>
- (BOOL)savePassword:(NSString *)password forService:(NSString *)service account:(NSString *)account {
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: service,
(__bridge id)kSecAttrAccount: account,
(__bridge id)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding]
};
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
return status == errSecSuccess;
}
- (NSString *)retrievePasswordForService:(NSString *)service account:(NSString *)account {
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: service,
(__bridge id)kSecAttrAccount: account,
(__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne
};
CFDataRef dataRef = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&dataRef);
if (status == errSecSuccess && dataRef != NULL) {
NSString *password = [[NSString alloc] initWithData:(__bridge NSData *)dataRef encoding:NSUTF8StringEncoding];
CFRelease(dataRef);
return password;
}
return nil;
}
- 文件加密:对于存储在应用沙盒中的文件,可以使用第三方库(如RNCryptor)进行加密。在写入文件前加密数据,读取文件后解密数据。
- 数据传输加密:
- 使用HTTPS:在网络请求中,使用HTTPS协议来加密数据传输。确保服务器配置了有效的SSL证书。在iOS中,可以使用
NSURLSession
进行HTTPS请求:
NSURL *url = [NSURL URLWithString:@"https://example.com/api"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
// 设置请求体等其他参数
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 处理响应
}];
[task resume];
运行时检测
- 越狱检测:
- 检查文件系统:越狱设备通常会有一些特定的文件或目录。可以检查是否存在这些标志性文件,例如:
- (BOOL)isJailbroken {
NSArray *jailbreakDirectories = @[@"/Applications/Cydia.app", @"/Library/MobileSubstrate/MobileSubstrate.dylib", @"/bin/bash", @"/usr/sbin/sshd"];
for (NSString *directory in jailbreakDirectories) {
if ([[NSFileManager defaultManager] fileExistsAtPath:directory]) {
return YES;
}
}
return NO;
}
- 检测环境变量:越狱设备可能会设置一些特定的环境变量。可以通过检查环境变量来判断:
- (BOOL)isJailbrokenByEnv {
return getenv("DYLD_INSERT_LIBRARIES") != NULL;
}
- 调试器检测:
- 使用
ptrace
函数:在应用启动时,可以使用ptrace
函数检测是否有调试器附着。例如:
#import <sys/types.h>
#import <sys/ptrace.h>
- (BOOL)isDebuggerAttached {
int result = ptrace(PT_DENY_ATTACH, 0, 0, 0);
if (result == -1) {
return YES; // 表示有调试器附着
}
return NO;
}
- 应对措施:
- 一旦检测到异常:可以采取多种措施,如终止应用、弹出警告提示用户、或者限制应用部分功能。例如,终止应用:
if ([self isJailbroken] || [self isDebuggerAttached]) {
exit(0);
}