一
概述
if else
语句转换成switch
语句。switch
结构体包含多个分支,各个分支的执行顺序是随机的,但并不影响真正的程序逻辑。然后在switch
结构的外层,再套一个或多个while
循环。main
函数中执行一个名称为add
的自定义函数。add
函数里会判断参数num1
是否等于100
,如果等于,则返回0
,否则继续执行,而后将参数num1
和num2
相加,结果赋值给num3
,并返回num3
。具体代码如下:int add(int num1, int num2){
if (num1 == 100) {
return 0;
}
int num3 = num1 + num2;
return num3;
}int main(){
int num1 = 10;
int num2 = 20;
int num3 = add(num1,num2);
return 0;
}
二
源码分析
namespace {
struct Flattening : public FunctionPass {
static char ID; // Pass identification, replacement for typeid
bool flag;Flattening() : FunctionPass(ID) {}
Flattening(bool flag) : FunctionPass(ID) { this->flag = flag; }bool runOnFunction(Function &F);
bool flatten(Function *f);
};
}char Flattening::ID = 0;
static RegisterPass<Flattening> X("flattening", "Call graph flattening");
Pass *llvm::createFlattening(bool flag) { return new Flattening(flag); }bool Flattening::runOnFunction(Function &F) {
Function *tmp = &F;
// Do we obfuscate
if (toObfuscate(flag, tmp, "fla")) {
if (flatten(tmp)) {
++Flattened;
}
}return false;
}
po f->dump()
,打印参数 F,输出 add函数的所有中间层代码,代码如下,为了方便理解,每一行都做了相应注释:; Function Attrs: noinline nounwind ssp uwtable
define i32 @add(i32 %num1, i32 %num2) #0 {entry: ;第 1 个 BasicBlock,名称是 entry
%retval = alloca i32, align 4 ;为返回值分配 4 字节空间
%num1.addr = alloca i32, align 4 ;为变量 num1.addr 分配 4 字节空间
%num2.addr = alloca i32, align 4 ;为变量 num2.addr 分配 4 字节空间
%num3 = alloca i32, align 4 ;为变量 num3 分配 4 字节的空间
store i32 %num1, i32* %num1.addr, align 4 ;将变量 num1 保存到 num1.addr
store i32 %num2, i32* %num2.addr, align 4 ;将变量 num2 保存到 num2.addr
%0 = load i32, i32* %num1.addr, align 4 ;将变量 num1.addr 保存到变量 0
%cmp = icmp ne i32 %0, 100 ;比较变量 0 是否等于 100
br i1 %cmp, label %if.then, label %if.end ;如果条件比较成立则会跳转到 if.thenif.then: ;第 2 个 BasicBlock,名称是 if.then ; preds = %entry
store i32 0, i32* %retval, align 4
br label %return ;跳转到 returnif.end: ;第 3 个 BasicBlock,名称是 if.end ; preds = %entry
%1 = load i32, i32* %num1.addr, align 4 ;将变量 num1.addr 保存到变量 1
%2 = load i32, i32* %num2.addr, align 4 ;将变量 num2.addr 保存到变量 2
%add = add nsw i32 %1, %2 ;将变量 1 和变量 2 相加,结果保存到变量 add
store i32 %add, i32* %num3, align 4 ;将变量 add 保存到变量 num3
%3 = load i32, i32* %num3, align 4 ;将变量 num3 保存到变量 3
store i32 %3, i32* %retval, align 4 ;将变量 3 保存到变量 retval
br label %return ;跳转到 returnreturn: ;第 4 个 BasicBlock,名称是 return ; preds = %if.end, %if.then
%4 = load i32, i32* %retval, align 4
ret i32 %4
}
entry
、if.then
、if.end
和return
。第 63 行是flatten
62 //生成随机数
63 char scrambling_key[16];
64 llvm::cryptoutils->get_bytes(scrambling_key, 16);
switch
语句转换成if
语句:67 //转换 switch 语句
68 FunctionPass *lower = createLowerSwitchPass();
69 lower->runOnFunction(*f);
BasicBlock
到origBB
容器:71 //保存所有原始的 BasicBlock
72 for (Function::iterator i = f->begin(); i != f->end(); ++i) {
73 BasicBlock *tmp = &*i;
74 origBB.push_back(tmp);
75
76 BasicBlock *bb = &*i;
77 if (isa<InvokeInst>(bb->getTerminator())) {
78 return false;
79 }
80 }
origBB
容器里的第一个BasicBlock
,因为第一个BasicBlock
需要单独处理:87 //清空第一个 BasicBlock
88 origBB.erase(origBB.begin());
89
90 //获取指向第一个 BasicBlock 的指针
91 Function::iteratortmp = f->begin(); //++tmp;
92 BasicBlock *insert = &*tmp;
93
BasicBlock
是否含有条件语句,如果有,就获取条件语句的内容,并赋给br
变量:94 //如果第一个 BasicBlock 含有条件语句
95 BranchInst *br = NULL;
96 if (isa<BranchInst>(insert->getTerminator())) {
97 br = cast<BranchInst>(insert->getTerminator());
98 }
br i1 %cmp, label %if.then, label %if.end
BasicBlock
的倒数第二行:100 if ((br != NULL && br->isConditional()) ||
101 insert->getTerminator()->getNumSuccessors() > 1) {
102 BasicBlock::iterator i = insert->end();
103 --i;
104
105 if (insert->size() > 1) {
106 --i;
107 }
108
%cmp = icmp ne i32 %0, 100
BasicBlock
进行分割,第 110 行是将分割后的内容放入origBB
容器的第一条记录中,还记得第88 行清空了origBB
容器的第一条记录吧?109 BasicBlock *tmpBB = insert->splitBasicBlock(i, "first");
110 origBB.insert(origBB.begin(), tmpBB);
111 }
112
tmp
得到的内容如下:first: ; preds = %entry
%cmp = icmp ne i32 %0, 100
br i1 %cmp, label %if.then, label %if.end
insert
的内容如下:entry:
%retval = alloca i32, align 4
%num1.addr = alloca i32, align 4
%num2.addr = alloca i32, align 4
%num3 = alloca i32, align 4
store i32 %num1, i32* %num1.addr, align 4
store i32 %num2, i32* %num2.addr, align 4
%0 = load i32, i32* %num1.addr, align 4
br label %first
br label%first
移除:113 // 移除跳转
114 insert->getTerminator()->eraseFromParent();
switch
语句需用的变量switchVar
,变量的值就是最前面第 63 行和第 64 行随机生成的scrambling_key
:116 //创建 switchVar,并按其进行设置
117 switchVar = new AllocaInst(Type::getInt32Ty(f->getContext()), 0, "switchVar", insert);
118
119 new StoreInst(
120 ConstantInt::get(Type::getInt32Ty(f->getContext()),
121 llvm::cryptoutils->scramble32(0, scrambling_key)),
122 switchVar, insert);
switchVar
之后,再打印insert
,可以看到其内容的最后多了两条语句:%switchVar = alloca i32
store i32 1207049111, i32* %switchVar
loopEntry
和loopEnd
,此时里面的代码还是空的。第 128 行是创建一条load
指令,将switchvar
放入loopEntry
中。124 //创建主循环
125 loopEntry = BasicBlock::Create(f->getContext(), "loopEntry", f, insert);
126 loopEnd = BasicBlock::Create(f->getContext(), "loopEnd", f, insert);
127
128 load = new LoadInst(switchVar, "switchVar", loopEntry);
129
130 //在顶部移动第一个 BasicBlock
131 insert->moveBefore(loopEntry);
132 BranchInst::Create(loopEntry, insert);
133
134 //从 loopEnd 跳转到 loopEntry
135 BranchInst::Create(loopEntry, loopEnd);
136
137 BasicBlock *swDefault = BasicBlock::Create(f->getContext(), "switchDefault", f, loopEnd);
138
139 BranchInst::Create(loopEnd, swDefault);
140
141 //创建 switch 语句本身,并设置条件
142 switchI = SwitchInst::Create(&*f->begin(), swDefault, 0, loopEntry);
143 switchI->setCondition(load);
144
145 //移除第一个BasicBlock 中的跳转分支,并且创建一个到 while 循环的跳转
146 f->begin()->getTerminator()->eraseFromParent();
147
148 BranchInst::Create(loopEntry, &*f->begin()); //添加上 br label %loopEntry
origBB
里每个的BasicBlock
填充switch
分支:150 //把所有 BasicBlock 都放入 switch 分支
151 for (vector<BasicBlock *>::iterator b = origBB.begin(); b != origBB.end();
152 ++b) {
153 BasicBlock *i = *b;
154 ConstantInt *numCase = NULL;
155
156 //把 BasicBlock 移动到 switch 内(只是视觉上的,不涉及代码逻辑)
157 i->moveBefore(loopEnd);
158
159 //给 switch 添加 case,switchVar 是随机数
160 numCase = cast<ConstantInt>(ConstantInt::get(
161 switchI->getCondition()->getType(),
162 llvm::cryptoutils->scramble32(switchI->getNumCases(), scrambling_key)));
163 switchI->addCase(numCase, i); //添加 switch case
164 }
(lldb) po f->dump()
; Function Attrs: noinline nounwind ssp uwtable
define i32 @add(i32 %num1, i32 %num2) #0 {
entry:
%retval = alloca i32, align 4
%num1.addr = alloca i32, align 4
%num2.addr = alloca i32, align 4
%num3 = alloca i32, align 4
store i32 %num1, i32* %num1.addr, align 4
store i32 %num2, i32* %num2.addr, align 4
%0 = load i32, i32* %num1.addr, align 4
%switchVar = alloca i32
store i32 1207049111, i32* %switchVar
br label %loopEntryloopEntry: ; preds = %entry,%loopEnd
%switchVar1 = load i32, i32* %switchVar
switch i32 %switchVar1, label %switchDefault [
i32 1207049111, label %first
i32 -677357051, label %if.then
i32 -1251090459, label %if.end
i32 1194405227, label %return
]switchDefault: ; preds = %loopEntry
br label %loopEndfirst: ; preds = %loopEntry
%cmp = icmp ne i32 %0, 100
br i1 %cmp, label %if.then, label %if.endif.then: ; preds = %loopEntry,%first
store i32 0, i32* %retval, align 4
br label %returnif.end: ; preds = %loopEntry, %first
%1 = load i32, i32* %num1.addr, align 4
%2 = load i32, i32* %num2.addr, align 4
%add = add nsw i32 %1, %2
store i32 %add,i32* %num3, align 4
%3 = load i32, i32* %num3, align 4
store i32 %3, i32* %retval, align 4
br label %returnreturn: ; preds = %loopEntry, %if.end, %if.then
%4 = load i32, i32* %retval, align 4
ret i32 %4loopEnd: ; preds = %switchDefault
br label %loopEntry
}
loopEntry
和loopEnd
是一个while
循环,while
循环中有一个switch
结构,switch
结构中有 4 个case
分支,分别是first
、if.then
、if.end
、return
。如果switchVar
的值是1207049111
,就跳转到first
分支;如果是-677357051
,则跳转到if.then
;如果是-125090459
,跳转到if.end
分支;如果是1194405227
,跳转到return
分支。switch
结构了,但是在执行跳转时并没有更新switchVar
,这样会使真实的程序逻辑没有正确执行。第 166 行到第 237 行的作用就是更新switchVar
,因为只有一个后续块相当于无条件跳转,所以直接将switchVar
更新成后续块对应的case
:166 //重新计算 switchVar
167 for (vector<BasicBlock *>::iterator b = origBB.begin(); b != origBB.end();
168 ++b) {
169 BasicBlock *i = *b;
170 ConstantInt *numCase = NULL;
171
172 // Ret BasicBlock 是个返回块,不需要更新 caseVar
173 if (i->getTerminator()->getNumSuccessors() == 0) {
174 continue;
175 }
176
177 //如果是无条件跳转
178 if (i->getTerminator()->getNumSuccessors() == 1) {
179 // Get successor and delete terminator
180 BasicBlock *succ = i->getTerminator()->getSuccessor(0);
181 i->getTerminator()->eraseFromParent();
182
183 //获取下一个 case 分支
184 numCase = switchI->findCaseDest(succ);
185
186 // If next case == default case (switchDefault)
187 if (numCase == NULL) {
188 numCase = cast<ConstantInt>(
189 ConstantInt::get(switchI->getCondition()->getType(),
190 llvm::cryptoutils->scramble32(
191 switchI->getNumCases() - 1, scrambling_key)));
192 }
193
194 //更新 switchVar,并且跳转到循环尾部
195 new StoreInst(numCase, load->getPointerOperand(), i); //store 给%switchVar 赋值为 numCase
196 BranchInst::Create(loopEnd, i); //插入 br label %loopEnd
197 continue;
198 }
199
200 //If it's a conditional jump //有两个后续块,也就是条件跳转,successor 对象就是后续块的意思
201 if (i->getTerminator()->getNumSuccessors() == 2) {
202 //获取接下来的 case 分支
203 ConstantInt *numCaseTrue =
204 switchI->findCaseDest(i->getTerminator()->getSuccessor(0));
205 ConstantInt *numCaseFalse =
206 switchI->findCaseDest(i->getTerminator()->getSuccessor(1));
207
208 //检查 next case 和 default case (switchDefault) 是否相等
209 if (numCaseTrue == NULL) {
210 numCaseTrue = cast<ConstantInt>(numCaseTrue = cast<ConstantInt>(
211 ConstantInt::get(switchI->getCondition()->getType(),
212 llvm::cryptoutils->scramble32(
213 switchI->getNumCases() - 1, scrambling_key)));
214 }
215
216 if (numCaseFalse == NULL) {
217 numCaseFalse = cast<ConstantInt>(
218 ConstantInt::get(switchI->getCondition()->getType(),
219 llvm::cryptoutils->scramble32(
220 switchI->getNumCases() - 1, scrambling_key)));
221 }
222
223 // Create a SelectInst %1 = select i1 %cmp, i32 117441206, i32 -880348549
224 BranchInst *br = cast<BranchInst>(i->getTerminator());
225 SelectInst *sel =
226 SelectInst::Create(br->getCondition(), numCaseTrue, numCaseFalse, "",
227 i->getTerminator());
228
229 //清除终止符
230 i->getTerminator()->eraseFromParent();
231
232 //更新 switchVar,并且跳转到循环尾部
233 new StoreInst(sel, load->getPointerOperand(), i);
234 BranchInst::Create(loopEnd, i);
235 continue;
236 }
237 }
238
239 fixStack(f);
(lldb) po f->dump() ; Function Attrs: noinline nounwind ssp uwtable
define i32 @add(i32 %num1, i32 %num2) #0 {entry:
%.reg2mem = alloca i32
%retval = alloca i32, align 4
%num1.addr = alloca i32, align 4
%num2.addr = alloca i32, align 4
%num3 = alloca i32, align 4
store i32 %num1, i32* %num1.addr, align 4
store i32 %num2,i32* %num2.addr, align 4
%0 = load i32, i32* %num1.addr, align 4
store i32 %0, i32* %.reg2mem
%switchVar = alloca i32
store i32 1207049111, i32* %switchVar
br label %loopEntryloopEntry: ; preds = %entry, %loopEnd
%switchVar1 = load i32, i32* %switchVar
switch i32 %switchVar1, label %switchDefault [
i32 1207049111, label %first
i32 -677357051, label %if.then
i32 -1251090459, label %if.end
i32 1194405227, label %return
]switchDefault: ; preds = %loopEntry
br label %loopEndfirst: ; preds = %loopEntry
%.reload = load volatile i32, i32* %.reg2mem
%cmp = icmp ne i32 %.reload, 100
%1 = select i1 %cmp, i32 -677357051, i32 -1251090459
store i32 %1, i32* %switchVar
br label %loopEndif.then: ; preds = %loopEntry
store i32 0, i32* %retval, align 4
store i32 1194405227, i32* %switchVar
br label %loopEndif.end: ; preds = %loopEntry
%2 = load i32, i32* %num1.addr, align 4
%3 = load i32, i32* %num2.addr, align 4
%add = add nsw i32 %2, %3
store i32 %add, i32* %num3, align 4
%4 = load i32, i32* %num3, align 4
store i32 %4, i32* %retval, align 4
store i32 1194405227, i32* %switchVar
br label %loopEndreturn: ; preds = %loopEntry
%5 = load i32, i32* %retval, align 4
ret i32 %5loopEnd: ; preds = %if.end,%if.then, %first, %switchDefault
br label %loopEntry}
first
、if.then
、if.end
这三个BasicBlock
的代码都会更新switchVar
,这样便达到了混淆代码逻辑的效果,并且不影响真实的程序逻辑。至此,整个代码扁平化的过程全部分析完。三
总结
看雪ID:ElainaDaemon
https://bbs.kanxue.com/user-home-945395.htm
# 往期推荐
2、恶意木马历险记
球分享
球点赞
球在看
点击阅读原文查看更多