首页
社区
课程
招聘
[求助](已解决)关于angr去除虚假控制流混淆中遇到的死循环问题
发表于: 2022-5-10 16:45 10464

[求助](已解决)关于angr去除虚假控制流混淆中遇到的死循环问题

2022-5-10 16:45
10464

最近在研究虚假控制流的去除问题,参考了大佬 34r7hm4n 的文章 https://bbs.pediy.com/thread-266005.htm 和代码。


我自己编译了一个加混淆的程序,混淆后的函数(0x400500)如下:

然后使用大佬 34r7hm4n 的脚本进行去混淆,发现一直输出 call 的hook记录。日志如下

(base) F:\code\12_py\ollvm\debogus-main>python debogus.py -f ..\bcf_ida\test_bcf -s 0x400500
['0x400500', '0x400601', '0x400700', '0x400606', '0x40070b', '0x400710', '0x400599', '0x40051a', '0x40059e', '0x4006fb', '0x4005a3', '0x40072b', '0x40052c', '0x40082d', '0x400832', '0x4006b2', '0x4008b6', '0x400837', '0x4006b7', '0x400737', '0x4007b6', '0x4008bb', '0x4007bb', '0x4007c0', '0x4008c0', '0x4005c3', '0x400647', '0x4008c7', '0x40064c', '0x4005cd', '0x4008d0', '0x4008d5', '0x400657', '0x4008da', '0x40065c', '0x4008df', '0x400560', '0x400565', '0x40086b', '0x40076b', '0x40066d', '0x400870', '0x400770', '0x4007f4', '0x4008f6', '0x4007f9', '0x4008fb', '0x40067e']
Hook [call      0x400400] at 0x40087a
Hook [call      0x400400] at 0x40087a
Hook [call      0x400400] at 0x40087a
Hook [call      0x400400] at 0x40087a
Hook [call      0x400400] at 0x40087a
Hook [call      0x400400] at 0x40087a
Hook [call      0x400400] at 0x40087a
Hook [call      0x400400] at 0x40087a
Hook [call      0x400400] at 0x40077a
Hook [call      0x400400] at 0x40087a
Hook [call      0x400400] at 0x40087a
Hook [call      0x400400] at 0x40087a
......

经过初步分析是进入了块 0x400870 中一直循环,但是我不知道怎么处理这种情况,希望有大佬指点迷津,万分感谢!


混淆后的程序(test_bcf)和去混淆脚本(debogus.py)见附件。



[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2022-7-7 20:02 被luoye_ATL编辑 ,原因: 问题已经解决
上传的附件:
收藏
免费 0
支持
分享
最新回复 (3)
雪    币: 1242
活跃值: (1434)
能力值: ( LV5,RANK:78 )
在线值:
发帖
回帖
粉丝
2

经过多次尝试,我发现了问题原因。主要是去混淆的这个函数存在char*入参,而我在初始化的时候没有提供该参数的信息,导致angr将其当为默认符号化参数,长度会一直增加,在第一个循环中出不来,就导致了死循环。

解决方法也很简单,使用gdb调试下,查看参数通过哪个寄存器传递,然后使用blank_state获得开始的状态后,对指定寄存器进行初始化。

经过调试,发现我的程序通过rdi传递指针,我就在内存中找了块空间存放符号化变量,然后地址传给rdi就好了。

注意,这里有个问题,不能使用堆栈存放符号化变量,会导致程序只执行一次循环就退出,原因未知,待深入研究。

添加的代码如下:

flag_chars = [claripy.BVS('%d' % i, 8) for i in range(4)]   
flag = claripy.Concat(*flag_chars + [claripy.BVV(0,32)])
state.regs.rdi = 0x600FB0
passwd = 0x600FB0 # 找到内存空间
state.memory.store(passwd, flag)
state.memory.store(passwd+8, 0)

还原后的汇编F5结果如下:


总结:感觉自己能遇到这个问题,一是对angr的理解不够深入,会的东西太少;二是粗心,没有考虑到参数的初始化问题。说到底还是自己太菜了,55555

2022-5-12 18:05
0
雪    币: 2590
活跃值: (5038)
能力值: ( LV9,RANK:225 )
在线值:
发帖
回帖
粉丝
3
//Ver1, 可以去掉bogus
if (x * (x + 1) % 2 == 0) {
  bogus();
}
//Ver2, 应该不可以去掉bogus,因为angr会在第一个循环进入路径爆炸
int x_i = 0;
for (x_i; x_i < x; x_i++); //等价于x_i = x
if (x_i * (x_i + 1) % 2 != 0) {
  bogus();
}
2022-5-12 19:02
0
雪    币: 1242
活跃值: (1434)
能力值: ( LV5,RANK:78 )
在线值:
发帖
回帖
粉丝
4
天水姜伯约 //Ver1, 可以去掉bogus if (x * (x + 1) % 2 == 0) { bogus(); } //Ver2, 应该不可以去掉bogus,因为angr会在第一个循环进入路 ...

我今天按照你说的第二种代码编了个程序试了下,用原来的代码确实不行,在 

proj.factory.simgr(state)

中加入 veritesting=True 参数也不行。

感觉这种情况应该需要人工干预去处理了。


尝试:

我的源码反汇编之后是

因为这里符号执行是为了找到执行过的块,考虑到后面的if条件永假,所以我这边做了个简单处理:循环执行一次就剪该链路。

处理后的执行代码如下:

while len(simgr.active) > 0:
    for active in simgr.active:
        if active.addr not in blocks and active.addr == 0x4007a2: # 指定该块,执行过后就剪掉
            simgr.active.remove(active)
        blocks.discard(active.addr)
        # hook call instructions
        ......

    simgr.step()

最后的结果反汇编如下

可以看到是去除了。


思考:

我这种处理方法是特例,因为变量 i 对后续代码的影响有限。

要是后续有使用 i 且产生分支,我觉得有两种处理方法,都是人工干预

1、在剪掉这个路径后,在下一个代码块开始时,手动对循环中变化的变量(本例中为 i )进行符号化赋值,或者直接跳过这个循环,然后符号化赋值。

2、针对本例 for 循环这种单输入输入的代码块,进入前切换到模拟执行(使用类似unicorn工具),该块代码执行完后,切换到符号执行模式,同步变量。思路类似于symbion,也就是帖子 http://www.hackdig.com/09/hack-467817.htm 提到的第四种方法。

感觉第一种方法容易实现些。第二种方法工作量比较大,不知道业内有没有成熟的方法。


当然,这只是我的个人看法,如果有什么不对的地方,希望大佬指针,也希望论坛的朋友们提出建议。

2022-5-13 11:50
0
游客
登录 | 注册 方可回帖
返回
//