目录

iOS代码混淆

功能分析

实现流程

类名修改

方法名修改

生成垃圾代码

替换png等静态资源MD5

info.plist文件添加垃圾字段

功能分析

实现流程

类名修改

方法名修改

生成垃圾代码

替换png等静态资源MD5

info.plist文件添加垃圾字段

混淆前后对比


iOS代码混淆

目前公司产品线中存在大量功能类似的APP,按照模块化方式开发项目,核心模块业务代码是复用的,使用同一个开发者账号下iOS上架流程中有些APP在苹果机审过程中惨遭被拒的下场,通过更改部分页面UI效果也无济于事,于是采用代码混淆的方式也就是马甲包方案去绕过机审;

功能分析

  • 二进制不同,图标,包名,工程名,代码,静态资源等的修改。

  • 差异化UI风格,产品功能,页面布局等的修改

实现流程

  • 核心模块类名修改

  • 核心方法名修改

  • 加入垃圾代码

  • 替换png等静态资源MD5

  • info.plist文件添加垃圾字段

  • 编辑

类名修改

  • 遍历查找需要替换的核心模块目录 (主工程\Pods目录)

  • 找到所有需要替换的类名(项目专用前缀),将其存放到数组中

  • 遍历查找整个工程的所有目录,查找所有.h、.m、.xib、.string文件,逐行扫描文件,找到需要替换的类名关键字替换成别的名字前缀

  • 如发现.h、.m、.xib、.string文件的文件名包含需要替换的类名,替换之(xcodeproj工程需要重新引入文件,通过脚本动态引入)

  • 遇到有"+"号的分类文件,筛选出"+"号前面的类名然后替换之

applescript复制代码#遍历查找所有.h、.m、.xib、.strings文件,逐行扫描文件,找到需要替换的类名关键字替换成别的名字前缀
def do_replace_file_name(dir_path,need_name_list)
Dir.foreach(dir_path) do |file|
if file != "." and file != ".."
file_path = dir_path + "/" + file
if File.directory? file_path
do_replace_file_name(file_path,need_name_list)
else
if file.end_with?(".h") or file.end_with?(".m") or file.end_with?(".xib") or file.end_with?(".strings") #只查找.h .m .xib .strings文件批量修改
aFile = File.new(file_path, "r")
if aFile
file_content = aFile.read()
aFile.close length = need_name_list.length - 1
for i in 0..length do need_name = need_name_list[i]
file_content = split_file_content(file_content,need_name) end aFile = File.new(file_path, "w")
aFile.syswrite(file_content)
aFile.rewind end
end #如.h、.m、.xib、.string文件的文件名包含需要替换的类名,替换之
if file.include?".h" or file.include?".m" or file.include?".xib" or file.include?".strings"
file_suffix = file.split(".")[1]
need_name_list.each { |need_name|
need_file_name = need_name + "." + file_suffix
if file.start_with?(need_file_name) new_file_name = new_file_name(file) new_file_path = dir_path + "/" + new_file_name
File.rename(file_path, new_file_path) #文件名称替换
end
} end end
end
end
end

方法名修改

  • 获取系统文件关键字并缓存,主要是获取iOS SDK中Frameworks所有方法名和参数名作为忽略关键字

  • 遍历查找整个工程的所有.h、.m、.mm文件,提取关键字,主要提取方法名和参数名

  • 将系统关键字、IBAction方法的关键字、属性property的关键字(防止懒加载方法名造成冲突)去除

  • 将剩余的关键字进行方法混淆,混淆方案是将名字用#define宏定义方式替换名称,方法不能替换成随机字符串,这样任然不能通过机审,应替换成规律的单词拼接方法名

  • 将替换后方法名关键字宏名称写入到全局pch文件,xcodeproj动态引入

