为什么要代码混淆?

主要是为了给App加固

为什么要给App加固?

为了增加应用的安全性,防止应用被破解、盗版、二次打包、注入、反编译等

常见的加固方式

不同平台有不同的做法

代码混淆

iOS程序可以通过class-dump,Hopper,IDA等获取类名,方法名,以及分析程序的执行逻辑

如果进行了代码混淆,可以加大别人的分析难度

示例:使用class-dump导出可执行文件的头文件 以网上以前写的一个demo为示例MVPDemo-> Product->MVPDemo.app 右键Show in Finder ->MVPDemo.app 右键显示包内容,找到可执行文件MVPDemo

把这个文件拷贝到桌面,启动终端,cd到桌面目录 执行

class-dump -H MVPDemo -o Headers

demo 可以看到头文件的信息非常清晰

把可执行文件MVPDemo放到Hopper Disassembler同样可以查看到方法名,类名 demo

iOS代码混淆方案

一.源码的混淆

二.LLVM中间代码IR的混淆(容易产生bug)

1.自己编写Pass 2.ollvm:https://github.com/obfuscator-llvm/obfuscator

使用宏定义混淆方法名,类名 以示例应用的Model为演示,可以先看下之前导出的头文件,我们可以看到哪些关键信息

#import "NSObject.h"

#import "ModelProtocol.h"

@class NSString;

@interface Model : NSObject <ModelProtocol>
{
    NSString *_titleString;
    NSString *_subTitleString;
}

@property(copy, nonatomic) NSString *subTitleString; // @synthesize subTitleString=_subTitleString;
@property(copy, nonatomic) NSString *titleString; // @synthesize titleString=_titleString;
- (void).cxx_destruct;
- (double)height;
- (id)identifier;

// Remaining properties
@property(readonly, copy) NSString *debugDescription;
@property(readonly, copy) NSString *description;
@property(readonly) unsigned long long hash;
@property(readonly) Class superclass;

@end

很明显有属性,方法,协议相关重要信息

@interface Model : NSObject <ModelProtocol>

@property(copy, nonatomic) NSString *subTitleString; 
@property(copy, nonatomic) NSString *titleString; 

- (double)height;
- (id)identifier;

这里使用pch文件,定义混淆的宏定义

#ifndef PrefixHeader_pch
#define PrefixHeader_pch

#define ModelProtocol DXPdllsdsc
#define titleString ddsdlls
#define subTitleString sccdlsd

#define testTitle dsdhowe
#define subTitles dsdsdfd

#endif /* PrefixHeader_pch */

编译后,使用class-dump重新生成头文件

查看model代码

#import "NSObject.h"

#import "DXPdllsdsc.h"

@class NSString;

@interface Model : NSObject <DXPdllsdsc>
{
    NSString *_ddsdlls;
    NSString *_sccdlsd;
}

@property(copy, nonatomic) NSString *sccdlsd; // @synthesize sccdlsd=_sccdlsd;
@property(copy, nonatomic) NSString *ddsdlls; // @synthesize ddsdlls=_ddsdlls;
- (void).cxx_destruct;
- (double)height;
- (id)identifier;
- (void)dsdhowe:(id)arg1 dsdsdfd:(id)arg2;

// Remaining properties
@property(readonly, copy) NSString *debugDescription;
@property(readonly, copy) NSString *description;
@property(readonly) unsigned long long hash;
@property(readonly) Class superclass;

@end

协议名字DXPdllsdsc 属性

@property(copy, nonatomic) NSString *sccdlsd;
@property(copy, nonatomic) NSString *ddsdlls;

方法名

- (void)dsdhowe:(id)arg1 dsdsdfd:(id)arg2;

把可执行文件放到Hopper查看

demo 关键属性,方法名,协议,也无法查看到

注意点

建议:给需要混淆的符号加上了一个特定的前缀,防止替换了其他人或者系统的或者第三方的方法

字符串加密

很多时候,可执行文件中的字符串信息,对破解者来说,非常关键,是破解的捷径之一

为了加大破解、逆向难度,可以考虑对字符串进行加密

字符串的加密技术有很多种,可以根据自己的需要自行制定算法

这里举一个简单的例子,如下

- (void)viewDidLoad {
    [super viewDidLoad];

    NSString *token = @"abcdefg123456";
    NSLog(@"%@",token);
}

使用Hopper查看可执行文件 demo 关键字符串token完全是明文.

对每个字符进行异或(^)处理 需要使用字符串时,对异或(^)过的字符再进行一次异或(^),就可以获得原字符

比如,字符串abcd123,我们先对每个字符进行按位异或^处理

const char *str2 = "abcd123";

for (int i = 0; i<strlen(str2); i++) {
    // {100,103,102,97,52,55,54};
    NSLog(@"%d",str2[i]^5);
}

这样拿到的相对应的字符是{100,103,102,97,52,55,54};

现在对上面的C数组字符,封装一个方法,对每一个字符再次异或,那么就得到了原字符abcd123,方法如下

- (NSString *)getString{
    // 最后加上0表示结束
    char str[] = {100,103,102,97,52,55,54,0};
    NSMutableString *strM = [NSMutableString string];
    for (int i = 0; i<strlen(str); i++) {
        [strM appendFormat:@"%c",str[i]^5];
    }
    return strM;
}

第三方的混淆,比如https://github.com/CoderMJLee/MJCodeObfuscation 使用起来也是很方便