LLVM Study Log

0x0. 简述

刚接触LLVM的时候的记录,算是笔记吧,想从代码混淆的思路学习,学习如何写Pass,以及把自己写的Pass应用到实际的程序中。
学习笔记更新中…

0x1. LLVM

1.1 简介

LLVM是一个编译器框架,LLVM框架提供的中间表示(IR),可以作为多种语言的后端,并且根据IR可以做语言无关的优化以及
生成对应各种构架(x86,amd64,arm等)的代码。

主要分为三个部分:前端、Pass、后端

  • 前端: 获取源码,转成IR。
  • Pass:做各种优化工作或者一些过程的变换工作。
  • 后端: 生成对应平台的机器码。
1
2
3
Source Code ----> Frontend ----> Optimizer ----> Backend ----> Machine Code
|
Pass Work Here

更多细节直接看官网

1.2 安装

安装的话直接按照官方的文档去安装就可以了.

下载源码

1
2
3
$ cd where-you-want-llvm-to-live
$ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
$ cd where-you-want-llvm-to-live

迁移出clang

1
2
$ cd llvm/tools
$ svn co http://llvm.org/svn/llvm-project/cfe/trunk clang

运行库

1
2
3
$ cd where-you-want-llvm-to-live
$ cd llvm/projects
$ svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt

编译

1
2
3
4
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE:String=Release ../llvm/
$ make -j x

就是最后编译的时候,时间会比较久,make -j x,x给的大一点
会编译的快一点。

1.3 IR
1
LLVM IR有三种形式,可读的文本形式(.ll),硬盘上存储的二进制形式(.bc),内存中的编译器检测和修改的形式。

下面编写测试代码,来看一下IR语言。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

int test(int a,int b){

return a + b;

}

int main(int argc,char *argv[]){

int c = 0;
c = test(4,6);
printf("I got : %d \n",c);
return 0;
}

编译clang -emit-llvm test.cpp -S -o test.ll
得到IR

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
; ModuleID = 'test.cpp'
source_filename = "test.cpp"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@.str = private unnamed_addr constant [13 x i8] c"I got : %d \0A\00", align 1

; Function Attrs: noinline nounwind uwtable
define i32 @_Z4testii(i32, i32) #0 {
%3 = alloca i32, align 4
%4 = alloca i32, align 4
store i32 %0, i32* %3, align 4
store i32 %1, i32* %4, align 4
%5 = load i32, i32* %3, align 4
%6 = load i32, i32* %4, align 4
%7 = add nsw i32 %5, %6
ret i32 %7
}

; Function Attrs: noinline norecurse uwtable
define i32 @main(i32, i8**) #1 {
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i8**, align 8
%6 = alloca i32, align 4
store i32 0, i32* %3, align 4
store i32 %0, i32* %4, align 4
store i8** %1, i8*** %5, align 8
store i32 0, i32* %6, align 4
%7 = call i32 @_Z4testii(i32 4, i32 6)
store i32 %7, i32* %6, align 4
%8 = load i32, i32* %6, align 4
%9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i32 0, i32 0), i32 %8)
ret i32 0
}

declare i32 @printf(i8*, ...) #2

attributes #0 = { noinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { noinline norecurse uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.ident = !{!0}

!0 = !{!"clang version 4.0.0 (trunk 291212)"}

感觉配合着官网的文档,很容易就可以读懂,语法也很清晰明了。
比如test函数

1
2
3
4
5
6
7
8
9
10
define i32 @_Z4testii(i32, i32) #0 { //@是全局标识符,%是局部标识符
%3 = alloca i32, align 4 //局部变量声明,并分配空间,4字节对齐
%4 = alloca i32, align 4
store i32 %0, i32* %3, align 4 //刚才的变量存储在 0号寄存器的位置
store i32 %1, i32* %4, align 4 //..............1号寄存器的位置
%5 = load i32, i32* %3, align 4
%6 = load i32, i32* %4, align 4 //分别载入到5号寄存器和6号寄存器的位置
%7 = add nsw i32 %5, %6 //然后相加,存到7号寄存器的位置
ret i32 %7 //返回结果(32位整数)
}

弱弱的说一句…感觉好像JAVA字节码啊

更多的内容,还是官方文档

0x2. Pass

