在Unity项目中接入第三方SDK时,需要在Plugin中加一部分原生接口代码(这里仅针对iOS平台),并且在C#层通过DllImport("__Internal")去声明对应的方法实现通信,与此同时在导出Xcode工程中,Unity会将C#代码转换成C++并生成在Classes/Native目录下,如你提包也需要混淆这部分接口。
这里介绍并实现个小脚本,帮助你动态找出这类方法,并且实现混淆,规避一些4.3等常规的扫描(当然只是其中一个细节,还需要结合其它的混淆处理,以后再逐一分享)。
1、首先定位到Classes/Native目录下找出类似的接口方法分析其特征。
例如上面代码中的这部分,就是我们需要找的函数Login()
extern "C" void DEFAULT_CALL Login();
2、我们只需要读入CPP文件,正则去匹配对应的函数名,这里注意不同工程可能有些不一样函数或返回值定义,例如这里的void还可能存在其它的类型,需要自行兼容形如bool/int/char等等。参考的正则可以这么写:
extern\s+\"C\"\s+(?:void|double|bool|int|char\*)\s+DEFAULT_CALL\s+([\w]+)\(.*\)\;
3、已经找好函数,这个时候只需要组织一下生成混淆或组合词来替换原来的函数名就可以了。这里建议用宏替换的方式编译时自动替换,而不需要去修改c++源代码。找到pch文件加入:
#define Login newLogin
4、有些点需要注意的是,上述简单介绍基本逻辑,实际使用时会匹配出许多不同的函数或接口,需要做特殊处理,例如有接入谷歌广告SDK,需要排除一些GAD相关的函数,不然混淆后会有各种函数调用异常。
附参考实现(Python):
#!/usr/bin/env python
# coding=utf-8
import os
import getopt,sys
import re
import random
def getRandomString(length):
'''
生成随机字符串 \n
length 字符串长度
'''
name = ""
for i in range(0, length):
name+=chr(random.randint(97, 122))
return name
def confuseExternDefaultCall(projectPath):
'''
混淆C++与OC通信接口
'''
nativePath = os.path.join(projectPath, "Classes/Native")
if not os.path.isdir(nativePath):
print("目录不存在:%s" % nativePath)
return
# 默认固定
externMethods = []
fileList = os.listdir(nativePath)
for p in fileList:
path = os.path.join(nativePath, p)
with open(path) as f:
for line in f.readlines():
match = re.match('extern\s+\"C\"\s+(?:void|double|bool|int|char\*)\s+DEFAULT_CALL\s+([\w]+)\(.*\)\;', line)
if match:
method = match.groups()[0].strip()
if not(method in externMethods):
externMethods.append(method)
f.close()
# 添加到pch文件
if externMethods:
pch = os.path.join(projectPath, "Classes/Prefix.pch")
with open(pch) as f:
content = f.read()
f.close()
code = []
for method in externMethods:
# 不需要的可在这里加判断排除
regex = re.compile('\#define\s+.*%s\s+.*' % method)
match = re.findall(regex, content)
if match.__len__() > 0:
for m in match:
content = content.replace(m+"\n", "")
newMethod = "New%s" % getRandomString(6) # 自行调整
code.append('#define %s %s' % (method, newMethod))
print("replace %s => %s" % (method, newMethod))
code = "\n".join(code)
with open(pch, "w") as f:
f.write("%s\n\n%s" % (content, code))
f.close()
if __name__ == "__main__":
projectPath = None
opts, args = getopt.getopt(sys.argv[1:], "p:")
if opts.__len__()==0:
print(os.path.basename(__file__)+" 参数说明:\n-p 指定项目路径")
exit()
# 参数解析
for args in opts:
if args[0]=="-p":
projectPath = args[1]
if os.path.isdir(projectPath):
confuseExternDefaultCall(projectPath)
本文暂时没有评论,来添加一个吧(●'◡'●)