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
46
; 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
22
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
20

#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
119
# 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) {}

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
9
$ 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
36
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
4
$ 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
34
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