LLVM IR指令VM混淆分析

2024-09-07 18:20
文章标签 分析 指令 vm ir 混淆 llvm

本文主要是介绍LLVM IR指令VM混淆分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 未混淆编译

 编写一个最简单的测试代码,对 test_add函数用于对两个数相加:

int __attribute((__annotate__("vm"))) test_add(int a, int b) 
{int c = a + b;return c;
}int main(void) {int c = test_add(1, 2);return c;
}

编译成中间代码:

 未加入混淆时编译的中间代码如下:

; Function Attrs: noinline nounwind optnone ssp uwtable
define i32 @test_add(i32 %0, i32 %1) #0 {%3 = alloca i32, align 4%4 = alloca i32, align 4%5 = alloca i32, align 4store i32 %0, i32* %3, align 4store i32 %1, i32* %4, align 4%6 = load i32, i32* %3, align 4%7 = load i32, i32* %4, align 4%8 = add nsw i32 %6, %7store i32 %8, i32* %5, align 4%9 = load i32, i32* %5, align 4ret i32 %9
}

首先分配 3 个局部变量,参数 1 保存在 %3所在地址,参数 2 保存在 %4所在地址,接着 %3 地址取值到 %6,%4 地址取值到 %7,然后 %6 和 %7 相加保存到%8,%8 保存到 地址%5,再从 %5 地址取值到 %9,返回 %9。表达式简化后如下:

*%3 = %0

*%4 = %1

%6 = *%3

%7 = *%4

%8 = %6 + %7

*%5 = %8

%9 = *%5

return %9

实际上 %9 = %0 + %1

虚拟化混淆编译

接着加载虚拟化混淆 PASS生成处理后的 IR中间代码:

