代码混淆、字符串加密
代码混淆
-
pch文件,宏定义替换,项目污染少
-
类名加后缀,文件名不能更改!
#ifndef PrefixHeader_pch #define PrefixHeader_pch #define UserInfoClass CHDKSALI458 #define isVipWithAccount KDKSLQWIP45 #endif /* PrefixHeader_pch */
替换后的二进制文件
字符串加密
//多个参数的方法混淆
#define EncryptionToolsClass KDKSLJDK556
#define encryptString KDKSLUEKD41
#define keyString KDKSLUDJW36
NSString * str = [[EncryptionToolsClass sharedEncryptionTools] encryptString:@"some message want to encrypted" keyString:AES_KEY() iv:nil];
- 字符串常量如何加密?隐藏?
-
数组
//断点CCCrypt跟进去还是可以看到key //采用^运算直接换算成结果.不会进入字符串常量区 #define STRING_ENCRYPT_KEY 0xAC static NSString * AES_KEY(){ unsigned char key[] = { (STRING_ENCRYPT_KEY ^ 'I'), (STRING_ENCRYPT_KEY ^ 'U'), (STRING_ENCRYPT_KEY ^ 'I'), (STRING_ENCRYPT_KEY ^ 'D'), (STRING_ENCRYPT_KEY ^ 'I'), (STRING_ENCRYPT_KEY ^ 'S'), (STRING_ENCRYPT_KEY ^ 'T'), (STRING_ENCRYPT_KEY ^ 'P'), (STRING_ENCRYPT_KEY ^ '#'), (STRING_ENCRYPT_KEY ^ '\0') }; unsigned char * p = key; //((*p) ^= STRING_ENCRYPT_KEY)拿到i while (((*p) ^= STRING_ENCRYPT_KEY) != '\0') p++; return [NSString stringWithUTF8String:(const char *)key]; }
-
隐藏CCCrypt
-
符号断点CCCrypt
-
bt拿到函数所在的动态库
-
image list搜索这个动态库,拿到动态库位置,从/usr后面开始
-
通过dlsm函数调用
//导入头文件 #import <dlfcn.h> - (NSString *)encryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv { // 设置秘钥 NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding]; uint8_t cKey[self.keySize]; bzero(cKey, sizeof(cKey)); [keyData getBytes:cKey length:self.keySize]; // 设置iv uint8_t cIv[self.blockSize]; bzero(cIv, self.blockSize); int option = 0; if (iv) { [iv getBytes:cIv length:self.blockSize]; option = kCCOptionPKCS7Padding; } else { option = kCCOptionPKCS7Padding | kCCOptionECBMode; } // 设置输出缓冲区 NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; size_t bufferSize = [data length] + self.blockSize; void *buffer = malloc(bufferSize); // 开始加密 size_t encryptedSize = 0; //加密解密都是它 -- CCCrypt //找函数之前异或加密。防止字符串暴露 unsigned char str[] = { ('a' ^ 'C'), ('a' ^ 'C'), ('a' ^ 'C'), ('a' ^ 'r'), ('a' ^ 'y'), ('a' ^ 'p'), ('a' ^ 't'), ('a' ^ '\0') }; unsigned char * p = str; while (((*p) ^= 'a') != '\0') p++; //句柄,就是一个操作:不直接调用,拿到函数地址和指针,通过指针调用。裁剪符号表,就看不到函数的符号了,达到隐藏的目的 //第一个参数:你要找的动态库的路径 //第二个参数:懒加载符号表还是非懒加载符号表 void * handle = dlopen("/usr/lib/system/libcommonCrypto.dylib",RTLD_LAZY); CCCryptorStatus (* CCCrypt_p)( CCOperation op, /* kCCEncrypt, etc. */ CCAlgorithm alg, /* kCCAlgorithmAES128, etc. */ CCOptions options, /* kCCOptionPKCS7Padding, etc. */ const void *key, size_t keyLength, const void *iv, /* optional initialization vector */ const void *dataIn, /* optional per op and alg */ size_t dataInLength, void *dataOut, /* data RETURNED here */ size_t dataOutAvailable, size_t *dataOutMoved) //dlsym会返回一个函数指针。类型是CCCrypt //第一个参数:通过句柄拿到动态库 //第二个参数:通过动态库找哪一个函数 __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0) = dlsym(handle, (const char *)str); //一定记得先判断有没有值 if (!CCCrypt_p) { return nil; } CCCryptorStatus cryptStatus = CCCrypt_p(kCCEncrypt, self.algorithm, option, cKey, self.keySize, cIv, [data bytes], [data length], buffer, bufferSize, &encryptedSize); NSData *result = nil; if (cryptStatus == kCCSuccess) { result = [NSData dataWithBytesNoCopy:buffer length:encryptedSize]; } else { free(buffer); NSLog(@"[错误] 加密失败|状态编码: %d", cryptStatus); } return [result base64EncodedStringWithOptions:0]; }
-
防住汇编里面去分析在哪里调用的。防不住符号断点调试,只不过符号被抹掉
使用汇编进行系统调用
-
根本就不调用ptrace、exit
- (void)viewDidLoad { [super viewDidLoad]; //直接上汇编代码!! 通过中断 触发 asm( "mov X0,#31\n"//31 PT_DENY_ATTACH "mov X1,#0\n" "mov X2,#0\n" "mov X3,#0\n" "mov w16,#26\n"//这个26代表着ptrace "svc #0x80"//svc触发中断 ); } //为什么是26点进去看~ #import <sys/syscall.h> //exit汇编 asm( "mov X0,#0\n" "mov w16,#1\n" "svc #0x80" );
重签名防护
-
描述文件embedded.mobileprovision
-
证书里面的组织单位不能被篡改,比较字符串或者md5值
//检测TeamID #import "NSObject+Codesign.h" @implementation NSObject (Codesign) //传一个key void checkCodesign(NSString *id){ // 描述文件路径 NSString *embeddedPath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"]; // NSLog(@"Path:%@",embeddedPath); // 读取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:@""].location+8; NSInteger toPosition = [embeddedProvisioningLines[i+1] rangeOfString:@""].location; NSRange range; range.location = fromPosition; range.length = toPosition - fromPosition; NSString *fullIdentifier = [embeddedProvisioningLines[i+1] substringWithRange:range]; // NSLog(@"%@", fullIdentifier); NSArray *identifierComponents = [fullIdentifier componentsSeparatedByString:@"."]; NSString *appIdentifier = [identifierComponents firstObject]; // NSLog(@"%@", appIdentifier); // 对比签名ID if ([appIdentifier isEqual:id]) { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:appIdentifier forKey:@"key"]; [defaults synchronize]; }else{ //exit asm( "mov X0,#0\n" "mov w16,#1\n" "svc #0x80" ); } break; } } } @end
总结
- 重签名防护
- ptrace防护(用汇编)
- 反HOOK防护(动态库提前调用)
- 混淆关键代码