跳至主要内容

iOS 马甲包、代码混淆、编译混淆实践

原文链接

1. 马甲包

1.1 什么是马甲包?

马甲包是利用 App store 的规则漏洞,通过技术手段,多次上架同一款产品的方法。马甲包和主产品包拥有基本一致的内容和功能,项目代码基本都是完全复用的,除了图标,应用名称,包名等不一致,其他基本一致。

1.2 为什么做马甲包,以及马甲包的好处

  • AB 测试 可以测试跨度大的新功能,好的功能就在主包上迭代,不好的也无所谓,不影响主包的使用体验,避免用户流失。
  • 导流 主包和马甲包同属一个平台,用户信息可以共享;通过弹窗,广告,Push等引导用户到App Store下载主App;有一部份App接了网盟相互导流。
  • 增加关键词覆盖 App Store关键词长度上限是100个字符,据了解人为正常优化的极限是关键词覆盖数在4000左右,那些覆盖数在8000+的都是利用了苹果漏洞。所以,多做一个马甲,也就意味着覆盖的关键词可以更多。
  • 刷榜 积分墙、真机、机刷等。用马甲包来刷排名,抵抗主包风险。

1.3 马甲包制作注意事项

  • 二进制不同 应用名称,图标,包名,工程名,打包电脑,代码,静态资源等的修改。
  • 差异化 整体UI,产品功能,页面布局等的修改

1.4 实践

KLGenerateSpamCode 是一个应对苹果对重复应用的审核(Guideline 4.3 Design Spam)的工具,用于避免苹果机审检测概率。
主要功能有:
  • 修改工程名
  • 修改类名前缀
  • 扫描工程中的代码,生成同等数量的 Category 文件,文件中及是同等方法数量的垃圾代码。
  • 修改 xxx.xcassets 文件夹中的 png 资源文件名。
  • 删除代码中的所有注释和空行。
-w910
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 项目名地址,注意不是项目文件夹地址
/Users/vincent/Desktop/code/spamCode/VHLSpamCodeDemo/VHLSpamCodeDemo   
# 2. 修改项目名称
-modifyProjectName VHLSpamCodeDemo>VHLSpamCodeDemo1
# 3. 忽略文件夹
-ignoreDirNames Depends
# 4. 修改类名前缀。没有前缀的会加上前缀,有前缀的会修改。注意修改后是否重名
-modifyClassNamePrefix /Users/vincent/Desktop/code/spamCode/VHLSpamCodeDemo/VHLSpamCodeDemo.xcodeproj VHL>VIN
# 5. 生成垃圾代码
-spamCodeOut /Users/vincent/Desktop/code/spamCode/VHLSpamCodeDemo/VHLSpamCodeDemo2/SpamCode VSpam
# 6. 修改 xxx.xcassets 文件夹中的 png 资源文件名
-handleXcassets
# 7. 删除空行和注释 
-deleteComments
-w1075
修改工程名后,如果包含 pod,需要到文件夹下重新执行 pod update

1.5 优化图片大小,修改图片 hash 值

  • mac 下无损压缩图片工具,将包含图片的文件夹拖入工具,会自动扫描文件夹下的所有文件,自动选择最优算法进行优化。一般第一次优化项目时,会有几M到10几M的优化效果。
  • ImageMagick: 是一款创建、编辑、合成,转换图像的命令行工具。
    通过 brew 安装 imagemagick
    1
    
      brew install imagemagick
    
    压缩文件夹下所有 png 文件,会修改 hash 值
    1
    
      find . -iname "*.png" -exec echo {} \; -exec convert {} {} \;
    
    更多 imagemagick 命令以及使用方法 图像处理 - ImageMagick 简单介绍与案例

2. 逆向基础,以及为什么要做混淆

当我们用 Xcode 构建一个程序的过程时,编译器会把源文件 (.m 和 .h) 文件转换为一个可执行文件。这个可执行文件中包含的字节码会将被 CPU 执行,虽然不能直接从这些字节码中查看我们的核心代码,但是黑客可以通过一些反汇编的逆向工具来分析我们的程序,找到漏洞进行破解。
可执行文件格式
EXE Windows 下可直接执行的文件扩展名 ELF Linux 下的可执行文件 Mach-O MacOS/iOS 下主要的可执行文件

