我们在手游平台SDK的iOS版本中, 除了AppStore官方支付之外还集成了第三方支付(微信支付H5和支付宝支付H5版本)。 如果用于企业签,不需要做处理,直接使用即可。 但是如果需要上架AppStore,我们需要屏蔽第三方支付。 这个我们后台设置开关,直接后台切换支付方式即可。 上架审核的时候, 切换为Appstore支付;审核通过的时候,切换为第三方支付。因为第三方支付,是H5方式,不会被机审检测到。 同时,防止被抽查,后台也可以设置,将每次前多少次支付,设置为AppStore支付,之后用第三方支付,降低被抽查到的风险。
但是只是做到这个还不够, 如果需要用这套SDK上多个游戏,或者多个马甲包。 防止一样的代码被4.3的风险或者被代码标记的风险,我们需要对SDK的代码进行混淆, 每次出一个马甲SDK。
关于iOS代码的混淆, 查阅了网上很多文章,混淆的原理性文章都是多年之前的了纯字符随机替换,这种按照目前的机审政策,已经很难过审了。另外也有一些混淆工具,不过是按时间收费的。
所以,我们这里就U8SDK下面的XSDK(手游平台SDK)这个项目,来谈一谈iOS中的代码混淆。
如果在开发之初, 开发者经验丰富, 或者运营经验丰富, 提前将混淆这个事情重视起来, 那么混淆就会很简单。 可以事先指定代码编写规则,尽量保证类方法变量等的唯一性。 比如所有的类以X开头,所有的方法函数以F开头,所有的属性以P开头, 所有的内部变量以S开头等等这样的规则。 那么在后续写混淆的时候,几行代码就可以搞定了。
但是我相信多数项目和XSDK一样,可能都是事后诸葛亮,等到需要做这件事情的时候,才发现代码命名随心所欲带来的代价。 但是,为了方便混淆来重写框架是不可能的了,程序也不愿意呀。
所以,我们就只能来费些心思来写混淆脚本了。 我们采用python来写混淆脚本,用python来干这个事情,还是很合适的。
首先,我们确定一下混淆目标:
列出了混淆目标, 我们来设计混淆思路了。
一、词汇替换
混淆,也就是替换, 将老的名称替换为新的名称。 之前网上很多都是使用纯随机字符来替换,但是目前这个方式风险很大,很容易被苹果检测出来有隐藏功能的意图。
所以,我们采用双词库的形式来进行混淆。 我们准备两套词库。 一套是常用的单词词库(词汇量大概1000个);一套是程序常用单词词库(词汇量大概100个)。
在生成混淆后的新名称的时候, 我们根据需要, 从两套词库中随机单词,进行拼接。
比如混淆类名称的时候, 当前有一个类名是AppStorePay。 我们先从常用词库中随机单词进行拼接,直到长度大于等于AppStorePay长度的时候为止。 然后再从程序词库中随机一个单词作为后缀。 这样混淆后的名称可能是OneForTestListener这样的形式。 看起来就接近了正常的类名命名。
另外混淆的时候, 我们记录一个已经生成的新的名称的列表, 如果生成的新名称已经存在,那么重新生成。 防止生成的新名称重复导致问题。
二、混淆思路
混淆的时候, 我们可以逐行混淆, 但是这种方式, 误差可能会很大。 我们希望执行混淆之后, 一次性编译通过, 不需要再手动修改某些混淆失败的地方。 所以我们放弃了逐行混淆的方式。采用一种更加精细化的混淆方式。
另外混淆的方式,我们希望尽可能地符合语法通用的规则,而不简简单单地适应XSDK这个项目。 (防止后续我们其他项目也需要做混淆)
首先,我们将代码目录下所有的代码文件加载进来, 于是我们设计一个符合代码结构的层级:
图中我们可以看到, 我们首先设计了四个类。
XFolder代表一个代码目录;
XClass类代表一个类文件;
XFunction类代表一个类中的一个函数;
XProperty类代表一个类中的一个属性。
设计好了这个之后,我们就可以调用XFolder中的load方法,加载XFolder对应目录下的所有class文件,以及所有Class文件中的函数和属性。
三、混淆实现
将代码类文件都加载到内存之后, 现在我们就需要对混淆目标中的各个目标的混淆进行实现了。 我们设计一系列混淆实现类,来进行混淆:
1、PCHCodeMixer: 对XSDK.pch文件中定义的全局变量进行混淆,同时对源码中所有引用的地方进行替换
2、PropertyCodeMixer: 对每个类中定义的属性进行混淆, 同时对源码目录中所有引用的地方进行替换
3、FunctionCodeMixer: 对每个类中定义的函数进行混淆,同时对源码目录中所有引用的地方进行替换
4、FunctionCodePostMixer: 对函数内部定义的属性进行混淆,同时在合适的地方插入垃圾代码。
5、ClassCodeMixer:对类名称进行混淆,同时对源码目录以及xcode工程文件中所有引用的地方进行替换
6、ResourceMixer:对资源进行混淆, 对所有图片文件进行md5变更,对所有图片名称进行混淆,对所有源码目录以及xcode工程文件中所有引用的地方进行替换
7、GlobalNameMixer: 对目录,对框架名称,对Bundle名称等全局名称进行混淆。 同时对所有文件和xcode工程文件中所有引用的地方进行替换
有了以上这些组件,就可以完成对框架代码和资源进行混淆和替换。另外还有两个辅助的mixer类,主要完成对demo工程和文档内容的替换:
8、DemoMixer: 对demo工程中的引用进行替换。
9、DocMixer:对文档中对应的引用进行替换。
另外注意下,混淆的时候, 通过正则表达式来解析对应的函数和属性, 但是因为iOS的特殊性, 函数中有中括号,而且函数的地方可以无限嵌套。 所以,在对函数进行处理的时候,可以采用栈结构来解析和匹配。
另外解析函数体内容的时候, 我们也是采用栈结构来解析。 也就是先解析出了类中所有的函数名称, 然后从后往前解析函数体内容。这样解析也方便很多。 其他地方的混淆,基本用正则表达式就可以完成,只是写正则的时候或者用正则匹配替换的时候, 尽可能的精细化规则,严谨地匹配。替换的时候, 按照被替换串的长度,优先替换长串,再替换短串。
目前XSDK代码经过混淆工具混淆之后, 无需再手动替换, 直接xcode打开,编译重新生成XSDK的framework和bundle文件,然后替换到XSDK的Demo工程中的SDK目录下,然后直接运行XSDK Demo就可以了。
如果提供给研发那边的话, 只需要将上面替换过的XSDK Demo和文档提供给研发那边即可。 经过混淆工具混淆之后, Demo工程和文档中都会被替换,无需手动替换操作。