OLLVM代碼混淆移植與使用(再續)

關鍵修改

Legacy PM模式不生效

現在由于默認是NEW PM所以經常有人郵件我移植很完美編譯也成功,就是沒效果,這里做一下解答。主要兩種方式解決,一種是在cmake的時候加一下-DLLVM_ENABLE_NEW_PASS_MANAGER =OFF來禁用掉NEW PM,這樣在編譯完成后使用的時候就可以了,還有一種就是走默認開啟這,然后用ollvm編譯自己項目時加上-flegacy-pass-manager的cflag,再加-mllvm原來哪些就可以正常使用了

14.0以后的修改

主要是StringObfuscation.cpp里面的兩個地方,第一個是宏的修改編譯時機,第二個就是CreateGEP,CreateLoad等多個方法需要傳遞指針類型了,原來不傳會設置為null到了13.0里就開始內部通過對象獲取類型,就像修改的這樣,到了14.0干脆就是強制你必須傳類型了。

1649574671473.jpg

改為New Pass Manager

這個不好說修改了是好是壞,畢竟如果不修改,現在正常開啟NEWPM編譯完,利用-flegacy-pass-manager標識才能混淆,官方也說了在努力去除掉所有legacy pass manager,所以還是該準備一下。
這里只那兩個Pass舉例,因為改法都差不多,之所以是兩個是因為一個是Function一個是Module,其實還有其他的好多種,只不過網上封裝的混淆Pass也就用了這兩種,想詳細了解的,文檔我反正沒找到,但可以從源碼中讀邏輯。

FunctionAnalysisManager

首先以BogusControlFlow為例在.h里添加#include "llvm/IR/PassManager.h"的頭文件,然后創建如下的Pass類

    class BogusControlFlowPass : public PassInfoMixin<BogusControlFlowPass>{ 
        public:
            PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);

            static bool isRequired() { return true; }
    };

.m文件里先把之前初始化的方法包一層,為了在另一個Pass類里方便調用

WX20220411-141915@2x.png

把下面的代碼加完補上之前定義的類的方法就完成了

PreservedAnalyses BogusControlFlowPass::run(Function& F, FunctionAnalysisManager& AM) {
  BogusControlFlow bcf;
  if (bcf.runOnCustomFunction(F))
    return PreservedAnalyses::none();
  return PreservedAnalyses::all();
}

ModuleAnalysisManager

然后以StringObfuscation為例在.h里添加#include "llvm/IR/PassManager.h"的頭文件,然后創建如下的Pass類

      class StringObfuscationPass : public PassInfoMixin<StringObfuscationPass>{ 
        public:
            PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);

            static bool isRequired() { return true; }
      };

.m文件里比之前那個改動要多點,主要之前類就帶了Pass后綴,如下直接去掉

WX20220411-143016@2x.png

WX20220411-143058@2x.png

再把之前初始化方法包一層,還是為了在另一個Pass類里方便調用


WX20220411-143722@2x.png

把下面的代碼加完補上之前定義的類的方法就完成了

PreservedAnalyses StringObfuscationPass::run(Module &M, ModuleAnalysisManager& AM) {
  StringObfuscation sop;
  if (sop.runCustomModule(M))
        return PreservedAnalyses::none();
  return PreservedAnalyses::all();
}

注冊 添加 Pass

把Pass類做完支持后就該添加和注冊了,首先把頭文件加到PassBuilder.cpp下,因為PassRegistry.def里注冊Pass是從這邊調用的。


WX20220411-144549@2x.png

WX20220411-152632@2x.png

然后找合適的時機插入Pass,與之前PassManagerBuilder.cpp對應的是PassBuilderPipelines.cpp,而方法則是populateModulePassManager對應buildModuleOptimizationPipeline,如下加到和之前類似的調用位置即可。
這里是頭文件,和之前的一些處理,但基本沒有用過

WX20220411-153711@2x.png

下面是按之前位置挑了開頭和結尾位置添加

WX20220411-153643@2x.png

Swift支持

想支持swift的混淆直接用我之前的swift分支是不現實的,因為蘋果大量的修改想直接編譯llvm就支持基本不可能,但如果直接選擇編譯swift的工具鏈來實現就簡單的多,這個時候只需要下載Swift的源碼,它在編譯Toolchain時下載的llvm上把我之前的修改移植過來,然后編譯出來就可以直接支持swift的混淆了。(后來認真編譯了幾次,結果都是不報錯混淆無效果,但整體思路應該沒錯下面寫的沒興趣可以略過了。。。)

根據Xcode Wiki上的對應去切換git上的branch對應版本,有一點要注意通常你當前Xcode版本編譯不了你當前Xcode用的,只能用上個版本編譯你當前Xcode對應的,不過你也可以不管對應直接就用master的版本編譯最新的。