2.1 Mach 与 Mach-O

Mac 是苹果电脑 Macintosh 的简称,而 Mach 则是一种操作系统内核。Mach 内核被 NeXT 公司的NeXTSTEP 操作系统使用。在Mach上,一种可执行的文件格是就是 Mach-O(Mach Object file format)。1996年,乔布斯将 NeXTSTEP 带回苹果,成为了OS X的内核基础。所以虽然 MacOS X 是 Unix 的“后代”,但所主要支持的可执行文件格式是 Mach-O。

2.2 iOS 可执行文件

通常我们通过 Xcode 打包或者从应用商店下载的 APP 都是 ipa 包(iPhone Application)。这是一个变相的 zip 压缩包,可以通过 unzip 命令进行解压。
解压之后,会有一个 Payload 目录,而 Payload 里则是一个 .app 文件,而这个实际上又是一个目录,或者说是一个完整的 App Bundle。
在这个目录中,里面体积最大的文件通常就是和 ipa 包同名的一个二进制文件。通过 file 命令可以查看该文件的类型
1
2
3
xx: Mach-O universal binary with 2 architectures: [arm_v7:Mach-O executable arm_v7] [arm64]
xx (for architecture armv7): Mach-O executable arm_v7
xx (for architecture arm64): Mach-O 64-bit executable arm64
这里可以看出,这是一个支持 armv7 和 armv7s 两种处理器架构的通用程序包,里面包含的两部分都是 Mach-O 格式。
i386 模拟器32位处理器 x86_64 模拟器64位处理器 armv7,armv7s 真机32位处理器 arm64 真机64位处理器
Mach-O 的文件结构:
从这张图上来看,Mach-O文件的数据主体可分为三大部分,分别是头部(Header)、加载命令(Load commands)、和最终的数据(Data)。

2.3 class-dump

class-dump 是一个利用 Objective-C 语言的 runtime 特性,将存储在 Mach-O 文件结构里 data 部分的类属性和方法等信息提取出来,并生成对应的 .h 文件的工具。注意: 必须是脱壳后的可执行文件。(比如通过越狱手机或者第三方应用下载渠道)

2.4 安装使用

在 class-dump 官网下载 dmg,将 dmg 里面的 class-dump 拷贝到 /usr/local/bin 文件夹下,然后就可以在终端中使用 class-dump 命令了。
简单使用:
1
class-dump -H [需要被导出的 Mach-O 文件路径] -o [头文件输出目录地址]
也可以可以查看导出后的头文件目录:
-w621

2.5 hopper

Hopper 是一种适用于 OS X 和 Linux 的逆向工程工具,可以用于反汇编、反编译和调试 32位/64位英特尔处理器的 Mac、Linux、Windows 和 iOS 可执行程序。
将 Mach-O 文件拖入 hopper 可以查看。
-w1425
通过以上的方式,别人可以对照应用的功能来分析代码逻辑,从而进行针对性的破解。所以对项目的核心的代码进行混淆就是非常有必要的了。

3. 代码混淆

常规的混淆思路:
  • 花代码花指令,即随意往程序中加入迷惑人的代码指令
  • 易读字符替换
防止被 class-dump 后,被人分析核心代码逻辑。可以对关键,已读的字符串进行替换,比如 advip 等关键词。

3.1 替换字符串混淆

但是我们又不想替换成不想关的字符串后给自己调试带来麻烦,这里我们可以使用 HSKConfuse 的 shell 脚本,通过在Build Phrase 中设定在编译之前进行方法名的字符串替换,从而在不影响自己编写代码的基础上,对编译后的二进制包进行混淆。
首先在项目中新建文件:
1
2
confuse.sh: 存放混淆的脚本
func.list: 需要混淆的方法、变量名
confuse.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#!/usr/bin/env bash

TABLENAME=symbols
SYMBOL_DB_FILE="symbols"
STRING_SYMBOL_FILE="$PROJECT_DIR/VHLObfuscationDemo/VHLObfuscation/func.list"

CONFUSE_FILE="$PROJECT_DIR/VHLObfuscationDemo"
HEAD_FILE="$PROJECT_DIR/VHLObfuscationDemo/VHLObfuscation/codeObfuscation.h"

export LC_CTYPE=C

# ** 可以不执行下面的方法,替换成自己的规则手动写入到 func.list 中**
# 取以.m或.h结尾的文件以+号或-号开头的行,并以 vhl_ 开头的方法 |去掉所有+号或-号|用空格代替符号|n个空格跟着<号 替换成 <号|开头不能是IBAction|用空格split字串取第二部分|排序|去重复|删除空行|删掉以init开头的行>写进func.list
grep -h -r -I  "^[-+]" $CONFUSE_FILE  --include '*.[mh]' |sed "s/[+-]//g"|sed "s/[();,: *\^\/\{]/ /g"|sed "s/[ ]*</</"| sed "/^[ ]*IBAction/d"|awk '{split($0,b," "); print b[2]; }'| sort|uniq |sed "/^$/d"|sed -n "/^vhl_/p" >$STRING_SYMBOL_FILE

#维护数据库方便日后作排重,以下代码来自念茜的微博
createTable()
{
echo "create table $TABLENAME(src text, des text);" | sqlite3 $SYMBOL_DB_FILE
}

insertValue()
{
echo "insert into $TABLENAME values('$1' ,'$2');" | sqlite3 $SYMBOL_DB_FILE
}

query()
{
echo "select * from $TABLENAME where src='$1';" | sqlite3 $SYMBOL_DB_FILE
}

ramdomString()
{
openssl rand -base64 64 | tr -cd 'a-zA-Z' |head -c 16

}

rm -f $SYMBOL_DB_FILE
rm -f $HEAD_FILE
createTable

touch $HEAD_FILE
echo '#ifndef Demo_codeObfuscation_h
#define Demo_codeObfuscation_h' >> $HEAD_FILE
echo "//confuse string at `date`" >> $HEAD_FILE
cat "$STRING_SYMBOL_FILE" | while read -ra line; do
if [[ ! -z "$line" ]]; then
ramdom=`ramdomString`
echo $line $ramdom
insertValue $line $ramdom
echo "#define $line $ramdom" >> $HEAD_FILE
fi
done
echo "#endif" >> $HEAD_FILE

sqlite3 $SYMBOL_DB_FILE .dump

以上的 shell 脚本运行后会自动获取 .hm 文件中以 vhl_ 开头的方法写到到该文件。如果需要自定义的话,可以不执行以上那句命令,然后换成自己的规则写入 func.list 中。例如:
1
2
3
vhl_isVIP
xx_showAD
yy_isAllow
然后在 Build Phases 中,左上角加号添加 run script 填入地址
-w1125

3.2 查看编译结果

-w899成功编译后,会在指定目录下生成一个 codeObfuscation.h 文件。这里可以看到,在编译过程中会执行 shell 脚本,对相关字符串进行替换
添加 .pch 文件,并在 Build Setttings > Prefix Header 中链接该 .pch 文件,在 .pch 文件中导入 codeObfuscation.h,此时项目中指定的方法都被替换成了codeObfuscation.h 中的宏定义。
-w906
运行成功后将 Products 中的二进制文件拖入到 hopper 中查看
-w1008
此时,方法已经被混淆了。
注意:这个不要大面积的使用,会有被审核拒绝的风险,拒绝原因是说你程序里面可能包含有隐藏功能,因为 apple 无法从这些混淆后的方法获取到有用的信息。

4. 编译混淆

以上我们虽然对关键字符串进行了混淆,但是实际代码的执行逻辑等并没有改变。黑客仍然可以通过分析代码逻辑或者伪代码来寻找程序的漏洞。那有没有可能改变编译后的代码逻辑,从而让别人无法分析我们的程序呢。

4.1 iOS 是如何编译的

Objective C采用 Clang 作为前端,而 Swift 则采用 swift() 作为前端,二者都是用 LLVM(Low level vritual machine) 作为编译器后端。
编译命令地址
1
2
3
4
# Objective-C
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
# Swift
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift
Xcode 编译过程:
-w1132

4.2 编译器

4.2.1 相关名词

