从0到1的ollvm-3-基于llvm-pass自制fla混淆

两只羊 Lv3

MyOllvm

大致思路

对于每一个Function,我们要对其BasicBlock进行处理

这里我把其Block类型大致分为入口点的EntryBlock,其他的普通分支BasicBlock,和返回块EndBlock

image-20260225124127316

我们要做的,就是在EntryBlock中新建一个SwitchValue变量,并建立一个SwitchValueBB块,根据当前SwitchValue的值,去控制下一个执行的Block。对于每个BasicBlock,我们先给其一个唯一的SwitchVal Key,用哈希表储存起来。然后再重新遍历所有的BasicBlock,将其后继对应的SwitchVal Key给SwitchValue赋值,再跳回SwitchValueBB块,删除原来的terminator,也就是原来的跳转指令。

代码编写

我们先用哈希表对每一个基本块都赋予一个唯一的switchValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
std::default_random_engine e;
std::uniform_int_distribution<unsigned int> u(0,0xFFFFFFFF); // 左闭右闭区间
e.seed(time(0));

std::unordered_set<unsigned int> SwitchValSet;
std::unordered_map<BasicBlock*, unsigned int> SwitchValMap;
for(BasicBlock* BB: OriginalBasicBlocks) {
unsigned int RandomSwitchVal = u(e);
while(SwitchValSet.find(RandomSwitchVal) != SwitchValSet.end()) {
RandomSwitchVal = u(e);
}
SwitchValMap[BB] = RandomSwitchVal;
}

for(BasicBlock* BB: OriginalBasicBlocks) {
BB->print(errs());
errs() << SwitchValMap[BB] << "\n";
}

然后要插入建立SwitchValue变量的指令

如果想要建立一条新建switchVar的指令,要插入在EntryBlock中,不然可能影响优化、也可能在某些 CFG/异常路径下不符合预期。

但如果直接在插入末尾,则会出现Basic Block in function ‘cmp’ does not have terminator!的错误

1
2
3
4
5
6
7
8
9
10
11
LLVMContext &Ctx = F.getContext();
//AllocaInst *Var = new AllocaInst(Type::getInt32Ty(F.getContext()), 0, "switchVar", EntryBlock);
//BasicBlock *initSwitchValBB = BasicBlock::Create(Ctx, "initSwitchValBB", &F, EntryBlock);

Instruction *IP = &*EntryBlock->getFirstInsertionPt(); // PHI之后最早插入点

IRBuilder<> Builder(Ctx);

Builder.SetInsertPoint(IP);
AllocaInst *SwitchValue = Builder.CreateAlloca(Type::getInt32Ty(Ctx), nullptr, "SwitchValue");
SwitchValue->setAlignment(Align(4));

BasicBlock 必须以 terminator 结束(br/ret/switch 等)。新建块后如果没插终结指令,IR 是非法的。

查看一波IR,SwitchValue已经在PHI之后最早插入点创建

1
2
3
4
5
6
7
8
9
10
11
12
define dso_local i32 @cmp(i32 noundef %0, i32 noundef %1) #0 {
%SwitchValue = alloca i32, align 4
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i32, align 4
store i32 %0, i32* %3, align 4
store i32 %1, i32* %4, align 4
store i32 0, i32* %5, align 4
%6 = load i32, i32* %3, align 4
%7 = load i32, i32* %4, align 4
%8 = icmp eq i32 %6, %7
br label %SwitchValueBB

然后是创建一个SwitchValue分配handler块

这里需要注意switch的后继,最好设置为自己,不能设置为EntryBlock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BasicBlock *SwitchValueBB = BasicBlock::Create(Ctx, "SwitchValueBB", &F);

IRBuilder<> DB(SwitchValueBB);
Value *Cur = DB.CreateLoad(Type::getInt32Ty(Ctx), SwitchValue, "sv");

// 默认跳转目标,随便选一个在函数里的块
BasicBlock *DefaultTarget = SwitchValueBB;

// 创建 switch 终结指令(它本身就是 terminator)
SwitchInst *SW = DB.CreateSwitch(Cur, DefaultTarget, /*NumCases=*/0);
for (BasicBlock *Target : OriginalBasicBlocks) {
// 你也可以选择不把 entry 当 case,只要保证能到达就行
unsigned int id = SwitchValMap[Target];
SW->addCase(ConstantInt::get(Type::getInt32Ty(Ctx), id), Target);
}

查看IR的效果

1
2
3
4
5
6
7
8
9
10
11
SwitchValueBB:                                    ; preds = %16, %15, %14, %10, %9, %2, %SwitchValueBB
%sv = load i32, i32* %SwitchValue, align 4
switch i32 %sv, label %SwitchValueBB [
i32 -2058324383, label %9
i32 1176119844, label %10
i32 -93860928, label %14
i32 971602666, label %15
i32 1944500023, label %16
i32 -1727544718, label %17
]
}

然后就是对每个BasicBlock的关键处理

对于ret块,switch/indirectbr,我们直接略过不处理。

对于单一的无条件跳转,我们获取其后继哈希表对应的SwitchValue,在结尾进行赋值

对于条件跳转,我们需要分别获取条件True和False时对应产生的后继,并从哈希表获取对应的SwitchValue,通过创建select指令来给SwitchValue赋值

最后对于处理的情况,还需要将原terminator删除,使其都直接跳转到SwitchValueBB

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
for (BasicBlock &BB : F) {

Instruction *Term = BB.getTerminator();
if (!Term) continue;

// 4.1 return 块:不处理
if (isa<ReturnInst>(Term)) {
continue;
}
//errs() <<"aaaaaaaaaaaaaaaaaaaaaaaaaa" <<"\n";
// 仅处理 br(你描述的场景:后继数=1 或 条件判断)
auto *Br = dyn_cast<BranchInst>(Term);
if (!Br) {
// 如果还有 switch/indirectbr 等,你可以在这里扩展
continue;
}

Builder.SetInsertPoint(Term);

// 4.2 无条件跳转:SwitchValue = map[succ]
if (Br->isUnconditional()) {
//errs() <<"bbbbbbbbbbbbbbbbbbbbbbbbbb" <<"\n";
BasicBlock *Succ = Br->getSuccessor(0);
//auto It = SwitchValMap.find(Succ);
//if (It == SwitchValMap.end()) continue; // succ 不在 map(理论上不该发生)
//errs() <<"ccccccccccccccccccccccccc" <<"\n";
errs() << SwitchValMap[Succ] << "\n";
StoreInst *SI = Builder.CreateStore(
ConstantInt::get(Type::getInt32Ty(Ctx), SwitchValMap[Succ]),
SwitchValue
);
SI->setAlignment(Align(4));

} else {
// 4.3 条件分支:根据 cond 选择 succ0/succ1 的值
// br i1 %cond, label %T, label %F
Value *Cond = Br->getCondition();
BasicBlock *TrueSucc = Br->getSuccessor(0);
BasicBlock *FalseSucc = Br->getSuccessor(1);

//auto ItT = SwitchValMap.find(TrueSucc);
//auto ItF = SwitchValMap.find(FalseSucc);
//if (ItT == SwitchValMap.end() || ItF == SwitchValMap.end()) continue;

Value *TV = ConstantInt::get(Type::getInt32Ty(Ctx), SwitchValMap[TrueSucc]);
Value *FV = ConstantInt::get(Type::getInt32Ty(Ctx), SwitchValMap[FalseSucc]);

// 用 select 生成一个 i32 值,然后 store 一次
// %v = select i1 %cond, i32 TV, i32 FV
Value *Sel = Builder.CreateSelect(Cond, TV, FV, "switch.sel");

StoreInst *SI = Builder.CreateStore(Sel, SwitchValue);
SI->setAlignment(Align(4));
}



Builder.CreateBr(SwitchValueBB);

// 删除旧 terminator(否则一个BB会有两个 terminator,IR 非法)
Term->eraseFromParent();
}

再看一波生成的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
47
48
49
50
51
52
53
54
55
56
57
define dso_local i32 @cmp(i32 noundef %0, i32 noundef %1) #0 {
%SwitchValue = alloca i32, align 4
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i32, align 4
store i32 %0, i32* %3, align 4
store i32 %1, i32* %4, align 4
store i32 0, i32* %5, align 4
%6 = load i32, i32* %3, align 4
%7 = load i32, i32* %4, align 4
%8 = icmp eq i32 %6, %7
%switch.sel = select i1 %8, i32 -2058324383, i32 1176119844
store i32 %switch.sel, i32* %SwitchValue, align 4
br label %SwitchValueBB

9: ; preds = %SwitchValueBB
store i32 0, i32* %5, align 4
store i32 -1727544718, i32* %SwitchValue, align 4
br label %SwitchValueBB

10: ; preds = %SwitchValueBB
%11 = load i32, i32* %3, align 4
%12 = load i32, i32* %4, align 4
%13 = icmp slt i32 %11, %12
%switch.sel1 = select i1 %13, i32 -93860928, i32 971602666
store i32 %switch.sel1, i32* %SwitchValue, align 4
br label %SwitchValueBB

14: ; preds = %SwitchValueBB
store i32 1, i32* %5, align 4
store i32 1944500023, i32* %SwitchValue, align 4
br label %SwitchValueBB

15: ; preds = %SwitchValueBB
store i32 2, i32* %5, align 4
store i32 1944500023, i32* %SwitchValue, align 4
br label %SwitchValueBB

16: ; preds = %SwitchValueBB
store i32 -1727544718, i32* %SwitchValue, align 4
br label %SwitchValueBB

17: ; preds = %SwitchValueBB
%18 = load i32, i32* %5, align 4
ret i32 %18

SwitchValueBB: ; preds = %16, %15, %14, %10, %9, %2, %SwitchValueBB
%sv = load i32, i32* %SwitchValue, align 4
switch i32 %sv, label %SwitchValueBB [
i32 -2058324383, label %9
i32 1176119844, label %10
i32 -93860928, label %14
i32 971602666, label %15
i32 1944500023, label %16
i32 -1727544718, label %17
]
}

最终效果

