折腾着玩。
因为一些原因,需要使用带混淆的 NDK。在 NDK r21 版本的时候,还可以直接编译 OLLVM 9.0.1 然后用 clang 的几个可执行文件替换 NDK 下的同名文件,不过 NDK r21 不支持 API 30 之后的版本,需要升级到 NDK r23。这时候以前直接替换的方法无法正常编译 Native 项目,因此需要换一种方法。
这里使用的是 Pluto-Obfuscator 作为基础。根据作者自述,是可以成功将混淆 Pass 集成到 NDK 中的。不过因为后来更新的缘故,原有的方法有一些步骤需要进行改动,在这里记录一下。
从拉取 NDK 源代码到第一次编译结束的步骤与原文相同,主要是后面集成 Pass。作者将原来的多个头文件合成了一个 PassManagerBuilder.h,这样相比以前一个一个添加会方便很多。
首先将 llvm/include/llvm/Transforms/Obfuscation 文件夹整个复制到 NDK 源码目录中的 toolchain/llvm-project/llvm/include/llvm/Transforms 文件夹,然后打开 toolchain/llvm-project/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp 文件,进行下面几处更改并保存。
// 引入头文件,加在所有 include 最后
#include "llvm/Transforms/Obfuscation/PassRegistry.h"
// 在 300 行附近,找到 populateFunctionPassManager 函数
void PassManagerBuilder::populateFunctionPassManager(
legacy::FunctionPassManager &FPM) {
addExtensionsToPM(EP_EarlyAsPossible, FPM);
registerFunctionPasses(FPM); // Add this line
// ...
}
// 在 520 行附近,找到 populateModulePassManager 函数
void PassManagerBuilder::populateModulePassManager(
legacy::PassManagerBase &MPM) {
// Whether this is a default or *LTO pre-link pipeline. The FullLTO post-link
// is handled separately, so just check this is not the ThinLTO post-link.
bool DefaultOrPreLinkPipeline = !PerformThinLTO;
registerModulePasses(MPM); // Add this line
//...
}
将 llvm/lib/Transforms/Obfuscation 文件夹整个复制到 NDK 源码目录中的 toolchain/llvm-project/llvm/lib/Transforms 文件夹中。
修改 toolchain/llvm-project/llvm/lib/Transforms/IPO/CMakeLists.txt 文件,在最后加上 Obfuscation:
add_llvm_component_library(LLVMipo
AlwaysInliner.cpp
Annotation2Metadata.cpp
ArgumentPromotion.cpp
Attributor.cpp
AttributorAttributes.cpp
BarrierNoopPass.cpp
BlockExtractor.cpp
CalledValuePropagation.cpp
ConstantMerge.cpp
CrossDSOCFI.cpp
DeadArgumentElimination.cpp
ElimAvailExtern.cpp
ExtractGV.cpp
ForceFunctionAttrs.cpp
FunctionAttrs.cpp
FunctionImport.cpp
GlobalDCE.cpp
GlobalOpt.cpp
GlobalSplit.cpp
HotColdSplitting.cpp
IPO.cpp
IROutliner.cpp
InferFunctionAttrs.cpp
InlineSimple.cpp
Inliner.cpp
Internalize.cpp
LoopExtractor.cpp
LowerTypeTests.cpp
MergeFunctions.cpp
OpenMPOpt.cpp
PartialInlining.cpp
PassManagerBuilder.cpp
PruneEH.cpp
SampleContextTracker.cpp
SampleProfile.cpp
SampleProfileProbe.cpp
SCCP.cpp
StripDeadPrototypes.cpp
StripSymbols.cpp
SyntheticCountsPropagation.cpp
ThinLTOBitcodeWriter.cpp
WholeProgramDevirt.cpp
ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms
${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms/IPO
DEPENDS
intrinsics_gen
omp_gen
COMPONENT_NAME
IPO
LINK_COMPONENTS
AggressiveInstCombine
Analysis
BitReader
BitWriter
Core
FrontendOpenMP
InstCombine
IRReader
Linker
Object
ProfileData
Scalar
Support
TransformUtils
Vectorize
Instrumentation
Obfuscation
)
修改 toolchain/llvm-project/llvm/lib/Transforms/CMakeList.txt 文件,在最后加上 add_subdirectory(Obfuscation):
add_subdirectory(Utils) add_subdirectory(Instrumentation) add_subdirectory(AggressiveInstCombine) add_subdirectory(InstCombine) add_subdirectory(Scalar) add_subdirectory(IPO) add_subdirectory(Vectorize) add_subdirectory(Hello) add_subdirectory(HelloNew) add_subdirectory(ObjCARC) add_subdirectory(Coroutines) add_subdirectory(CFGuard) add_subdirectory(Obfuscation)
将 llvm/include/Eigen 文件夹整个复制到 toolchain/llvm-project/llvm/include 中。
完成以上步骤之后,即可继续执行重新编译命令:
python toolchain/llvm_android/build.py --no-build linux
最终在 out/install/windows-x86/clang-dev 中可以找到编译结果,按照原文的步骤进行文件替换即可。
在使用 NDK 进行开发的时候,有一个问题需要注意,展平控制流(Flattening 和 FlatteningEnhanced)在某些情况下并不能正常工作,也就是说,无论是 -fla 还是 -fla-ex 都有可能导致编译失败。另外,虚假控制流(Bogus Control Flow, -bcf 选项)存在某些问题会导致编译卡死,不知道是不是我自编译的二进制有问题。
最后附上编译完的二进制文件,方便有需要的人使用(仅在 NDK r23 下测试通过):OneDrive。
为了编译个这玩意儿专门开了个 16 核 32GB RAM 256GB 磁盘的腾讯云,虽然是按量付费但还是很心疼啊。