思路
间接跳转混淆,就是将汇编的跳转或者跳转指令,变化为通过值计算得到地址,再通过jmp 寄存器的形式的混淆
在现在Agent的能力下,其实单纯的CFF已很容易被分析了,但是遇到复杂的间接跳转控制流时,表现得还是不尽人意

其实无论对于间接混淆还是别的混淆,最关键的一点就是要保证执行流的正常,并在此基础上做调整。
我的方案是·,对于每一个块都有一个key,获取出加密后的后继偏移地址,然后经过一个key XOR解密拿到真实偏移,再加上当前BasicBlock的地址,获得后继真实地址后进行跳转
预处理
那么老套路,我们第一步要做的,就是先获取每一个BasicBlock的后继,判断是直接跳转还是条件跳转(这里仍然暂时不考虑PHI和SwitchInst,因为最终可以降级处理)
先给每一个BasicBlock赋一个Key,毕竟我们不可能直接明文存储后继
这里我用了llvm独有的DenseMap,在处理llvm对象的时候似乎更有优势
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| std::vector<BasicBlock*> orignalBasicBlocks; llvm::DenseMap<BasicBlock*, uint64_t> basicBlockKey; llvm::DenseSet<uint64_t> usedKeySet;
for(BasicBlock& bb: F) { orignalBasicBlocks.push_back(&bb); }
std::default_random_engine e; std::uniform_int_distribution<uint64_t> u(0,0xFFFFFFFFFFFFFFFF); e.seed(time(0));
for(BasicBlock* BB: orignalBasicBlocks) { uint64_t RandomSwitchVal = u(e); while(usedKeySet.find(RandomSwitchVal) != usedKeySet.end()) { RandomSwitchVal = u(e); } usedKeySet.insert(RandomSwitchVal); basicBlockKey[BB] = RandomSwitchVal; }
|
先处理并存储各Successor的地址
这里注意要跳过entryBlock,因为在llvm pass中它的地址是不能拿来运算的,编译时会直接报错
也就是说默认情况下,entryBlock只能保持原有的形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| BasicBlock* bb = orignalBasicBlocks[i]; Instruction* terminator = bb->getTerminator(); if(bb == entryBasicBlock) { continue; }
if(isa<ReturnInst>(terminator)) { continue; }
auto* Br = dyn_cast<BranchInst>(terminator); if(!Br) { continue; }
|
然后是对地址值的计算,可以看到获取BlockAddress对象后,还需要通过ConstantExpr::getPtrToInt转化为Constant后才能参与运算
经过多次尝试,这里的存储必要要存放在全局变量,如果存放在栈上,编译出来后将不会看到预计算好的结果,而是它把地址的计算式完整的保留了下来,后继地址直接暴露,起不到任何保护效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| if(Br->isUnconditional()) { BasicBlock* successorBlock = terminator->getSuccessor(0);
BlockAddress* successorBlockAddress = BlockAddress::get(successorBlock); BlockAddress* curBlockAddress = BlockAddress::get(bb); IRBuilder<> builder(bb); Constant* curBlockAddressConstant = ConstantExpr::getPtrToInt(curBlockAddress, builder.getInt64Ty()); Constant* successorBlockAddressConstant = ConstantExpr::getPtrToInt(successorBlockAddress, builder.getInt64Ty()); Constant* subKey = ConstantExpr::getSub(successorBlockAddressConstant, curBlockAddressConstant); Constant* xorKey = ConstantInt::get(builder.getInt64Ty(), basicBlockKey[bb]); Constant* secretKey = ConstantExpr::getXor(subKey, xorKey); GlobalVariable* globalSubKey = new GlobalVariable(*M, builder.getInt64Ty(), true, GlobalValue::InternalLinkage,secretKey, "subKey" ); successorsSubKeyMap[bb].push_back(globalSubKey);
}
|
这里对于条件跳转的处理也类似
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
| else { BasicBlock* trueSuccessorBlock = terminator->getSuccessor(0); BasicBlock* falseSuccessorBlock = terminator->getSuccessor(1);
BlockAddress* trueSuccessorBlockAddress = BlockAddress::get(trueSuccessorBlock); BlockAddress* falseSuccessorBlockAddress = BlockAddress::get(falseSuccessorBlock);
BlockAddress* curBlockAddress = BlockAddress::get(bb); IRBuilder<> builder(bb); Constant* curBlockAddressConstant = ConstantExpr::getPtrToInt(curBlockAddress, builder.getInt64Ty()); Constant* xorKey = ConstantInt::get(builder.getInt64Ty(), basicBlockKey[bb]);
Constant* trueSuccessorBlockAddressConstant = ConstantExpr::getPtrToInt(trueSuccessorBlockAddress, builder.getInt64Ty()); Constant* trueSubKey = ConstantExpr::getSub(trueSuccessorBlockAddressConstant, curBlockAddressConstant); Constant* trueSecretKey = ConstantExpr::getXor(trueSubKey, xorKey);
GlobalVariable* globalTrueSubKey = new GlobalVariable(*M, builder.getInt64Ty(), true, GlobalValue::InternalLinkage,trueSecretKey, "trueSubKey" ); successorsSubKeyMap[bb].push_back(globalTrueSubKey);
Constant* falseSuccessorBlockAddressConstant = ConstantExpr::getPtrToInt(falseSuccessorBlockAddress, builder.getInt64Ty()); Constant* falseSubKey = ConstantExpr::getSub(falseSuccessorBlockAddressConstant, curBlockAddressConstant); Constant* falseSecretKey = ConstantExpr::getXor(falseSubKey, xorKey);
GlobalVariable* globalFalseSubKey = new GlobalVariable(*M, builder.getInt64Ty(), true, GlobalValue::InternalLinkage,falseSecretKey, "falseSubKey" ); successorsSubKeyMap[bb].push_back(globalFalseSubKey);
}
|
块处理
上面已经处理好了偏移的计算表达式,并已经存放到全局变量的中了,那么记下来就是取值并计算再跳转的过程
这里要十分注意的是,虽然我们已经通过CreateIndirectBr为块建立了一个terminator指令,但直接编译还是会报错,因为对于编译器来说,它不清楚你的这个计算地址是否有效。
因为需要用indirectInst->addDestination(successorBlock)明确指向一个后继。
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
| if(Br->isUnconditional()) { BasicBlock* successorBlock = terminator->getSuccessor(0); BlockAddress* curBlockAddress = BlockAddress::get(bb); Value* basicBlockAddressValue = builder.CreatePtrToInt(curBlockAddress, builder.getInt64Ty());
GlobalVariable* globalSubKey = successorsSubKeyMap[bb][0]; Value* loadSecretKey = builder.CreateLoad(Type::getInt64Ty(ctx), globalSubKey); Value* xorKey = ConstantInt::get(builder.getInt64Ty(), basicBlockKey[bb]); Value* loadSubKey = builder.CreateXor(loadSecretKey, xorKey); Value* realBlockAddressValue = builder.CreateAdd(basicBlockAddressValue, loadSubKey); Value* realBlockAddress = builder.CreateIntToPtr(realBlockAddressValue, builder.getInt8PtrTy()); IndirectBrInst* indirectInst = builder.CreateIndirectBr(realBlockAddress, 1);
indirectInst->addDestination(successorBlock); terminator->eraseFromParent();
}
|
然后条件跳转也类似,唯一不同的就是,从中取出true和false的不同加密偏移,要添加一个当前块Condition下的SelectInst,来选择需要解密的偏移。
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
| else { BasicBlock* trueSuccessorBlock = terminator->getSuccessor(0); BasicBlock* falseSuccessorBlock = terminator->getSuccessor(1); BlockAddress* curBlockAddress = BlockAddress::get(bb);
Value* basicBlockAddressValue = builder.CreatePtrToInt(curBlockAddress, builder.getInt64Ty()); GlobalVariable* globalTrueSubKey = successorsSubKeyMap[bb][0]; GlobalVariable* globalFalseSubKey = successorsSubKeyMap[bb][1];
Value* loadTrueSubKey = builder.CreateLoad(builder.getInt64Ty(), globalTrueSubKey); Value* loadFalseSubKey = builder.CreateLoad(builder.getInt64Ty(), globalFalseSubKey); Value* condition = Br->getCondition(); Value* loadSecretKey = builder.CreateSelect(condition, loadTrueSubKey, loadFalseSubKey); Constant* xorKey = ConstantInt::get(builder.getInt64Ty(), basicBlockKey[bb]); Value* loadSubKey = builder.CreateXor(loadSecretKey, xorKey); Value* realBlockAddressValue = builder.CreateAdd(basicBlockAddressValue, loadSubKey); Value* realBlockAddress = builder.CreateIntToPtr(realBlockAddressValue, builder.getInt8PtrTy()); IndirectBrInst* indirectInst = builder.CreateIndirectBr(realBlockAddress, 2);
indirectInst->addDestination(trueSuccessorBlock); indirectInst->addDestination(falseSuccessorBlock); terminator->eraseFromParent();
continue; }
|