在 Windows下搭建LLVM 使用环境

VSole2023-04-04 09:36:31

一、目的

搭建一个windows 下应用层能够快捷使用的 llvm 工具链,文中将会解释为什么要这么做,以及阐述其他方式可能会遇到的坑点,同时这个文章只是一个实践文,并不涉及具体原理,只为了提供一个windows 下搭建llvm的最佳实践方案。

① 为什么不使用 VS2019 自带的llvm进行使用?

不编译的话,就无法编译生成Pass,你只能利用clang去编译生成exe,比起VS2019 自带的cl并没有明显优势,作为一个小白,并不清楚支持llvm的原因。而Pass相当于一个插件,起到中间层实际执行混淆的作用,相当于我们这个功能的核心,所以必须要它。

② 为什么不用 VS2019 进行项目编译?

经过在12.0.1 / 12.0.0 / 15.0.6 几个版本的实践,会遇到各种问题,其中最主要的问题是,如果不使用12.0.0 ,你编译出来的llvm 集成到VS 2019中使用时,会出现各种意想不到的错误。另外用MSVC 编译llvm时,不支持开启BUILD_SHARED_LIBS 选项,但可以使用LLVM_EXPORT_SYMBOLS_FOR_PLUGINS 选项或LLVM_ENABLE_PLUGINS选项,但这样会出现一个问题,编译后的pass仅能使用new Pass语法,而且必须使用opt 进行加载插件使用,实际只有registerPipelineParsingCallback回调函数可以正常使用。

③ 既然我们需要的只是Pass,也就是一个dll文件,那么我们是否能生成12.0.0 版本的llvm pass,然后就直接用VS 2019 自带的llvm进行编译集成呢?

我也尝试过,但没有成功,不是自己编译出来的llvm pass 和 自己编译出来的 llvm clang-cl 在使用时会报无法加载模块,0x7E的错误,实在不想在windows下试图调试llvm 源码找出报错原因,因为这样也许还要被折磨几周。

二、环境搭建

基础环境

windows 10

https://github.com/llvm/llvm-project/releases/tag/llvmorg-12.0.0

