上一篇在这里https://bbs.pediy.com/thread-265335.htm利用编译器优化干掉虚假控制流
上一篇我们通过优化去除了类似x * (x + 1) % 2 == 0的虚假控制流,这里的混淆去除bcf之后如下图,很明显的平坦化混淆
图上已经比较清晰了,黄色圈住的块是进入平坦化,离开平坦化return,选择控制流的switch混淆块,
蓝色的两块如果执行到的话就是真实块,其余基本都是case混淆块
具体算法过程如下,这里参考了大佬的思路,看到大佬又发了一篇vmp的,下面必须抽空继续学习
一种通过后端编译优化脱混淆壳的方法https://bbs.pediy.com/thread-260626.htm
0.把ir中所有switch指令转换为cmp+br指令
1.遍历所有block,如果某个block的Predecessor >6,可能是混淆节点,也就是switch块,标记为ob
2.获取ob的Successor后续块的Terminator指令,如果是条件跳转cmp+br,getOperand(0)就得到switchvar
3.根据switchvar这个Value类的实例,遍历所有使用这个值的Users ,挑出其中指令为cmp且switchvar为op0的Users,标记为case分发块
4.紧随case分发块之后一直到ob混淆块或者return的为真实块。这里大概有这几种情况
4.1赋值->多个case分发块->1个或多个真实块->ob块->赋值->下一次循环 这种是包含了真实块的有用执行路径
4.2赋值->多个case分发块->赋值->ob块->下一次循环 这种是只改变switchvar的无用路径
4.3赋值->多个case分发块->ob块->下一次循环 这种在ob块的phi节点上没有赋值,是永远不可能执行的路径,在分析ob块的phi节点时可以直接删掉
5.根据所有分发块建立二叉树,根节点为ob节点,往下遍历block寻找结尾cmp+br指令,左节点为true,右为false,直到没有br指令或者cmp不为switchvar,就到达真实块开头,它正对着的混淆phi节点前驱块为真实块结尾
6.从混淆phi节点开始,根据switchvar的Use往上追踪所有赋值,遇到phi继续追踪直到定值,建立混淆phi节点前驱块与switchvar赋值一一对应,没有switchvar赋值的phi路径肯定不会执行
7.模拟执行,第一次switchvar的值在进入switch时赋予,也就是ob块的后继块的phi左值,其余的在到达ob混淆节点后由phi指令赋予,最后一次必然到达return指令,理清楚整个路径的顺序
8.把真实块通过br连接起来,优化,这个函数其实只有一个真实块
遍历所有block,如果某个block的Predecessor >6,可能是混淆节点,也就是switch块,标记为ob
获取ob混淆块的Successor后续块的Terminator指令,如果是条件跳转cmp+br,getOperand(0)就得到switchvar
根据switchvar这个Value类的实例,遍历所有使用这个值的Users ,挑出其中指令为cmp且switchvar为op0的Users,标记为case分发块
紧随case分发块之后一直到ob混淆块或者return的为真实块
遍历ob块phi节点获取所有switchvar,这里有一些retdec识别错误的ConstantExpr我们把它替换成Constant
本来想学着用klee的,破单位网速太差一直下一半断掉,没办法只能自己试着写
blk向量存储了所有判断状态变量switchvar的的block,blk2向量存储了所有ob混淆block的前驱块,excute向量存储了所有执行过的block,
很明显每次执行首先获得switchvar,然后沿着路径根据blk的br一直执行到blk2也就是ob混淆block的前驱块,
通过ob混淆块的phi节点更改switchvar,执行下一条路径
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-3-6 17:25
被挤蹭菌衣编辑
,原因: