间接跳转混淆去除

type
status
date
slug
tags
summary
category
icon
password

原理

找了个demo练手 大概结构有: ① 调用函数混淆
notion image
② 检查zf位,实现jz jnz的条件跳转
notion image

过程

(包括一些失败的心路历程,很久没写了,可以直接跳过看代码)

打印

第一步,是先匹配并把这些值计算出来。
找了若干个跳转的混淆,发现有两个非常固定的特征:间接跳转使用的寄存器都是rax,这个特点就很方便我们匹配,还有一个无关紧要的,这里都是用rax + rcx来寻址
那么,第一思路其实很简单,利用angr模拟执行,执行到call rax的位置,这时候rax的值是收敛的,可以直接拿到;在jmp rax的地址,rax应该是有两解的符号表示,分开记录为jz jnz就行。
于是直接开始模拟,还不错,直接就算出来了
notion image
然后就开始遇到模拟执行最常见的问题:陷入系统函数:
notion image
只有个jmp,不能直接跳下条指令,那模拟一下退栈给他弹出来
跳过了也不对,掉read里面了,考虑到这是间接跳转,函数边界不好确定,连直接hook跳外层read都不好搞
那第一反应也不对了,没办法很漂亮的一口气把main里面全去干净,先退而求其次,把call的地址拿到就不进去了,继续执行。
于是call这部分算解决了,然后jmp,首先jmp存在两种情况,但无非根据rax为0/1来判别,这里的想法是手动加判别:
然后jmp就会有相应的两条路径了,加了个小剪枝防止路径爆炸,得到结果
 
notion image
 
下一步就是把这些东西patch回去

还原

第二步,将计算出来的值重新写入源文件
当然,针对这个附件而言,直接做模式匹配是比较容易的,但是这样感觉是很没意思
这里得想想方案,call前面很有可能有函数参数,rcx在最后一次参与rax的计算后也有可能再走一个值,那合理的想法就是从这些地址倒推回去,找到rax最后一次改变的地址,再往上找到用于生成这个值的点,将这几个修改值的位置拿出来,做写入
查了一下,这个思路比较接近我想要的效果:Liveness Analysis 参考了这个人的方法:感觉实现上没有用到高级的东西,但整体效果挺好的,可惜存在不足之处,下面这段先看看他的做法: 去除反混淆后程序中的冗余汇编代码 - gla2xy's blog

Liveness Analysiss

Liveness Analysis是一种数据流分析技术,用于确定程序中每个变量在程序的不同点上是否”活跃“。”活跃“意为该变量在这之前没有被重新定义且在这之后将被使用。
  • def集合:当前汇编指令显式和隐式改变的寄存器、内存的集合。
  • use集合:当前汇编指令显式和隐式使用的寄存器、内存的集合。
notion image
  • 这份代码全用的正则匹配,光是表就用了两三百行,主要就是想依此将复杂的汇编结构先分析为def和use集,而优点在于,它将rflags等寄存器加入考虑范围内,更好解决了一些分支跳转的问题 分析示例:
跟踪策略是:
notion image
可以拿到:
操作数解析策略:
  • 寄存器类型直接放入def和use集中
  • 内存需要直接放入def和use集中,其表达式中的寄存器也要放入def和use集合中
  • 立即数不应当放入def和use集合中,应认为是某一寄存器跟踪结束的标志
[-] 这里的算法需要,对cmov特判了一下,针对其执行和不执行两种结果分别向上扫一轮
最终算法:
notion image
源代码是直接匹配的,这里借助ai搞了个现代一点的版本: 加入了feature:
  • 用ida的函数边界识别,更加准确
  • 支持输入特定汇编,用该汇编作为上边界(call rax这类必须进去)
  • 主要对sp bp 和cmov做了特判,对是否执行了该mov递归两次取交集
效果:
notion image
注意,这里有
notion image
代码:

合成

那么,现在混淆定位完成了,patch的方案也出来了,就该把代码合一下了,还没弄特别方便,要手动运行两个代码
上一篇
pythonJail
下一篇
brunnerCTF
Loading...
文章列表
Hi~, I ‘m moyao
reverse
ctf
pwn
pentest
iot
android
others
iOS