官方給的編譯教程在這里,有興趣的可以自己研磨,下面是我總結的。

這里我建議用ssh的方式,https的有時會出現連不上的情況,不加后面的-with-ssh走的就是https的。

git clone git@github.com:apple/swift.git
cd swift
utils/update-checkout --clone-with-ssh
WX20220410-211919@2x.png

指令結束后swift同級目錄會多出一堆當前master對應的依賴庫,所以第一步git clone的時候一定要找個干凈的目錄。
然后切換到自己想要編譯的版本,編譯之前一定要看看自己Xcode版本

utils/update-checkout --scheme mybranchname
# OR
utils/update-checkout --tag mytagname

最后執行build_toolchain或build_script,其中build_toolchain最簡單,傻瓜式編譯,全自動,先看看有沒有錯,不報錯就可以做ollvm移植了。

# 后面必須要跟個唯一標識
utils/build_toolchain com.xxxx

編譯成功會如下圖,多出兩個文件夾兩個tar.gz文件,里面都是.toolchain放/Library/Developer/Toolchains里Xcode就可以用了

WX20220411-214257@2x.png

移植的話在swift同級目錄有個llvm-project,這就是標準的llvm,之前怎么移植現在就怎么移植即可。比較簡單的方式可以選擇git patch文件或者找我swift-llvm-clang的分支用git cherry pick拉過去,這里的llvm都是指向的swift-llvm的。

下面提供了git的patch具體命令

cd ../llvm-project
# 下載patch文件
wget https://heroims.github.io/obfuscator/LegacyPass/ollvm14.patch
# 使用patch
git apply ollvm14.patch

如果失敗,沖突了則用另一條命令如下,會生成沖突文件的對應.rej,直接看一下沖突的地方按提示修改即可。(大部分情況都會有沖突。。。)

git apply --reject --ignore-whitespace ollvm14.patch

把llvm移植完再用build_toolchain把工具鏈編譯出來,這里再簡單說一下build_toolchain其實內部就是在調用build_script。

./utils/build-script ${DRY_RUN} ${DISTCC_FLAG} ${PRESET_FILE_FLAGS} \
        ${SCCACHE_FLAG} \
        --preset="${PRESET_PREFIX}${SWIFT_PACKAGE}${NO_TEST}${USE_OS_RUNTIME}" \
        install_destdir="${SWIFT_INSTALL_DIR}" \
        installable_package="${SWIFT_INSTALLABLE_PACKAGE}" \
        install_toolchain_dir="${SWIFT_TOOLCHAIN_DIR}" \
        install_symroot="${SWIFT_INSTALL_SYMROOT}" \
        symbols_package="${SYMBOLS_PACKAGE}" \
        darwin_toolchain_bundle_identifier="${BUNDLE_IDENTIFIER}" \
        darwin_toolchain_display_name="${DISPLAY_NAME}" \
        darwin_toolchain_display_name_short="${DISPLAY_NAME_SHORT}" \
        darwin_toolchain_xctoolchain_name="${TOOLCHAIN_NAME}" \
        darwin_toolchain_version="${TOOLCHAIN_VERSION}" \
        darwin_toolchain_alias="Local" \
        darwin_toolchain_require_use_os_runtime="${REQUIRE_USE_OS_RUNTIME}"

如果想用build_toolchain做一些特殊配置也可以用--preset-file指定文件設置默認使用的是build-presets.ini,里面用[preset: xxxx,xxx]來指定模塊,而且還可以調用已有的,里面最基礎的是[preset: mixin_osx_package_base],仿照這可以寫一個針對自己的llvm配置挑些自己需要的,我第一次直接用的master然后全量編譯愣是直接編譯了12個小時以上。。。。

Android 輕量編譯

之前我都是直接看一眼當前用的版本對應google llvm的版本就去下載切到對應commit位置,直接移植ollvm整體編譯,后來看網上有個套路居然能利用ndk編譯個Pass的.so文件直接搞定,所以記錄一下。
對方也有些文章具體講述這里這里都有。

通用的輕量級編譯

根據上面的套路,其實核心是Legacy Pass提供了自動注冊功能,然后編譯一個獨立的動態加載的Pass,這樣不需要對llvm本身進行修改,只是擴展一個模塊,由此有了下邊的代碼分別對應Legacy Pass和New Pass兩種模式的自動注冊。

Legacy Pass Manager

#include "Transforms/Obfuscation/BogusControlFlow.h"
#include "Transforms/Obfuscation/Flattening.h"
#include "Transforms/Obfuscation/Split.h"
#include "Transforms/Obfuscation/Substitution.h"
#include "Transforms/Obfuscation/StringObfuscation.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"

using namespace llvm;

