首页
社区
课程
招聘
[原创]让LLVM16在windows上再次优雅起来
发表于: 2023-11-30 16:35 14661

[原创]让LLVM16在windows上再次优雅起来

2023-11-30 16:35
14661

​ 有许多文章介绍了可以在windows动态加载的pass插件的方式使用LLVM,但都是针对一些老版本的LLVM,譬如12、8等。本文以LLVM16进行动态编译适配VS2022 pro。

​ 前提准备按照此https://bbs.kanxue.com/thread-272346.htm的前提准备即可,需要注意的是一切版本安装最新的即可。

​ 和前面文章的不同,我们下载的源码文件比他多了一个clang源码,注意此源码是必须的,经过教训得出这个结论,下面是我的源码目录结构

image-20231129153505358

​ 这里需要修改一下LLVM的源码,首先是llvm\lib\CMakeLists.txt文件,因为本身在window上编译是没有Mac的环境,因此会报一些Mac的头文件错误,我们只需要MACRO的组件去掉就行了。

image-20231129163856609

​ 还有就是注释完此行之后会有一些地方在引用MACRO会产生一些报错,直接修改就完事了,策略就是哪里报错改哪里(因为我也记不住改的哪里了)。

​ 编译源码的时候可以按照前文的方式进行编译,编译的时候推荐加上-DCMAKE_VERBOSE_MAKEFILE=ON选项,这样可以输出详细的构建信息,方便修改源码。下面是我构建完成的LLVM。

image-20231129172008448image-20231129172118983

​ 因为是使用gcc编译的会缺少一些dll,因此我们要把这些dll加入路径中。

image-20231129172348527

​ 简单来说就是llvm在编译的时候加了一层IR,如下图所示,而LLVM又给我们提供了一些列的接口,可以通过Pass插件操控和分析LLVM IR的生成。因以vs的cl编译为例对llvm pass的介绍这里就不做过的的赘述了,详细的介绍可以参考此文文章https://kiprey.github.io/2020/06/LLVM-IR-pass。

image-20231129234842049

​ 在进行下面的知识以前有一些预热的知识关于,关于ll文件bc文件的含义参考:https://zhuanlan.zhihu.com/p/596579389?utm_id=0。

​ 和老版本的Hello World!不同新通行证,这个pass的功能非常简单,就是打印函数名字和参数个数,示例如下:

​ 编写完源码之后我们构建CMakeLists.txt,下面是我的CMakeLists可以进行参考:

​ 写完cmake之后构建编译脚本,下面是我的编译脚本,其中路径信息根据自己的情况进行修改。

image-20231129190546641

​ 编译好 LLVMPass 插件之后,我们就可以使用这个插件了,下面我以test.cpp为例:

首先我们要用clang编译器生成我们的ll文件,会生成如下的代码,此时已经生成了LLVM IR代码。

image-20231129185038407

​ 生成LLVM IR代码之后,我们要使用我们的pass插件修改LLVM IR代码,用以下命令,可以看到已经成功调用我们的插件了,成功的输出了我们的函数名称和参数。

image-20231129190828541

​ 上面生成的是bc文件,我们也可以把bc文件再次翻译成ll文件使用以下命令

​ 当生成bc文件之后,我们就可以使用llc把bc文件翻译.s文件了,或者直接编译成obj文件,通过一下命令进行编译:

image-20231129200133752

​ 最后在使用clang编译器编译成可执行文件:

image-20231129200255045

​ 使用上面的方法加载pass无疑是非常麻烦的,因此我们可以直接使用clang编译器加载Pass,使用以下命令进行加载,使用此方式必须有一个前提,那就是registerPipelineStartEPCallback注册管道回调函数。下面就让我们来改造我们的HelloWorld!

PreservedAnalyses run(Function &F, FunctionAnalysisManager &)这就不能使用函数管理对象了,要修改成模块管理对象。修改如下:

​ 后面就是使用registerPipelineStartEPCallback注册回调了,下面是修改完之后的代码:

​ 完整的代码如下:

下面就是效果,至此我们的就可以直接在VS2022 pro上直接动态加载PASS插件了,食用方法可参考https://bbs.kanxue.com/thread-272346.htm,VS效果如下。