CMake (https://cmake.org/download/)

VS 2019 16.11.10

MSYS2

Python

Git

三、安装步骤

1、安装cmake,https://cmake.org/download/ ,下载对应windows安装包安装即可。

PS: 安装时选择添加环境变量给所有用户使用。

2、安装MSYS2。

PS:llvm 是支持 VS 2019直接去生成llvm的,不用安装这些东西,官方有相关文章(https://llvm.org/docs/GettingStartedVS.html),编译很快,但编出来不会写相关Pass并利用,这里用gcc + vs2019 构造工具链,欢迎踩完坑后分享。

官网:https://www.msys2.org/

详细安装步骤:https://bbs.kanxue.com/thread-272346.htm#msg_header_h3_1

安装后安装gcc工具链

pacman -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-i686-toolchain cmake

3、在VS 2019 中安装clang cmake 等工具。

4、下载llvm 12.0.0源码,只需要下载clang和llvm文件夹即可,其他的编译pass用不到。

https://github.com/llvm/llvm-project/releases/tag/llvmorg-12.0.0

5、将源码解压,文件树如下:

PS:其中build* 文件夹是我运行bat文件后产生的目录,lld 可以不用下载,是之前踩坑的产物。

6、编译源码:

x64_gcc_build.bat 文件内容

cd C:\Users\admin\Documents\llvmProject12set PATH=%PATH%;C:\msys64\mingw64\bingcc --versioncmake -G "MinGW Makefiles" -S ./llvm -B ./build_dyn_x64  ^-DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;" ^-DLLVM_TARGETS_TO_BUILD="X86" -DBUILD_SHARED_LIBS=ON ^-DLLVM_INCLUDE_TESTS=OFF -DLLVM_BUILD_TESTS=OFF ^-DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_BUILD_BENCHMARKS=OFF -DLLVM_ENABLE_DUMP=ONcmake --build ./build_dyn_x64 -j 4cmake -DCMAKE_INSTALL_PREFIX="C:\Program Files\LLVM\llvm12\llvm12_dyn_x64" -P .\build_dyn_x64\cmake_install.cmake

x86_gcc_build.bat 文件内容

cd C:\Users\admin\Documents\llvmProject12set PATH=%PATH%;C:\msys64\mingw32\bingcc --versioncmake -G "MinGW Makefiles" -S ./llvm -B ./build_dyn_x32  ^-DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;" ^-DLLVM_TARGETS_TO_BUILD="X86" -DBUILD_SHARED_LIBS=ON ^-DLLVM_INCLUDE_TESTS=OFF -DLLVM_BUILD_TESTS=OFF ^-DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_BUILD_BENCHMARKS=OFF -DLLVM_ENABLE_DUMP=ONcmake --build ./build_dyn_x32 -j 4cmake -DCMAKE_INSTALL_PREFIX="C:\Program Files\LLVM\llvm12\llvm12_dyn_x32" -P .\build_dyn_x32\cmake_install.cmake

运行 bat 文件即可编译对应的 llvm。

7、编译安装完成之后可以在下面两个目录中找到编译好的clang 文件。

C:\Program Files\LLVM\llvm12\llvm12_dyn_x64C:\Program Files\LLVM\llvm12\llvm12_dyn_x32

8、安装后,我们已经可以用clang 编译hello world的cpp文件,但是编译正常的项目文件时,我们需要去解决动态链接库缺失和静态库的问题。

缺失的动态库可以去C:\msys64\mingw64\bin去寻找,并且复制到C:\Program Files\LLVM\llvm12\llvm12_dyn_x64\bin 中。

libstdc++-6.dlllibgcc_s_seh-1.dlllibwinpthread-1.dllzlib1.dll

缺失的动态库可以去C:\msys64\mingw32\bin去寻找,并且复制到C:\Program Files\LLVM\llvm12\llvm12_dyn_x32\bin 中。

libstdc++-6.dlllibgcc_s_dw2-1.dll (这个库名称不一样)libwinpthread-1.dllzlib1.dll

静态库则可以去对应的C:\Users\admin\Documents\llvmProject12\build_dyn_x32\lib 或者C:\Users\admin\Documents\llvmProject12\build_dyn_x64\lib中,将所有a文件添加到对应目录下。

四、测试使用

1、搭建测试的Pass文件和 测试程序源码,测试的文件树如下:

Transforms 文件夹里存放pass文件,TestProgram是项目源码文件夹。

2、其中 CMakeLists.txt 文件内容如下:

project(mydemo)cmake_minimum_required(VERSION 3.10) 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}]") if(NOT DEFINED ENV{LLVM_DIR})    # Default llvm config file path    set(ENV{LLVM_DIR} $ENV{LLVM_HOME}/lib/cmake/llvm)endif() # Check the pathif (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 "Path ($ENV{LLVM_DIR}) found!")    endif()else()    message(STATUS "Path ($ENV{LLVM_DIR}) found!")endif() # Enable verbose output for debug,# Same as: cmake -D CMAKE_VERBOSE_MAKEFILE=ON or make VERBOSE=1# set(CMAKE_VERBOSE_MAKEFILE on) find_package(LLVM REQUIRED CONFIG)add_definitions(${LLVM_DEFINITIONS})include_directories(${LLVM_INCLUDE_DIRS})link_directories(${LLVM_LIBRARY_DIRS}) # Debugmessage(STATUS "LLVM_DEFINITIONS  : ${LLVM_DEFINITIONS}")message(STATUS "LLVM_INCLUDE_DIRS : ${LLVM_INCLUDE_DIRS}")message(STATUS "LLVM_LIBRARY_DIRS : ${LLVM_LIBRARY_DIRS}") add_library(mydemo SHARED    # Add your source file here, header file is not neccessary    ./src/mydemo.cpp) # Use C++11 to compile your pass (i.e., supply -std=c++11).target_compile_features(mydemo PRIVATE cxx_range_for cxx_auto_type) include_directories(./include) # 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(mydemo 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(mydemo PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")endif(APPLE) target_link_libraries(mydemo     libLLVMCore.dll.a    libLLVMSupport.dll.a    libLLVMipo.dll.a     libLLVMDemangle.dll.a    libLLVMTransformUtils.dll.a    libLLVMAnalysis.dll.a    libpthread.a)

3、bat文件的内容如下(x86 就是将前面的环境设置为x86的即可):

cd C:\Users\admin\Documents\llvmProject\ollvm++:: Set MSYS2 envset PATH=%PATH%;C:\msys64\mingw64\bin :: Set LLVM_HOMEset LLVM_HOME=%PROGRAMFILES%/LLVM/llvm12/llvm12_dyn_x64set CC=%LLVM_HOME%/bin/clang.exeset CXX=%LLVM_HOME%/bin/clang++.exeset OPT=%LLVM_HOME%/bin/opt.exeset LLC=%LLVM_HOME%/bin/llc.exe rd /Q /S .\Build :: Build release versioncmake -S ./Transforms -B ./Build -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON  -G "MinGW Makefiles" :: Build debug version:: cmake -S ./Transforms -B ./Build -DCMAKE_BUILD_TYPE=Debug -DLLVM_ENABLE_ASSERTIONS=ON :: Build  projectcmake --build ./Build -j 4 copy .\Build\libmypass.dll .\Bin\libmypassx64.dll "%CC%" -Xclang -load -Xclang .\Bin\libmypassx64.dll .\TestProgram\TestProgram.cpp -o .\TestProgram\Bin\TestProgram.exe :: /D LLVM  -mrdrnd -Xclang -load -Xclang "C:\\Users\\admin\\Desktop\\Work\\ollvm++\\Build\\libmypass.dll".\TestProgram\Bin\TestProgram.exe flag{s1mpl3_11vm_d3m0}

