间接跳转混淆去除
type
status
date
slug
tags
summary
category
icon
password
原理
找了个demo练手
大概结构有:
① 调用函数混淆

② 检查zf位,实现jz jnz的条件跳转

过程
(包括一些失败的心路历程,很久没写了,可以直接跳过看代码)
打印
第一步,是先匹配并把这些值计算出来。
找了若干个跳转的混淆,发现有两个非常固定的特征:间接跳转使用的寄存器都是rax,这个特点就很方便我们匹配,还有一个无关紧要的,这里都是用rax + rcx来寻址
那么,第一思路其实很简单,利用angr模拟执行,执行到call rax的位置,这时候rax的值是收敛的,可以直接拿到;在jmp rax的地址,rax应该是有两解的符号表示,分开记录为jz jnz就行。
于是直接开始模拟,还不错,直接就算出来了

然后就开始遇到模拟执行最常见的问题:陷入系统函数:

只有个jmp,不能直接跳下条指令,那模拟一下退栈给他弹出来
跳过了也不对,掉read里面了,考虑到这是间接跳转,函数边界不好确定,连直接hook跳外层read都不好搞
那第一反应也不对了,没办法很漂亮的一口气把main里面全去干净,先退而求其次,把call的地址拿到就不进去了,继续执行。
于是call这部分算解决了,然后jmp,首先jmp存在两种情况,但无非根据rax为0/1来判别,这里的想法是手动加判别:
然后jmp就会有相应的两条路径了,加了个小剪枝防止路径爆炸,得到结果

下一步就是把这些东西patch回去
还原
第二步,将计算出来的值重新写入源文件
当然,针对这个附件而言,直接做模式匹配是比较容易的,但是这样感觉是很没意思
这里得想想方案,call前面很有可能有函数参数,rcx在最后一次参与rax的计算后也有可能再走一个值,那合理的想法就是从这些地址倒推回去,找到rax最后一次改变的地址,再往上找到用于生成这个值的点,将这几个修改值的位置拿出来,做写入
查了一下,这个思路比较接近我想要的效果:Liveness Analysis
参考了这个人的方法:感觉实现上没有用到高级的东西,但整体效果挺好的,可惜存在不足之处,下面这段先看看他的做法:
去除反混淆后程序中的冗余汇编代码 - gla2xy's blog
Liveness Analysiss
Liveness Analysis是一种数据流分析技术,用于确定程序中每个变量在程序的不同点上是否”活跃“。”活跃“意为该变量在这之前没有被重新定义且在这之后将被使用。
def
集合:当前汇编指令显式和隐式改变的寄存器、内存的集合。
use
集合:当前汇编指令显式和隐式使用的寄存器、内存的集合。

- 这份代码全用的正则匹配,光是表就用了两三百行,主要就是想依此将复杂的汇编结构先分析为def和use集,而优点在于,它将rflags等寄存器加入考虑范围内,更好解决了一些分支跳转的问题 分析示例:
跟踪策略是:

可以拿到:
操作数解析策略:
- 寄存器类型直接放入def和use集中
- 内存需要直接放入def和use集中,其表达式中的寄存器也要放入def和use集合中
- 立即数不应当放入def和use集合中,应认为是某一寄存器跟踪结束的标志
[-] 这里的算法需要,对cmov特判了一下,针对其执行和不执行两种结果分别向上扫一轮
最终算法:

源代码是直接匹配的,这里借助ai搞了个现代一点的版本:
加入了feature:
- 用ida的函数边界识别,更加准确
- 支持输入特定汇编,用该汇编作为上边界(call rax这类必须进去)
- 主要对sp bp 和cmov做了特判,对是否执行了该mov递归两次取交集
效果:

注意,这里有

代码:
合成
那么,现在混淆定位完成了,patch的方案也出来了,就该把代码合一下了,还没弄特别方便,要手动运行两个代码
上一篇
pythonJail
下一篇
brunnerCTF
Loading...