简介
在上一个教学中,学习了如何写一个LLVM Pass示例来进行控制流平坦化,这篇文章,来学习如何二利用LLVM实现字符串的加密。
案例
需要加密的对象
这是一个经典的c语言样例,目的是输出字符串:hello world
c
#include <stdio.h>
int main(){
printf("hello world\n");
return 0;
}
加密pass编写
下面是完整的pass代码:
代码的目的是:让字符串在编译的过程中进行异或加密,并且在运行的过程中动态解密。
cpp
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Constants.h"
using namespace llvm;
namespace{
struct mypass : public PassInfoMixin<mypass>{
const uint8_t KEY = 0x42;
Function *createDecryptFunc(Module *M){
LLVMContext &Ctx = M->getContext();
std::vector<Type*> paramTypes = { PointerType::getUnqual(Ctx), Type::getInt32Ty(Ctx) };
FunctionType *funcType = FunctionType::get(Type::getVoidTy(Ctx),paramTypes,false);
Function *Func = Function::Create(funcType, GlobalValue::InternalLinkage, "my_decrypt_func", M);
Argument *ArgData = Func->getArg(0);
Argument *ArgLen = Func->getArg(1);
BasicBlock *EntryBB = BasicBlock::Create(Ctx, "entry", Func);
BasicBlock *LoopCheckBB = BasicBlock::Create(Ctx, "loop_check", Func);
BasicBlock *LoopBodyBB = BasicBlock::Create(Ctx, "loop_body", Func);
BasicBlock *ExitBB = BasicBlock::Create(Ctx, "exit", Func);
IRBuilder<> builder(EntryBB);
builder.CreateBr(LoopCheckBB);
builder.SetInsertPoint(LoopCheckBB);
PHINode *LoopVar = builder.CreatePHI(Type::getInt32Ty(Ctx), 2, "i");
LoopVar->addIncoming(builder.getInt32(0), EntryBB);
Value *Cond = builder.CreateICmpSLT(LoopVar, ArgLen, "cond");
builder.CreateCondBr(Cond, LoopBodyBB, ExitBB);
builder.SetInsertPoint(LoopBodyBB);
Value *Ptr = builder.CreateInBoundsGEP(builder.getInt8Ty(), ArgData, LoopVar);
Value *ByteVal = builder.CreateLoad(builder.getInt8Ty(), Ptr);
Value *XorVal = builder.CreateXor(ByteVal, builder.getInt8(KEY));
builder.CreateStore(XorVal, Ptr);
Value *NextVar = builder.CreateAdd(LoopVar, builder.getInt32(1));
LoopVar->addIncoming(NextVar, LoopBodyBB);
builder.CreateBr(LoopCheckBB);
builder.SetInsertPoint(ExitBB);
builder.CreateRetVoid();
return Func;
}
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM){
errs() << "Running On-Demand String Encryption Pass...\n";
std::vector<GlobalVariable*> EncryptedGlobals;
for(GlobalVariable &GV : M.globals()){
if (!GV.hasInitializer() || !GV.isConstant() || GV.getName().contains("llvm.")) continue;
Constant *Init = GV.getInitializer();
ConstantDataArray* DataArray = dyn_cast<ConstantDataArray>(Init);
if(DataArray && DataArray->isString()){
StringRef OriginalString = DataArray->getAsString();
if(OriginalString.size() < 2) continue;
errs() << "Encrypting string: " << OriginalString << "\n";
std::string EncryptedStr = OriginalString.str();
for(int i = 0; i < EncryptedStr.size(); i++){
EncryptedStr[i] ^= KEY;
}
Constant *NewInit = ConstantDataArray::getString(M.getContext(), EncryptedStr, false);
GV.setInitializer(NewInit);
GV.setConstant(false);
EncryptedGlobals.push_back(&GV);
}
}
if(EncryptedGlobals.empty()) return PreservedAnalyses::all();
Function *DecryptFunc = createDecryptFunc(&M);
for(GlobalVariable *GV : EncryptedGlobals){
std::vector<Instruction*> user;
for(User *U: GV->users()){
Instruction* ins = dyn_cast<Instruction>(U);
if(ins->getFunction() == DecryptFunc) continue;
user.push_back(ins);
}
for(Instruction *ins : user){
IRBuilder<> builder(ins);
ConstantDataArray* DataArray = dyn_cast<ConstantDataArray>(GV->getInitializer());
int len = DataArray->getNumElements();
builder.CreateCall(DecryptFunc, {GV, builder.getInt32(len)});
}
}
return PreservedAnalyses::none();
}
};
}
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo(){
return{
LLVM_PLUGIN_API_VERSION,
"mypass",
"v0.1",
[](PassBuilder &PB){
PB.registerPipelineParsingCallback(
[](StringRef Name, ModulePassManager &MPM, ArrayRef<PassBuilder::PipelineElement>){
if(Name == "mypass"){
MPM.addPass(mypass());
return true;
}
return false;
}
);
}
};
}
代码解释
看到上面的pass代码,我们会感到头晕目眩,没关系,下面我们一步步拆解。
上面的代码分主要分为两部分:
- 构建异或的解密函数(
createDecryptFunc) - 加密字符串,并且将上面构建的解密函数插入代码。
CMakeLists.txt
编译可以直接用下面的cmake配置文件,编译过程在之前文章详细讲解
python
cmake_minimum_required(VERSION 4.1.1)
project(mypass)
set(LLVM_DIR "D:/LLVM/llvm-project/build/lib/cmake/llvm")
find_package(LLVM REQUIRED CONFIG)
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
include(AddLLVM)
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
set(CMAKE_CXX_STANDARD 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加密了。
如果❤喜欢❤本系列教程,就点个关注吧,后续不定期更新~