LLVM IR 深入研究分析

张裕  金牌会员 | 2024-8-20 09:56:41 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 566|帖子 566|积分 1698

前置知识

LLVM是C++编写的构架编译器的框架体系,可用于优化以任意程序语言编写的程序。
LLVM IR可以理解为LLVM平台的汇编语言,以是官方也是以语言参考手册(Language Reference Manual)的情势给出LLVM IR的文档说明。既然是汇编语言,那么就和传统的CUP类似,有特定的汇编指令集。但是它又与传统的特定平台相干的指令集(x86,ARM,RISC-V等)不一样,它定位为平台无关的汇编语言。也就是说,LLVM IR是一种相对于CUP指令集高级,但是又是一种低级的代码中间表示(比抽象语法树等高级表示更加低级)。
LLVM IR即代码的中间表示,有三种情势:

  • .ll 格式:人类可以阅读的文本(汇编码) -->这个就是我们要学习的IR
  • .bc 格式:适合呆板存储的二进制文件
  • 内存表示
下面给出.ll格式和.bc格式生成及相互转换的常用指令清单:
  1. .c -> .ll:clang -emit-llvm -S a.c -o a.ll
  2. .c -> .bc: clang -emit-llvm -c a.c -o a.bc
  3. .ll -> .bc: llvm-as a.ll -o a.bc
  4. .bc -> .ll: llvm-dis a.bc -o a.ll
  5. .bc -> .s: llc a.bc -o a.s
复制代码
那么我们以一道CTF赛题来分析实验,学习LLVM IR
实验剖析

标题附件直接给出了中间表示.II文件
[img=720,33.23076923076923]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618479.png[/img]

打开查看一下汇编码,毕竟.II文件是人类可以阅读的文本,这边笔者使用的是Sublime Text(使用VScode查看即可)代码量不多,大概600行
[img=720,355.49265106151336]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618199.png[/img]

标题初步分析

我们直接寻找一下main函数
[img=720,386.9940278699403]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618569.png[/img]

[img=720,366.6779089376054]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618544.png[/img]

我们可以看出标题经历了两次RC4,然后Base64,我们从上面可以看到密文,RC4_key,我们直接一把锁,cyberchef启动,会发现解不出来,那么程序应该做了其他的操纵,最质朴的,我们可以想到把RC4魔改了,base64魔改等等。
[img=720,540.1798201798201]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618553.png[/img]

So!继续学习研究ing
【----帮助网安学习,以下所有学习资料免费领!加vx:dctintin,备注 “博客园” 获取!】
 ① 网安学习成长路径思维导图
 ② 60+网安经典常用工具包
 ③ 100+SRC漏洞分析陈诉
 ④ 150+网安攻防实战技术电子书
 ⑤ 最权威CISSP 认证测验指南+题库
 ⑥ 超1800页CTF实战技巧手册
 ⑦ 最新网安大厂面试题合集(含答案)
 ⑧ APP客户端安全检测指南(安卓+IOS)
.II详细分析

以是本着学习的态度,我们这时候应该掏出LLVM Language Reference Manual(官方文档)来简朴了解学习一些常见指令、符号标识以及特性。这边给出一些分析 .ll 中间文件的算法流程
  1. @ - 全局变量
  2. % - 局部变量
  3. alloca - 在当前执行的函数的堆栈帧中分配内存,当该函数返回到其调用者时,将自动释放内存
  4. i32 - 32位4字节的整数
  5. align - 对齐
  6. load - 读出,store写入
  7. icmp - 两个整数值比较,返回布尔值
  8. br - 选择分支,根据条件来转向label,不根据条件跳转的话类型goto
  9. label - 代码标签
  10. call - 调用函数
复制代码
首先看到一些全局变量,知道了RC4_key = llvmbitccipher = "TSzkWKgbMHszXaj@kLBmRrnTxsNtZsSOtZzqYikCw="
[img=720,492.11009174311926]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618578.png[/img]

我们继续分析,重点分析各个function
b64encode

b64encode 魔改

  • 每三个字符,24位,切分成4断,每段6位。
  • 将6位对应的值 (value+ 59)&0xff 则是编码后的值。