@opcodes = private global [84 x i8] c"\05\00\00\00\00\05\0C\00\00\00\01\05\04\00\00\00\05\18\00\00\00\01\05\0C\00\00\00\02,\00\00\00\05\18\00\00\00\020\00\00\00\05,\00\00\00\050\00\00\00\034\00\00\00\054\00\00\00\05$\00\00\00\01\05$\00\00\00\02,\00\00\00\05,\00\00\00\04"define i32 @test_add(i32 %0, i32 %1) {
entry:%2 = alloca i32%3 = alloca [56 x i8]%4 = alloca [256 x i32]%5 = alloca i32%6 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 8%7 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 12%8 = bitcast i8* %7 to i8**store i8* %6, i8** %8%9 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 20%10 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 24%11 = bitcast i8* %10 to i8**store i8* %9, i8** %11%12 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 32%13 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 36%14 = bitcast i8* %13 to i8**store i8* %12, i8** %14%15 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 0%16 = bitcast i8* %15 to i32*store i32 %0, i32* %16%17 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 4%18 = bitcast i8* %17 to i32*store i32 %1, i32* %18store i32 0, i32* %2store i32 0, i32* %5br label %dispatchdispatch:                                         ; preds = %loopend, %entry%19 = load i32, i32* %2%20 = getelementptr [84 x i8], [84 x i8]* @opcodes, i32 0, i32 %19%21 = load i8, i8* %20%22 = load i32, i32* %2%23 = add i32 %22, 1store i32 %23, i32* %2switch i8 %21, label %loopend [i8 1, label %handler_storei8 2, label %handler_loadi8 3, label %handler_addi8 4, label %handler_reti8 5, label %push_addri8 6, label %store_imm1i8 7, label %store_imm2i8 8, label %store_imm4i8 9, label %store_imm8]handler_store:                                    ; preds = %dispatch%24 = load i32, i32* %2%25 = load i32, i32* %5%26 = sub i32 %25, 2%27 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %26%28 = load i32, i32* %27%29 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %28%30 = bitcast i8* %29 to i32*%31 = load i32, i32* %30%32 = sub i32 %25, 1%33 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %32%34 = load i32, i32* %33%35 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %34%36 = bitcast i8* %35 to i32**%37 = load i32*, i32** %36store i32 %31, i32* %37, align 4%38 = sub i32 %25, 2store i32 %38, i32* %5br label %loopendhandler_load:                                     ; preds = %dispatch%39 = load i32, i32* %2%40 = load i32, i32* %5%41 = sub i32 %40, 1%42 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %41%43 = load i32, i32* %42%44 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %43%45 = bitcast i8* %44 to i32**%46 = load i32*, i32** %45%47 = load i32, i32* %46, align 4%48 = getelementptr [84 x i8], [84 x i8]* @opcodes, i32 0, i32 %39%49 = bitcast i8* %48 to i32*%50 = load i32, i32* %49%51 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %50%52 = bitcast i8* %51 to i32*store i32 %47, i32* %52%53 = add i32 %39, 4store i32 %53, i32* %2%54 = sub i32 %40, 1store i32 %54, i32* %5br label %loopendhandler_add:                                      ; preds = %dispatch%55 = load i32, i32* %2%56 = load i32, i32* %5%57 = sub i32 %56, 2%58 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %57%59 = load i32, i32* %58%60 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %59%61 = bitcast i8* %60 to i32*%62 = load i32, i32* %61%63 = sub i32 %56, 1%64 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %63%65 = load i32, i32* %64%66 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %65%67 = bitcast i8* %66 to i32*%68 = load i32, i32* %67%69 = add nsw i32 %62, %68%70 = getelementptr [84 x i8], [84 x i8]* @opcodes, i32 0, i32 %55%71 = bitcast i8* %70 to i32*%72 = load i32, i32* %71%73 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %72%74 = bitcast i8* %73 to i32*store i32 %69, i32* %74%75 = add i32 %55, 4store i32 %75, i32* %2%76 = sub i32 %56, 2store i32 %76, i32* %5br label %loopendhandler_ret:                                      ; preds = %dispatch%77 = load i32, i32* %2%78 = load i32, i32* %5%79 = sub i32 %78, 1%80 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %79%81 = load i32, i32* %80%82 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %81%83 = bitcast i8* %82 to i32*%84 = load i32, i32* %83ret i32 %84push_addr:                                        ; preds = %dispatch%85 = load i32, i32* %2%86 = load i32, i32* %5%87 = getelementptr [84 x i8], [84 x i8]* @opcodes, i32 0, i32 %85%88 = bitcast i8* %87 to i32*%89 = load i32, i32* %88%90 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %86store i32 %89, i32* %90%91 = add i32 %86, 1store i32 %91, i32* %5%92 = add i32 %85, 4store i32 %92, i32* %2br label %loopendstore_imm1:                                       ; preds = %dispatch%93 = load i32, i32* %2%94 = load i32, i32* %5%95 = getelementptr [84 x i8], [84 x i8]* @opcodes, i32 0, i32 %93%96 = load i8, i8* %95%97 = sub i32 %94, 1%98 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %97%99 = load i32, i32* %98%100 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %99store i8 %96, i8* %100%101 = sub i32 %94, 1store i32 %101, i32* %5%102 = add i32 %93, 1store i32 %102, i32* %2br label %loopendstore_imm2:                                       ; preds = %dispatch%103 = load i32, i32* %2%104 = load i32, i32* %5%105 = getelementptr [84 x i8], [84 x i8]* @opcodes, i32 0, i32 %103%106 = bitcast i8* %105 to i16*%107 = load i16, i16* %106%108 = sub i32 %104, 1%109 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %108%110 = load i32, i32* %109%111 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %110%112 = bitcast i8* %111 to i16*store i16 %107, i16* %112%113 = sub i32 %104, 1store i32 %113, i32* %5%114 = add i32 %103, 2store i32 %114, i32* %2br label %loopendstore_imm4:                                       ; preds = %dispatch%115 = load i32, i32* %2%116 = load i32, i32* %5%117 = getelementptr [84 x i8], [84 x i8]* @opcodes, i32 0, i32 %115%118 = bitcast i8* %117 to i32*%119 = load i32, i32* %118%120 = sub i32 %116, 1%121 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %120%122 = load i32, i32* %121%123 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %122%124 = bitcast i8* %123 to i32*store i32 %119, i32* %124%125 = sub i32 %116, 1store i32 %125, i32* %5%126 = add i32 %115, 4store i32 %126, i32* %2br label %loopendstore_imm8:                                       ; preds = %dispatch%127 = load i32, i32* %2%128 = load i32, i32* %5%129 = getelementptr [84 x i8], [84 x i8]* @opcodes, i32 0, i32 %127%130 = bitcast i8* %129 to i64*%131 = load i64, i64* %130%132 = sub i32 %128, 1%133 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %132%134 = load i32, i32* %133%135 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %134%136 = bitcast i8* %135 to i64*store i64 %131, i64* %136%137 = sub i32 %128, 1store i32 %137, i32* %5%138 = add i32 %127, 8store i32 %138, i32* %2br label %loopendloopend:                                          ; preds = %store_imm8, %dispatch, %store_imm4, %store_imm2, %store_imm1, %push_addr, %handler_add, %handler_load, %handler_storebr label %dispatch
}

就一个加法运算,搞成这样至于吗😩

虚拟化混淆 IR层面分析

接下来分析字节码是如何一步一步运算的:

首先分析函数的入口代码块:

entry:%2 = alloca i32  分配一个变量表示当前执行的字节码下标%3 = alloca [56 x i8]   分配虚拟内存%4 = alloca [256 x i32]  分配虚拟栈%5 = alloca i32  分配一个变量表示虚拟栈下标%6 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 8%7 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 12%8 = bitcast i8* %7 to i8**store i8* %6, i8** %8%9 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 20%10 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 24%11 = bitcast i8* %10 to i8**store i8* %9, i8** %11%12 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 32%13 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 36%14 = bitcast i8* %13 to i8**store i8* %12, i8** %14%15 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 0 取虚拟内存首地址%16 = bitcast i8* %15 to i32* 地址从 int8* 类型转换成 int32类型store i32 %0, i32* %16   参数 1 保存到虚拟内存首地址%17 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 4%18 = bitcast i8* %17 to i32*store i32 %1, i32* %18   参数 2 保存到虚拟内存第二个 4 字节处store i32 0, i32* %2     初始化当前字节码下标为 0store i32 0, i32* %5     初始化当前虚拟栈下标为 0br label %dispatch       跳转到 dispatch 执行

看注解,entry 块主要做了一些初始化的操作,分配虚拟内存空间和虚拟栈空间,保存参数到虚拟内存中,初始化字节码当前下标 和 虚拟栈下标为 0

接着看 dispatch 块是怎么处理的:

dispatch:                                         ; preds = %loopend, %entry%19 = load i32, i32* %2   取字节码下标值%20 = getelementptr [84 x i8], [84 x i8]* @opcodes, i32 0, i32 %19  取字节码地址%21 = load i8, i8* %20    加载一个字节码%22 = load i32, i32* %2   取字节码下标值%23 = add i32 %22, 1      字节码下标值加 1store i32 %23, i32* %2    保存字节码下标值switch i8 %21, label %loopend [ 根据字节码值进行跳转i8 1, label %handler_storei8 2, label %handler_loadi8 3, label %handler_addi8 4, label %handler_reti8 5, label %push_addri8 6, label %store_imm1i8 7, label %store_imm2i8 8, label %store_imm4i8 9, label %store_imm8]

dispatch 是调度器,根据opcode 执行对应的 handler 解释器,dispatch 执行取字节码,并把当前字节码指针+1,根据字节码跳转到对应的解释器执行。

接下来从字节码序列分析执行过程,字节码内容如下:

@opcodes = private global [84 x i8] c"\05\00\00\00\00\05\0C\00\00\00\01\05\04\00\00\00\05\18\00\00\00\01\05\0C\00\00\00\02,\00\00\00\05\18\00\00\00\020\00\00\00\05,\00\00\00\050\00\00\00\034\00\00\00\054\00\00\00\05$\00\00\00\01\05$\00\00\00\02,\00\00\00\05,\00\00\00\04"

字节码 opcodes是长 84 的 i8类型数组,第一个字节码是 05,根据上面的跳转是到 push_addr继续执行:

push_addr:                                        ; preds = %dispatch%85 = load i32, i32* %2   取当前字节码下标%86 = load i32, i32* %5   取当前虚拟栈下标%87 = getelementptr [84 x i8], [84 x i8]* @opcodes, i32 0, i32 %85 取当前字节码地址%88 = bitcast i8* %87 to i32*  当前字节码地址转成 int32*%89 = load i32, i32* %88.      从当前字节码地址取 4 个字节%90 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %86  取当前栈顶地址store i32 %89, i32* %90    将当前字节码取出的 4 个字节保存到栈顶%91 = add i32 %86, 1       虚拟栈下标+1store i32 %91, i32* %5     更新虚拟栈下标%92 = add i32 %85, 4       字节码下标+4store i32 %92, i32* %2     更新当前字节码下标br label %loopend          跳转到 loopend

