lichao 发表于 2024-9-22 20:14:06

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;
}
```

## 控制编译器优化

&emsp;&emsp;由于现代编译器的卓越能力, 一些混淆手段很可能在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]
查看完整版本: OLLVM学习之六 —— 指定模块和函数操作