[img=720,415.30227948463823]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618312.png[/img]
  1.  %22 = getelementptr inbounds i8, i8* %19, i64 %21        // 取出当前处理字符
  2.  %23 = load i8, i8* %22, align 1
  3.  %24 = zext i8 %23 to i32                                 // 类型强制转化
  4.  %25 = ashr i32 %24, 2                                   // 算数右移两位   input[i]>>2
  5.  %26 = add nsw i32 %25, 59                                 //    input[i]+59
  6.  %27 = trunc i32 %26 to i8                                //    强制转化  相当于 &0xff
  7.  %28 = load i8*, i8** %6, align 8
  8.  %29 = load i32, i32* %9, align 4
  9.  %30 = sext i32 %29 to i64
  10.  %31 = getelementptr inbounds i8, i8* %28, i64 %30        // 存储base64 编码串
  11.  store i8 %27, i8* %31, align 1
  12.  %32 = load i8*, i8** %4, align 8
  13.  %33 = load i32, i32* %7, align 4
  14.  %34 = sext i32 %33 to i64
  15.  %35 = getelementptr inbounds i8, i8* %32, i64 %34
  16.  %36 = load i8, i8* %35, align 1
  17.  %37 = zext i8 %36 to i32
  18.  %38 = and i32 %37, 3                              // 获取第一个字符 低两位
  19.  %39 = shl i32 %38, 4                                // 左移四位
复制代码
RC4_init

RC4_init 正常,无魔改
[img=720,451.97492163009406]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618416.png[/img]

[img=720,422.25122349102776]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618452.png[/img]
  1. define dso_local void @Rc4_Init(i8*, i32) #0 {                           //RC4_init function
  2.  %3 = alloca i8*, align 8
  3.  %4 = alloca i32, align 4
  4.  %5 = alloca i32, align 4
  5.  %6 = alloca i32, align 4
  6.  store i8* %0, i8** %3, align 8
  7.  store i32 %1, i32* %4, align 4                                         //初始化S,T盒
  8.  call void @llvm.memset.p0i8.i64(i8* align 16 getelementptr inbounds ([256 x i8], [256 x i8]* @s, i64 0, i64 0), i8 0, i64 256, i1 false)
  9.  call void @llvm.memset.p0i8.i64(i8* align 16 getelementptr inbounds ([256 x i8], [256 x i8]* @t, i64 0, i64 0), i8 0, i64 256, i1 false)
  10.  store i32 0, i32* %5, align 4
  11.  br label %7
  12. 7:                                                ; preds = %26, %2
  13.  %8 = load i32, i32* %5, align 4
  14.  %9 = icmp slt i32 %8, 256
  15.  br i1 %9, label %10, label %29                          //如果 %9 为真(即 %8 小于 256),跳转到标签 %10;否则跳转到标签 %29,根据t打乱s盒
  16. 10:                                               ; preds = %7
  17.  %11 = load i32, i32* %5, align 4
  18.  %12 = trunc i32 %11 to i8
  19.  %13 = load i32, i32* %5, align 4
  20.  %14 = sext i32 %13 to i64
  21.  %15 = getelementptr inbounds [256 x i8], [256 x i8]* @s, i64 0, i64 %14
  22.  store i8 %12, i8* %15, align 1
  23.  %16 = load i8*, i8** %3, align 8
  24.  %17 = load i32, i32* %5, align 4
  25.  %18 = load i32, i32* %4, align 4
  26.  %19 = urem i32 %17, %18
  27.  %20 = zext i32 %19 to i64
  28.  %21 = getelementptr inbounds i8, i8* %16, i64 %20
  29.  %22 = load i8, i8* %21, align 1
  30.  %23 = load i32, i32* %5, align 4
  31.  %24 = sext i32 %23 to i64
  32.  %25 = getelementptr inbounds [256 x i8], [256 x i8]* @t, i64 0, i64 %24
  33.  store i8 %22, i8* %25, align 1
  34.  br label %26
  35. 26:                                               ; preds = %10
  36.  %27 = load i32, i32* %5, align 4
  37.  %28 = add nsw i32 %27, 1
  38.  store i32 %28, i32* %5, align 4
  39.  br label %7
  40. 29:                                               ; preds = %7
  41.  store i32 0, i32* %6, align 4
  42.  store i32 0, i32* %5, align 4
  43.  br label %30
  44. 30:                                               ; preds = %54, %29
  45.  %31 = load i32, i32* %5, align 4
  46.  %32 = icmp slt i32 %31, 256
  47.  br i1 %32, label %33, label %57
  48. 33:                                               ; preds = %30
  49.  %34 = load i32, i32* %6, align 4
  50.  %35 = load i32, i32* %5, align 4
  51.  %36 = sext i32 %35 to i64
  52.  %37 = getelementptr inbounds [256 x i8], [256 x i8]* @s, i64 0, i64 %36
  53.  %38 = load i8, i8* %37, align 1
  54.  %39 = zext i8 %38 to i32
  55.  %40 = add nsw i32 %34, %39
  56.  %41 = load i32, i32* %5, align 4
  57.  %42 = sext i32 %41 to i64
  58.  %43 = getelementptr inbounds [256 x i8], [256 x i8]* @t, i64 0, i64 %42
  59.  %44 = load i8, i8* %43, align 1
  60.  %45 = zext i8 %44 to i32
  61.  %46 = add nsw i32 %40, %45
  62.  %47 = srem i32 %46, 256
  63.  store i32 %47, i32* %6, align 4
  64.  %48 = load i32, i32* %5, align 4
  65.  %49 = sext i32 %48 to i64
  66.  %50 = getelementptr inbounds [256 x i8], [256 x i8]* @s, i64 0, i64 %49
  67.  %51 = load i32, i32* %6, align 4
  68.  %52 = sext i32 %51 to i64
  69.  %53 = getelementptr inbounds [256 x i8], [256 x i8]* @s, i64 0, i64 %52
  70.  call void @swap(i8* %50, i8* %53)                                                //call swap function
  71.  br label %54