用一个较复杂的xxtea算法测试一下,发现能够生成fla的实际效果,虽然cfg看起来一点也不平坦就是了

image-20260225120003246

最后的完整代码

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
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {

errs() << "Function Name: " << F.getName() << "\n";
//errs() << "Block Count: " << F.size() << "\n";
std::vector<BasicBlock*> OriginalBasicBlocks;
BasicBlock* EntryBlock = &F.getEntryBlock();
for (BasicBlock &BB : F) {
if(&BB != EntryBlock) {
OriginalBasicBlocks.push_back(&BB);
}
/*StringRef bbName = BB.getName();
errs() << " -> BasicBlock Name: ";
if (bbName.empty()) {
errs() << "[Unnamed/Numbered]";
} else {
errs() << bbName;
}
for (Instruction &I : BB) {
errs() << " " << I << "\n";
}
BB.print(errs()); */
}

auto &BBList = F.getBasicBlockList();

std::default_random_engine e;
std::uniform_int_distribution<unsigned int> u(0,0xFFFFFFFF); // 左闭右闭区间
e.seed(time(0));

std::unordered_set<unsigned int> SwitchValSet;
std::unordered_map<BasicBlock*, unsigned int> SwitchValMap;
for(BasicBlock* BB: OriginalBasicBlocks) {
unsigned int RandomSwitchVal = u(e);
while(SwitchValSet.find(RandomSwitchVal) != SwitchValSet.end()) {
RandomSwitchVal = u(e);
}
SwitchValSet.insert(RandomSwitchVal);
SwitchValMap[BB] = RandomSwitchVal;
}

/*for(BasicBlock* BB: OriginalBasicBlocks) {
BB->print(errs());
errs() << SwitchValMap[BB] << "\n";
}*/

LLVMContext &Ctx = F.getContext();

Instruction *IP = &*EntryBlock->getFirstInsertionPt(); // PHI之后最早插入点

IRBuilder<> Builder(Ctx);

Builder.SetInsertPoint(IP);
AllocaInst *SwitchValue = Builder.CreateAlloca(Type::getInt32Ty(Ctx), nullptr, "SwitchValue");
SwitchValue->setAlignment(Align(4));

BasicBlock *SwitchValueBB = BasicBlock::Create(Ctx, "SwitchValueBB", &F);

IRBuilder<> DB(SwitchValueBB);
Value *Cur = DB.CreateLoad(Type::getInt32Ty(Ctx), SwitchValue, "sv");

BasicBlock *DefaultTarget = SwitchValueBB;

SwitchInst *SW = DB.CreateSwitch(Cur, DefaultTarget, 0);
for (BasicBlock *Target : OriginalBasicBlocks) {
unsigned int id = SwitchValMap[Target];
SW->addCase(ConstantInt::get(Type::getInt32Ty(Ctx), id), Target);
}

for (BasicBlock &BB : F) {

Instruction *Term = BB.getTerminator();
if (!Term) continue;

// 4.1 return 块:不处理
if (isa<ReturnInst>(Term)) {
continue;
}
// 仅处理 br(后继数=1 或 条件判断)
auto *Br = dyn_cast<BranchInst>(Term);
if (!Br) {
continue;
}

Builder.SetInsertPoint(Term);

// 4.2 无条件跳转:SwitchValue = map[succ]
if (Br->isUnconditional()) {
BasicBlock *Succ = Br->getSuccessor(0);
//errs() << SwitchValMap[Succ] << "\n";
StoreInst *SI = Builder.CreateStore(
ConstantInt::get(Type::getInt32Ty(Ctx), SwitchValMap[Succ]),
SwitchValue
);
SI->setAlignment(Align(4));

} else {
// 4.3 条件分支:根据 cond 选择 succ0/succ1 的值
// br i1 %cond, label %T, label %F
Value *Cond = Br->getCondition();
BasicBlock *TrueSucc = Br->getSuccessor(0);
BasicBlock *FalseSucc = Br->getSuccessor(1);

Value *TV = ConstantInt::get(Type::getInt32Ty(Ctx), SwitchValMap[TrueSucc]);
Value *FV = ConstantInt::get(Type::getInt32Ty(Ctx), SwitchValMap[FalseSucc]);

// 用 select 生成一个 i32 值,然后 store 一次
// %v = select i1 %cond, i32 TV, i32 FV
Value *Sel = Builder.CreateSelect(Cond, TV, FV, "switch.sel");

StoreInst *SI = Builder.CreateStore(Sel, SwitchValue);
SI->setAlignment(Align(4));
}



Builder.CreateBr(SwitchValueBB);

// 删除旧 terminator(否则一个BB会有两个 terminator,IR 非法)
Term->eraseFromParent();
}

return PreservedAnalyses::none();
}
  • 标题: 从0到1的ollvm-3-基于llvm-pass自制fla混淆
  • 作者: 两只羊
  • 创建于 : 2026-02-25 12:10:27
  • 更新于 : 2026-02-25 22:14:16
  • 链接: https://twogoat.github.io/2026/02/25/从0到1的ollvm-3-基于llvm-pass自制fla混淆/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
从0到1的ollvm-3-基于llvm-pass自制fla混淆