Pass 的主要分类有以下几种。

  • ImmutablePass
    Immutable,字面意思一成不变,即这种pass不是普通的用来转换、分析的pass,他可以提供当前编译器配置的信息。这种pass不需要运行、也不改变状态、也不需要更新。
  • MoudlePass
    从ModulePass派生表示这个pass使用整个程序作为一个单元,不可预测的顺序引用函数体,或者添加、删除函数;这种pass对子类行为并不了解,所以无法对其做优化。

  • CallGraphSCCPass
    这种需要遍历自下而上的函数调用图。

  • FuncationPass
    在每个函数上执行。这个pass再llvm的文档上有例子,那个hello world的例子。

  • LoopPass
    这种再每个循环上执行,与函数中其他的循环无关;LoopPass使用嵌套顺序处理循环,外层最后处理。

  • RegionPass
    类似LoopPass,但是在函数执行中的每个单个条目的退出区域上执行。还是嵌套顺序处理区域,即最外部的区域最后被处理。

  • BasicBlockPass
    类似FunctionPass,但是必须一次限制它们对基本块的检查和修改范围,具体的限制见文档。

  • MachineFunctionPass
    LLVM代码生成器的一部分,在程序中的每个LLVM函数的依赖于机器的表示上执行。

根据LLVM for Grad Students文章的方式去动态
使用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
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
using namespace llvm;

namespace {
struct SkeletonPass : public FunctionPass {
static char ID;
SkeletonPass() : FunctionPass(ID) {}

virtual bool runOnFunction(Function &F) {
errs() << "In a function called " << F.getName() << "!\n"; //如果是函数,输出函数名字

errs() << "Function body:\n";
F.dump(); //打印函数体

for (auto &B : F) {
errs() << "Basic block:\n"; //是bb块就输出这行
B.dump();

for (auto &I : B) {
errs() << "Instruction: "; //指令的话就输出这行
I.dump();
}
}

return false;
}
};
}

char SkeletonPass::ID = 0;
// Automatically enable the pass.
// http://adriansampson.net/blog/clangpass.html
// 注册Pass
static void registerSkeletonPass(const PassManagerBuilder &,
legacy::PassManagerBase &PM) {
PM.add(new SkeletonPass());
}
static RegisterStandardPasses
RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible,
registerSkeletonPass);

CmakeList.txt文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
add_library(SkeletonPass MODULE
# List your source files here.
Skeleton.cpp
)

# Use C++11 to compile our pass (i.e., supply -std=c++11).
target_compile_features(SkeletonPass PRIVATE cxx_range_for cxx_auto_type)

# 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(SkeletonPass 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(SkeletonPass PROPERTIES
LINK_FLAGS "-undefined dynamic_lookup"
)
endif(APPLE)

编译pass

1
2
3
4
mkdir build
cd build
cmake ..
make

然后就可以了

1
2
3
4
5
6
# muhe @ muhe-work in ~/Code/llvm-pass-skeleton/build on git:master x [17:10:02] 
$ make
Scanning dependencies of target SkeletonPass
[ 50%] Building CXX object skeleton/CMakeFiles/SkeletonPass.dir/Skeleton.cpp.o
[100%] Linking CXX shared module libSkeletonPass.so
[100%] Built target SkeletonPass

现在编写一个程序来测试这个Pass,我们的Pass可以标识出代码块和指令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

#include<stdio.h>

void func(){

printf("I am a func\n");
}

int main(int argc,char*argv[]){

int i = 0;
for(;i<10;i++){
printf("test time : %d\n",i);
}

func();
printf("Done\n");
return 0;
}

现在运行这个Pass
clang -Xclang -load -Xclang build/skeleton/libSkeletonPass.so -c test.c

得到的结果略长,如下

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
# muhe @ muhe-work in ~/Code/llvm-pass-skeleton on git:master x [17:20:12] 
$ clang -Xclang -load -Xclang build/skeleton/libSkeletonPass.so -c test.c
In a function called func!
Function body:

; Function Attrs: noinline nounwind uwtable
define void @func() #0 {
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i32 0, i32 0))
ret void
}

Basic block:

%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i32 0, i32 0))
ret void

Instruction: %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i32 0, i32 0))
Instruction: ret void
In a function called main!
Function body:

; Function Attrs: noinline nounwind uwtable
define i32 @main(i32, i8**) #0 {
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i8**, align 8
%6 = alloca i32, align 4
store i32 0, i32* %3, align 4
store i32 %0, i32* %4, align 4
store i8** %1, i8*** %5, align 8
store i32 0, i32* %6, align 4
br label %7

; <label>:7: ; preds = %13, %2
%8 = load i32, i32* %6, align 4
%9 = icmp slt i32 %8, 10
br i1 %9, label %10, label %16

; <label>:10: ; preds = %7
%11 = load i32, i32* %6, align 4
%12 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str.1, i32 0, i32 0), i32 %11)
br label %13

; <label>:13: ; preds = %10
%14 = load i32, i32* %6, align 4
%15 = add nsw i32 %14, 1
store i32 %15, i32* %6, align 4
br label %7

; <label>:16: ; preds = %7
call void @func()
%17 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.2, i32 0, i32 0))
ret i32 0
}

Basic block:

%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i8**, align 8
%6 = alloca i32, align 4
store i32 0, i32* %3, align 4
store i32 %0, i32* %4, align 4
store i8** %1, i8*** %5, align 8
store i32 0, i32* %6, align 4
br label %7

Instruction: %3 = alloca i32, align 4
Instruction: %4 = alloca i32, align 4
Instruction: %5 = alloca i8**, align 8
Instruction: %6 = alloca i32, align 4
Instruction: store i32 0, i32* %3, align 4
Instruction: store i32 %0, i32* %4, align 4
Instruction: store i8** %1, i8*** %5, align 8
Instruction: store i32 0, i32* %6, align 4
Instruction: br label %7
Basic block:

; <label>:7: ; preds = %13, %2
%8 = load i32, i32* %6, align 4
%9 = icmp slt i32 %8, 10
br i1 %9, label %10, label %16

Instruction: %8 = load i32, i32* %6, align 4
Instruction: %9 = icmp slt i32 %8, 10
Instruction: br i1 %9, label %10, label %16
Basic block:

; <label>:10: ; preds = %7
%11 = load i32, i32* %6, align 4
%12 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str.1, i32 0, i32 0), i32 %11)
br label %13

Instruction: %11 = load i32, i32* %6, align 4
Instruction: %12 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str.1, i32 0, i32 0), i32 %11)
Instruction: br label %13
Basic block:

; <label>:13: ; preds = %10
%14 = load i32, i32* %6, align 4
%15 = add nsw i32 %14, 1
store i32 %15, i32* %6, align 4
br label %7

Instruction: %14 = load i32, i32* %6, align 4
Instruction: %15 = add nsw i32 %14, 1
Instruction: store i32 %15, i32* %6, align 4
Instruction: br label %7
Basic block:

; <label>:16: ; preds = %7
call void @func()
%17 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.2, i32 0, i32 0))
ret i32 0

Instruction: call void @func()
Instruction: %17 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.2, i32 0, i32 0))
Instruction: ret i32 0

  • 一些包含关系:
    [Module [Function [BasicBlock [Instruction]]]]

他这篇文章后面例子还有使用Pass替换一些指令,如把add换成了mul,感兴趣可以跟着他的文章写一下。

0x3. 使用LLVM来做混淆

add替换成sub指令,即add a,b换成了sub a,-b

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
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"


using namespace llvm;

namespace {
struct SimplePass : public FunctionPass {
static char ID; // Pass identification, replacement for typeid

SimplePass() : FunctionPass(ID) {}
4
bool runOnFunction(Function &F) override {
Function *tmp = &F;
// 遍历函数中的所有基本块
for (Function::iterator bb = tmp->begin(); bb != tmp->end(); ++bb) {
// 遍历基本块中的每条指令
for (BasicBlock::iterator inst = bb->begin(); inst != bb->end(); ++inst) {
// 是否是add指令
if (inst->isBinaryOp()) {
if (inst->getOpcode() == Instruction::Add) {
return ob_add(cast<BinaryOperator>(inst));
}
}
}
}

return false;
}

// a+b === a-(-b)
bool ob_add(BinaryOperator *bo) {
BinaryOperator *op = NULL;

if (bo->getOpcode() == Instruction::Add) {
// 生成 (-b)
op = BinaryOperator::CreateNeg(bo->getOperand(1), "", bo);
// 生成 a-(-b)
op = BinaryOperator::Create(Instruction::Sub, bo->getOperand(0), op, "", bo);

op->setHasNoSignedWrap(bo->hasNoSignedWrap());
op->setHasNoUnsignedWrap(bo->hasNoUnsignedWrap());
// 替换所有出现该指令的地方
bo->replaceAllUsesWith(op);
return true;
}

}
};
}

char SimplePass::ID = 0;

// Automatically enable the pass.
// http://adriansampson.net/blog/clangpass.html
static void registerSimplePass(const PassManagerBuilder &,
legacy::PassManagerBase &PM) {
PM.add(new SimplePass());
}
static RegisterStandardPasses
RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible,
registerSimplePass);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
add_library(SimplePass MODULE
# List your source files here.
SimplePass.cpp
)

# Use C++11 to compile our pass (i.e., supply -std=c++11).
target_compile_features(SimplePass PRIVATE cxx_range_for cxx_auto_type)