image-20231129202932372

image-20231129204151184

​ 写了两个HelloWorld之后下面我们写个字符串混淆的PASS热热身。首先注册通行证,注册同行正我们使用registerPipelineStartEPCallback回调函数进行注册,因为我们要给VS使用。

​ 再写字符串加密之前我们先做一下准备工作,首先我们有一个结构体或类能够保存我们全局字符串的信息。通过这个类,可以更方便地管理和识别 LLVM IR 中的全局字符串。

​ 准备完之后了接下来写字符串加密函数,我们的加密策略先简单一些,就对每个字符串的ascii值加上一就可以。

​ 写完加密函数之后我们就要获取全局字符串常量进行加密了,通过Module可以直接获取全局常量,然后判断常量是否是字符串,是的话进行加密。

​ 将每个全局常量字符串加密之后,我们就要进行解密,首先我们要创建一个解密函数,

​ 创建完解密函数之后,我们就要为每个全局常量字符串进行解密了,下面创建为每个常量字符串解密的函数。

​ 至此解密函数和解密动作已经创建完成,下面我们就要把解密动作插入main函数中,当main函数执行时进行解密。

​ 下面就是在插件运行的之后调用这些函数了。

​ 直接使用clang加载插件,成功插入解密函数。

image-20231130155512562

image-20231130160620733

使用vs加载插件。

image-20231130161604334

image-20231130161626674

参考:https://bbs.kanxue.com/thread-272346.htm#msg_header_h3_10

http://www.qfrost.com/posts/llvm/llvmwindows%E4%B8%8B%E4%BC%98%E9%9B%85%E4%BD%BF%E7%94%A8llvmpass/

https://llvm.org/docs/NewPassManager.html

https://kiprey.github.io/2020/06/LLVM-IR-pass/

https://github.com/tsarpaul/llvm-string-obfuscator/blob/master/StringObfuscator

https://github.com/banach-space/llvm-tutor/

# 生成ll 文件
clang++ -S -O0 -emit-llvm test.cpp
 
# 生成bc文件
opt  test.ll -o test.bc
# 生成ll 文件
clang++ -S -O0 -emit-llvm test.cpp
 
# 生成bc文件
opt  test.ll -o test.bc
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
 
using namespace llvm;
 
namespace {
     
    // 通过PassInfoMixin模板类简化注册过程
    struct HelloWorld : PassInfoMixin<HelloWorld> {
      // Pass插件主入口点,为回调函数,会把源码的函数IR对象和和函数函数管理对象传递此函数
      PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
         
        // 打印了函数名字和参数个数
        errs() << "Hello from: "<< F.getName() << "\n";
        errs() << "number of arguments: " << F.arg_size() << "\n";
           
        //表示该 Pass 不会改变任何分析结果。
        return PreservedAnalyses::all();
      }
      static bool isRequired() { return true; }
    };
}
 
// 导出函数返回LLVM 插件的信息,包括 LLVM插件API版本、插件名称、LLVM 版本字符串以及注册插件的回调函数
llvm::PassPluginLibraryInfo getHelloWorldPluginInfo() {
  return {LLVM_PLUGIN_API_VERSION, "HelloWorld", LLVM_VERSION_STRING,
          [](PassBuilder &PB) {
            PB.registerPipelineParsingCallback(
                [](StringRef Name, FunctionPassManager &FPM,
                   ArrayRef<PassBuilder::PipelineElement>) {
                  if (Name == "hello-world") {
                    FPM.addPass(HelloWorld());
                    return true;
                  }
                  return false;
                });
          }};
}
 
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
  return getHelloWorldPluginInfo();
}
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
 
using namespace llvm;
 
namespace {
     
    // 通过PassInfoMixin模板类简化注册过程
    struct HelloWorld : PassInfoMixin<HelloWorld> {
      // Pass插件主入口点,为回调函数,会把源码的函数IR对象和和函数函数管理对象传递此函数
      PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
         
        // 打印了函数名字和参数个数
        errs() << "Hello from: "<< F.getName() << "\n";
        errs() << "number of arguments: " << F.arg_size() << "\n";
           
        //表示该 Pass 不会改变任何分析结果。
        return PreservedAnalyses::all();
      }
      static bool isRequired() { return true; }
    };
}
 
// 导出函数返回LLVM 插件的信息,包括 LLVM插件API版本、插件名称、LLVM 版本字符串以及注册插件的回调函数
llvm::PassPluginLibraryInfo getHelloWorldPluginInfo() {
  return {LLVM_PLUGIN_API_VERSION, "HelloWorld", LLVM_VERSION_STRING,
          [](PassBuilder &PB) {
            PB.registerPipelineParsingCallback(
                [](StringRef Name, FunctionPassManager &FPM,
                   ArrayRef<PassBuilder::PipelineElement>) {
                  if (Name == "hello-world") {
                    FPM.addPass(HelloWorld());
                    return true;
                  }
                  return false;
                });
          }};
}
 
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
  return getHelloWorldPluginInfo();
}
#项目名称
project(hello-world)
 
#cmake最低版本
cmake_minimum_required(VERSION 3.16)
 
# 设置LLVM的安装路径
if(NOT DEFINED ENV{LLVM_HOME})
    # User must define the LLVM_HOME environment that point to the root installation dir of llvm
    message(FATAL_ERROR "Environment variable $LLVM_HOME is not defined, user should define it before running cmake!")
endif()
 
message(STATUS "LLVM_HOME = [$ENV{LLVM_HOME}]")
 
# Default llvm config file path
set(ENV{LLVM_DIR} "$ENV{LLVM_HOME}\\lib\\cmake\\llvm")
message(STATUS "LLVM_DIR  : ${LLVM_DIR}")
 
 
# Check the path
if (NOT EXISTS $ENV{LLVM_DIR})
    message(STATUS "Path ($ENV{LLVM_DIR}) not found!")
 
    # If default llvm config path not found, try this one,
    # which is config with [-DLLVM_LIBDIR_SUFFIX=64] before building llvm
    set(ENV{LLVM_DIR} $ENV{LLVM_HOME}/lib64/cmake/llvm)
    if (NOT EXISTS $ENV{LLVM_DIR})
        message(FATAL_ERROR "Path ($ENV{LLVM_DIR}) not found!")
    else()
        message(STATUS "LLVM_DIR ($ENV{LLVM_DIR}) found!")
    endif()
else()
    message(STATUS "LLVM_DIR ($ENV{LLVM_DIR}) found!")
endif()
 
# 查找并加载LLVM的CMake模块
find_package(LLVM REQUIRED CONFIG)
 
# 将LLVM的CMake模块路径添加到CMake模块路径中
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
 
# 添加LLVM的头文件路径
include_directories(${LLVM_INCLUDE_DIRS})
 
# 添加LLVM的库路径
link_directories(${LLVM_LIBRARY_DIRS})
 
# 添加LLVM的依赖库
add_definitions(${LLVM_DEFINITIONS})
 
# Debug
message(STATUS "CMAKE_MODULE_PATH : ${LLVM_CMAKE_DIR}")
message(STATUS "LLVM_DEFINITIONS  : ${LLVM_DEFINITIONS}")
message(STATUS "LLVM_INCLUDE_DIRS : ${LLVM_INCLUDE_DIRS}")
message(STATUS "LLVM_LIBRARY_DIRS : ${LLVM_LIBRARY_DIRS}")
 
# 添加项目文件
add_library( hello-world SHARED
    hello-world.cpp
  )
 
# Use C++11 to compile your pass (i.e., supply -std=c++11).
target_compile_features(hello-world PRIVATE cxx_range_for cxx_auto_type)
 
include_directories(./)
 
# LLVM is (typically) built with no C++ RTTI. We need to match that;
# otherwise, we'll get linker errors about missing RTTI data.
set_target_properties(hello-world PROPERTIES COMPILE_FLAGS "-fno-rtti")
 