GCC
(GNU Compiler Collection)缩写,一个编程语言编译器,是GNU(自由软件理事会)的关键部分。也是GNU工具链的一部分。GCC常被认为是跨平台编译器的事实标准,特别是它的C语言编译器。GCC原本只能处理C语言。但是面对Clang的竞争,很快作出了扩展,现在已经可以处理C++,Fortran、Pascal、Object-C、Java、Ada,以及Go语言。许多操作系统,包括许多Unix系统,如Linux及BSD家族都采用GCC作为标准编译器。MacOSX也是采用这个编译器。
LLVM
是Low Level Virtual Machine的简称。这个库提供了与编译器相关的支持,能够进行程序语言的编译期优化、链接优化、在线编译优化、代码生成。可以作为多种语言编译器的后台来使用。
Clang
Clang 是 LLVM 的子项目,是 C,C++ 和 Objective-C 编译器,目的是提供惊人的快速编译,比 GCC 快3倍,其中的 clang static analyzer 主要是进行语法分析,语义分析和生成中间代码,当然这个过程会对代码进行检查,出错的和需要警告的会标注出来。
前端编译器
编译器前端的任务是进行:语法分析,语义分析,生成中间代码(intermediate representation )。在这个过程中,会进行类型检查,如果发现错误或者警告会标注出来在哪一行。
后端编译器
编译器后端会进行机器无关的代码优化,生成机器语言,并且进行机器相关的代码优化。iOS的编译过程,后端的处理如下
LVVM 优化器会进行 BitCode 的生成,链接期优化等等。
LLVM机器码生成器会针对不同的架构,比如arm64等生成不同的机器码。
2000年,伊利诺伊大学厄巴纳-香槟分校(University of Illinois at Urbana-Champaign 简称UIUC)这所享有世界声望的一流公立研究型大学的 Chris Lattner(他的 twitter @clattner_llvm ) 开发了一个叫作 Low Level Virtual Machine 的编译器开发工具套件,后来涉及范围越来越大,可以用于常规编译器,JIT编译器,汇编器,调试器,静态分析工具等一系列跟编程语言相关的工作,于是就把简称 LLVM 这个简称作为了正式的名字。Chris Lattner 后来又开发了 Clang,使得 LLVM 直接挑战 GCC 的地位。2012年,LLVM 获得美国计算机学会 ACM 的软件系统大奖,和 UNIX,WWW,TCP/IP,Tex,JAVA 等齐名。
Chris Lattner 生于 1978 年,2005年加入苹果,将苹果使用的 GCC 全面转为 LLVM。2010年开始主导开发 Swift 语言。
iOS 开发中 Objective-C 是 Clang / LLVM 来编译的。

4.3 Hikari

Hikari 是一个基于 Obfuscator-LLVM 对 Xcode9的适配。
OLLVM 是瑞士西北应用科技大学安全实验室于2010年6月份发起的一个项目,该项目旨在提供一套开源的针对 LLVM 的代码混淆工具,以增加对逆向工程的难度。目前,OLLVM已经支持LLVM-4.0版本。

4.3.1 安装

下载 github 提供的 pkg 安装文件并双击安装
1
https://github.com/HikariObfuscator/Hikari/releases
然后重启 Xcode 就能看到已经有 Kikari 编译工具
-w640

4.3.2 使用

  1. Xcode -> Toolchains -> Hikari 将混淆工具和项目关联
  2. 将所有与要运行的 target 相关的 target(包括pod进来的库)Enable Index-While-Building 的值改为 NO
  3. Optimization Level 的值设置为 None[-O0]
  4. 在 Build Settings -> Other C Flags 中加入混淆标记
1
2
3
4
5
6
7
8
9
-mllvm -enable-bcfobf   启用伪控制流  
-mllvm -enable-cffobf   启用控制流平坦化
-mllvm -enable-splitobf  启用基本块分割  
-mllvm -enable-subobf   启用指令替换  
-mllvm -enable-acdobf   启用反class-dump  
-mllvm -enable-indibran  启用基于寄存器的相对跳转,配合其他加固可以彻底破坏IDA/Hopper的伪代码(俗称F5)  
-mllvm -enable-strcry   启用字符串加密  
-mllvm -enable-funcwra   启用函数封装
-mllvm -enable-allob        依次性启用上述所有标记
重新编译就会使用 Hikari 在编译器后端对项目进行编译了。
将编译后的二进制文件拖入 hopper 中进行对比
-w1194
发现代码已经被混淆过了。 最新的 Xcode10 通过非官方默认的 Toolchains 编译后的的 ipa 是拒绝提交审核的。

总结

以上是对 iOS 下马甲包,代码混淆,编译混淆的一些学习实践,实际使用中需要根据自己的实际需求进行调整尝试。
demo 地址:iOSObfuscator
加密与破解,编译与反编译永远都是在不停攻防的。没有一堵绝对安全的墙,也没有绝对完美的银弹。只有在开发成本与安全成本中找个一个平衡,才是可持续发展的选择。

参考文章

评论

此博客中的热门博文

Resolving errSecInternalComponent errors during code signing

原文链接 One code signing issue I commonly see, both here on DevForums and in my Day Job™ with DTS, is that the codesign command fails with errSecInternalComponent. This issue crops up in a wide variety of circumstances and the correct fix depends on the specific problem. This post is my attempt to clarify the potential causes of this error and help folks resolve it. If you have any questions or comments about this, please start a new thread, tagging it with Code Signing so that I see it. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Resolving errSecInternalComponent errors during code signing In some circumstances the codesign command might fail with the error errSecInternalComponent. For example: % codesign -s "Apple Development" "MyTrue" MyTrue: errSecInternalComponent This typically affects folks who are signing code in a nonstandard environm...

iOS:检测使用VPN或Proxy

参考链接: https://www.jianshu.com/p/c3b950dbf86a https://gist.github.com/PramodJoshi/4faad4c91f7dcb4eb9b06be8390c01db http://noodlecode.net/2018/04/check-if-ios-app-is-connected-to-vpn 第一种方法 需要导入框架CFNetwork 然后,这个方法是mrc的:需要添加-fno-objc-arc的flag 代码如下: + ( BOOL )getProxyStatus { NSDictionary *proxySettings = NSMakeCollectable ([( NSDictionary *) CFNetworkCopySystemProxySettings () autorelease]); NSArray *proxies = NSMakeCollectable ([( NSArray *) CFNetworkCopyProxiesForURL (( CFURLRef )[ NSURL URLWithString: @"http://www.google.com" ], ( CFDictionaryRef )proxySettings) autorelease]); NSDictionary *settings = [proxies objectAtIndex: 0 ]; NSLog ( @"host=%@" , [settings objectForKey:( NSString *)kCFProxyHostNameKey]); NSLog ( @"port=%@" , [settings objectForKey:( NSString *)kCFProxyPortNumberKey]); NSLog ( @"type=%@" , [settings objectForKey:( NSString *)kCFProxyTypeKey]); if ([[settings object...

去广告DNS设置,国内ADGuard DNS方案,手机电脑iOS去广告,保护隐私

 原文链接 之前分享过使用mac系统搭建adguard home,这几个月用下来零零散散基本上也被弃用了。主要原因是因为需要保持电脑一直开机。但是我的电脑是笔记本,存在移动各个地域的情况,也就是说只能够屏蔽电脑自身,对于手机而言不太现实。今天偶然发现dnspod推出了高级版的公共解析。dnspod背靠腾讯云,肯定是合法合规的公共解析服务,这个高级版用起来不错。 国内自己搭建解析服务是违法行为,所以这也是为什么使用dnspod的原因。 后台截图 开始使用 首先我们先进入dnspod的公共解析页面,点击开始使用。 专业版公共解析 dnspod会提供几种预设,我们选择「开发者」即可 开发者 然后你就成功的申请到自己个人使用的dns了! 更新拦截规则 我们可以将常见的广告过滤规则加入到dns中。我们在顶部选项卡中选择「拦截规则」。 拦截规则设置 打开adguard adguard 绑定iOS设备 推荐使用描述文件的方式,删除配置时删除描述文件即可。 描述文件 绑定macOS 推荐使用描述文件的方式,删除配置时删除描述文件即可。 描述文件 mac需要在「系统偏好设置」的「网络」中查看是否正在运行。 代理 如果没有运行需要点击「···」来启动服务。 启动服务 绑定路由器 找到自己路由器的DHCP设置,修改dns,然后记得绑定自己的ip。 修改dns 绑定ip 费用 目前有300万次/月的免费额度,但没有超出之后的价格。300万次一个人比较难用完,可以放心使用。 我个人使用iOS设备两台、智能家居、电脑两台,日均请求数大致2万/日。 判断是否搭建成功 可以通过查看日志的方式,日志大概有半小时到一小时的延迟,请耐心等待。