布景
代码混杂一向是安全加固一项重要的才能,现在我司的加固才能都是外采,为了便于后期定制化开发以及节约成本,故进行代码加固的自研探求。在本次自研加固项目中,我首要担任iOS方向的加固作业,到现在已经测试完结,故整理此文记载一下进程踩过的坑。
发展史
说到代码加固以及混杂由易到难大致分为四个阶段:
字符串加密
字符串会暴露APP的许多要害信息,攻击者能够依据从界面获取的字符串,快速找到相关逻辑的处理函数,从而进行分析破解。一般的处理方法是对需求加密的字符串加密,并保存加密后的数据,再在运用字符串的当地刺进解密算法。简略的加密算法能够把NSString转为byte或许NSData的方法,还能够把字符串放到后端来返回,尽量少的暴露页面信息。例如把NSString转为16进制的字符串:
+(NSString*)globalString{
return@"5f48494748203d20323b202020202020202020202020202020202020202020202020202020202020676c6f62616c2e44495350415443485f51554555455f5052494f524954595f44454641554c54203d20303b2020202020202020202020202020202020202020202020202";
}
符号混杂
符号混杂的中心思想是将类名、方法名、变量名替换为无意义符号,提高运用安全性;避免灵敏符号被class-dump东西提取,避免IDA Pro等东西反编译后分析事务代码。由于收到审核问题,现在市面上的IOS运用基本上是没有运用类名方法名混杂的。
逻辑混杂
代码逻辑混杂有以下几个方面的含义:
- 对方法体进行混杂,确保源码被逆向后该部分的代码有很大的迷惑性,由于有一些废物代码的存在;
- 对运用程序逻辑结构进行打乱混排,确保源码可读性降到最低,这很简略把破解者带到沟里去;
混杂之后,拥有和原始的代码一样的功用,这是最要害的,不会损坏代码逻辑。本次咱们采用的也是这种才能,后续会进行深入分析。
虚拟化
代码的虚拟化是安全加固等级最高的才能,虚拟化实际上便是运用一套自界说的字节码来替换掉程序中原有的指令,而字节码在履行的时分又由程序中的解说器来解说履行。自界说的字节码是只有解说器才能辨认的,所以一般的破解东西是无法辨认自界说的字节码,因此根据虚拟机的维护相对其他维护而言要愈加难破解。
适配进程
OLLVM(Obfuscator-LLVM)是瑞士西北运用科技大学安全实验室于2010年6月份发起的一个项目,该项目旨在供给一套开源的针对LLVM的代码混杂东西,以增加对逆向工程的难度。
github.com/obfuscator-…
1. 快速了解LLVM、clang和OLLVM
llvm是一个完好的编译器架构,作用能够了解为制作一个编译器,llvm先将源码生成为与方针机器无关的LLVM IR代码,然后把LLVM IR代码先优化,再向方针机器的汇编语言而努力。经典编译器都能够分为前端、中层优化和后端:
咱们从上图也了解了clang,是前端的一个套件,但在实际运用时,咱们对于clang是最为熟悉的,由于编译的时分,是调用clang或clang++来编译源码。而OLLVM是根据LLVM的代码分支的代码混杂,在中心表明IR层,经过编写自界说的pass来完成IR的混杂,这样方针机器的汇编语言也就被混杂了:
即使不用OLLVM,LLVM自身也是有许多pass的,LLVM IR自身是一种与方针机器无关的虚拟化代码,而在转化为实在汇编代码时,肯定要删除一些虚拟的东西或许做特定的优化,必定也需求pass。
2. OLLVM的适配
OLLVM有三大功用,分别是:Instructions Substitution(指令替换)、Bogus Control Flow(混杂操控流)、Control Flow Flattening(操控流平展)。Github上也有OLLVM每个功用详细的介绍和举例:
github.com/obfuscator-…
针对功用说明在此不做赘述,可自行检查,首要共享一下适配进程。该项目仅支撑到OLLVM-4.0,后续转向商业项目strong.protect。所以如果咱们想要运用这些混杂pass,需求进行最新版的LLVM以及clang的适配作业。
2.1 下载LLVM
llvm地址:github.com/llvm/llvm-p…
swift-llvm地址:github.com/apple
从上面的地址下载最新的自己需求的llvm和clang,此刻需求留意要找自己Xcode版别对应的clang版别来进行移植。经过clang –version能够检查自己的版别,之后在下方查询对应联系。 en.wikipedia.org/wiki/Xcode#…
2.2 增加混杂代码
首要便是将OLLVM文件夹里/include/llvm/Transforms/Obfuscation
和/lib/Transforms/Obfuscation
移动到方才下载好的llvm源码文件夹相同的位置。然后需求手动修改以下文件:
2.3 编译&运用
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_CREATE_XCODE_TOOLCHAIN=ON ../obfuscator/
make -j12
2.4 适配Xcode
cdbuild
sudomakeinstall-xcode-toolchain
mv/usr/local/Toolchains /Library/Developer/
2.5 Swift支撑
由于Apple很多的修改以及优化,适配Swift想直接编译llvm就适配基本不可能,咱们能够选择编译swift的东西链来完成,这个时分只需求下载Swift的源码,它在编译Toolchain时下载的llvm上把之前的修改移植过来,然后编译出来就能够直接支撑swift的混杂了。
官方的教程在这里: github.com/apple/swift…
这边给到我总结的大致过程,仅供参阅。首先在文稿中新建一个文件夹,然后 cd
到咱们的目录:
$ mkdir ~/Documents/swift-project
$ cd ~/Documents/swift-project
然后找到Xcode所支撑的 Swift 版别,比如我的 Xcode 为 14.3.1 版别,所以直接下载 Swift 5.8.1 Release
。查找 Xcode 对应的 Swift 的版别有两种方法:
- 去官网,检查 Xcode 的 Release Notes,在 Overview 中会有介绍。例如:Xcode 14.3.1Release Notes
- 终端运转命令检查
xcrun swift -version
。
在方才新建的目录中履行如下命令拉取对应的 Swift 源码,并 cd
到源码目录:
$ git clone --branch swift-5.8.1-RELEASE git@github.com:apple/swift.git
$ cd swift
拉取源码后还须拉取依靠
$ utils/update-checkout --tag swift-5.8.1-RELEASE --clone
最后履行build_toolchain或build_script,其间build_toolchain最简略,全自动,先看看有没有错,不报错就能够做ollvm移植了。
# 后边必须要跟个仅有标识
utils/build_toolchain com.xxxx
2.6 OLLVM遇到的问题
Q:代码中遇到 @available的时分,会报错 _isPlatformVersionAtLeast A:没链接clangRT,需求cmake编译时增加:-DLLVM_ENABLE_PROJECTS=”compiler-rt”
Compiler-RT(RT指运转时)项目用于为硬件不支撑的初级功用供给特定于方针的支撑。例如,32位方针通常缺少支撑64位除法的指令。Compiler-RT经过供给特定于方针并经过优化的功用来处理这个问题,该功用在运用32位指令的一起完成了64位除法。它供给相同的功用,因此是LLVM项目中libgcc的替代品。 ——《LLVM编译器实战教程》P17
Q: 编译没问题,增加flags也成功编译,可是没有混杂作用
A:首要原因是Legacy PM模式不生效,临时处理方案有以下两种,最好的方法是进行New Pass Manager的适配
- 在cmake的时分加一下
-DLLVM_ENABLE_NEW_PASS_MANAGER =OFF
来禁用掉NEW PM - 在项目中设置-flegacy-pass-manager的cflag
3. Hikari的适配
Hikari(github.com/HikariObfus…)也是开源的优秀混杂项目,而且支撑的功用愈加全面。
- -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 启用函数封装
Hikari的运用直接参阅git地址即可,写的比较全面,此处不再赘述。首要记载遇到的一个刺手问题。
问题描述
对接的某个事务线是OC&Swift混编项目,而且需悉数支撑Module化,项目中还装备了PCH文件,在实际运用时编译会报错:
看报错信息像是clang未敞开pch选项,试着去搜索CLANG_ENABLE_PCH,未找到相关的装备项。多方查找无果,最终在Hikari的作者Naville那得到回复:第三方的Clang/LLVM一向不支撑Modules。 依据他的回复,试着将项目中C/OC的Modules封闭今后,的确该问题不存在。可是封闭该选项,事务线的适配作业更大,只能另寻出路。
此期间安全厂商的技术人员给了新的研讨方向,在看了现在我的完成方法今后,他提出直接替换编译链的方法会遇到许多问题,由于Apple-clang不开源,自身做的许多优化与适配,仅靠开源LLVM项目是难以掩盖的。所以最好是做成刺进的方法:
- 运用Apple clang编译出LLVM IR
- 调用自己的混杂库去混杂LLVM IR
- 再调用Apple clang编译LLVM IR
依照这个思路,惊喜的发现Hikari的作者已经有了这方面的实践:
If you are targeting Apple platforms on macOS, it’s strongly advised to use Hanabi which injects into Apple Clang instead of compiling the full toolchain. This methods takes way shorter time to build (~5 Minutes) and introduces way less compatibility issues
4. Hanabi的运用
Hanabi的项目地址在: github.com/HikariObfus…
该项目的原理是将混杂的pass编译成独自的模块生成动态库。然后将该动态库注入到Apple clang/Apple swift-frontend,然后hook对应的pass加载流程,将自界说的混杂pass注册进去。核心代码不到100行,贴出来大致看一下:
void(*old_pmb)(void*dis,legacy::PassManagerBase&MPM);
staticvoidnew_pmb(void*dis,legacy::PassManagerBase&MPM) {
MPM.add(createObfuscationLegacyPass());
old_pmb(dis,MPM);
}
ModulePassManager(*old_bo0dp)(void*Level,boolLTOPreLink);
staticModulePassManagernew_bo0dp(void*Level,boolLTOPreLink) {
ModulePassManagerMPM=old_bo0dp(Level,LTOPreLink);
MPM.addPass(ObfuscationPass());
returnMPM;
}
ModulePassManager(*old_bpmdp)(void*Level,boolLTOPreLink);
staticModulePassManagernew_bpmdp(void*Level,boolLTOPreLink) {
ModulePassManagerMPM=old_bpmdp(Level,LTOPreLink);
MPM.addPass(ObfuscationPass());
returnMPM;
}
static__attribute__((__constructor__))voidInj3c73d(intargc,char*argv[]) {
char*executablePath=argv[0];
// Initialize our own LLVM Library
if(strstr(executablePath,"swift-frontend"))
errs()<<"Applying Apple SwiftC Hooks...\n";
else
errs()<<"Applying Apple Clang Hooks...\n";
#if defined(__x86_64__)
intret=0;
size_tsize=sizeof(ret);
if(sysctlbyname("sysctl.proc_translated",&ret,&size,NULL,0)!=-1&&
ret==1)
errs()<<"[Hanabi] Looks like you are currently running the process in "
"Rosetta 2 mode, which will prevent DobbyHook from "
"working.\nPlease close it.\n";
#endif
DobbyHook(DobbySymbolResolver(
executablePath,
"__ZN4llvm18PassManagerBuilder25populateModulePassManagerERNS_"
"6legacy15PassManagerBaseE"),
(dobby_dummy_func_t)new_pmb, (dobby_dummy_func_t*)&old_pmb);
DobbyHook(
DobbySymbolResolver(executablePath,
"__ZN4llvm11PassBuilder22buildO0DefaultPipelineENS_"
"17OptimizationLevelEb"),
(dobby_dummy_func_t)new_bo0dp, (dobby_dummy_func_t*)&old_bo0dp);
DobbyHook(DobbySymbolResolver(
executablePath,
"__ZN4llvm11PassBuilder29buildPerModuleDefaultPipelineENS_"
"17OptimizationLevelEb"),
(dobby_dummy_func_t)new_bpmdp, (dobby_dummy_func_t*)&old_bpmdp);
}
编译
首先获取LLVM对应的源码,跟其他项目一样,要留意Xcode版别的对应联系。然后将Hanabi的代码下载到llvm项目里面即可:
gitclone https://github.com/61bcdefg/Hanabi.git$(LLVM_SOURCE_PATH)/projects/
在$(LLVM_SOURCE_PATH)下,运转git submodule update --init --recursive --remote
确保子模块也悉数更新。之后进行对应的编译即可:
cmake$(LLVM_SOURCE_PATH)-DCMAKE_BUILD_TYPE=Release-DLLVM_ABI_BREAKING_CHECKS=FORCE_OFF-GNinja
ninja LLVMHanabi
大致等候几分钟,在编译路径下lib文件夹能找到ibLLVMHanabiDeps.dylib、libLLVMHanabi.dylib。将这两个动态库拷贝到 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/
打补丁
首先下载 github.com/alexzielens…,将之放入到环境变量$PATH,然后依照以下顺序履行:
sudo optool install -c load -p @executable_path/libLLVMHanabi.dylib -t /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
sudo optool install -c load -p @executable_path/libLLVMHanabi.dylib -t /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend
sudo codesign -fs - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
sudo codesign -fs - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend
遇到的问题
Q:编译完结今后,运用会报错:
ApplyingAppleClangHooks...
:CommandLineError:Option'eagerly-invalidate-analyses'registeredmorethanonce!
LLVMERROR:inconsistencyinregisteredCommandLineoptions
A: submodule的branch不对,要切换到hanabi
Q: 在运用进程中,编译会报错:Assertion failed: (dylib != NULL), function classicOrdinalForProxy, file LinkEditClassic.hpp
A: 查询得知Xcode 14以上引进的一项优化功用,该功用是在链接时进行优化来削减msgSend调用的开销的,默许是敞开状况。经过www.wwdcnotes.com/notes/wwdc2… 咱们能够看到:
(ObjC)Messagesend
withnewcompilersandlinkerinXcode14,messagesendcallsareupto8bytessmaller,downfrom12bytes,onARM64
binariesupto2%smalleroverall
enabledbyXcode14,evenwhentargetinganolderOSreleaseasdeploymenttarget
Defaultstobalancedperformanceandsizeoptimization
Optintooptimizingforsizeonlyusing-Wl,-objc_stubs_smalllinkerflag
咱们首要是发现敞开这个功用后加固后会在link阶段发生报错,所以建议设置-fno-objc-msgsend-selector-stubs封闭这项新的优化。
总结
该文章干货含量比较少,首要是记载加固进程中遇到的一系列问题。一整套流程走下来,对于LLVM以及对应的pass算是有了开始了解。现在完成了根据开源LLVM的OLLVM、Hikari的适配,完成了根据Swift源码的Hikari适配以及根据注入的Hanabi完成,依据不同的项目布景可自由选择相应混杂方法。后续会针对详细的混杂PASS进行分析,共享一下混杂PASS详细是怎样起作用的。