# Get proper shared-library behavior (where symbols are not necessarily
# resolved when the shared library is linked) on OS X.
if(APPLE)
    set_target_properties(hello-world PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
endif(APPLE)
 
# 添加多线程支持
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(hello-world Threads::Threads)
 
# 添加链接库
target_link_libraries(hello-world
    libLLVMCore.dll.a
    libLLVMSupport.dll.a
    libLLVMipo.dll.a
    libLLVMPasses.dll.a
    libLLVMTransformUtils.dll.a
    libclang.dll.a
    libclangAnalysis.dll.a
    libclangAnalysisFlowSensitive.dll.a
    libclangAnalysisFlowSensitiveModels.dll.a
    libclangAPINotes.dll.a
    libclangARCMigrate.dll.a
    libclangAST.dll.a
    libclangASTMatchers.dll.a
    libclangBasic.dll.a
    libclangCodeGen.dll.a
    libclangCrossTU.dll.a
    libclangDependencyScanning.dll.a
    libclangDirectoryWatcher.dll.a
    libclangDriver.dll.a
    libclangDynamicASTMatchers.dll.a
    libclangEdit.dll.a
    libclangExtractAPI.dll.a
    libclangFormat.dll.a
    libclangFrontend.dll.a
    libclangFrontendTool.dll.a
    libclangHandleCXX.dll.a
    libclangHandleLLVM.dll.a
    libclangIndex.dll.a
    libclangIndexSerialization.dll.a
    libclangInterpreter.dll.a
)
#项目名称
project(hello-world)
 
#cmake最低版本
cmake_minimum_required(VERSION 3.16)
 
# 设置LLVM的安装路径
if(NOT DEFINED ENV{LLVM_HOME})
    # User must define the LLVM_HOME environment that point to the root installation dir of llvm
    message(FATAL_ERROR "Environment variable $LLVM_HOME is not defined, user should define it before running cmake!")
endif()
 
message(STATUS "LLVM_HOME = [$ENV{LLVM_HOME}]")
 
# Default llvm config file path
set(ENV{LLVM_DIR} "$ENV{LLVM_HOME}\\lib\\cmake\\llvm")
message(STATUS "LLVM_DIR  : ${LLVM_DIR}")
 
 
# Check the path
if (NOT EXISTS $ENV{LLVM_DIR})
    message(STATUS "Path ($ENV{LLVM_DIR}) not found!")
 
    # If default llvm config path not found, try this one,
    # which is config with [-DLLVM_LIBDIR_SUFFIX=64] before building llvm
    set(ENV{LLVM_DIR} $ENV{LLVM_HOME}/lib64/cmake/llvm)
    if (NOT EXISTS $ENV{LLVM_DIR})
        message(FATAL_ERROR "Path ($ENV{LLVM_DIR}) not found!")
    else()
        message(STATUS "LLVM_DIR ($ENV{LLVM_DIR}) found!")
    endif()
else()
    message(STATUS "LLVM_DIR ($ENV{LLVM_DIR}) found!")
endif()
 
# 查找并加载LLVM的CMake模块
find_package(LLVM REQUIRED CONFIG)
 
# 将LLVM的CMake模块路径添加到CMake模块路径中
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
 
# 添加LLVM的头文件路径
include_directories(${LLVM_INCLUDE_DIRS})
 
# 添加LLVM的库路径
link_directories(${LLVM_LIBRARY_DIRS})
 
# 添加LLVM的依赖库
add_definitions(${LLVM_DEFINITIONS})
 
# Debug
message(STATUS "CMAKE_MODULE_PATH : ${LLVM_CMAKE_DIR}")
message(STATUS "LLVM_DEFINITIONS  : ${LLVM_DEFINITIONS}")
message(STATUS "LLVM_INCLUDE_DIRS : ${LLVM_INCLUDE_DIRS}")
message(STATUS "LLVM_LIBRARY_DIRS : ${LLVM_LIBRARY_DIRS}")
 
# 添加项目文件
add_library( hello-world SHARED
    hello-world.cpp
  )
 
# Use C++11 to compile your pass (i.e., supply -std=c++11).
target_compile_features(hello-world PRIVATE cxx_range_for cxx_auto_type)
 
include_directories(./)
 
# LLVM is (typically) built with no C++ RTTI. We need to match that;
# otherwise, we'll get linker errors about missing RTTI data.
set_target_properties(hello-world PROPERTIES COMPILE_FLAGS "-fno-rtti")
 
# Get proper shared-library behavior (where symbols are not necessarily
# resolved when the shared library is linked) on OS X.
if(APPLE)
    set_target_properties(hello-world PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
endif(APPLE)
 
# 添加多线程支持
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(hello-world Threads::Threads)
 
# 添加链接库
target_link_libraries(hello-world
    libLLVMCore.dll.a
    libLLVMSupport.dll.a
    libLLVMipo.dll.a
    libLLVMPasses.dll.a
    libLLVMTransformUtils.dll.a
    libclang.dll.a
    libclangAnalysis.dll.a
    libclangAnalysisFlowSensitive.dll.a
    libclangAnalysisFlowSensitiveModels.dll.a
    libclangAPINotes.dll.a
    libclangARCMigrate.dll.a
    libclangAST.dll.a
    libclangASTMatchers.dll.a
    libclangBasic.dll.a
    libclangCodeGen.dll.a
    libclangCrossTU.dll.a
    libclangDependencyScanning.dll.a
    libclangDirectoryWatcher.dll.a
    libclangDriver.dll.a
    libclangDynamicASTMatchers.dll.a
    libclangEdit.dll.a
    libclangExtractAPI.dll.a
    libclangFormat.dll.a
    libclangFrontend.dll.a
    libclangFrontendTool.dll.a
    libclangHandleCXX.dll.a
    libclangHandleLLVM.dll.a
    libclangIndex.dll.a
    libclangIndexSerialization.dll.a
    libclangInterpreter.dll.a
)
:: Set MSYS2 env
set PATH=%PATH%;C:\msys64\mingw64\bin
 
:: Set LLVM_HOME
set LLVM_HOME=C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\Llvm\x64
set CC=%LLVM_HOME%/bin/clang.exe
set CXX=%LLVM_HOME%/bin/clang++.exe
 
rd /Q /S .\build
 
:: Build release version
cmake -S ./ -B ./build -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON  -G "MinGW Makefiles" -DCMAKE_VERBOSE_MAKEFILE=ON
:: Build debug version
:: cmake -S ./ -B ./build -DCMAKE_BUILD_TYPE=Debug -DLLVM_ENABLE_ASSERTIONS=ON
 
:: Build  project
cmake --build ./build -j 4
 
pause
:: Set MSYS2 env
set PATH=%PATH%;C:\msys64\mingw64\bin
 
:: Set LLVM_HOME
set LLVM_HOME=C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\Llvm\x64
set CC=%LLVM_HOME%/bin/clang.exe
set CXX=%LLVM_HOME%/bin/clang++.exe
 
rd /Q /S .\build
 
:: Build release version
cmake -S ./ -B ./build -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON  -G "MinGW Makefiles" -DCMAKE_VERBOSE_MAKEFILE=ON
:: Build debug version
:: cmake -S ./ -B ./build -DCMAKE_BUILD_TYPE=Debug -DLLVM_ENABLE_ASSERTIONS=ON
 
:: Build  project
cmake --build ./build -j 4
 
pause
clang -O1 -S -emit-llvm test.cpp -o test.ll
clang -O1 -S -emit-llvm test.cpp -o test.ll
opt -load-pass-plugin libhello-world.dll  --passes="hello-world" test.ll -o test.bc
opt -load-pass-plugin libhello-world.dll  --passes="hello-world" test.ll -o test.bc
llvm-dis test.bc -o test2.ll
llvm-dis test.bc -o test2.ll
llc test.bc -o test.s/test.obj
llc test.bc -o test.s/test.obj
clang test.s -o test.exe
clang test.s -o test.exe
clang.exe -Xclang -fpass-plugin="libHelloWorld.dll" .\test.cpp -o test.exe
clang.exe -Xclang -fpass-plugin="libHelloWorld.dll" .\test.cpp -o test.exe
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
    for (Function& F : M)
    {
        errs() << "(llvm-tutor) Hello from: " << F.getName() << "\n";
        errs() << "number of arguments: " << F.arg_size() << "\n";
    }
  return PreservedAnalyses::all();
}
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
    for (Function& F : M)
    {
        errs() << "(llvm-tutor) Hello from: " << F.getName() << "\n";
        errs() << "number of arguments: " << F.arg_size() << "\n";
    }
  return PreservedAnalyses::all();
}
llvm::PassPluginLibraryInfo getHelloWorldPluginInfo() {
  return {LLVM_PLUGIN_API_VERSION, "HelloWorld", LLVM_VERSION_STRING,
          [](llvm::PassBuilder &PB) {
            PB.registerPipelineStartEPCallback(
                [&](llvm::ModulePassManager &MPM,
                   OptimizationLevel Level) {
                  MPM.addPass(HelloWorld());
                });
          }};
}
llvm::PassPluginLibraryInfo getHelloWorldPluginInfo() {
  return {LLVM_PLUGIN_API_VERSION, "HelloWorld", LLVM_VERSION_STRING,
          [](llvm::PassBuilder &PB) {
            PB.registerPipelineStartEPCallback(
                [&](llvm::ModulePassManager &MPM,
                   OptimizationLevel Level) {
                  MPM.addPass(HelloWorld());
                });
          }};
}
#include "llvm/Pass.h"
#include "llvm/Passes/OptimizationLevel.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/PassManager.h"
using namespace llvm;
namespace{
    struct HelloWorld : PassInfoMixin<HelloWorld> {
      PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
          for (Function& F : M)
          {
              errs() << "(llvm-tutor) Hello from: " << F.getName() << "\n";
              errs() << "number of arguments: " << F.arg_size() << "\n";
          }
        return PreservedAnalyses::all();
      }
      static bool isRequired() { return true; }
    };
} // namespace
 
