LLVM的混淆之旅(六)-字符串加密

admin 2026-04-16 04:47:26 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细介绍了利用LLVMPass实现字符串加密的技术方案,通过异或运算在编译阶段加密字符串常量,并生成动态解密函数在运行时还原。核心内容包括完整的Pass代码实现、CMake编译配置以及加解密效果对比展示,为软件保护提供了一种有效的代码混淆方法。 综合评分: 85 文章分类: 安全开发,二进制安全,代码审计,逆向分析,漏洞分析


cover_image

LLVM的混淆之旅(六)-字符串加密

原创

黑与白的边界 黑与白的边界

黑与白的边界

2026年2月13日 16:12 广东

在小说阅读器读本章

去阅读

简介

在上一个教学中,学习了如何写一个LLVM Pass示例来进行控制流平坦化,这篇文章,来学习如何二利用LLVM实现字符串的加密。

案例

需要加密的对象

这是一个经典的c语言样例,目的是输出字符串:hello world

#include&nbsp;<stdio.h>

int&nbsp;main(){
&nbsp; &nbsp;&nbsp;printf("hello world\n");
&nbsp; &nbsp;&nbsp;return&nbsp;0;
}

加密pass编写

下面是完整的pass代码:

代码的目的是:让字符串在编译的过程中进行异或加密,并且在运行的过程中动态解密。

#include&nbsp;"llvm/Passes/PassBuilder.h"
#include&nbsp;"llvm/Passes/PassPlugin.h"
#include&nbsp;"llvm/Support/raw_ostream.h"
#include&nbsp;"llvm/IR/Function.h"
#include&nbsp;"llvm/IR/Instructions.h"
#include&nbsp;"llvm/IR/IRBuilder.h"
#include&nbsp;"llvm/IR/PassManager.h"
#include&nbsp;"llvm/IR/GlobalVariable.h"
#include&nbsp;"llvm/IR/Constants.h"

using&nbsp;namespace&nbsp;llvm;

namespace{
&nbsp; &nbsp;&nbsp;struct&nbsp;mypass&nbsp;:&nbsp;public&nbsp;PassInfoMixin<mypass>{
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;const&nbsp;uint8_t&nbsp;KEY =&nbsp;0x42;

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Function *createDecryptFunc(Module *M){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LLVMContext &Ctx = M->getContext();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std::vector<Type*> paramTypes = { PointerType::getUnqual(Ctx), Type::getInt32Ty(Ctx) };
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FunctionType *funcType = FunctionType::get(Type::getVoidTy(Ctx),paramTypes,false);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Function *Func = Function::Create(funcType, GlobalValue::InternalLinkage,&nbsp;"my_decrypt_func", M);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Argument *ArgData = Func->getArg(0);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Argument *ArgLen &nbsp;= Func->getArg(1);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BasicBlock *EntryBB = BasicBlock::Create(Ctx,&nbsp;"entry", Func);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BasicBlock *LoopCheckBB = BasicBlock::Create(Ctx,&nbsp;"loop_check", Func);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BasicBlock *LoopBodyBB &nbsp;= BasicBlock::Create(Ctx,&nbsp;"loop_body", Func);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BasicBlock *ExitBB &nbsp; &nbsp; &nbsp;= BasicBlock::Create(Ctx,&nbsp;"exit", Func);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; IRBuilder<>&nbsp;builder(EntryBB);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; builder.CreateBr(LoopCheckBB);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; builder.SetInsertPoint(LoopCheckBB);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PHINode *LoopVar = builder.CreatePHI(Type::getInt32Ty(Ctx),&nbsp;2,&nbsp;"i");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LoopVar->addIncoming(builder.getInt32(0), EntryBB);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Value *Cond = builder.CreateICmpSLT(LoopVar, ArgLen,&nbsp;"cond");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; builder.CreateCondBr(Cond, LoopBodyBB, ExitBB);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; builder.SetInsertPoint(LoopBodyBB);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Value *Ptr = builder.CreateInBoundsGEP(builder.getInt8Ty(), ArgData, LoopVar);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Value *ByteVal = builder.CreateLoad(builder.getInt8Ty(), Ptr);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Value *XorVal = builder.CreateXor(ByteVal, builder.getInt8(KEY));
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; builder.CreateStore(XorVal, Ptr);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Value *NextVar = builder.CreateAdd(LoopVar, builder.getInt32(1));
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LoopVar->addIncoming(NextVar, LoopBodyBB);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; builder.CreateBr(LoopCheckBB);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; builder.SetInsertPoint(ExitBB);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; builder.CreateRetVoid();

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;Func;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;PreservedAnalyses&nbsp;run(Module &M, ModuleAnalysisManager &AM){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;errs() <<&nbsp;"Running On-Demand String Encryption Pass...\n";
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std::vector<GlobalVariable*> EncryptedGlobals;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for(GlobalVariable &GV : M.globals()){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(!GV.hasInitializer() || !GV.isConstant() || GV.getName().contains("llvm."))&nbsp;continue;

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Constant *Init = GV.getInitializer();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ConstantDataArray* DataArray =&nbsp;dyn_cast<ConstantDataArray>(Init);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if(DataArray && DataArray->isString()){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; StringRef OriginalString = DataArray->getAsString();

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if(OriginalString.size() <&nbsp;2)&nbsp;continue;

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;errs() <<&nbsp;"Encrypting string: "&nbsp;<< OriginalString <<&nbsp;"\n";
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std::string EncryptedStr = OriginalString.str();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for(int&nbsp;i =&nbsp;0; i < EncryptedStr.size(); i++){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EncryptedStr[i] ^= KEY;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Constant *NewInit = ConstantDataArray::getString(M.getContext(), EncryptedStr,&nbsp;false);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GV.setInitializer(NewInit);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GV.setConstant(false);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EncryptedGlobals.push_back(&GV);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if(EncryptedGlobals.empty())&nbsp;return&nbsp;PreservedAnalyses::all();

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Function *DecryptFunc =&nbsp;createDecryptFunc(&M);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for(GlobalVariable *GV : EncryptedGlobals){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std::vector<Instruction*> user;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for(User *U: GV->users()){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Instruction* ins =&nbsp;dyn_cast<Instruction>(U);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if(ins->getFunction() == DecryptFunc)&nbsp;continue;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; user.push_back(ins);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for(Instruction *ins : user){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; IRBuilder<>&nbsp;builder(ins);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ConstantDataArray* DataArray =&nbsp;dyn_cast<ConstantDataArray>(GV->getInitializer());
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;int&nbsp;len = DataArray->getNumElements();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; builder.CreateCall(DecryptFunc, {GV, builder.getInt32(len)});
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;PreservedAnalyses::none();
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; };
}

extern&nbsp;"C"&nbsp;LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo(){
&nbsp; &nbsp;&nbsp;return{
&nbsp; &nbsp; &nbsp; &nbsp; LLVM_PLUGIN_API_VERSION,
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"mypass",
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"v0.1",
&nbsp; &nbsp; &nbsp; &nbsp; [](PassBuilder &PB){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PB.registerPipelineParsingCallback(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [](StringRef Name, ModulePassManager &MPM, ArrayRef<PassBuilder::PipelineElement>){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if(Name ==&nbsp;"mypass"){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MPM.addPass(mypass());
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;true;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;false;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; );
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; };
}

代码解释

看到上面的pass代码,我们会感到头晕目眩,没关系,下面我们一步步拆解。 上面的代码分主要分为两部分:

  1. 构建异或的解密函数(createDecryptFunc
  2. 加密字符串,并且将上面构建的解密函数插入代码。

CMakeLists.txt

编译可以直接用下面的cmake配置文件,编译过程在之前文章详细讲解

cmake_minimum_required(VERSION&nbsp;4.1.1)
project(mypass)

set(LLVM_DIR&nbsp;"D:/LLVM/llvm-project/build/lib/cmake/llvm")
find_package(LLVM REQUIRED CONFIG)
list(APPEND CMAKE_MODULE_PATH&nbsp;"${LLVM_CMAKE_DIR}")
include(AddLLVM)
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
set(CMAKE_CXX_STANDARD&nbsp;17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_library(mypass MODULE mypass.cpp)
set_target_properties(mypass PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
target_link_libraries(mypass LLVMCore LLVMSupport)
target_compile_options(mypass PRIVATE /utf-8)#用中文

使用效果展示

编译并执行

图片中可以看出,可以正常运行

反编译查看区别

原始test.exe

#

加密test_opt.exe

#

根据上面的图片,可以看到,上面使用的pass,成功将HelloWorld加密了。

如果❤喜欢❤本系列教程,就点个关注吧,后续不定期更新~


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:黑与白的边界 黑与白的边界 黑与白的边界《LLVM的混淆之旅(六)-字符串加密》

评论:0   参与:  0