找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 280|回复: 0

OLLVM学习之六 —— 指定模块和函数操作

[复制链接]
发表于 2024-9-22 20:14:06 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
本帖最后由 lichao 于 2024-10-16 11:00 编辑

前言

  在很多实际项目中, 由于以下原因无法对整个项目完全混淆, 实际操作时, 常常需要根据业务敏感程度使用不同程度的混淆, 比如攻防模块多用一些混淆:

  • 项目较大, 依赖较多, 或使用了很多header-only的库, 混淆了很多不需要混淆的代码, 导致编译出来的二进制过大
  • 项目较大, 依赖较多, 使用了平坦化(或其他方式)混淆了很多不需要混淆的代码, 导致编译极其缓慢, Ollvm比较耗内存
  • 混淆了复杂算法, 导致运行时耗时比正常大很多, 一般使用平坦化后耗时会增加10%以上
  • 混淆过多可能不允许上架AppStore, GooglePlay等

模块及函数混淆开关

  不同的Ollvm采用的方式大同小异, 无非是以下几种:

  • 对需要混淆的模块单独指定命令行参数, 如-llvm -fla, 这种方式兼容所有支持llvm命令行参数的编译器前端
  • 使用环境变量指定混淆参数
  • 对需要混淆的函数指定注解, 如__attribute((__annotate__(("fla"))))(新式语法[[clang::annotate("fla")]]), 这种方式仅支持C/C++, Objective-C和其他语言均不支持
  • 对需要混淆的函数指定标记函数, 如下所示, 这种方式支持Objective-C
extern void hikari_fla(void);
@implementation foo2:NSObject
+(void)foo{
  hikari_fla();
  NSLog(@"FOOOO2");
}
@end
  • 使用配置文件来指定需要混淆的函数和模块, 这种方式兼容所有编译器前端, 用于解决前几种方式搞不定的情况

Ollvm命令行参数混淆开关

static cl:pt<bool> EnableFlattening("enable-cffobf", cl::init(false),
                                      cl::NotHidden,
                                      cl::desc("Enable Flattening."));

Ollvm函数注解混淆开关

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标记函数混淆开关

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>

回复

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-21 18:01 , Processed in 0.029786 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表