记需要反混淆的函数为output=obf-function(input)
。
反混淆的思路,首先标记输入的变量记为input-symbol
,通过Taint跟踪改变量的流向,并提取与该输入变量input-symbol
和输出变量output-symbol
有关的所有表达式exprs
。再将exprs
转化为Arybo
表达式,然后再将其转化为LLVM-IR
指令的形式。最后使用llvm编译器-o3选项对llvm-ir
指令做编译优化,生成可执行文件。
提取binary
文件中PT_LOAD
段,将其加载到模拟器内存中。
1 2 3 4 5 6 |
|
其中ctx = TritonContext()
,用于获取一个Triton实例对象,用于动态分析。
对binary
文件中的导入符号
做hook
处理(具体可以看makeRelocation()
),确保模拟执行时能够正常执行所需的API函数,如:printf
,memcpy
,rand
,strtoul
等,即使用python
语法来实现这些API函数。用于后面模拟器执行。
binary
程序的执行过程,首先使用LIEF
获取pc入口地址(binary.entrypoint
).opcodes = ctx.getConcreteMemoryAreaValue(pc, 16)
)。ctx.processing
执行指令。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
hookingHandler,从名字可以知道,这个函数用于处理之前被hook的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
|
###a 这里假设程序只有一条执行路径
即程序不会根据输入值input-symbol
的不同而执行不同的分支。
则此时,全局变量paths[0]
中得到与之相关的所有表达式。
使用下面代码,将所有表达式保存到文件中,其中pathNumber=0
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
使用Arybo开源项目中tritonexprs2arybo()
方法,将exprs
中所所有表达式转化为arybo
表达式;以及使用tritonast2arybo()
获取输入变量input-symbol
。
最后使用to_llvm_function
将其转化为llvm-ir
指令,再recompile将这些ir指令重新编译生成可执行文件。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
流程如下:
1 2 3 4 5 |
|
流程如下,直接在代码里面解释,这代码里面假设程序只有两条可能的路径会走。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
|
结果如图1,图2,图3所示。其中图1为原程序编译后使用ida打开看到的控制流程图;图2为使用相关混淆方法对源程序做混淆处理后编译生成的可执行文件,再使用ida打开看到的控制流程图;图3为使用本文方法对混淆后可执行文件做反混淆处理后得到的新的可执行文件,再使用ida打开看到的控制流程图。虽然反混淆后程序的控制流图跟原程序有所不同,但执行结果是一致的。
[图1] 原程序控制流程图
我只是解读搬运工,有兴趣读者可以直接下载该开源项目,点击链接。
当然,该方法仍存在一定程度上的局限性,还无法做到通用。有什么问题可留言。
https://bbs.pediy.com/thread-249210.htm