|
| 1 | +# iOS 安全防护相关 |
| 2 | +> 道高一尺,魔高一丈,关于iOS App安全是个攻防不断(恶心🤢)的过程 |
| 3 | +
|
| 4 | +#### 越狱相关 |
| 5 | +##### 越狱设备或者环境判断 |
| 6 | +**1. 越狱设备可能会存在以下文件** |
| 7 | +```bash |
| 8 | + "/Applications/Cydia.app", |
| 9 | + "/Library/MobileSubstrate/MobileSubstrate.dylib", |
| 10 | + "/bin/bash", |
| 11 | + "/usr/sbin/sshd", |
| 12 | + "/etc/apt" |
| 13 | +``` |
| 14 | +**2. 判断是否存在[cydia](https://cydia-app.com/)应用** |
| 15 | + |
| 16 | +**3. 能够读取所有应用名称** |
| 17 | +> 非越狱手机是无法获取系统应用参数的 |
| 18 | +
|
| 19 | +**4. 读取环境变量 `DYLD_INSERT_LIBRARIES`** |
| 20 | +> 非越狱手机DYLD_INSERT_LIBRARIES获取到的环境变量为NULL。 |
| 21 | +
|
| 22 | +**综合以上观点:** |
| 23 | +```swift |
| 24 | +// 常见越狱文件 |
| 25 | +const char *Jailbreak_Tool_pathes[] = { |
| 26 | + "/Applications/Cydia.app", |
| 27 | + "/Library/MobileSubstrate/MobileSubstrate.dylib", |
| 28 | + "/bin/bash", |
| 29 | + "/usr/sbin/sshd", |
| 30 | + "/etc/apt" |
| 31 | +}; |
| 32 | + |
| 33 | +char *printEnv(void){ |
| 34 | + char *env = getenv("DYLD_INSERT_LIBRARIES"); |
| 35 | + return env; |
| 36 | +} |
| 37 | + |
| 38 | +/** 当前设备是否越狱 */ |
| 39 | ++ (BOOL)isDeviceJailbreak |
| 40 | +{ |
| 41 | + // 判断是否存在越狱文件 |
| 42 | + for (int i = 0; i < 5; i++) { |
| 43 | + if ([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithUTF8String:Jailbreak_Tool_pathes[i]]]) { |
| 44 | + NSLog(@"此设备越狱!"); |
| 45 | + return YES; |
| 46 | + } |
| 47 | + } |
| 48 | + // 判断是否存在cydia应用 |
| 49 | + if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://package/com.example.package"]]){ |
| 50 | + NSLog(@"此设备越狱!"); |
| 51 | + return YES; |
| 52 | + } |
| 53 | + |
| 54 | + // 读取系统所有的应用名称 |
| 55 | + if ([[NSFileManager defaultManager] fileExistsAtPath:@"/User/Applications/"]){ |
| 56 | + NSLog(@"此设备越狱!"); |
| 57 | + return YES; |
| 58 | + } |
| 59 | + |
| 60 | + // 读取环境变量 |
| 61 | + if(printEnv()){ |
| 62 | + NSLog(@"此设备越狱!"); |
| 63 | + return YES; |
| 64 | + } |
| 65 | + |
| 66 | + NSLog(@"此设备没有越狱"); |
| 67 | + return NO; |
| 68 | +} |
| 69 | +``` |
| 70 | +#### IPA文件被篡改 |
| 71 | +##### 检测ipa文件是否被篡改可以通过以下方法: |
| 72 | + **1. 通过对`CodeResources`读取资源文件原始`hash`,和当前`hash`进行对比,判断是否经过篡改,被篡改过的文件应从服务器重新请求资源文件进行替换;** |
| 73 | + |
| 74 | +**2. 可以通过检测`SignerIdentity`判断是`Mach-O`文件否被篡改;** |
| 75 | + |
| 76 | +**3. 可以通过检测`cryptid`的值来检测`Mach-O`文件是否被篡改,篡改过`cryptid`的值为`1`** |
| 77 | + |
| 78 | +**4. IPA包上传到TestFlight或者App Store后,计算安装包中重要文件的MD5 值,服务器记录,在应用运行前首先将本地计算的 MD5 值和服务器记录的 MD5 值 进行对比,如不同,则退出应用** |
| 79 | + |
| 80 | +*这里介绍通过检测`SignerIdentity`判断是`Mach-O`文件否被篡改* |
| 81 | + |
| 82 | +**原理:** |
| 83 | + |
| 84 | +> `SignerIdentity`的值在`Info.plist`中是不存在的,开发者不会加上去,苹果也不会,只是当`ipa`包被反编译后篡改文件再次打包,需要伪造`SignerIdentity` |
| 85 | +
|
| 86 | +```swift |
| 87 | + NSBundle *bundle = [NSBundle mainBundle]; |
| 88 | + NSDictionary *info = [bundle infoDictionary]; |
| 89 | + if ([info objectForKey:@"SignerIdentity"] != nil) |
| 90 | + { |
| 91 | + return YES; |
| 92 | + } |
| 93 | + return NO; |
| 94 | +``` |
| 95 | + |
| 96 | +#### 防止`gdb/lldb` 调试 |
| 97 | +```swift |
| 98 | +// 阻止 gdb/lldb 调试 |
| 99 | +// 调用 ptrace 设置参数 PT_DENY_ATTACH,如果有调试器依附,则会产生错误并退出 |
| 100 | +#import <dlfcn.h> |
| 101 | +#import <sys/types.h> |
| 102 | + |
| 103 | +typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data); |
| 104 | +#if !defined(PT_DENY_ATTACH) |
| 105 | +#define PT_DENY_ATTACH 31 |
| 106 | +#endif |
| 107 | + |
| 108 | +void anti_gdb_debug() { |
| 109 | + void *handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW); |
| 110 | + ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace"); |
| 111 | + ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0); |
| 112 | + dlclose(handle); |
| 113 | +} |
| 114 | + |
| 115 | +int main(int argc, char * argv[]) { |
| 116 | +#ifndef DEBUG |
| 117 | + // 非 DEBUG 模式下禁止调试 |
| 118 | + anti_gdb_debug(); |
| 119 | +#endif |
| 120 | + @autoreleasepool { |
| 121 | + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); |
| 122 | + } |
| 123 | +} |
| 124 | +``` |
| 125 | +#### 防止抓包 |
| 126 | +##### 1. 检测代理 |
| 127 | +```swift |
| 128 | +对于抓包,现在的手段基本是设置代理,所以我们可以通过判断是否设置了代理的方式来进行下一步的防范。 |
| 129 | +在网络请求前插入这个方法,再根据需求做相应的防范 |
| 130 | ++ (BOOL)getDelegateStatus |
| 131 | +{ |
| 132 | + NSDictionary *proxySettings = CFBridgingRelease((__bridge CFTypeRef _Nullable)((__bridge NSDictionary *)CFNetworkCopySystemProxySettings())); |
| 133 | + NSArray *proxies = CFBridgingRelease((__bridge CFTypeRef _Nullable)((__bridge NSArray *)CFNetworkCopyProxiesForURL((__bridge CFURLRef)[NSURL URLWithString:@"http://www.google.com"], (__bridge CFDictionaryRef)proxySettings))); |
| 134 | + NSDictionary *settings = [proxies objectAtIndex:0]; |
| 135 | + NSLog(@"host=%@", [settings objectForKey:(NSString *)kCFProxyHostNameKey]); |
| 136 | + NSLog(@"port=%@", [settings objectForKey:(NSString *)kCFProxyPortNumberKey]); |
| 137 | + NSLog(@"type=%@", [settings objectForKey:(NSString *)kCFProxyTypeKey]); |
| 138 | + if ([[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:@"kCFProxyTypeNone"]) |
| 139 | + { |
| 140 | + //没有设置代理 |
| 141 | + return NO; |
| 142 | + |
| 143 | + } else { |
| 144 | + //设置代理了 |
| 145 | + return YES; |
| 146 | + } |
| 147 | +} |
| 148 | +``` |
| 149 | +##### 2. 接口参数和数据使用对称加密处理 |
| 150 | +* AES128 |
| 151 | +* RSA |
| 152 | + |
| 153 | +#### 代码混淆加固等 |
| 154 | + |
| 155 | + |
| 156 | + |
| 157 | +#### 相关博客介绍 |
| 158 | + |
| 159 | +* [iOS应用安全 - 完整性检测](https://www.jianshu.com/p/f043b72db736) |
| 160 | +* [iOS App的几种安全防范](https://www.cnblogs.com/tangyuanby2/p/11384481.html) |
| 161 | +* [防止App被修改,加签名判断](https://makezl.github.io/2016/06/21/CodeSign/) |
| 162 | +* [iOS安全防护---越狱检测、二次打包检测、反调试](https://blog.csdn.net/u013602835/article/details/86545106) |
| 163 | +* [如何防止客户端被破解](https://tanqisen.github.io/blog/2014/06/06/how-to-prevent-app-crack/) |
| 164 | +* [iOS——防止反编译总结](https://icocos.github.io/2017/10/26/iOS%E2%80%94%E2%80%94%E9%98%B2%E6%AD%A2%E5%8F%8D%E7%BC%96%E8%AF%91%E6%80%BB%E7%BB%93/) |
| 165 | + |
| 166 | + |
| 167 | + |
0 commit comments