pgsql复制代码    # 生成混淆文件
@staticmethod
def create_confuse_file(output_file, confused_dict):
log_info("Start creating confuse file, file fullpath is {0}".format(os.path.realpath(output_file)), 2, True)
f = open(output_file, 'wb')
f.write(bytes('#ifndef NEED_CONFUSE_h\n', encoding='utf-8'))
f.write(bytes('#define NEED_CONFUSE_h\n', encoding='utf-8'))
f.write(bytes('// 生成时间: {0}\n'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')), encoding='utf-8'))
for (key, value) in confused_dict.items():
f.write(bytes('#define {0} {1}\n'.format(key, value), encoding='utf-8'))
f.write(bytes('#endif', encoding='utf-8'))
f.close()

生成垃圾代码

  • 遍历查找整个工程的所有.m、.mm文件

  • 为避免和混淆后的方法重名,添加垃圾方法的时候使用 随机前缀 + "_" + 规律单词 作为方法名,随意在方法中添加日志代码

  • 在文件结尾@end前插入这些方法

haxe复制代码#oc代码以@end结尾,在其前面添加text
def appendTextToOCFile(file_path, text):
with open(file_path, "r") as fileObj:
old_text = fileObj.read()
fileObj.close()
end_mark_index = old_text.rfind("@end")
if end_mark_index == -1:
print "\t非法的结尾格式: " + file_path
return
new_text = old_text[:end_mark_index]
new_text = new_text + text + "\n"
new_text = new_text + old_text[end_mark_index:] with open(file_path, "w") as fileObj:
fileObj.write(new_text) #处理单个OC文件,添加垃圾函数。确保其对应头文件存在于相同目录
def dealWithOCFile(filename, file_path):
global target_ios_folder,create_func_min,create_func_max,funcname_set
funcname_set.clear()
end_index = file_path.rfind(".")
pre_name = file_path[:end_index]
header_path = pre_name + ".h"
if not os.path.exists(header_path):
print "\t相应头文件不存在:" + file_path
return new_func_num = random.randint(create_func_min, create_func_max)
print "\t给%s添加%d个方法" %(filename, new_func_num) prefix_list = ['btt_', 'gym_', 'muut_', 'ora_', 'vend_', 'enyt_', 'qotb_', 'ldt_', 'zndy_', 'tim_', 'yar_', 'toa_', 'rewwy_', 'twof_', 'theg_', 'guis_', 'dui_' ] random_index_list = random.sample(range(0,new_func_num), new_func_num) for i in range(new_func_num): prefix = prefix_list[random_index_list[i]]
header_text = getOCHeaderFuncText(prefix)
# print "add %s to %s" %(header_text, header_path.replace(target_ios_folder, ""))
appendTextToOCFile(header_path, header_text + ";\n")
funcText = getOCFuncText(header_text)
appendTextToOCFile(file_path, funcText)

替换png等静态资源MD5

livecodeserver复制代码        if file_type == ".png":
text = "".join(random.sample(string.ascii_letters, 11))
elif file_type == ".jpg":
text = "".join(random.sample(string.ascii_letters, 20))
elif file_type == ".lua":
text = "\n--#*" + "".join(random.sample(string.ascii_letters, 10)) + "*#--"
else:
text = " "*random.randint(1, 100)
fileObj.write(text)
fileObj.close()

info.plist文件添加垃圾字段

在info.plist中插入规律英文单词(已排除系统专用字段),值为随机字符串

scss复制代码def addPlistField(plist_file):

    global create_field_min,create_field_max,word_name_list
create_field_num = random.randint(create_field_min, create_field_max)
random_index_list = random.sample(word_name_list, create_field_num) tree = ET.parse(plist_file)
root = tree.getroot() root_dict = root.find("dict") for i in range(create_field_num): key_node = ET.SubElement(root_dict,"key")
key_node.text = random_index_list[i] string_node = ET.SubElement(root_dict,"string")
string_node.text = getOneName() tree.write(plist_file,"UTF-8")

目前公司产品线中存在大量功能类似的APP,按照模块化方式开发项目,核心模块业务代码是复用的,使用同一个开发者账号下iOS上架流程中有些APP在苹果机审过程中惨遭被拒的下场,通过更改部分页面UI效果也无济于事,于是采用代码混淆的方式也就是马甲包方案去绕过机审;

功能分析

  • 二进制不同,图标,包名,工程名,代码,静态资源等的修改。

  • 差异化UI风格,产品功能,页面布局等的修改

实现流程

  • 核心模块类名修改

  • 核心方法名修改

  • 加入垃圾代码

  • 替换png等静态资源MD5

  • info.plist文件添加垃圾字段

类名修改

  • 遍历查找需要替换的核心模块目录 (主工程\Pods目录)

  • 找到所有需要替换的类名(项目专用前缀),将其存放到数组中

  • 遍历查找整个工程的所有目录,查找所有.h、.m、.xib、.string文件,逐行扫描文件,找到需要替换的类名关键字替换成别的名字前缀

  • 如发现.h、.m、.xib、.string文件的文件名包含需要替换的类名,替换之(xcodeproj工程需要重新引入文件,通过脚本动态引入)

  • 遇到有"+"号的分类文件,筛选出"+"号前面的类名然后替换之

applescript复制代码#遍历查找所有.h、.m、.xib、.strings文件,逐行扫描文件,找到需要替换的类名关键字替换成别的名字前缀
def do_replace_file_name(dir_path,need_name_list)
Dir.foreach(dir_path) do |file|
if file != "." and file != ".."
file_path = dir_path + "/" + file
if File.directory? file_path
do_replace_file_name(file_path,need_name_list)
else
if file.end_with?(".h") or file.end_with?(".m") or file.end_with?(".xib") or file.end_with?(".strings") #只查找.h .m .xib .strings文件批量修改
aFile = File.new(file_path, "r")
if aFile
file_content = aFile.read()
aFile.close length = need_name_list.length - 1
for i in 0..length do need_name = need_name_list[i]
file_content = split_file_content(file_content,need_name) end aFile = File.new(file_path, "w")
aFile.syswrite(file_content)
aFile.rewind end
end #如.h、.m、.xib、.string文件的文件名包含需要替换的类名,替换之
if file.include?".h" or file.include?".m" or file.include?".xib" or file.include?".strings"
file_suffix = file.split(".")[1]
need_name_list.each { |need_name|
need_file_name = need_name + "." + file_suffix
if file.start_with?(need_file_name) new_file_name = new_file_name(file) new_file_path = dir_path + "/" + new_file_name
File.rename(file_path, new_file_path) #文件名称替换
end
} end end
end
end
end

方法名修改

  • 获取系统文件关键字并缓存,主要是获取iOS SDK中Frameworks所有方法名和参数名作为忽略关键字

  • 遍历查找整个工程的所有.h、.m、.mm文件,提取关键字,主要提取方法名和参数名

  • 将系统关键字、IBAction方法的关键字、属性property的关键字(防止懒加载方法名造成冲突)去除

  • 将剩余的关键字进行方法混淆,混淆方案是将名字用#define宏定义方式替换名称,方法不能替换成随机字符串,这样任然不能通过机审,应替换成规律的单词拼接方法名

  • 将替换后方法名关键字宏名称写入到全局pch文件,xcodeproj动态引入

pgsql复制代码    # 生成混淆文件
@staticmethod
def create_confuse_file(output_file, confused_dict):
log_info("Start creating confuse file, file fullpath is {0}".format(os.path.realpath(output_file)), 2, True)
f = open(output_file, 'wb')
f.write(bytes('#ifndef NEED_CONFUSE_h\n', encoding='utf-8'))
f.write(bytes('#define NEED_CONFUSE_h\n', encoding='utf-8'))
f.write(bytes('// 生成时间: {0}\n'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')), encoding='utf-8'))
for (key, value) in confused_dict.items():
f.write(bytes('#define {0} {1}\n'.format(key, value), encoding='utf-8'))
f.write(bytes('#endif', encoding='utf-8'))
f.close()

生成垃圾代码

  • 遍历查找整个工程的所有.m、.mm文件

  • 为避免和混淆后的方法重名,添加垃圾方法的时候使用 随机前缀 + "_" + 规律单词 作为方法名,随意在方法中添加日志代码

  • 在文件结尾@end前插入这些方法

haxe复制代码#oc代码以@end结尾,在其前面添加text
def appendTextToOCFile(file_path, text):
with open(file_path, "r") as fileObj:
old_text = fileObj.read()
fileObj.close()
end_mark_index = old_text.rfind("@end")
if end_mark_index == -1:
print "\t非法的结尾格式: " + file_path
return
new_text = old_text[:end_mark_index]
new_text = new_text + text + "\n"
new_text = new_text + old_text[end_mark_index:] with open(file_path, "w") as fileObj:
fileObj.write(new_text) #处理单个OC文件,添加垃圾函数。确保其对应头文件存在于相同目录
def dealWithOCFile(filename, file_path):
global target_ios_folder,create_func_min,create_func_max,funcname_set
funcname_set.clear()
end_index = file_path.rfind(".")
pre_name = file_path[:end_index]
header_path = pre_name + ".h"
if not os.path.exists(header_path):
print "\t相应头文件不存在:" + file_path
return new_func_num = random.randint(create_func_min, create_func_max)
print "\t给%s添加%d个方法" %(filename, new_func_num) prefix_list = ['btt_', 'gym_', 'muut_', 'ora_', 'vend_', 'enyt_', 'qotb_', 'ldt_', 'zndy_', 'tim_', 'yar_', 'toa_', 'rewwy_', 'twof_', 'theg_', 'guis_', 'dui_' ] random_index_list = random.sample(range(0,new_func_num), new_func_num) for i in range(new_func_num): prefix = prefix_list[random_index_list[i]]
header_text = getOCHeaderFuncText(prefix)
# print "add %s to %s" %(header_text, header_path.replace(target_ios_folder, ""))
appendTextToOCFile(header_path, header_text + ";\n")
funcText = getOCFuncText(header_text)
appendTextToOCFile(file_path, funcText)

替换png等静态资源MD5

livecodeserver复制代码        if file_type == ".png":
text = "".join(random.sample(string.ascii_letters, 11))
elif file_type == ".jpg":
text = "".join(random.sample(string.ascii_letters, 20))
elif file_type == ".lua":
text = "\n--#*" + "".join(random.sample(string.ascii_letters, 10)) + "*#--"
else:
text = " "*random.randint(1, 100)
fileObj.write(text)
fileObj.close()

info.plist文件添加垃圾字段

在info.plist中插入规律英文单词(已排除系统专用字段),值为随机字符串

scss复制代码def addPlistField(plist_file):

    global create_field_min,create_field_max,word_name_list
create_field_num = random.randint(create_field_min, create_field_max)
random_index_list = random.sample(word_name_list, create_field_num) tree = ET.parse(plist_file)
root = tree.getroot() root_dict = root.find("dict") for i in range(create_field_num): key_node = ET.SubElement(root_dict,"key")
key_node.text = random_index_list[i] string_node = ET.SubElement(root_dict,"string")
string_node.text = getOneName() tree.write(plist_file,"UTF-8")

混淆前后对比

代码混淆前

Hopper查看混淆前

代码混淆后

Hopper查看混淆后

假如你不知道如何代码混淆和如何创建文件混淆,你可以参考下面这个教程来使用我们平台代码混淆和文件混淆以及重签名:怎么保护苹果手机移动应用程序ios ipa中的代码 | ipaguard使用教程

Ipa Guard是一款功能强大的ipa混淆工具,不需要ios app源码,直接对ipa文件进行混淆加密。可对IOS ipa 文件的代码,代码库,资源文件等进行混淆保护。 可以根据设置对函数名、变量名、类名等关键代码进行重命名和混淆处理,降低代码的可读性,增加ipa破解反编译难度。可以对图片,资源,配置等进行修改名称,修改md5。只要是ipa都可以,不限制OC,Swift,Flutter,React Native,H5类app。

总结

在移动互联网时代,代码混淆越来越受到开发者的重视。 iOS代码混淆可以提高难度,从而防止应用程序被盗用或反编译,保护开发者的权益。但是同时也带来了一些问题,例如混淆后的函数名可能会影响代码的可维护性。因此,在使用代码混淆时需要进行合理规划。

参考资料

  1. IpaGuard文档 - 代码混淆

  2. iOS代码混淆方案

  3. iOS文件混淆方案

  4. iOS重签名与测试

如何通过代码混淆绕过苹果机审,解决APP被拒问题的更多相关文章

  1. Xcode8之后,苹果列出了最新App被拒十大原因

    开发者在开发应用程序之前,熟悉苹果审核应用的技术.内容以及设计准则是非常重要的,可以大大降低应用审核被拒的可能性. 最近,苹果通过一个专门的页面给出了截止2016年10月10日应用提交审核被拒的十大原 ...

  2. powershell代码混淆绕过

    目前大多数攻击者已经将PowerShell 利用在了各种攻击场景中,如内网渗透,APT攻击甚至包括现在流行的勒索软件中.powershell的功能强大且调用方式十分灵活,灵活使用powershell可 ...

  3. Eclipse与Android源码中ProGuard工具的使用(代码混淆)

    由于工作需要,这两天和同事在研究android下面的ProGuard工具的使用,通过查看android官网对该工具的介绍以及网络上其它相关资料,再加上自己的亲手实践,算是有了一个基本了解.下面将自己的 ...

  4. 代码混淆和dump

    首先是安装和使用dump: 下载dump地址 1.选择class-dump-3.5.dmg 下载: 2.下载之后,点击打开,复制class-dump文件, 3.shift+command+G 打开fi ...

  5. .net代码混淆 .NET Reactor 研究 脚本一键混淆一键发布

    .net代码混淆 .NET Reactor 研究 为什么要混淆? .net比较适合快速开发桌面型应用,但缺点是发布出来的文件是可以反编译的,有时候为了客户的安全,我们的代码或者我们的逻辑不想让别人知道 ...

  6. ChaosTool,iOS添加垃圾代码工具,代码混淆工具,代码生成器,史上最好用的垃圾代码添加工具,自己开发的小工具

    最近在H5游戏项目中需要添加垃圾代码作混淆,提高过审机率.手动添加太费时费力,在网上并没有找到合适的比较好的工具,就自己动手写了一个垃圾代码添加工具,命名为ChaosTool. 扣扣交流群:81171 ...

  7. O-MVLL代码混淆方式

    在介绍O-MVLL之前,首先介绍什么是代码混淆以及基于LLVM的代码混淆,O-MVLL项目正是基于此而开发来的. 有关O-MVLL的概括介绍以及安装和基本使用方式,可参见另一篇随笔 https://w ...

  8. 【Android】Android Studio 进行代码混淆,打包release APK

    整了一天,感觉坑挺多. 1. 选择如图中的选项Android Studio进行签名打包: 2. 填写APP对应的信息:(最好用个文本记下来放在项目中同步给Team) - Key store path: ...

  9. ProGuard代码混淆技术详解

    前言     受<APP研发录>启发,里面讲到一名Android程序员,在工作一段时间后,会感觉到迷茫,想进阶的话接下去是看Android系统源码呢,还是每天继续做应用,毕竟每天都是画UI ...

  10. Android Studio代码混淆插件

    之前给公司的App添加代码混淆,在代码的混淆过程也遇到了不少的问题,再加上最近学习了一下Android Studio插件的开发,所以就开发一个代码混淆插件方便项目的代码混淆. 截图 第三方库列表清单 ...

随机推荐

  1. SQL Server 根据一个表数据修改另外一个表数据

    今天在写代码的时候发现一个有趣的问题,同时也暴露了之前写的代码有问题,还好之前没有出现重复的情况,及时发现了这个问题,及时改了回来,不然就GG了 下面先上代码,再给大家解说一下 CREATE TABL ...

  2. UE5打包SDK未正确安装的问题

    正文 Windows(笔者之前用的电脑是windows10,最新电脑使用的是windows11)下UE5打包项目的需要安装Visual Studio. 而且安装的时候需要选择上C++ 游戏开发相关模块 ...

  3. MyBatis实现动态SQL更新

    博主记得在一个周五快下班的下午,产品找到我(为什么总感觉周五快下班就来活 ),跟我说有几个业务列表查询需要加上时间条件过滤数据,这个条件可能会变,不保证以后不修改,这个改动涉及到多个列表查询,于是博主 ...

  4. C++与Java共同点

    前言 首先我们来了解一下C++语言,大多人都C++语言是C语言基础上的改编,所以它拥有一个和C语言一样的类似结构,但是它与Java又有不可分割的关系 接下来我们来看几道题: 例如:常数O运行次数与N大 ...

  5. 如何为物联网设备注入“华为云+鸿蒙DNA”?

    本文分享自华为云社区<如何为物联网设备注入"华为云+鸿蒙DNA"?看华为云IoT怎么答[华为云IoT +鸿蒙]>,作者: 华为IoT云服务. 根据市场咨询机构预测,20 ...

  6. fastapi之helloworld

    简介 以下简介来自官网描述: FastAPI是一个用于构建API的现代.快速(高性能)的web框架,使用Python3.6+并基于标准的Python类型提示. 关键特性: 快速:可与NodeJS和Go ...

  7. 基于Pair-wise和CrossEncoder训练单塔模型

    本文分享自华为云社区<语义检索系统排序模块:基于ERNIE-Gram的Pair-wise和基于RocketQA的CrossEncoder训练单塔模型>,作者: 汀丶. 文本匹配任务数据每一 ...

  8. XAML格式化工具:XAML Styler

    XAML格式化的意义 在开发WPF应用过程中,编写XAML时需要手动去缩进或者换行,随着时间的推移或者参与开发的人增多,XAML文件内容的格式会越来越乱.要么属性全都写在一行,内容太宽一屏无法完整展现 ...

  9. 极简工作流「GitHub 热点速览」

    原以为 LLM 很难,但其实可以很简单,比如 Flowise 拖拽拖拽就能出来一个 LLM 流程,非常简单你的 LLM 就可以 run 起来了.同样的 web-check 也能极快速地帮你解决 Web ...

  10. 谈一谈电商api的未来

    随着互联网的飞速发展,电商行业已经成为了现代消费的主流模式.在电商平台上,商品的交易.物流.支付等环节都需要使用API(Application Programming Interface)接口来实现信 ...