首页
社区
课程
招聘
[原创]去间接跳转/模拟执行学习
发表于: 2025-11-30 13:10 7503

[原创]去间接跳转/模拟执行学习

2025-11-30 13:10
7503

间接跳转这个高级混淆,接触了unicorn,unidbg这种模拟执行脚本后,发现这种特别适合对抗反编译器无法分辨的一些混淆,本文记录学习过程以及以京麒2024的drillbeam为样本尝试去去除间接跳转。笔者的脚本能力确实有限,写的不好的地方大佬们勿喷(

如上的样本就是经典的间接跳转混淆,这种jmp eax,可以让反编译工具无法计算跳转的地址,导致反编译出错,这种混淆手段在vmp中也有体现,我们分别看看这个在ida和bn的效果

ida:

直接导致cfg丢失

bn:

bn虽然能分析个大概,不过好像仍然有问题,这里粗暴的把查表的操作分析成了switch

不过有办法

把data段改成只读

就可以正常分析了,因为计算地址存储在了data段(比如我这里的查表),反编译器不会去解析data段的数据,因为data段默认可写,导致被引用都的值被看作的是变量而非常量,阻止了常量传播,设置为可读的话,bn会去把data段看成常量,这也的话动态分析就可能解析出跳转的地址,但是这种方法并不高枕无忧,还是会存在问题


对于一般的ctf题的话,可能就只会存在一两种间接跳转的计算方式,这个时候我们就可以手动计算出然后写脚本简单去除就行,但是我们把情况放极端一点,如果一个程序充斥着几千种不重样的间接跳转,或者说ollvm里面还嵌套了间接跳转的话,挨个计算然后去除岂不是效率低


回到手动计算,总之就是计算,有没有自动化工具计算然后识别,然后去除,这不就满足了我们手动计算->ida脚本去除的完整过程了吗


这种一般就是模拟执行的过程,模拟执行的框架一般有unicorn unidbg qiling angr等等,我写unidbg要稍微多一点


这几种可以说是都在unicorn基础上成立的,为了更加理解这种去除思路,故用unicorn写一下,而且unicorn更适合跑指令


可以很好的模拟出结果


可是unicorn是纯裸的CPU,意味着我们如果暴力的把整个exe直接载入CPU的内存里,一般是运行不起来的,还需要对一些系统函数进行重定位的操作



我们先假设只有一个间接跳转,先试着模拟一下

对于这种只有一个的话,我们的模拟手段就很简单的,因为我们已经找到了间接跳转的位置,所以自然可以找到需要模拟开始的地址以及结束地址,但是,这一路也不能直接裸调用,因为可以看到这里的查表操作是涉及到了读取数据段的东西了,我选择的是直接载入整个exe,然后单个开机模拟执行一段即可

模拟PE的话,跟我在模拟ELF是不一样的,ELF我模拟的时候,基地址设置0,然后模拟区间和ida看到的偏移一样即可,但是PE文件不一样,因为PE的RAW和RVA是按照节表换算的



这里是把栈映射进去,把这个映射进去也是模拟栈帧创建,局部变量等等,这里减少4是为了防止栈崩掉

方便我们看到是哪条指令崩掉了,快速查找或者看值

这样可以看到已经完成了模拟

现在我们可以加一点东西了,首先,需要知道何时去patch,这里我们可以用到反汇编引擎,去识别到jmp eax,然后直接改成jmp xxxx

这里涉及到了keystone和capstone,感觉只要是模拟执行都绕不过这两个东西



上面就是简单的修改,不过这种修改有个bug感觉,就是修改后的指令大小有一点怕覆盖下面如果有用的指令(当然这里不成立,因为下面四个字节都没用)


这里修好了,那如何去patch回文件呢

这里可以选择直接把unicorn的内存直接修改后复制到新文件


我们选择去把unicorn内存修改然后试着覆盖回程序

但是为了防止覆盖后面的指令,所以我们先设置一个可写区域

0x11CAF-0x11CC8其实都是可以随便修改的(根据间接跳转而不同)




对比一下

去除前

去除后


缺陷:以上只是简单的单个模拟,目的只是学习,缺陷也十分明显

第一是eax寄存器是固定的情况,当然可能会出现jmp ecx这种,如果为了更全面的考虑可以把所有通用寄存器都加入进去

第二是如果间接跳转存在条件判断的问题,例如arm的 CSEL和BR模式的话,也无能为力了

第三是回溯修改的区间被我固定了,如果遇到大小不一致的话,也很难去去除


这个题是间接跳转+ollvm混淆,导致解题难度比较大,赛中尝试的是去用trace到每一个函数,然后挨个看,找到了xxtea加密,结合d810去混淆勉强能看,不过init_array也有间接跳转,而且是存在修改delta的操作的

这里我看了一个师傅的unidbg脚本去间接跳转和去平坦化

https://bbs.kanxue.com/thread-277086.htm

学习一下并且尝试为这个题写一下


注:这里只讨论如何去除间接跳转,不讨论做这个题的过程

(含有检测模拟器的操作)

可以看到间接跳转数量多,而且是这种存在条件判断的,不能简单的去除,如果只是patch,cfg不会被修复

我们试一下unidbg而不是unicorn因为unidbg在模拟安卓层面可以让我们少走一些弯路


像unidbg这种模拟执行框架,属于是比较成熟的了,可以帮我们完成so的加载和解析



这里就是一些初始化的操作了,加载了模拟器,内存管理模块以及要调用的so模块,如果这里我们需要补充其他的so的加载,也只需要去加载一下就行了


注意把代码放在loadlibrary之前

这种trace手段的话,是有寄存器信息的,这里的setRedirect是设置重定向,把输出结果从默认的控制台保存到文本文件里面。




配合前面的init_array就可以成功调试init_array函数


这一类的间接跳转感觉都比较相似

都是CMP CSEL的形式或者有

不过都是 比较,然后根据比较的结果选取值作为偏移,然后去查表加基址常量,然后br跳转

这种条件是不固定的话,我们就需要去计算不同情况下的跳转情况


我们发现程序的间接跳转可以说有很多种,比如cmp到br之间有CSEL LT   和CSEL  EQ       CSINC    NE

以及cmp和br中间还有有用的逻辑,但凡多patch一点,就会导致程序进入死循环,作者应该是采用了LLVM的间接跳转去替换掉了一些条件跳转,所以去除难道比较大,而且兼容必须要好才行,因为cmp到br之间的长度也不固定,计算方式不一样



[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 2025-11-30 13:38 被zzzhangyu编辑 ,原因:
收藏
免费 18
支持
分享
最新回复 (8)
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
与上一篇有啥区别呢
2025-11-30 13:22
0
雪    币: 1241
活跃值: (1546)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
mb_ldbucrik 与上一篇有啥区别呢

上一篇有点问题,已经被我删除了

最后于 2025-11-30 13:53 被zzzhangyu编辑 ,原因:
2025-11-30 13:30
0
雪    币: 1495
活跃值: (3698)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
4
快快快快,都用Binaryninja,网上啥也没有
2025-12-4 18:18
0
雪    币: 5630
活跃值: (9442)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
its very nice
2025-12-5 09:21
0
雪    币: 5607
活跃值: (6477)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
玩unicorn的人很多 但至今为止我没有在网上看到一个用来还原vmp指令的,这是为什么。unicorn开发到最后有啥缺陷吗?
2025-12-5 17:23
0
雪    币: 179
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7

2025-12-5 18:10
0
雪    币: 1241
活跃值: (1546)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
wx_ㅤ_683
这是没有去干净吧,可以尝试去手动指定去除范围的区间,我代码里有示例, 因为有些cmp到br的距离太长了超出代码的预定范围了或者是unidbg根本没有执行到那里导致的,模拟执行还是不能通杀这种混淆的
2025-12-5 19:48
0
雪    币: 102
活跃值: (3305)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
mark
2025-12-7 00:09
0
游客
登录 | 注册 方可回帖
返回