看注解,这个基本块的主要作用是从当前字节码处取一个 int32值,保存到虚拟栈顶,并把虚拟栈顶+1,把当前字节码下标+4,对应的虚拟指令就是 vpush 0 . 即 字节码 0x5执行的操作是将取出字节码后面的 4 字节转为 int32,这是一个指向虚拟内存的地址,将该地址push到虚拟栈中。

那么字节码 \05\00\00\00\00\05\0C\00\00\00 对应的操作就是 vpush 0 和 vpush 12

接下来取字节码是 0x1,对应的处理 handler 是 handler_store:

handler_store:                                    ; preds = %dispatch%24 = load i32, i32* %2    取当前字节码地址下标%25 = load i32, i32* %5    取虚拟栈当前下标%26 = sub i32 %25, 2       虚拟栈下标 - 2%27 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %26 取出栈地址%28 = load i32, i32* %27   从栈地址取出一个int32值%29 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %28 值对应的虚拟内存地址%30 = bitcast i8* %29 to i32* 虚拟内存地址从 int8* 转 int32*%31 = load i32, i32* %30.     从虚拟地址取出这个 int32值%32 = sub i32 %25, 1       虚拟栈下标 - 1 ,后面重复前面的步骤%33 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %32 虚拟栈对应的地址%34 = load i32, i32* %33   从虚拟栈对应的地址取值,这个值是虚拟内存的下标%35 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %34 取虚拟内存地址%36 = bitcast i8* %35 to i32**  虚拟内存地址从 int8* 转成 int32**%37 = load i32*, i32** %36.     从虚拟内存处取出的值是一个 int32*地址store i32 %31, i32* %37, align 4  将前面第一次取出的值存放在这个地址处%38 = sub i32 %25, 2       当前虚拟栈顶指针 - 2store i32 %38, i32* %5     更新当前栈顶指针br label %loopend

看注解,这个虚拟指令 vstore 的逻辑是从当前栈顶取两个元素,这两个元素都是指向虚拟内存的地址,实际上是虚拟内存的下标,第一个元素指向虚拟内存中的一个 int32值,第二个元素指向虚拟内存中的一个 int32*地址值,然后把第一个元素取出的 int32值,保存到第二个元素取出的int32*地址处,然后把栈顶下标移动2 个。即 vstore 将虚拟内存 0 处的值,保存到虚拟内存 12 处值指向的地址处。

接下来又是两个 vpush和一个 vstore,分别是 vpush 4, vpush 24 和 vstore。

函数的 entry 处是做一些初始化的操作,就包括把参数 1 存储在虚拟地址下标 0 处,把参数 2 存储在虚拟地址下标 4 处。这几步的虚拟指令:

vpush 0
vpush 12 
vStore
vpush 4
vpush 24
vStore 

可以理解成就是把参数 1 和参数 2 分别存放到了虚拟内存 12 和 24 处的值指向的地址处。 根据函数 entry 处的分析,虚拟内存下标 12 和 24 处分别存放的是下标 8 和 20 处的地址。即最终把参数 1 和 参数 2 存放到了虚拟内存下标 8 和 20 处。

接下来是一个虚拟vpush指令,具体操作是 vpush 12.

然后后面是字节码 0x2,对应的 handler 是 handler_load:

handler_load:                                     ; preds = %dispatch%39 = load i32, i32* %2    取当前字节码下标%40 = load i32, i32* %5    取当前虚拟栈下标%41 = sub i32 %40, 1       当前虚拟栈下标 - 1%42 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %41  对应栈中的地址%43 = load i32, i32* %42   从栈中取出这个值,也上个虚拟指令vpush的操作数%44 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %43 这个值内存虚拟内存下标处%45 = bitcast i8* %44 to i32** 将地址从 i8* 转成 i32**%46 = load i32*, i32** %45     从这个虚拟内存取出的值是一个地址%47 = load i32, i32* %46, align 4  从这个地址处取值%48 = getelementptr [84 x i8], [84 x i8]* @opcodes, i32 0, i32 %39 当前字节码地址%49 = bitcast i8* %48 to i32*  转 i32*%50 = load i32, i32* %49.      从当前字节码处取4 字节%51 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %50  对应虚拟内存地址%52 = bitcast i8* %51 to i32*   转成 i32*store i32 %47, i32* %52.       将上面取的值,存放在这个虚拟地址处%53 = add i32 %39, 4          当前字节码下标 - 4store i32 %53, i32* %2        更新当前字节码下标%54 = sub i32 %40, 1          当前虚拟栈顶 - 1store i32 %54, i32* %5         并更新当前栈顶br label %loopend