复制代码
RC4_enc

RC4_enc 魔改 多了一层xor 89
[img=720,454.90909090909093]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618511.png[/img]

[img=720,596.9784172661871]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618616.png[/img]
  1. define dso_local void @Rc4_Encrypt(i8*, i32) #0 {                                //RC4_enc function
  2.  %3 = alloca i8*, align 8
  3.  %4 = alloca i32, align 4
  4.  %5 = alloca i8, align 1
  5.  %6 = alloca i8, align 1
  6.  %7 = alloca i8, align 1
  7.  %8 = alloca i8, align 1
  8.  store i8* %0, i8** %3, align 8
  9.  store i32 %1, i32* %4, align 4
  10.  store i8 0, i8* %6, align 1
  11.  store i8 0, i8* %7, align 1
  12.  store i8 0, i8* %8, align 1
  13.  br label %9
  14. 9:                                                ; preds = %14, %2
  15.  %10 = load i8, i8* %8, align 1
  16.  %11 = zext i8 %10 to i32
  17.  %12 = load i32, i32* %4, align 4
  18.  %13 = icmp ult i32 %11, %12
  19.  br i1 %13, label %14, label %64
  20. 14:                                               ; preds = %9
  21.  %15 = load i8, i8* %6, align 1
  22.  %16 = zext i8 %15 to i32
  23.  %17 = add nsw i32 %16, 1
  24.  %18 = srem i32 %17, 256
  25.  %19 = trunc i32 %18 to i8
  26.  store i8 %19, i8* %6, align 1
  27.  %20 = load i8, i8* %7, align 1
  28.  %21 = zext i8 %20 to i32
  29.  %22 = load i8, i8* %6, align 1
  30.  %23 = zext i8 %22 to i64
  31.  %24 = getelementptr inbounds [256 x i8], [256 x i8]* @s, i64 0, i64 %23               //生成密钥流
  32.  %25 = load i8, i8* %24, align 1
  33.  %26 = zext i8 %25 to i32
  34.  %27 = add nsw i32 %21, %26
  35.  %28 = srem i32 %27, 256
  36.  %29 = trunc i32 %28 to i8
  37.  store i8 %29, i8* %7, align 1
  38.  %30 = load i8, i8* %6, align 1
  39.  %31 = zext i8 %30 to i64
  40.  %32 = getelementptr inbounds [256 x i8], [256 x i8]* @s, i64 0, i64 %31
  41.  %33 = load i8, i8* %7, align 1
  42.  %34 = zext i8 %33 to i64
  43.  %35 = getelementptr inbounds [256 x i8], [256 x i8]* @s, i64 0, i64 %34              //经典Swap了再加
  44.  call void @swap(i8* %32, i8* %35)
  45.  %36 = load i8, i8* %6, align 1
  46.  %37 = zext i8 %36 to i64
  47.  %38 = getelementptr inbounds [256 x i8], [256 x i8]* @s, i64 0, i64 %37
  48.  %39 = load i8, i8* %38, align 1
  49.  %40 = zext i8 %39 to i32
  50.  %41 = load i8, i8* %7, align 1
  51.  %42 = zext i8 %41 to i64
  52.  %43 = getelementptr inbounds [256 x i8], [256 x i8]* @s, i64 0, i64 %42
  53.  %44 = load i8, i8* %43, align 1
  54.  %45 = zext i8 %44 to i32
  55.  %46 = add nsw i32 %40, %45
  56.  %47 = srem i32 %46, 256
  57.  %48 = sext i32 %47 to i64
  58.  %49 = getelementptr inbounds [256 x i8], [256 x i8]* @s, i64 0, i64 %48
  59.  %50 = load i8, i8* %49, align 1
  60.  store i8 %50, i8* %5, align 1
  61.  %51 = load i8, i8* %5, align 1
  62.  %52 = zext i8 %51 to i32
  63.  %53 = xor i32 %52, 89                                                         //xor 89
  64.  %54 = load i8*, i8** %3, align 8
  65.  %55 = load i8, i8* %8, align 1
  66.  %56 = zext i8 %55 to i64
  67.  %57 = getelementptr inbounds i8, i8* %54, i64 %56
  68.  %58 = load i8, i8* %57, align 1
  69.  %59 = zext i8 %58 to i32
  70.  %60 = xor i32 %59, %53                                                        //xor k
  71.  %61 = trunc i32 %60 to i8
  72.  store i8 %61, i8* %57, align 1
  73.  %62 = load i8, i8* %8, align 1
  74.  %63 = add i8 %62, 1
  75.  store i8 %63, i8* %8, align 1
  76.  br label %9
  77. 64:                                               ; preds = %9
  78.  ret void
  79. }
复制代码
main

main函数逻辑cipher -->RC4_init-->RC4_enc-->RC4_enc-->b64encode需要注意一下在RC4_enc的参数中,传入的数据块长度是固定的16,以是说程序进行两次RC4_enc的原因也就确定了,是为了分两次对程序进行加密,也算是一点点小本领,总之,即使让你好好分析.II代码,观察对软件分析的细节,耐心,嘻嘻。
[img=720,379.7227722772277]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618234.png[/img]

[img=720,318.28877005347596]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202408191618152.png[/img]

OK,理清晰逻辑,就可以试着敲代码解密啦。
解密

逆向分析过程明白之后,那么写代码就简朴多了
[code]#includeunsigned char s[300],t[300];void b64decode(unsigned char * enc,unsigned char* dec);void Rc4_dec1(int len, unsigned char *enc);void Rc4_Init(char *key,int len);void Rc4_dec2(int len, unsigned char *enc);int main() {    unsigned char enc[50]="TSz`kWKgbMHszXaj`@kLBmRrnTxsNtZsSOtZzqYikCw=";    unsigned char dec1[50]={0x00};    char key[10] ="llvmbitc";    unsigned char a[50];    int i=0;           b64decode(enc,dec1);    Rc4_Init(key,8);    Rc4_dec1(16,&dec1[16]);    for(i=0;i
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

张裕

金牌会员
这个人很懒什么都没写!

标签云

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