1 添加pass
在目录 lib\Transforms\Obfuscation 新建MyFla.cpp
2 什么是平坦控制流
说白了就是把原来的代码的基本块进行拆分 ,再用switch case打乱分发,把原本代码的逻辑连接起来。让逆向者不知道代码块的原本顺序。
3 编写一个测试demo
int myaddA(int a ,int b)
{
int i=10;
if(a % 2 == 0)
{
i=10;
}
else{
i=20;
}
return a+b+i;
}
没有平坦流的效果
平坦流后的效果
可以看到经过平坦流 该函数多了很多基本块!
pass 代码实现
上代码
bool MyFla::flatten(Function *f)
{
vector<BasicBlock *> origBB;
BasicBlock *loopEntry;
BasicBlock *loopEnd;
LoadInst *load;
SwitchInst *switchI;
AllocaInst *switchVar;
//生成随机数key case
char scrambling_key[16];
llvm::cryptoutils->get_bytes(scrambling_key, 16);
for (int i = 0; i < 16; i++)
{
errs() <<"scrambling_key "<< scrambling_key[i] << "\n";
}
int index = 0;
for (Function::iterator i = f->begin(); i != f->end(); ++i) {
BasicBlock *tmp = &*i;
index++;
errs() <<"baseblock "<< *tmp << "\n";
}
errs() <<"baseblock count "<< index << "\n";
//调用了一个外部Pass, LowerSwitch这个Pass, 主动调用了这个Pass的runOnFunction函数,
//内部逻辑是消除了当前函数中的switch方式组织的代码,抓换成if else这种分支调用,方便后面进行代码块分割,从而进行平坦化操作
errs() <<" ================================ " << "\n";
FunctionPass *lower = createLowerSwitchPass();
lower->runOnFunction(*f);
// 遍历函数所有BasicBlock,并保存到origBB 数组中
index = 0;
for (Function::iterator i = f->begin(); i != f->end(); ++i)
{
BasicBlock *tmp = &*i;
origBB.push_back(tmp);
index++;
//errs() <<"origBB "<< *tmp << "\n";
//末尾是一条Invoke指令 返回
/*
try {
foo();
} catch (MyError err)
{
}
IR
%4 = alloca %struct.MyError, align 1
invoke void @_Z3foov()
to label %5 unwind label %6
*/
BasicBlock *bb = &*i;
if (isa<InvokeInst>(bb->getTerminator())) {
return false;
}
}
errs() <<"origBB count "<< index << "\n";
//基本款太少 不平坦化
if (origBB.size() <= 1) {
return false;
}
// 去掉第一个基本块 第一块是函数的入口块,顺序不能乱
origBB.erase(origBB.begin());
//1 本函数第一个基本块的处理
Function::iterator tmp = f->begin(); //++tmp;
BasicBlock *insert = &*tmp;
errs() <<"firstblock "<< *insert << "\n";
// 如果是分支指令 br cmp
//getTerminator 如果区块结构完整,则返回终止指令,如果区块结构不完整,则返回空。
BranchInst *br = NULL;
if (isa<BranchInst>(insert->getTerminator()))
{
br = cast<BranchInst>(insert->getTerminator());
}
//isConditional 如果是有条件跳转分支指令 或者如果该跳转分支有两个后继块 则进行分割 比如 br i1 %3, label %4, label %13
if ((br != NULL && br->isConditional()) || insert->getTerminator()->getNumSuccessors() > 1)
{
BasicBlock::iterator i = insert->end();
--i;
if (insert->size() > 1)
{
--i;
}
//从倒数第二条指令 开始分割基本块
BasicBlock *tmpBB = insert->splitBasicBlock(i, "first");
errs() <<"firstblock tmpBB "<< *tmpBB << "\n";
origBB.insert(origBB.begin(), tmpBB);
}
// 脱离父节点的关系
insert->getTerminator()->eraseFromParent();
// 第一个基本块的处理 处理完毕
// 创建一个空的switch 语句 基本骨架
// 利用AllocaInst分配一个空间给switchVar
switchVar = new AllocaInst(Type::getInt32Ty(f->getContext()), 0, "switchVar", insert);
// 为switchVar变量赋一个伪随机值
new StoreInst( ConstantInt::get(Type::getInt32Ty(f->getContext()), llvm::cryptoutils->scramble32(0, scrambling_key)), switchVar, insert);
// 2 创建一个代码块loopEntry, 空代码块
loopEntry = BasicBlock::Create(f->getContext(), "loopEntry", f, insert);
loopEntry->setName("loopEntry");
loopEnd = BasicBlock::Create(f->getContext(), "loopEnd", f, insert); // 创建一个代码块loopEnd, 也是空代码块
loopEnd->setName("loopEntry");
load = new LoadInst(switchVar, "switchVar", loopEntry);
// 将这个基本块从当前函数中脱离,并将其插入loopEntry所在的函数中,并且移动到loopEntry之前。 insert块在前,loopEntry块在后;
insert->moveBefore(loopEntry);
// 在第一个基本块insert块末尾添加 无条件跳转到 loopEntry
BranchInst::Create(loopEntry, insert);
errs() <<"insertA "<< *insert << "\n";
// 无条件跳转到 loopEntry
BranchInst::Create(loopEntry, loopEnd);
//再创建一个switchDefault块 无条件跳转到 loopEnd
BasicBlock *swDefault = BasicBlock::Create(f->getContext(), "switchDefault", f, loopEnd);
BranchInst::Create(loopEnd, swDefault);
swDefault->setName("loopEntry");
errs() <<"swDefault "<< *swDefault << "\n";
errs() <<"loopEndA "<< *loopEnd << "\n";
// 增加一条switch指令 比如 switch(xxx)
switchI = SwitchInst::Create(&*f->begin(), swDefault, 0, loopEntry);
switchI->setCondition(load);
errs() <<"loopEntryA "<< *loopEntry << "\n";
// 第一个基本块脱离父节点的关系
f->begin()->getTerminator()->eraseFromParent();
//在该函数第一个基本块结尾 无条件跳转到loopEntry块指令。
BranchInst::Create(loopEntry, &*f->begin());
Function::iterator tmpA = f->begin();
BasicBlock *begin = &*tmpA;
//errs() <<"f->begin "<< *begin << "\n";
// 创建一个空的switch 语句 基本骨架 end
// 开始往switch骨架代码中填充剩余的BasicBlock
for (vector<BasicBlock *>::iterator b = origBB.begin(); b != origBB.end();
++b) {
BasicBlock *i = *b;
ConstantInt *numCase = NULL;
// 把当前的基本块移动到loopEnd之前 (only visual, no code logic)
i->moveBefore(loopEnd);
// 通过 scramble32 生成一个随机值 该值与aes的常量key有关
// 参数1 int 右移值 scrambling_key 异或的key
numCase = cast<ConstantInt>(ConstantInt::get( switchI->getCondition()->getType(), llvm::cryptoutils->scramble32(switchI->getNumCases(), scrambling_key)));
//errs() <<"numCase "<< numCase << "\n";
// 在switch 中 添加case语句 为每个基本块设置一个case值
switchI->addCase(numCase, i);
}
errs() <<"switchI"<< *switchI << "\n";
//基本骨架 逻辑处理完毕 但是只是一个空家骨架 switchVar 也没有再继续赋值 当前switch 还是一个死循环
// 3 处理switch case 逻辑
for (vector<BasicBlock *>::iterator b = origBB.begin(); b != origBB.end(); ++b)
{
BasicBlock *i = *b;
ConstantInt *numCase = NULL;
// 没有后继块直接 continue
if (i->getTerminator()->getNumSuccessors() == 0)
{
continue;
}
// 获取最后一条指令 如果br 该基本块只有一个后继块
if (i->getTerminator()->getNumSuccessors() == 1)
{
BasicBlock *succ = i->getTerminator()->getSuccessor(0);
BranchInst *brA = cast<BranchInst>(i->getTerminator());
//errs() <<"getTerminator br "<< *brA << "\n";
i->getTerminator()->eraseFromParent();
// 查找给定后继块的唯一caseVar
numCase = switchI->findCaseDest(succ);
//findCaseDest numCase 0xa1382f0
errs() <<"findCaseDest numCase "<< numCase << "\n";
// If next case == default case (switchDefault)
if (numCase == NULL) {
numCase = cast<ConstantInt>(
ConstantInt::get(switchI->getCondition()->getType(),
llvm::cryptoutils->scramble32(
switchI->getNumCases() - 1, scrambling_key)));
}
// 更新 switchVar 的值 即:switch = numCase(2015464572)等再跳转到loopEntry 里面根据switchVar能直接跳转到 succ块 label %24
new StoreInst(numCase, load->getPointerOperand(), i);
//无条件跳转到loopEnd
BranchInst::Create(loopEnd, i);
continue;
}
//如果有两个后继块
if (i->getTerminator()->getNumSuccessors() == 2) {
// 获取两个后继块
errs() <<"BasicBlock Parent double "<< *i << "\n";
ConstantInt *numCaseTrue = switchI->findCaseDest(i->getTerminator()->getSuccessor(0));
ConstantInt *numCaseFalse = switchI->findCaseDest(i->getTerminator()->getSuccessor(1));
errs() <<"BasicBlock doubleA "<< *i->getTerminator()->getSuccessor(0) << "\n";
errs() <<"BasicBlock doubleB "<< *i->getTerminator()->getSuccessor(1) << "\n";
// Check if next case == default case (switchDefault)
if (numCaseTrue == NULL)
{
numCaseTrue = cast<ConstantInt>( ConstantInt::get(switchI->getCondition()->getType(), llvm::cryptoutils->scramble32( switchI->getNumCases() - 1, scrambling_key)));
}
if (numCaseFalse == NULL)
{
numCaseFalse = cast<ConstantInt>( ConstantInt::get(switchI->getCondition()->getType(), llvm::cryptoutils->scramble32( switchI->getNumCases() - 1, scrambling_key)));
}
// 创建一个选择分支
//br->getCondition()为True跳转到numCaseTrue 否则跳转到numCaseFalse
BranchInst *br = cast<BranchInst>(i->getTerminator());
SelectInst *sel = SelectInst::Create(br->getCondition(), numCaseTrue, numCaseFalse, "", i->getTerminator());
// Erase terminator
i->getTerminator()->eraseFromParent();
new StoreInst(sel, load->getPointerOperand(), i);
BranchInst::Create(loopEnd, i);
continue;
}
//处理switch case 逻辑 完毕
}
fixStack(f);
return true;
}
pass 代码我都加了注释
下面解释一下代码具体实现流程
1 生成随机的key
既然是switch case打乱分发 ,case 的key自然少不了
//生成随机数key case
char scrambling_key[16];
llvm::cryptoutils->get_bytes(scrambling_key, 16);
2 遍历函数所有BasicBlock,并保存到origBB 数组中
这里保存是为了后续操作基本块做准备 ,
for (Function::iterator i = f->begin(); i != f->end(); ++i)
{
BasicBlock *tmp = &*i;
origBB.push_back(tmp);
index++;
//errs() <<"origBB "<< *tmp << "\n";
//末尾是一条Invoke指令 返回
/*
try {
foo();
} catch (MyError err)
{
}
IR
%4 = alloca %struct.MyError, align 1
invoke void @_Z3foov()
to label %5 unwind label %6
*/
BasicBlock *bb = &*i;
if (isa<InvokeInst>(bb->getTerminator())) {
return false;
}
}
errs() <<"origBB count "<< index << "\n";
//基本款太少 不平坦化
if (origBB.size() <= 1) {
return false;
}
// 去掉第一个基本块 第一块是函数的入口块,顺序不能乱
origBB.erase(origBB.begin());
//1 本函数第一个基本块的处理
Function::iterator tmp = f->begin(); //++tmp;
BasicBlock *insert = &*tmp;
errs() <<"firstblock "<< *insert << "\n";
// 如果是分支指令 br cmp
//getTerminator 如果区块结构完整,则返回终止指令,如果区块结构不完整,则返回空。
BranchInst *br = NULL;
if (isa<BranchInst>(insert->getTerminator()))
{
br = cast<BranchInst>(insert->getTerminator());
}
//isConditional 如果是有条件跳转分支指令 或者如果该跳转分支有两个后继块 则进行分割 比如 br i1 %3, label %4, label %13
if ((br != NULL && br->isConditional()) || insert->getTerminator()->getNumSuccessors() > 1)
{
BasicBlock::iterator i = insert->end();
--i;
if (insert->size() > 1)
{
--i;
}
//从倒数第二条指令 开始分割基本块
BasicBlock *tmpBB = insert->splitBasicBlock(i, "first");
errs() <<"firstblock tmpBB "<< *tmpBB << "\n";
origBB.insert(origBB.begin(), tmpBB);
}
// 脱离父节点的关系
insert->getTerminator()->eraseFromParent();
3 创建一个switch case
大致的逻辑就
1 创建一个控的switvh 语句
2 放入基本块
3 为每一个基本块都添加一个casevalue 这样就可以标记每个基本块了
; <label>:36: ; preds = %11, %22
store i32 10, i32* %6, align 4, !dbg !248
store i32 -1325218060, i32* %9 //添加casevalue
br label %45
// 创建一个空的switch 语句 基本骨架
/*
伪代码表示
begin第一基本块
{
XXXX
XXXX
XXXX
loopEntry()
}
int switchVar
loopEntry()
{
switch(switchVar)
{
case 3:
origBB[0]
case 4:
origBB[1]
....
switchDefault:
loopEnd()
break;
}
}
loopEnd()
{
loopEntry()
}
*/
// 利用AllocaInst分配一个空间给switchVar
switchVar = new AllocaInst(Type::getInt32Ty(f->getContext()), 0, "switchVar", insert);
// 为switchVar变量赋一个伪随机值
new StoreInst( ConstantInt::get(Type::getInt32Ty(f->getContext()), llvm::cryptoutils->scramble32(0, scrambling_key)), switchVar, insert);
// 2 创建一个代码块loopEntry, 空代码块
loopEntry = BasicBlock::Create(f->getContext(), "loopEntry", f, insert);
loopEntry->setName("loopEntry");
loopEnd = BasicBlock::Create(f->getContext(), "loopEnd", f, insert); // 创建一个代码块loopEnd, 也是空代码块
loopEnd->setName("loopEntry");
load = new LoadInst(switchVar, "switchVar", loopEntry);
// 将这个基本块从当前函数中脱离,并将其插入loopEntry所在的函数中,并且移动到loopEntry之前。 insert块在前,loopEntry块在后;
insert->moveBefore(loopEntry);
// 在第一个基本块insert块末尾添加 无条件跳转到 loopEntry
BranchInst::Create(loopEntry, insert);
// 无条件跳转到 loopEntry
BranchInst::Create(loopEntry, loopEnd);
//再创建一个switchDefault块 无条件跳转到 loopEnd
BasicBlock *swDefault = BasicBlock::Create(f->getContext(), "switchDefault", f, loopEnd);
BranchInst::Create(loopEnd, swDefault);
swDefault->setName("loopEntry");
errs() <<"swDefault "<< *swDefault << "\n";
errs() <<"loopEndA "<< *loopEnd << "\n";
// 增加一条switch指令 比如 switch(xxx)
switchI = SwitchInst::Create(&*f->begin(), swDefault, 0, loopEntry);
switchI->setCondition(load);
// 第一个基本块脱离父节点的关系
f->begin()->getTerminator()->eraseFromParent();
//在该函数第一个基本块结尾 无条件跳转到loopEntry块指令。
BranchInst::Create(loopEntry, &*f->begin());
Function::iterator tmpA = f->begin();
BasicBlock *begin = &*tmpA;
//errs() <<"f->begin "<< *begin << "\n";
// 创建一个空的switch 语句 基本骨架 end
// 开始往switch骨架代码中填充剩余的BasicBlock
for (vector<BasicBlock *>::iterator b = origBB.begin(); b != origBB.end();
++b) {
BasicBlock *i = *b;
ConstantInt *numCase = NULL;
// 把当前的基本块移动到loopEnd之前 (only visual, no code logic)
i->moveBefore(loopEnd);
// 通过 scramble32 生成一个随机值
// 参数1 int 右移值 scrambling_key 异或的key
numCase = cast<ConstantInt>(ConstantInt::get( switchI->getCondition()->getType(), llvm::cryptoutils->scramble32(switchI->getNumCases(), scrambling_key)));
//errs() <<"numCase "<< numCase << "\n";
// 在switch 中 添加case语句 为每个基本块设置一个case值
switchI->addCase(numCase, i);
}
errs() <<"switchI"<< *switchI << "\n";
//基本骨架 逻辑处理完毕 但是只是一个空家骨架 switchVar 也没有再继续赋值 当前switch 还是一个死循环
打印switch IR 如下
switchI switch i32 %12, label %13 [
i32 440379866, label %14
i32 1930663248, label %16
i32 471975823, label %18
i32 1974666478, label %20
i32 -1329156448, label %22
i32 -664800156, label %24
i32 1746943394, label %26
i32 253656417, label %28
i32 -247427635, label %30
i32 1081079450, label %31
i32 -2031450923, label %32
i32 -866414378, label %36
i32 -1610036424, label %37
i32 -595102341, label %38
i32 -796366667, label %44
]
4 处理switch 基本块br逻辑
如果有的基本块遇到br逻辑具体要怎么处理这些逻辑呢 ?
因为如果不处理br 就跳转走了,我们的case就控制不了分发 ,就违背了我们原来的意思 对吧!
处理方式://伪代码表现形式
//伪代码表现形式
int switchVar
loopEntry()
{
switch(switchVar)
{
case 3:
origBB[0]
if have 'br'
switchVar = 4
case 4:
origBB[1]
if have 'br'
switchVar = 5
....
switchDefault:
loopEnd()
break;
}
}
loopEnd()
{
loopEntry()
}
处理逻辑 就是 把基本块 最后的br 脱离一下不让函数自己去跳转 而是根据我们的case 去跳转
比如 有个基本块 后面跟着一个br 执行完毕之后 br会跳转到 ; <label>:24:
; <label>:20: ; preds = %11, %18
store i32 10, i32* %5, align 4, !dbg !249
br label %24, !dbg !251
这时候我们就要处理一下 先脱离
i->getTerminator()->eraseFromParent();
; <label>:20: ; preds = %11, %18
store i32 10, i32* %5, align 4, !dbg !249
以上是脱离之后的效果 可以看到 br 已经没有了 那原本的br label %24 ,
那之后要怎么处理呢?
通过一下代码 我们在 基本块的最后添加一个 casevaluse 让switch去处理
new StoreInst(numCase, load->getPointerOperand(), i);
添加casevalue后的IR代码
; <label>:20: ; preds = %11, %18
store i32 10, i32* %5, align 4, !dbg !249
store i32 -664800156, i32* %10
前面我们也提到在在创建的switch的时候就已经为每个基本块添加了casevalue了
记住这个值 -664800156其实他在我们创建的时候已经跟br label %24 关联上了
// 3 处理switch case 逻辑
for (vector<BasicBlock *>::iterator b = origBB.begin(); b != origBB.end(); ++b)
{
BasicBlock *i = *b;
ConstantInt *numCase = NULL;
// 没有后继块直接 continue
if (i->getTerminator()->getNumSuccessors() == 0)
{
continue;
}
// 获取最后一条指令 如果br 该基本块只有一个后继块
if (i->getTerminator()->getNumSuccessors() == 1)
{
BasicBlock *succ = i->getTerminator()->getSuccessor(0);
BranchInst *brA = cast<BranchInst>(i->getTerminator());
i->getTerminator()->eraseFromParent();
errs() <<"succ "<< *succ << "\n";
// 查找给定后继块的唯一caseVar
numCase = switchI->findCaseDest(succ);
//findCaseDest numCase 0xa1382f0
errs() <<"findCaseDest numCase "<< numCase << "\n";
// If next case == default case (switchDefault)
if (numCase == NULL) {
numCase = cast<ConstantInt>(
ConstantInt::get(switchI->getCondition()->getType(),
llvm::cryptoutils->scramble32(
switchI->getNumCases() - 1, scrambling_key)));
}
// 更新 switchVar 的值 即:switch = numCase(2015464572)等再跳转到loopEntry 里面根据switchVar能直接跳转到 succ块 label %24
new StoreInst(numCase, load->getPointerOperand(), i);
errs() <<"add numCase block "<< *i << "\n";
//无条件跳转到loopEnd
BranchInst::Create(loopEnd, i);
continue;
}
//如果有两个后继块
if (i->getTerminator()->getNumSuccessors() == 2) {
// 获取两个后继块
errs() <<"BasicBlock Parent double "<< *i << "\n";
ConstantInt *numCaseTrue = switchI->findCaseDest(i->getTerminator()->getSuccessor(0));
ConstantInt *numCaseFalse = switchI->findCaseDest(i->getTerminator()->getSuccessor(1));
errs() <<"BasicBlock doubleA "<< *i->getTerminator()->getSuccessor(0) << "\n";
errs() <<"BasicBlock doubleB "<< *i->getTerminator()->getSuccessor(1) << "\n";
// Check if next case == default case (switchDefault)
if (numCaseTrue == NULL)
{
numCaseTrue = cast<ConstantInt>( ConstantInt::get(switchI->getCondition()->getType(), llvm::cryptoutils->scramble32( switchI->getNumCases() - 1, scrambling_key)));
}
if (numCaseFalse == NULL)
{
numCaseFalse = cast<ConstantInt>( ConstantInt::get(switchI->getCondition()->getType(), llvm::cryptoutils->scramble32( switchI->getNumCases() - 1, scrambling_key)));
}
// 创建一个选择分支
//br->getCondition()为True跳转到numCaseTrue 否则跳转到numCaseFalse
BranchInst *br = cast<BranchInst>(i->getTerminator());
SelectInst *sel = SelectInst::Create(br->getCondition(), numCaseTrue, numCaseFalse, "", i->getTerminator());
// Erase terminator
i->getTerminator()->eraseFromParent();
new StoreInst(sel, load->getPointerOperand(), i);
BranchInst::Create(loopEnd, i);
continue;
}
//处理switch case 逻辑 完毕