首页
社区
课程
招聘
[原创]OLLVM (一)字符串混淆
发表于: 2天前 173

[原创]OLLVM (一)字符串混淆

2天前
173

前言

最近学习了OLLVM,代码工程链接。我对原有代码添加了注释并做了代码分析。

目标

  1. 对全局字符串加密。
  2. 对使用全局变量的全局变量加密。

字符串混淆步骤

所谓的字符串混淆就是字符串加密。步骤:

  1. 遍历模块内的所有全局字符串

    1. 识别到全局字符串后对其加密
    2. 构建对应的解密函数
    3. 创建解密后字符串的存储位置,如:@dec0.str@dec1.str.1
    4. 创建字符串解密后的状态标志,如:@dec_status_0.str@dec_status_1.str.1
  2. 对使用全局变量的全局变量加密,然后构建对应的解密函数

  3. 创建加密字符串表EncryptedStringTable,存储所有加密的字符串

  4. 遍历模块内的所有命令,替换 全局变量和使用它的全局变量 为解密相关代码

  5. 替换 解密函数内的全局变量 为解密相关代码

  6. 删除未使用的全局变量

混淆后代码解释

; 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天前 被不歪编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回