llvm::PassPluginLibraryInfo getHelloWorldPluginInfo() {
  return {LLVM_PLUGIN_API_VERSION, "HelloWorld", LLVM_VERSION_STRING,
          [](llvm::PassBuilder &PB) {
            PB.registerPipelineStartEPCallback(
                [&](llvm::ModulePassManager &MPM,
                   OptimizationLevel Level) {
                  MPM.addPass(HelloWorld());
                });
          }};
}
 
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
  return getHelloWorldPluginInfo();
}
#include "llvm/Pass.h"
#include "llvm/Passes/OptimizationLevel.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/PassManager.h"
using namespace llvm;
namespace{
    struct HelloWorld : PassInfoMixin<HelloWorld> {
      PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
          for (Function& F : M)
          {
              errs() << "(llvm-tutor) Hello from: " << F.getName() << "\n";
              errs() << "number of arguments: " << F.arg_size() << "\n";
          }
        return PreservedAnalyses::all();
      }
      static bool isRequired() { return true; }
    };
} // namespace
 
llvm::PassPluginLibraryInfo getHelloWorldPluginInfo() {
  return {LLVM_PLUGIN_API_VERSION, "HelloWorld", LLVM_VERSION_STRING,
          [](llvm::PassBuilder &PB) {
            PB.registerPipelineStartEPCallback(
                [&](llvm::ModulePassManager &MPM,
                   OptimizationLevel Level) {
                  MPM.addPass(HelloWorld());
                });
          }};
}
 
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
  return getHelloWorldPluginInfo();
}
#include "llvm/Pass.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Passes/OptimizationLevel.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
 
using namespace llvm;
 
namespace {
    struct StringObfuscatorModPass : public PassInfoMixin<StringObfuscatorModPass> {
        PreservedAnalyses run(Module& M, ModuleAnalysisManager& MAM) {
            return PreservedAnalyses::all();
        };
        static bool isRequired() { return true; }
    };
 
llvm::PassPluginLibraryInfo getStringObfuscatorPlusPluginInfo() {
    return { LLVM_PLUGIN_API_VERSION, "StringObfuscatorPlus", LLVM_VERSION_STRING,
            [](llvm::PassBuilder& PB) {
                PB.registerPipelineStartEPCallback(
                    [&](llvm::ModulePassManager& MPM,
                        OptimizationLevel Level) {
                    MPM.addPass(StringObfuscatorModPass());
                    });
            } };
}
 
extern "C" LLVM_ATTRIBUTE_WEAK::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
    return getStringObfuscatorPlusPluginInfo();
}
#include "llvm/Pass.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Passes/OptimizationLevel.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
 
using namespace llvm;
 
namespace {
    struct StringObfuscatorModPass : public PassInfoMixin<StringObfuscatorModPass> {
        PreservedAnalyses run(Module& M, ModuleAnalysisManager& MAM) {
            return PreservedAnalyses::all();
        };
        static bool isRequired() { return true; }
    };
 
llvm::PassPluginLibraryInfo getStringObfuscatorPlusPluginInfo() {
    return { LLVM_PLUGIN_API_VERSION, "StringObfuscatorPlus", LLVM_VERSION_STRING,
            [](llvm::PassBuilder& PB) {
                PB.registerPipelineStartEPCallback(
                    [&](llvm::ModulePassManager& MPM,
                        OptimizationLevel Level) {
                    MPM.addPass(StringObfuscatorModPass());
                    });
            } };
}
 
extern "C" LLVM_ATTRIBUTE_WEAK::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
    return getStringObfuscatorPlusPluginInfo();
}
class GlobalString {
public:
    GlobalVariable* Glob;
    unsigned int index;
    int type;
    int string_length;
    static const int SIMPLE_STRING_TYPE = 1;
    static const int STRUCT_STRING_TYPE = 2;
 
