OLLVM学习之六 —— 指定模块和函数操作
本帖最后由 lichao 于 2024-10-16 11:00 编辑## 前言
  在很多实际项目中, 由于以下原因无法对整个项目完全混淆, 实际操作时, 常常需要根据业务敏感程度使用不同程度的混淆, 比如攻防模块多用一些混淆:
* 项目较大, 依赖较多, 或使用了很多header-only的库, 混淆了很多不需要混淆的代码, 导致编译出来的二进制过大
* 项目较大, 依赖较多, 使用了平坦化(或其他方式)混淆了很多不需要混淆的代码, 导致编译极其缓慢, Ollvm比较耗内存
* 混淆了复杂算法, 导致运行时耗时比正常大很多, 一般使用平坦化后耗时会增加10%以上
* 混淆过多可能不允许上架AppStore, GooglePlay等
## 模块及函数混淆开关
  不同的Ollvm采用的方式大同小异, 无非是以下几种:
* 对需要混淆的模块单独指定命令行参数, 如`-llvm -fla`, 这种方式兼容所有支持llvm命令行参数的编译器前端
* 使用环境变量指定混淆参数
* 对需要混淆的函数指定注解, 如`__attribute((__annotate__(("fla"))))`(新式语法`[]`), 这种方式仅支持C/C++, Objective-C和其他语言均不支持
* 对需要混淆的函数指定标记函数, 如下所示, 这种方式支持Objective-C
```ObjectiveC
extern void hikari_fla(void);
@implementation foo2:NSObject
+(void)foo{
hikari_fla();
NSLog(@"FOOOO2");
}
@end
```
* 使用配置文件来指定需要混淆的函数和模块, 这种方式兼容所有编译器前端, 用于解决前几种方式搞不定的情况
### Ollvm命令行参数混淆开关
```cpp
static cl::opt<bool> EnableFlattening("enable-cffobf", cl::init(false),
cl::NotHidden,
cl::desc("Enable Flattening."));
```
### Ollvm函数注解混淆开关
```cpp
std::string readAnnotate(Function *f) {
std::string annotation = "";
// Get annotation variable
GlobalVariable *glob =
f->getParent()->getGlobalVariable("llvm.global.annotations");
if (glob != NULL) {
// Get the array
if (ConstantArray *ca = dyn_cast<ConstantArray>(glob->getInitializer())) {
for (unsigned i = 0; i < ca->getNumOperands(); ++i) {
// Get the struct
if (ConstantStruct *cs = dyn_cast<ConstantStruct>(ca->getOperand(i))) {
if (ConstantExpr *expr = dyn_cast<ConstantExpr>(cs->getOperand(0))) {
// If it's a bitcast we can check if the annotation is concerning
// the current function
if (expr->getOpcode() == Instruction::BitCast && expr->getOperand(0) == f) {
ConstantExpr *note = cast<ConstantExpr>(cs->getOperand(1));
// If it's a GetElementPtr, that means we found the variable
// containing the annotations
if (note->getOpcode() == Instruction::GetElementPtr) {
if (GlobalVariable *annoteStr =
dyn_cast<GlobalVariable>(note->getOperand(0))) {
if (ConstantDataSequential *data =
dyn_cast<ConstantDataSequential>(
annoteStr->getInitializer())) {
if (data->isString()) {
annotation += data->getAsString().lower() + " ";
}
}
}
}
}
}
}
}
}
}
return annotation;
}
```
### Ollvm标记函数混淆开关
```cpp
bool readFlag(Function *f, std::string attribute) {
for (inst_iterator I = inst_begin(f); I != inst_end(f); I++) {
Instruction *Inst = &*I;
if (CallInst *CI = dyn_cast<CallInst>(Inst)) {
if (CI->getCalledFunction() != nullptr &&
CI->getCalledFunction()->getName().contains("hikari_" + attribute)) {
CI->eraseFromParent();
return true;
}
}
}
return false;
}
```
## 控制编译器优化
  由于现代编译器的卓越能力, 一些混淆手段很可能在Release下被还原导致实际未混淆, 这种情况下Ollvm项目以Debug编译反而能达到效果, 而笔者认为正确的解决方式为, 在静态区构造特殊数据, 用于关联待混淆常量及函数, 并将特殊数据指定为used避免被优化. 以下是Clang支持的针对函数和变量优化的语法, 对函数关闭优化可以同时防止内联, 对变量关闭优化可以防止其被优化成常量.
* `__attribute__((optnone))` 对函数关闭优化 (如果是Gcc可以指定优化等级)
* `#pragma clang optimize off` `#pragma clang optimize on` 对区间内的函数关闭优化
* `volatile` 对变量关闭优化
* `__attribute__((used))` 对全局变量关闭优化
未完待续,未来篇将会更新到本人github博客:<https://lich4.github.io>
页:
[1]