# LLVM is (typically) built with no C++ RTTI. We need to match that.
set_target_properties(SimplePass 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(SimplePass PROPERTIES
LINK_FLAGS "-undefined dynamic_lookup"
)
endif(APPLE)

我的测试代码如下

1
2
3
4
5
6
7
8
$ cat example.c 
#include <stdio.h>
int main(int argc, const char** argv) {
int num;
scanf("%i", &num);
printf("%i\n", num + 2);
return 0;
}

先编译正常的程序
clang example.c -o before

1
2
$ file before 
before: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, not stripped

看一下main的逻辑:

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
objdump -M intel -d  before 

. . .

0000000000400580 <main>:
400580: 55 push rbp
400581: 48 89 e5 mov rbp,rsp
400584: 48 83 ec 20 sub rsp,0x20
400588: 48 b8 64 06 40 00 00 movabs rax,0x400664
40058f: 00 00 00
400592: 48 8d 4d ec lea rcx,[rbp-0x14]
400596: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
40059d: 89 7d f8 mov DWORD PTR [rbp-0x8],edi
4005a0: 48 89 75 f0 mov QWORD PTR [rbp-0x10],rsi
4005a4: 48 89 c7 mov rdi,rax
4005a7: 48 89 ce mov rsi,rcx
4005aa: b0 00 mov al,0x0
4005ac: e8 af fe ff ff call 400460 <__isoc99_scanf@plt>
4005b1: 48 bf 67 06 40 00 00 movabs rdi,0x400667
4005b8: 00 00 00
4005bb: 8b 55 ec mov edx,DWORD PTR [rbp-0x14]
4005be: 83 c2 02 add edx,0x2 //num + 2 语句
4005c1: 89 d6 mov esi,edx
4005c3: 89 45 e8 mov DWORD PTR [rbp-0x18],eax
4005c6: b0 00 mov al,0x0
4005c8: e8 73 fe ff ff call 400440 <printf@plt>
4005cd: 31 d2 xor edx,edx
4005cf: 89 45 e4 mov DWORD PTR [rbp-0x1c],eax
4005d2: 89 d0 mov eax,edx
4005d4: 48 83 c4 20 add rsp,0x20
4005d8: 5d pop rbp
4005d9: c3 ret
4005da: 66 0f 1f 44 00 00 nop WORD PTR [rax+rax*1+0x0]

. . .

现在编译一个混淆过的

1
2
3
4
mkdir build_1
cd build_1
cmake ..
make

然后
clang -Xclang -load -Xclang build_1/simple/libSimplePass.so -c example.c
得到example.o文件,这个还没链接,我们使用clang再链接一下就好了。

1
2
3
$ clang example.o -o example 
$ file example
example: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, not stripped

反汇编看一下main的逻辑:

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
0000000000400580 <main>:
400580: 55 push rbp
400581: 48 89 e5 mov rbp,rsp
400584: 48 83 ec 20 sub rsp,0x20
400588: 48 b8 74 06 40 00 00 movabs rax,0x400674
40058f: 00 00 00
400592: 48 8d 4d ec lea rcx,[rbp-0x14]
400596: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
40059d: 89 7d f8 mov DWORD PTR [rbp-0x8],edi
4005a0: 48 89 75 f0 mov QWORD PTR [rbp-0x10],rsi
4005a4: 48 89 c7 mov rdi,rax
4005a7: 48 89 ce mov rsi,rcx
4005aa: b0 00 mov al,0x0
4005ac: e8 af fe ff ff call 400460 <__isoc99_scanf@plt>
4005b1: 48 bf 77 06 40 00 00 movabs rdi,0x400677
4005b8: 00 00 00
4005bb: 31 d2 xor edx,edx
4005bd: 44 8b 45 ec mov r8d,DWORD PTR [rbp-0x14]
4005c1: 83 ea 02 sub edx,0x2
4005c4: 41 29 d0 sub r8d,edx //已经被替换了
4005c7: 44 89 c6 mov esi,r8d
4005ca: 89 45 e8 mov DWORD PTR [rbp-0x18],eax
4005cd: b0 00 mov al,0x0
4005cf: e8 6c fe ff ff call 400440 <printf@plt>
4005d4: 31 d2 xor edx,edx
4005d6: 89 45 e4 mov DWORD PTR [rbp-0x1c],eax
4005d9: 89 d0 mov eax,edx
4005db: 48 83 c4 20 add rsp,0x20
4005df: 5d pop rbp
4005e0: c3 ret
4005e1: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0]
4005e8: 00 00 00
4005eb: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0]

这个只是demo而已,几乎没什么强度。

0x4. 参考和引用

llvm
基于LLVM的代码混淆
LLVM for Grad Students

文章目录
  1. 1. 0x0. 简述
  2. 2. 0x1. LLVM
    1. 2.1. 1.1 简介
    2. 2.2. 1.2 安装
    3. 2.3. 1.3 IR
  3. 3. 0x2. Pass
  4. 4. 0x3. 使用LLVM来做混淆
  5. 5. 0x4. 参考和引用
,