看注解,vload 指令有一,4 字节操作数,这里具体是 vload 44, 操作逻辑是将栈顶元素对应的虚拟内存下标处,取出一个值,这个值也是一个地址,然后用这个地址在虚拟内存处取值,将这个值保存在虚拟指令操作数对应的虚拟地址下标处。上一条指令是 vpush 12, 对应这条指令的栈顶元素就是 12,虚拟内存 12 处,存放的是地址 8,地址 8 处保存了参数 1。因此这条指令执行后将参数 1 保存到了虚拟内存 44 处。

下两条指令分别是 vpush 24 和 vload 48,同上,即将参数 2 保存在虚拟地址 48 处。

接着是 vpush 44 和 vpush 48,即将两个参数再次保存到栈中。

接着是字节码 0x3,对应 handler 是   push_addr:

handler_add:                                      ; preds = %dispatch%55 = load i32, i32* %2   取当前字节码下标%56 = load i32, i32* %5   取当前栈下标%57 = sub i32 %56, 2      栈下标 - 2%58 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %57 取栈地址%59 = load i32, i32* %58   栈地址取值,得到虚拟内存地址%60 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %59 取最内存地址%61 = bitcast i8* %60 to i32*  虚拟内存地址转成 i32*%62 = load i32, i32* %61.      从虚拟内存地址处取值   %63 = sub i32 %56, 1       栈下标 - 1, 重复上述步骤%64 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %63%65 = load i32, i32* %64%66 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %65%67 = bitcast i8* %66 to i32*%68 = load i32, i32* %67   取出了第二个值%69 = add nsw i32 %62, %68  两个值相加%70 = getelementptr [84 x i8], [84 x i8]* @opcodes, i32 0, i32 %55 字节码当前地址%71 = bitcast i8* %70 to i32*   地址转成 i32*%72 = load i32, i32* %71.       字节码当前地址处取出 4 字节,是一个下标地址%73 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %72  虚拟内存处这个地址%74 = bitcast i8* %73 to i32*   虚拟内存处地址转 i32*store i32 %69, i32* %74.        前面计算的和保存在这个地址处%75 = add i32 %55, 4       当前字节码下标 + 4store i32 %75, i32* %2     更新当前字节码下标%76 = sub i32 %56, 2       虚拟栈下标 - 2store i32 %76, i32* %5     更新虚拟栈下标br label %loopend

看注解,vadd指令有一个操作数,操作逻辑是将前两个 vpush指令压入栈中的地址,取出来并从虚拟内存中取出值,相加后保存在指令操作数指向的地址中。这里具体是 vadd 52。即相加的结果保存在了虚拟内存下标 52 处。

接下来的虚拟指令分别是:

vpush 52
vpush 36
vstore 
vpush 36
vload 44
vpush 44

最后一个字节码是 0x4,对应该的 handler 是 handler_ret:

handler_ret:                                      ; preds = %dispatch%77 = load i32, i32* %2    取当前字节码下标%78 = load i32, i32* %5    当前栈下标%79 = sub i32 %78, 1       栈下标 - 1%80 = getelementptr [256 x i32], [256 x i32]* %4, i32 0, i32 %79 栈地址%81 = load i32, i32* %80   取出栈中的值%82 = getelementptr [56 x i8], [56 x i8]* %3, i32 0, i32 %81 对应虚拟内存地址%83 = bitcast i8* %82 to i32*  转 i32*%84 = load i32, i32* %83.      取出这个值ret i32 %84                 返回这个值

看注解,vret指令的逻辑是从栈顶取出地址,并用该地址在虚拟内存中取出值,返回这个值。

综上分析得到字节码指令序列:

vpush 0
vpush 12
vStore 
vpush 4
vpush 24
vStore 
vpush 12
vload  44
vpush 24
vload 48
vpush 44
vpush 48
vadd 52
vpush 52
vpush 36
vstore
vpush 36
vload 44
vpush 44
vret

上述字节码的逻辑就是对输入的两个参数做转换赋值,结果相加后,再进行转换保存,最后返回。功能上与混淆前的函数等价。

上述虚拟化的过程实际上是对每个 IR指令的模拟,比如使用 vpush vpush vstore 来模拟一个store 指令,使用 vpush 和 vload 摸你 load 指令,并借助一个虚拟栈和虚拟内存。来模拟全部 IR指令的过程。

反编译

混淆前的反编译代码:

控制流图:

就一个基本块,没有控制流 

 混淆后的反编译代码:

__int64 __fastcall test_add(unsigned int a1, unsigned int a2)
{int v2; // edxint v3; // edxint v5; // [rsp+0h] [rbp-440h]int v6; // [rsp+4h] [rbp-43Ch]unsigned __int64 v7; // [rsp+8h] [rbp-438h]char v8; // [rsp+10h] [rbp-430h]char *v9; // [rsp+14h] [rbp-42Ch]char v10; // [rsp+1Ch] [rbp-424h]char *v11; // [rsp+20h] [rbp-420h]char v12; // [rsp+28h] [rbp-418h]char *v13; // [rsp+2Ch] [rbp-414h]int v14[256]; // [rsp+40h] [rbp-400h]v9 = &v8;v11 = &v10;v13 = &v12;v7 = __PAIR__(a2, a1);v6 = 0;v5 = 0;while ( 1 ){v2 = byte_100004000[v6++];switch ( v2 ){case 1:**(_DWORD **)((char *)&v7 + v14[v5 - 1]) = *(_DWORD *)((char *)&v7 + v14[v5 - 2]);v5 -= 2;continue;case 2:*(_DWORD *)((char *)&v7 + *(signed int *)&byte_100004000[v6]) = **(_DWORD **)((char *)&v7 + v14[v5 - 1]);v6 += 4;--v5;continue;case 3:*(_DWORD *)((char *)&v7 + *(signed int *)&byte_100004000[v6]) = *(_DWORD *)((char *)&v7 + v14[v5 - 1])+ *(_DWORD *)((char *)&v7 + v14[v5 - 2]);v6 += 4;v5 -= 2;continue;case 4:return *(unsigned int *)((char *)&v7 + v14[v5 - 1]);case 5:v3 = v6;v14[v5++] = *(_DWORD *)&byte_100004000[v6];goto LABEL_10;case 6:*((_BYTE *)&v7 + v14[v5-- - 1]) = byte_100004000[v6++];break;case 7:*(_WORD *)((char *)&v7 + v14[v5-- - 1]) = *(_WORD *)&byte_100004000[v6];v6 += 2;break;case 8:v3 = v6;*(_DWORD *)((char *)&v7 + v14[v5-- - 1]) = *(_DWORD *)&byte_100004000[v6];
LABEL_10:v6 = v3 + 4;break;case 9:*(unsigned __int64 *)((char *)&v7 + v14[v5-- - 1]) = *(_QWORD *)&byte_100004000[v6];v6 += 8;break;default:continue;}}
}

控制流图:

 

汇编层面分析虚拟opcode 指令流程

TODO

这篇关于LLVM IR指令VM混淆分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1145804

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

工作常用指令与快捷键

Git提交代码 git fetch  git add .  git commit -m “desc”  git pull  git push Git查看当前分支 git symbolic-ref --short -q HEAD Git创建新的分支并切换 git checkout -b XXXXXXXXXXXXXX git push origin XXXXXXXXXXXXXX

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in

三相直流无刷电机(BLDC)控制算法实现:BLDC有感启动算法思路分析

一枚从事路径规划算法、运动控制算法、BLDC/FOC电机控制算法、工控、物联网工程师,爱吃土豆。如有需要技术交流或者需要方案帮助、需求:以下为联系方式—V 方案1:通过霍尔传感器IO中断触发换相 1.1 整体执行思路 霍尔传感器U、V、W三相通过IO+EXIT中断的方式进行霍尔传感器数据的读取。将IO口配置为上升沿+下降沿中断触发的方式。当霍尔传感器信号发生发生信号的变化就会触发中断在中断

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除