static void registerOllvmPass(const PassManagerBuilder &,
                              legacy::PassManagerBase &PM) {

    PM.add(createBogus(true));
#if LLVM_VERSION_MAJOR >= 9
    PM.add(createLowerSwitchPass());
#endif
    PM.add(createFlattening(true));
    PM.add(createSplitBasicBlock(true));
    PM.add(createSubstitution(true));
}

static void registerOllvmModulePass(const PassManagerBuilder &,
                              legacy::PassManagerBase &PM) {
    PM.add(createStringObfuscation(true));
}
static RegisterStandardPasses
        RegisterMyPass1(PassManagerBuilder::EP_EnabledOnOptLevel0,
                       registerOllvmModulePass);
static RegisterStandardPasses
        RegisterMyPass2(PassManagerBuilder::EP_OptimizerLast,
                        registerOllvmModulePass);
static RegisterStandardPasses
        RegisterMyPass3(PassManagerBuilder::EP_EarlyAsPossible,
                       registerOllvmFunctionPass);

New Pass Manager

#include "Transforms/Obfuscation/BogusControlFlow.h"
#include "Transforms/Obfuscation/Flattening.h"
#include "Transforms/Obfuscation/Split.h"
#include "Transforms/Obfuscation/Substitution.h"
#include "Transforms/Obfuscation/StringObfuscation.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"

llvm::PassPluginLibraryInfo getOllvmPluginInfo() {
  return {
    LLVM_PLUGIN_API_VERSION, "OpcodeCounter", LLVM_VERSION_STRING,
        [](PassBuilder &PB) {

            // #1 注冊標記 "opt -passes=obf-bcf"
            PB.registerPipelineParsingCallback(
              [&](StringRef Name, FunctionPassManager &FPM,
                  ArrayRef<PassBuilder::PipelineElement>) {
                if (Name == "obf-bcf") {
                  FPM.addPass(BogusControlFlowPass());
                  return true;
                }
                if(Name == "obf-fla"){
                  FPM.addPass(FlatteningPass());
                  return true;
                }
                if(Name == "obf-sub"){
                  FPM.addPass(SubstitutionPass());
                  return true;
                }
                if(Name == "obf-split"){
                  FPM.addPass(SplitBasicBlockPass());
                  return true;
                }
                return false;
              });
            PB.registerPipelineParsingCallback(
              [&](StringRef Name, ModulePassManager &MPM,
                  ArrayRef<PassBuilder::PipelineElement>) {
                if (Name == "obf-str") {
                  MPM.addPass(StringObfuscationPass());
                  return true;
                }
                return false;
              });

            // #2 找到具體時機插入pass
            //registerVectorizerStartEPCallback這個方法插入需要加-O1的flag不然可能不生效會被跳過
            PB.registerVectorizerStartEPCallback(
              [](llvm::FunctionPassManager &PM,
                 llvm::PassBuilder::OptimizationLevel Level) {
                PM.addPass(SplitBasicBlockPass());
                PM.addPass(BogusControlFlowPass());
                #if LLVM_VERSION_MAJOR >= 9
                    PM.addPass(LowerSwitchPass());
                #endif
                PM.addPass(FlatteningPass());
                PM.addPass(SubstitutionPass());
                
              });
            PB.registerPipelineStartEPCallback(
              [](llvm::ModulePassManager &PM,
                 llvm::PassBuilder::OptimizationLevel Level) {
                PM.addPass(StringObfuscationPass());
              });
          
        };
}

extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
  return getOllvmPluginInfo();
}

創建個PMRegistration.cpp,放到Obfuscation里再改一下CMakeLists.txt,執行完cmake構建好項目可以單獨編譯這個Obfuscation模塊,其實用這個方法也就不需要再修改llvm本身了,移植起來方便多了只需要添加文件即可。用patch也就基本見不到沖突了。

最后跟一個注冊方法的表

回調函數 回調時提供的對象 對應 ExtensionPointTy
registerPeepholeEPCallback FunctionPassManager 對應EP_Peephole
registerLateLoopOptimizationsEPCallback LoopPassManager 對應EP_LoopOptimizerEnd
registerLoopOptimizerEndEPCallback LoopPassManager 對應EP_LateLoopOptimizations
registerScalarOptimizerLateEPCallback FunctionPassManager 對應 EP_ScalarOptimizerLate
registerCGSCCOptimizerLateEPCallback CGSCCPassManager 對應EP_CGSCCOptimizerLate
registerVectorizerStartEPCallback FunctionPassManager 對應EP_VectorizerStart
registerPipelineStartEPCallback ModulePassManager 對應EP_EarlyAsPossible
registerPipelineEarlySimplificationEPCallback ModulePassManager 對應 EP_ModuleOptimizerEarly
registerOptimizerLastEPCallback ModulePassManager 對應EP_OptimizerLast
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容