-
-
[原创]OLLVM (一)字符串混淆
-
发表于: 2天前 173
-
前言
最近学习了OLLVM,代码工程链接。我对原有代码添加了注释并做了代码分析。
目标
- 对全局字符串加密。
- 对使用全局变量的全局变量加密。
字符串混淆步骤
所谓的字符串混淆就是字符串加密。步骤:
遍历模块内的所有全局字符串
- 识别到全局字符串后对其加密
- 构建对应的解密函数
- 创建解密后字符串的存储位置,如:
@dec0.str
、@dec1.str.1
- 创建字符串解密后的状态标志,如:
@dec_status_0.str
、@dec_status_1.str.1
对使用全局变量的全局变量加密,然后构建对应的解密函数
创建加密字符串表
EncryptedStringTable
,存储所有加密的字符串遍历模块内的所有命令,替换 全局变量和使用它的全局变量 为解密相关代码
替换 解密函数内的全局变量 为解密相关代码
删除未使用的全局变量
混淆后代码解释
; ModuleID = '/Users/xx/Documents/Codes/llvm-build/test/OllvmTest/calculate.cpp' source_filename = "/Users/xx/Documents/Codes/llvm-build/test/OllvmTest/calculate.cpp" target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128-Fn32" target triple = "arm64-apple-macosx15.0.0" ; 定义一个结构体类型,包含一个整型和一个指针 %struct.StructTest = type { i32, ptr } ; 定义全局变量,用于存储解密后的字符串 @dec0.str = private global [31 x i8] zeroinitializer, align 1 ; 解密字符串缓冲区1 @dec_status_0.str = private global i32 0 ; 解密状态标志1 @dec1.str.1 = private global [20 x i8] zeroinitializer, align 1 ; 解密字符串缓冲区2 @dec_status_1.str.1 = private global i32 0 ; 解密状态标志2 @dec__ZL11struct_test = private global %struct.StructTest zeroinitializer, align 8 ; 解密后的结构体实例 @dec_status__ZL11struct_test = private global i32 0 ; 结构体解密状态标志 ; 加密字符串表,存储所有加密的字符串 @EncryptedStringTable = private global [141 x i8] c"\E3pjW;\F7X \87l\93\F0\D7\9C\18{$\14\95;\EB\E3\D0\D05\01\8B\F1e\7F1\7F\0A\CB\A1\CD\B6\91P\BE\CE7\BD\22\A4\FA;\F30'\1EM*&=\82~a\9D\B7-m\C8`\1An-m\BF R\9Dw\92\F5\B2f7\B0D-\BD\F1p\F4\17\85\F1:\B0\B7,\9E\02\D0C\1B\13\AA\02\B2\CD\C6\E39\BE\9D\AE\1FL\06\02H\C4\A0U\A0\EEj\92\BE\8C\FD\19\FB'(,,&'\18kl\18\19?\9Dg\82\F5" ; 计算函数,根据操作符对两个双精度浮点数进行运算 ; Function Attrs: mustprogress noinline optnone ssp uwtable(sync) define noundef double @_Z9calculateddc(double noundef %a, double noundef %b, i8 noundef signext %op) #0 { entry: %retval = alloca double, align 8 ; 返回值存储 %a.addr = alloca double, align 8 ; 参数a存储 %b.addr = alloca double, align 8 ; 参数b存储 %op.addr = alloca i8, align 1 ; 操作符存储 store double %a, ptr %a.addr, align 8 store double %b, ptr %b.addr, align 8 store i8 %op, ptr %op.addr, align 1 ; 解密并打印调试信息 %0 = load double, ptr %a.addr, align 8 %1 = load double, ptr %b.addr, align 8 %2 = load i8, ptr %op.addr, align 1 %conv = sext i8 %2 to i32 ; 解密字符串 call void @goron_decrypt_string_0(ptr @dec0.str, ptr getelementptr inbounds ([141 x i8], ptr @EncryptedStringTable, i32 0, i32 27)) %call = call i32 (ptr, ...) @printf(ptr noundef @dec0.str, double noundef %0, double noundef %1, i32 noundef %conv) ; 根据操作符进行不同的运算 %3 = load i8, ptr %op.addr, align 1 %conv1 = sext i8 %3 to i32 switch i32 %conv1, label %sw.default [ i32 43, label %sw.bb ; '+' 加法 i32 45, label %sw.bb2 ; '-' 减法 i32 42, label %sw.bb3 ; '*' 乘法 i32 47, label %sw.bb4 ; '/' 除法 ] sw.bb: ; 加法分支 %4 = load double, ptr %a.addr, align 8 %5 = load double, ptr %b.addr, align 8 %add = fadd double %4, %5 store double %add, ptr %retval, align 8 br label %return sw.bb2: ; 减法分支 %6 = load double, ptr %a.addr, align 8 %7 = load double, ptr %b.addr, align 8 %sub = fsub double %6, %7 store double %sub, ptr %retval, align 8 br label %return sw.bb3: ; 乘法分支 %8 = load double, ptr %a.addr, align 8 %9 = load double, ptr %b.addr, align 8 %mul = fmul double %8, %9 store double %mul, ptr %retval, align 8 br label %return sw.bb4: ; 除法分支 %10 = load double, ptr %b.addr, align 8 %cmp = fcmp une double %10, 0.000000e+00 ; 检查除数是否为0 br i1 %cmp, label %if.then, label %if.else if.then: ; 除数不为0的情况 %11 = load double, ptr %a.addr, align 8 %12 = load double, ptr %b.addr, align 8 %div = fdiv double %11, %12 store double %div, ptr %retval, align 8 br label %return if.else: ; 除数为0的情况 store double -1.000000e+00, ptr %retval, align 8 ; 返回-1表示错误 br label %return sw.default: ; 默认情况(无效操作符) store double -1.000000e+00, ptr %retval, align 8 ; 返回-1表示错误 br label %return return: ; 返回处理 %13 = load double, ptr %retval, align 8 ret double %13 } ; 标准printf函数声明 declare i32 @printf(ptr noundef, ...) #1 ; 主函数 ; Function Attrs: mustprogress noinline norecurse optnone ssp uwtable(sync) define noundef i32 @main(i32 noundef %argc, ptr noundef %argv) #2 { entry: %retval = alloca i32, align 4 %argc.addr = alloca i32, align 4 %argv.addr = alloca ptr, align 8 store i32 0, ptr %retval, align 4 store i32 %argc, ptr %argc.addr, align 4 store ptr %argv, ptr %argv.addr, align 8 ; 初始化全局结构体变量 call void @__global_variable_initializer__ZL11struct_test(ptr @dec__ZL11struct_test) ; 解密并打印结构体中的字符串 ; 解密后获得字符串类型的可变参数 %0 = getelementptr inbounds %struct.StructTest, ptr @dec__ZL11struct_test, i32 0, i32 1 %1 = load ptr, ptr %0, align 8 ; 解密后获得打印格式化字符串 call void @goron_decrypt_string_1(ptr @dec1.str.1, ptr getelementptr inbounds ([141 x i8], ptr @EncryptedStringTable, i32 0, i32 100)) ; 打印 %call = call i32 (ptr, ...) @printf(ptr noundef @dec1.str.1, ptr noundef %1) ; 测试计算函数 %call1 = call noundef double @_Z9calculateddc(double noundef 0.000000e+00, double noundef 0.000000e+00, i8 noundef signext 47) ; 0/0 %call2 = call noundef double @_Z9calculateddc(double noundef 1.000000e+00, double noundef 2.000000e+00, i8 noundef signext 47) ; 1/2 %call3 = call noundef double @_Z9calculateddc(double noundef 1.000000e+00, double noundef 2.000000e+00, i8 noundef signext 43) ; 1+2 %call4 = call noundef double @_Z9calculateddc(double noundef 1.000000e+00, double noundef 2.000000e+00, i8 noundef signext 45) ; 1-2 ret i32 0 } ; 字符串解密函数0 define private void @goron_decrypt_string_0(ptr nocapture %plain_string, ptr nocapture %data) { Enter: %0 = getelementptr inbounds i8, ptr %data, i32 17 ; 获取加密数据起始位置 %1 = load i32, ptr @dec_status_0.str, align 4 ; 检查解密状态 %2 = icmp eq i32 %1, 1 ; 如果已解密则跳过 br i1 %2, label %Exit, label %LoopBody.preheader LoopBody.preheader: ; 解密循环开始 br label %LoopBody LoopBody: ; 解密循环主体 %3 = phi i32 [ %23, %LoopEnd ], [ 0, %LoopBody.preheader ] ; 循环计数器 %4 = phi i8 [ %21, %LoopEnd ], [ 0, %LoopBody.preheader ] ; 解密中间值 %5 = getelementptr inbounds i8, ptr %0, i32 %3 ; 获取当前加密字符位置 %6 = load volatile i8, ptr %5, align 1 ; 加载加密字符 %7 = urem i32 %3, 17 ; 计算密钥索引 %8 = getelementptr inbounds i8, ptr %data, i32 %7 ; 获取密钥字符 %9 = load i8, ptr %8, align 1 ; 加载密钥字符 %10 = zext nneg i8 %9 to i32 ; 密钥字符转为整数 %11 = mul nuw nsw i32 %7, %10 ; 计算解密参数 %12 = and i32 %11, 1 ; 判断解密路径 %13 = icmp eq i32 %12, 0 br i1 %13, label %LoopBr0, label %LoopBr1 LoopBr0: ; 解密路径0 %14 = add i8 %6, %4 ; 加法解密步骤 %15 = xor i8 %14, %9 ; 异或解密步骤 %16 = xor i8 %15, -1 ; 取反解密步骤 br label %LoopEnd LoopBr1: ; 解密路径1 %17 = sub i8 %6, %4 ; 减法解密步骤 %18 = xor i8 %17, %9 ; 异或解密步骤 %19 = sub i8 0, %18 ; 取负解密步骤 br label %LoopEnd LoopEnd: ; 解密循环结束处理 %20 = phi i8 [ %16, %LoopBr0 ], [ %19, %LoopBr1 ] ; 选择解密结果 %21 = xor i8 %20, %9 ; 最终异或操作 %22 = getelementptr inbounds i8, ptr %plain_string, i32 %3 ; 获取结果存储位置 store i8 %21, ptr %22, align 1 ; 存储解密字符 %23 = add nuw nsw i32 %3, 1 ; 计数器递增 %24 = icmp eq i32 %23, 31 ; 检查循环结束条件 br i1 %24, label %UpdateDecStatus, label %LoopBody UpdateDecStatus: ; 更新解密状态 store i32 1, ptr @dec_status_0.str, align 4 ; 标记为已解密 br label %Exit Exit: ; 函数退出 ret void } ; 字符串解密函数1 (与解密函数0类似,参数不同) define private void @goron_decrypt_string_1(ptr nocapture %plain_string, ptr nocapture %data) { Enter: %0 = getelementptr inbounds i8, ptr %data, i32 21 ; 不同的偏移量 %1 = load i32, ptr @dec_status_1.str.1, align 4 %2 = icmp eq i32 %1, 1 br i1 %2, label %Exit, label %LoopBody.preheader LoopBody.preheader: br label %LoopBody LoopBody: %3 = phi i32 [ %23, %LoopEnd ], [ 0, %LoopBody.preheader ] %4 = phi i8 [ %21, %LoopEnd ], [ 0, %LoopBody.preheader ] %5 = getelementptr inbounds i8, ptr %0, i32 %3 %6 = load volatile i8, ptr %5, align 1 %7 = urem i32 %3, 21 ; 不同的模数 %8 = getelementptr inbounds i8, ptr %data, i32 %7 %9 = load i8, ptr %8, align 1 %10 = zext nneg i8 %9 to i32 %11 = mul nuw nsw i32 %7, %10 %12 = and i32 %11, 1 %13 = icmp eq i32 %12, 0 br i1 %13, label %LoopBr0, label %LoopBr1 LoopBr0: %14 = add i8 %6, %4 %15 = xor i8 %14, %9 %16 = xor i8 %15, -1 br label %LoopEnd LoopBr1: %17 = sub i8 %6, %4 %18 = xor i8 %17, %9 %19 = sub i8 0, %18 br label %LoopEnd LoopEnd: %20 = phi i8 [ %16, %LoopBr0 ], [ %19, %LoopBr1 ] %21 = xor i8 %20, %9 %22 = getelementptr inbounds i8, ptr %plain_string, i32 %3 store i8 %21, ptr %22, align 1 %23 = add nuw nsw i32 %3, 1 %24 = icmp eq i32 %23, 20 ; 不同的循环次数 br i1 %24, label %UpdateDecStatus, label %LoopBody UpdateDecStatus: store i32 1, ptr @dec_status_1.str.1, align 4 br label %Exit Exit: ret void } ; 全局结构体变量初始化函数 define private void @__global_variable_initializer__ZL11struct_test(ptr nocapture %this) { Enter: %0 = load i32, ptr @dec_status__ZL11struct_test, align 4 %1 = icmp eq i32 %0, 1 ; 检查是否已初始化 br i1 %1, label %Exit, label %InitBlock InitBlock: ; 初始化结构体 store i32 2, ptr @dec__ZL11struct_test, align 4 ; 设置结构体整型成员为2 %2 = getelementptr %struct.StructTest, ptr @dec__ZL11struct_test, i32 0, i32 1 %3 = getelementptr inbounds [141 x i8], ptr @EncryptedStringTable, i32 0, i32 27 call void @goron_decrypt_string_0(ptr @dec0.str, ptr %3) ; 解密字符串 store ptr @dec0.str, ptr %2, align 8 ; 设置结构体指针成员 store i32 1, ptr @dec_status__ZL11struct_test, align 4 ; 标记为已初始化 br label %Exit Exit: ret void }
缺点
字符串解密线程不安全,可以继续迭代。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2天前
被不歪编辑
,原因:
赞赏
他的文章
赞赏
雪币:
留言: