【iOS】APP加密保护

本文详细介绍了iOS应用程序的加密保护措施,包括APP加固保护如字符串混淆、代码混淆、防止动态调试和代码注入等;网络数据加密如哈希、对称加密、非对称加密和SSL Pinning等;以及远程数据删除策略,如MDM移动设备管理和后台接口控制。

 一、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安全防护之重签名防护和sysctl反调试 - 简书

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值