    GlobalString(GlobalVariable* Glob, int length) : Glob(Glob), index(-1), string_length(length), type(SIMPLE_STRING_TYPE) {}
    GlobalString(GlobalVariable* Glob, unsigned int index, int length) : Glob(Glob), index(index), string_length(length), type(STRUCT_STRING_TYPE) {}
};
class GlobalString {
public:
    GlobalVariable* Glob;
    unsigned int index;
    int type;
    int string_length;
    static const int SIMPLE_STRING_TYPE = 1;
    static const int STRUCT_STRING_TYPE = 2;
 
    GlobalString(GlobalVariable* Glob, int length) : Glob(Glob), index(-1), string_length(length), type(SIMPLE_STRING_TYPE) {}
    GlobalString(GlobalVariable* Glob, unsigned int index, int length) : Glob(Glob), index(index), string_length(length), type(STRUCT_STRING_TYPE) {}
};
char* EncodeString(const char* Data, unsigned int Length) {
    // 加密字符串
    char* NewData = (char*)malloc(Length);
    for (unsigned int i = 0; i < Length; i++) {
        NewData[i] = Data[i] + 1;
    }
    return NewData;
}
char* EncodeString(const char* Data, unsigned int Length) {
    // 加密字符串
    char* NewData = (char*)malloc(Length);
    for (unsigned int i = 0; i < Length; i++) {
        NewData[i] = Data[i] + 1;
    }
    return NewData;
}
vector<GlobalString*> encodeGlobalStrings(Module& M) {
    vector<GlobalString*> GlobalStrings;
    auto& Ctx = M.getContext();
 
    // 开始进行编码
    for (GlobalVariable& Glob : M.globals()) {
        // 确保全局变量有初始化器。如果全局变量没有初始化器,可能是外部定义的,这种情况下就不进行编码。
        // 确保全局变量没有外部链接。外部链接的全局变量可能在其他模块中定义。
        if (!Glob.hasInitializer() || Glob.hasExternalLinkage())
            continue;
 
        // 获取全局变量的初始化器。
        Constant* Initializer = Glob.getInitializer();
 
        // 判断初始化器是否是ConstantDataArray类型。ConstantDataArray是 LLVM 中表示常量数据数组的类。
        if (isa<ConstantDataArray>(Initializer)) {
            // 将初始化器强制转换为ConstantDataArray类型。
            auto CDA = cast<ConstantDataArray>(Initializer);
            // 检查ConstantDataArray对象是否表示一个字符串。
            if (!CDA->isString())
                continue;
 
            // 获取字符指针,和大小
            StringRef StrVal = CDA->getAsString();
            const char* Data = StrVal.begin();
            const int Size = StrVal.size();
 
            // 编码字符串,创建一个新的ConstantDataArray对象。
            char* NewData = EncodeString(Data, Size);
            Constant* NewConst = ConstantDataArray::getString(Ctx, StringRef(NewData, Size), false);
 
            // 设置为全局变量的新初始化器。
            Glob.setInitializer(NewConst);
             
            // 将一个指向全局变量及其大小的GlobalString对象添加到GlobalStrings容器中
            GlobalStrings.push_back(new GlobalString(&Glob, Size));
            Glob.setConstant(false);
        }
        // 判断初始化器是不是结构体类型
        else if (isa<ConstantStruct>(Initializer)) {
            // 处理结构体
            auto CS = cast<ConstantStruct>(Initializer);
            if (Initializer->getNumOperands() > 1)
                continue;
             
            // 变量结构体成员
            for (unsigned int i = 0; i < Initializer->getNumOperands(); i++) {
                auto CDA = dyn_cast<ConstantDataArray>(CS->getOperand(i));
                // 判断是否是字符串
                if (!CDA || !CDA->isString())
                    continue;
 
                // 是字符串获取字符串
                StringRef StrVal = CDA->getAsString();
                const char* Data = StrVal.begin();
                const int Size = StrVal.size();
 
                // 创建编码的字符串
                char* NewData = EncodeString(Data, Size);
                Constant* NewConst = ConstantDataArray::getString(Ctx, StringRef(NewData, Size), false);
 
                // 覆盖结构体成员
                CS->setOperand(i, NewConst);
 
                GlobalStrings.push_back(new GlobalString(&Glob, i, Size));
                Glob.setConstant(false);
            }
        }
    }
 
    return GlobalStrings;
}
vector<GlobalString*> encodeGlobalStrings(Module& M) {
    vector<GlobalString*> GlobalStrings;
    auto& Ctx = M.getContext();
 
    // 开始进行编码
    for (GlobalVariable& Glob : M.globals()) {

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2023-11-30 17:09 被l140w4n9编辑 ,原因:
收藏
免费 11
支持
分享
最新回复 (12)
雪    币: 12857
活跃值: (9172)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
2
不是很懂为什么都喜欢在LLVM上面整字符串加密
2023-11-30 23:39
0
雪    币: 384
活跃值: (528)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
主要练手用,字符串加密原理更容易理解,学会这个可以对llvm IR的模块、函数、基本块和指令的操作有初步的理解,这个帖子算是llvm入门贴。
2023-12-1 09:16
0
雪    币: 304
活跃值: (360)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
llvm是啥啊
2023-12-1 10:16
0
雪    币: 584
活跃值: (2186)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
2023-12-1 10:37
0
雪    币: 2445
活跃值: (5519)
能力值: ( LV10,RANK:160 )
在线值:
发帖
回帖
粉丝
6
感谢分享
2023-12-1 11:05
0
雪    币: 2092
活跃值: (2145)
能力值: ( LV12,RANK:243 )
在线值:
发帖
回帖
粉丝
7
非常好的文章,不过 https://bbs.kanxue.com/thread-272346.htm 这篇文章里说不能在Windows上用VS来编译LLVM,我等会抽空发一篇文章说一下怎么用VS2022的MSVC编译LLVM16,不需要对clone下来的源码进行任何改动
2023-12-1 11:25
0
雪    币: 4725
活跃值: (2660)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
8
学习了。。。
2023-12-1 14:05
0
雪    币: 74
活跃值: (748)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
2023-12-1 14:14
0
雪    币: 58
活跃值: (1145)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
古月浪子 非常好的文章,不过 https://bbs.kanxue.com/thread-272346.htm 这篇文章里说不能在Windows上用VS来编译LLVM,我等会抽空发一篇文章说一下怎么用VS202 ...
这个一直都可以啊,clone下来直接build就行。
2023-12-4 21:31
0
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
感谢分享
2024-1-12 01:00
0
雪    币: 1671
活跃值: (215852)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
12
优雅永不过时
2024-1-12 09:51
0
雪    币: 2034
活跃值: (3561)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
13
字符串加密代码中,处理结构体中字符串的代码判断条件好像存在一点问题,该条件“if (Initializer->getNumOperands() > 1) continue;”使得只能处理成员变量为1的结构体,是否可以将该条件去掉
2024-6-19 11:36
0
游客
登录 | 注册 方可回帖
返回
//