折腾着玩。
因为一些原因,需要使用带混淆的 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 磁盘的腾讯云,虽然是按量付费但还是很心疼啊。