一、APP加固保护
1、字符串混淆
代码中重要的明文字符串混淆加密
2、代码混淆
对类名,方法名混淆,防止反编译看到类名和方法名
3、防止动态调试
防止恶意动态分析调试程序
#import <sys/sysctl.h>
// 检查是否被调试
bool checkDebugger(){
//控制码
int name[4];//放字节码-查询信息
name[0] = CTL_KERN;//内核查看
name[1] = KERN_PROC;//查询进程
name[2] = KERN_PROC_PID; //通过进程id查进程
name[3] = getpid();//拿到自己进程的id
//查询结果
struct kinfo_proc info;//进程查询信息结果
size_t info_size = sizeof(info);//结构体大小
int error = sysctl(name, sizeof(name)/sizeof(*name), &info, &info_size, 0, 0);
assert(error == 0);//0就是没有错误
//结果解析 p_flag的第12位为1就是有调试
//p_flag 与 P_TRACED =0 就是有调试
return ((info.kp_proc.p_flag & P_TRACED) !=0);
}
4、防止代码注入
防止恶意代码动态库通过脚本注入ipa包
5、防止重签名
防止获取ipa包后,重新签名后安装到设备
// 检验签名
// id为开发者账号teamId,即组织单位
void checkCodesign(NSString *id){
// 描述文件路径
NSString *embeddedPath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
// 读取application-identifier 注意描述文件的编码要使用:NSASCIIStringEncoding
NSString *embeddedProvisioning = [NSString stringWithContentsOfFile:embeddedPath encoding:NSASCIIStringEncoding error:nil];
NSArray *embeddedProvisioningLines = [embeddedProvisioning componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (int i = 0; i < embeddedProvisioningLines.count; i++) {
if ([embeddedProvisioningLines[i] rangeOfString:@"application-identifier"].location != NSNotFound) {
NSInteger fromPosition = [embeddedProvisioningLines[i+1] rangeOfString:@"<string>"].location+8;
NSInteger toPosition = [embeddedProvisioningLines[i+1] rangeOfString:@"</string>"].location;
NSRange range;
range.location = fromPosition;
range.length = toPosition - fromPosition;
NSString *fullIdentifier = [embeddedProvisioningLines[i+1] substringWithRange:range];
NSArray *identifierComponents = [fullIdentifier componentsSeparatedByString:@"."];
NSString *appIdentifier = [identifierComponents firstObject];
// 对比签名ID
if (![appIdentifier isEqual:id]) {
NSLog(@"🈲ipa被重签名");
//exit
asm(
"mov X0,#0\n"
"mov w16,#1\n"
"svc #0x80"
);
}
break;
}
}
}
6、设备越狱效验
防止手机越狱,可设置越狱手机无法打开APP
- (BOOL)isJailBreak {
BOOL isJail = NO;
/// 根据是否能打开cydia判断
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://"]]) {
isJail = YES;
}
/// 根据是否能获取所有应用的名称判断 没有越狱的设备是没有读取所有应用名称的权限的。
if ([[NSFileManager defaultManager] fileExistsAtPath:@"User/Applications/"]) {
isJail = YES;
}
NSArray *jailbreak_tool_paths = @[
@"/Applications/Cydia.app",
@"/Library/MobileSubstrate/MobileSubstrate.dylib",
@"/bin/bash",
@"/usr/sbin/sshd",
@"/etc/apt"
];
/// 判断这些文件是否存在,只要有存在的,就可以认为手机已经越狱了。
for (int i = 0; i < jailbreak_tool_paths.count; i++) {
if ([[NSFileManager defaultManager] fileExistsAtPath:jailbreak_tool_paths[i]]) {
isJail = YES;
break;
}
}
if (!isJail)
NSLog(@"👍🏻 手机没有越狱");
return isJail;
}
二、网络数据加密
1、哈希函数加密
MD5
#import <CommonCrypto/CommonDigest.h>
NSString* YKMD5( NSString *str ) {
const char *cStr = [str UTF8String];
unsigned char result[32];
CC_MD5( cStr, strlen(cStr), result );
return [[NSString
stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
result[0], result[1],
result[2], result[3],
result[4], result[5],
result[6], result[7],
result[8], result[9],
result[10], result[11],
result[12], result[13],
result[14], result[15]
] lowercaseString];
}
2、对称加密
AES,DES
/**
aes256加密
*/
+ (NSString *)encryptString:(NSString *)str key:(NSString *)key {
const char *cstr = [str cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:str.length];
//对数据进行加密
NSData *result = [self data_aes256_encrypt:data key:key];
//转换为2进制字符串
if (result && result.length > 0) {
Byte *datas = (Byte*)[result bytes];
NSMutableString *output = [NSMutableString stringWithCapacity:result.length * 2];
for(int i = 0; i < result.length; i++){
[output appendFormat:@"%02x", datas[i]];
}
return output;
}
return nil;
}
/**
aes256解密
*/
+ (NSString *)decryptString:(NSString *)str key:(NSString *)key {
//转换为2进制Data
NSMutableData *data = [NSMutableData dataWithCapacity:str.length / 2];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [str length] / 2; i++) {
byte_chars[0] = [str characterAtIndex:i*2];
byte_chars[1] = [str characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[data appendBytes:&whole_byte length:1];
}
//对数据进行解密
NSData* result = [self data_aes256_decrypt:data key:key];
if (result && result.length > 0) {
return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}
return nil;
}
/**
对data数据进行加密
*/
+ (NSData *)data_aes256_encrypt:(NSData *)data key:(NSString *)key {
char keyPtr[kCCKeySizeAES256+1];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSASCIIStringEncoding];
NSUInteger dataLength = [data length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
//相比原文件,修改了kCCAlgorithmAES,kCCKeySizeAES256两个参数
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES,
kCCOptionECBMode | kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL,
[data bytes], dataLength,
buffer, bufferSize,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer);
return nil;
}
/**
对data数据进行解密
*/
+ (NSData *)data_aes256_decrypt:(NSData *)data key:(NSString *)key {
char keyPtr[kCCKeySizeAES256+1];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSASCIIStringEncoding];
NSUInteger dataLength = [data length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES,
kCCOptionPKCS7Padding | kCCOptionECBMode,
keyPtr, kCCKeySizeAES256,
NULL,
[data bytes], dataLength,
buffer, bufferSize,
&numBytesDecrypted);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}
free(buffer);
return nil;
}
//字符串DES加密
+ (NSString *)encryptString:(NSString *)str key:(NSString *)key {
NSString *ciphertext = nil;
NSData *textData = [str dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = [textData length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void * buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmDES,
kCCOptionPKCS7Padding | kCCOptionECBMode,
[key UTF8String], kCCBlockSizeDES,
NULL,
[textData bytes] , dataLength,
buffer, bufferSize,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
NSLog(@"DES加密成功");
NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
ciphertext = [self stringWithHexBytes:data];
}else{
NSLog(@"DES加密失败");
}
free(buffer);
return ciphertext;
}
//字符串DES解密
+ (NSString *)decryptString:(NSString *)str key:(NSString *)key {
NSString *plainText = nil;
NSData *textData = [self parseHexToByteArray:str];
NSUInteger dataLength = [textData length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmDES,
kCCOptionPKCS7Padding | kCCOptionECBMode,
[key UTF8String], kCCKeySizeDES,
NULL,
[textData bytes] , dataLength,
buffer, bufferSize,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
NSLog(@"DES解密成功");
NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
plainText = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}else{
NSLog(@"DES解密失败");
}
free(buffer);
return plainText;
}
//NSData DES加密
+ (NSData *)DESEncryptPlainData:(NSData *)plainData Key:(NSString *)key
{
size_t numBytesEncrypted = 0;
size_t bufferSize = [plainData length] + kCCBlockSizeDES;
void *buffer = malloc(bufferSize);
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding,
[key UTF8String],
kCCKeySizeDES,
[key UTF8String],
[plainData bytes],
[plainData length],
buffer,
bufferSize,
&numBytesEncrypted);
NSData *dataTemp = nil;
if (cryptStatus == kCCSuccess) {
dataTemp = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
}else{
NSLog(@"DES加密失败");
}
return dataTemp;
}
//NSData DES解密
+ (NSData *)DESDecryptCipherData:(NSData*)cipherData Key:(NSString *)key
{
size_t numBytesDecrypted = 0;
size_t bufferSize = [cipherData length] + kCCBlockSizeDES;
void *buffer = malloc(bufferSize);
// IV 偏移量不需使用
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding,
[key UTF8String],
kCCKeySizeDES,
[key UTF8String],
[cipherData bytes],
[cipherData length],
buffer,
bufferSize,
&numBytesDecrypted);
NSData *dataTemp = nil;
if (cryptStatus == kCCSuccess) {
dataTemp = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesDecrypted];
}else{
NSLog(@"DES解密失败");
}
return dataTemp;
}
//nsdata转成16进制字符串
+ (NSString*)stringWithHexBytes:(NSData *)sender {
static const char hexdigits[] = "0123456789ABCDEF";
const size_t numBytes = [sender length];
const unsigned char* bytes = [sender bytes];
char *strbuf = (char *)malloc(numBytes * 2 + 1);
char *hex = strbuf;
NSString *hexBytes = nil;
for (int i = 0; i<numBytes; ++i) {
const unsigned char c = *bytes++;
*hex++ = hexdigits[(c >> 4) & 0xF];
*hex++ = hexdigits[(c ) & 0xF];
}
*hex = 0;
hexBytes = [NSString stringWithUTF8String:strbuf];
free(strbuf);
return hexBytes;
}
/*
将16进制数据转化成NSData 数组
*/
+(NSData*) parseHexToByteArray:(NSString*) hexString
{
int j=0;
Byte bytes[hexString.length];
for(int i=0;i<[hexString length];i++)
{
int int_ch; /// 两位16进制数转化后的10进制数
unichar hex_char1 = [hexString characterAtIndex:i]; ////两位16进制数中的第一位(高位*16)
int int_ch1;
if(hex_char1 >= '0' && hex_char1 <='9')
int_ch1 = (hex_char1-48)*16; //// 0 的Ascll - 48
else if(hex_char1 >= 'A' && hex_char1 <='F')
int_ch1 = (hex_char1-55)*16; //// A 的Ascll - 65
else
int_ch1 = (hex_char1-87)*16; //// a 的Ascll - 97
i++;
unichar hex_char2 = [hexString characterAtIndex:i]; ///两位16进制数中的第二位(低位)
int int_ch2;
if(hex_char2 >= '0' && hex_char2 <='9')
int_ch2 = (hex_char2-48); //// 0 的Ascll - 48
else if(hex_char2 >= 'A' && hex_char1 <='F')
int_ch2 = hex_char2-55; //// A 的Ascll - 65
else
int_ch2 = hex_char2-87; //// a 的Ascll - 97
int_ch = int_ch1+int_ch2;
bytes[j] = int_ch; ///将转化后的数放入Byte数组里
j++;
}
NSData *newData = [[NSData alloc] initWithBytes:bytes length:hexString.length/2];
return newData;
}
3、非对称加密
RSA
/*
@author: ideawu
@link: https://github.com/ideawu/Objective-C-RSA
*/
4、国密加密
SM2,SM3,SM4
5、SSL Pinning
HTTPS请求证书效验,防止抓包
6、设备网络代理效验
防止抓包,可设置开启代理无法打开APP
- (BOOL)isNetProxy {
NSDictionary *proxySettings = (__bridge NSDictionary *)(CFNetworkCopySystemProxySettings());
NSArray *proxies = (__bridge NSArray *)(CFNetworkCopyProxiesForURL((__bridge CFURLRef _Nonnull)([NSURL URLWithString:@"http://www.baidu.com"]), (__bridge CFDictionaryRef _Nonnull)(proxySettings)));
NSDictionary *settings = [proxies objectAtIndex:0];
// NSLog(@"host=%@", [settings objectForKey:(NSString *)kCFProxyHostNameKey]);
// NSLog(@"port=%@", [settings objectForKey:(NSString *)kCFProxyPortNumberKey]);
// NSLog(@"type=%@", [settings objectForKey:(NSString *)kCFProxyTypeKey]);
if ([[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:@"kCFProxyTypeNone"]){
//没有设置代理
NSLog(@"👍🏻 未开启网络代理");
return NO;
}else{
//设置代理了
NSLog(@"🈲 已开启网络代理");
return YES;
}
}
三、远程数据删除
1、MDM-移动设备管理
Microsoft Intune 移动设备管理
2、请求后台接口
根据设备唯一标识,通过后台接口判断该设备是否退出登录并删除本地数据
相关链接:
本文详细介绍了iOS应用程序的加密保护措施,包括APP加固保护如字符串混淆、代码混淆、防止动态调试和代码注入等;网络数据加密如哈希、对称加密、非对称加密和SSL Pinning等;以及远程数据删除策略,如MDM移动设备管理和后台接口控制。

5316

被折叠的 条评论
为什么被折叠?



