在之前写的《利用机器学习分析vmp的思路》中,把读写内存的操作数直接替换成了绝对地址的形式,这就产生了大量赋值语句,阅读起来也不是很友好。写这篇文章的主要目的是如何做进一步的优化,本篇文章用到了程序切片技术和编译原理中的一些优化算法,复制传播、死代码删除和有向无环图DAG的局部优化。
在之前写的文章基础上对trace增加了eflags寄存器的记录。利用程序切片技术提取了handle与写内存相关的指令后,通过一些简单的特征就可以处理该样本trace中所有的handle,所以本篇没有使用深度学习的方法对handle进行分类处理,深度学习也只是通过有标签的handle数据集代替了人工提取特征的过程。
trace处理后会得到以下文件:
NormalCode表示正常的代码,不需要对其优化处理,VmProcedureCode表示虚拟机中执行的代码,后面的数字表示执行的顺序。handle的识别和处理参考相应的代码
复制传播(Copy Propagation)的思想:对于给定的关于某个变量v和s的赋值v=s,在没有出现其他关于v定值的程序范围内,可以用s来替代出现的v的引用。handle处理后,trace中含有大量的mov语句,可以利用复制传播配合死代码消除处理掉冗余的mov语句。比如有以下指令序列:
按照复制传播的思想,c可以用a代替,即d=a。如果c是不活跃的,那么c=b是可以删除的。
复制传播可以通过ud链(Use-Definition Chains)实现,ud链描述的是指令或语句中引用的变量可能定值点的位置。因为在很多情况下,一个定值是否能实际到达某一特定程序点是不可判定的,有时候需要依赖于特定的外部输入。当然,在trace中可以直接认为都是可到达的。ud链的构造可以通过到达定值分析实现,但是在trace中,可以认为语句之间是顺序执行的,ud链的构造只要向上遍历找到最后出现的对当前变量定值的语句即可。代码实现在VmProcedureCode类中的CopyPropagation和DeadCodeEliminatioin部分
在虚拟机的代码中,设定的活跃变量如下:
以VmProcedureCode0.txt中的最后几行代码为例:
最后一行的代码中esp=0xffffcb58,0x00688684地址处的0x68304e刚好对应虚拟机退出后的跳转地址,0x007df919地址处的0x74fbf3对应的是下一个虚拟机入口。0x006cbe9d地址处的 0x77cb5a 对应0x68304e函数的参数,ida中0x68304e地址处的代码反编译如下:
程序切片技术是为了替换之前使用的污点分析,方便获取handle中与写内存相关的指令。给定一个感兴趣的语句以及它所使用的变量,程序切片(program slicing)是一个影响该条语句变量值的语句集合,而切片准则(slicing criterion)用来描述这个感兴趣的语句及其变量。切片准则可以定义为C = <statement, variables>,statement可以为语句的唯一序号,variables为变量集。比如有以下程序,令切片准则C=<10, {product}>,箭头左边为原程序,箭头右边为对应C的程序切片。
程序切片的实现主要有基于程序依赖图和基于数据流方程两种方法。程序依赖图包含数据依赖和控制依赖,它的构建可以查看鲸书等相关资料,这里只介绍基于数据流方程的方法。数据流方程迭代的公式如下:
k表示迭代次数,如果把语句当做CFG中的一个结点而不是基本块的话,那么i和j就是一个语句,其中i是j的前驱结点。
DEF(i)和REG(i)分别表示i结点的变量定值集和引用集
C和(b, REF(b))表示相应的切边准则
B[k][C]是分支语句集,表示影响S[k][C]中切片语句的分支语句集合,用来跟踪控制依赖关系,迭代过程中当B[k][C]不在改变时,迭代终止
INFL(b)表示从b开始到距离它最近的后向支配语句之间的路径上除去端点以外所有语句的集合,INFL(b)在其直接后驱大于等于2时才不为空,否则为空集。INFL(b)中的语句执行受b语句执行结果的影响,控制依赖于b
R[k][C](i)表示结点i中与切片准则C相关的变量集合,用来跟踪数据依赖
S[k][C]表示程序切片
初始化时,R[0][C](n)等于切片准则C中的变量集variables,n为C中的statement,当n≠m时,R[0][C](m)为空集。之后再从以下公式计算各个结点的R[0][C]和S[0][C]
基于数据流方程的过程内切片伪算法如下:
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课