4、TestProgram.cpp的内容,是一道简单的re逆向题:

#include #include  char input[100] = {0};char enc[100] = "\x86\x8a\x7d\x87\x93\x8b\x4d\x81\x80\x8a\\x43\x7f\x49\x49\x86\x71\x7f\x62\x53\x69\x28\x9d"; void encrypt(unsigned char *dest, char *src){    int len = strlen(src);    for(int i = 0;i < len;i ++){         dest[i] = (src[i] + (32 - i)) ^ i;    }} //flag{s1mpl3_11vm_d3m0}int main(int _argc, char *_argv[]){    //scanf("%s", input);    if(_argc <= 1){        return 0;    }    strcpy(input,_argv[1]);    printf("Please input your flag: %s",input);    unsigned char dest[100] = {0};    encrypt(dest, input);    bool result = strlen(input) == 22 && !memcmp(dest, enc, 22);    if(result){         printf("Congratulations~");    }else{         printf("Sorry try again.");    }}

5、配置好之后,直接运行bat文件就能编译测试:

集成到Visual Studio 中

1、我使用的是vs 2019,在之前下载了llvm 12.0 ,并且我们编译替换成自己的,做好准备工作之后,只需要在命令行选项中加入调用语句即可。

-Xclang -load -Xclang "C:\\Users\\admin\\Desktop\\Work\\ollvm++\\Build\\libmypass.dll"

2、使用前还需要将平台工具集先改了。

编译程序llvm
本作品采用《CC 协议》,转载必须注明作者和本文链接
源码分析1、LLVM编译器简介LLVM 命名最早源自于底层虚拟机的缩写,由于命名带来的混乱,LLVM就是该项目的全称。LLVM 核心库提供了与编译器相关的支持,可以作为多种语言编译器的后台来使用。自那时以来,已经成长为LLVM的主干项目,由不同的子项目组成,其中许多是正在生产中使用的各种 商业和开源的项目,以及被广泛用于学术研究。
LLVM PASS类pwn题入门
2022-06-30 16:46:54
同时IR也是一个编译器组件接口。LLVM的IR有三种表示形式:内存格式,只保存在内存中,人无法看到。不可读的IR,被称作bitcode,文件后缀为bc。可读的IR,介于高级语言和汇编代码之间,文件后缀为ll。而LLVM PASS就是去处理IR文件,通过opt利用写好的so库优化已有的IR,形成新的IR。而LLVM PASS类的pwn就是利用这一过程中可能会出现的漏洞。
使用AFL++复现历史CVE
2022-08-12 17:36:45
安装调试目标从github等途径下载并解压。从网上找现成的样本sample。
假如想在x86平台运行arm程序,称arm为source ISA, 而x86为target ISA, 在虚拟化的角度来说arm就是Guest, x86为Host。这种问题被称为Code-Discovery Problem。每个体系结构对应的helper函数在target/xxx/helper.h头文件中定义。
常规调试下watchpoint功能的受限及trace的低效是由于我们是使用软件方式在用户态进行操作,受到了CPU及操作系统的限制。但QEMU主要关注于仿真,对于安全分析来说并不友好。原因在于这个程序只是在控制台打印了HelloWorld,并没有涉及到JNI相关操作。Qiling的这种做法,以最小的成本保证了对各类各个版本的系统最大的适配性,并且也保证了程序运行状态与真实环境差异较小。
AFL--模糊测试使用浅析
Ghostscript是一款Adobe PostScript语言和PDF的解释器软件,被诸多著名应用(如ImageMagick)所使用。 9月5日,海外安全研究员在Twitter公开Ghostscript的安全模式绕过0day,并给出ImagMgick的利用代码,该漏洞可以造成任意命令执行,影响诸多下游应用,当天TSRC紧急对该漏洞进行复现与分析。 9月9日,Ghostscript官方发布补丁代
美国国家安全局敦促企业和机构弃用C和C++等编程语言,并转向内存安全的编程语言,例如C#、Rust、Go、Java、Ruby和Swift等。
ChatGPT为快速获得逆向工程工具的帮助增加了另一条途径。ChatGPT的响应通常包括带注释的汇编代码,这进一步提高了学习效果。一种方法是指示ChatGPT将编写在一个指令集中的汇编代码转换为另一个指令集。
然而在内核态中,堆内存的分配策略发生了变化。并把这个slab划分为一个个object,并将这些object组成一个单向链表进行管理,这里需要注意slub系统把内存块当成object看待,而不是伙伴系统中的页。本次选择演示的例题是2019-SUCTF的sudrv例题,查看start.sh中的信息可以发现开启了kaslr保护与smep保护。
VSole
网络安全专家