01 Confused
首先参考链接 https://www.52pojie.cn/forum.php?mod=viewthread&tid=860237&page=1
首先分析到这个sub_1000011D0是关键函数是没有什么问题的,直接shift+f12定位DDCTF的字符串就到了这一部分逻辑
跟进去以后发现
这个函数首先分配了一个0xb8大小的空间,然后填充为0x00,然后把这个空间传入了一个sub_100001F60函数 跟进去
首先先按H把-16这些改成 unsigned_int8类型,看着方便。 这里对照前面那个文章里的看,就是在对vm_cpu结构体进行初始化,前6个四字节的显然是寄存器,后几个是绑定虚拟机字节码和字节码对应的函数 这里可以切换到structures窗口按insert创建一个结构体,这样来更清晰的看代码 首先要对下面初始化的结构体和opcode对应的每个函数简要分析 第一个0xF0虚拟机指令对应的函数
首先可以看到每个函数中一开始都在引用 (_QWORD*)(a1+24),于是猜测这是指令指针寄存器,在structure窗口中先创建结构体给这个偏移处的qword*取个名字叫myeip
可以看到v2是虚拟机指令指针寄存器指向的地方后面第二个字节的内容,然后有一个switch结构,是在判断虚拟机指令后面第一个字节指定的是哪个虚拟机寄存器,然后将v2赋值到寄存器里,如果是0x14的话就把 *((char*)qword_100003F58+*v2)赋值给第一个寄存器,显然这qword100003F58相当于虚拟机的栈,然后函数负责移动指令指针寄存器,这个函数对应的虚拟机指令占六个字节,所以在最后有 *(_QWORD*)(a1+24)+=6LL;,所以这个函数的功能就是把一个立即数传入虚拟机的一个寄存器中,给他取个名字叫movreg_imm,方便后面查看
然后第二个0xF1对应的函数
可以看到他把前两个寄存器里的值进行异或然后又把结果放到了第一个寄存器中,同样的这个虚拟机指令占一个字节,此函数负责将指令指针寄存器加1字节
0xF2对应的函数和0xF6对应的函数要结合来看
可以看到0xF2中他把
*(_DWORD*)a1==*((char*)qword_100003F58+*(unsigned__int8*)(*(_QWORD*)(a1+24)+1LL));这个表达式的结果放到了 *(_DWORD*)(a1+16)里,0xF6中他有根据 *(_DWORD*)(a1+16)移动eip指令指针寄存器,这就很明显了,这个寄存器的功能就是标志寄存器,存放对比的结果,然后后面一个函数就是条件跳转指令了
然后0xF4
很明显的看出这个实在做加法运算,把前两个寄存器的值加起来放到第一个寄存器里,取个名字叫addregimm
然后0xF5
与上面的函数类似,这个是减法,取个名字叫subregimm,跟进0xF3对应的函数发现只有一个花括号,于是取名为nop,然后0xF7
可以看到他把操作数放进了 (_DWORD*)(a1+176)处,先不管他是在干啥,先看0xF8
可以看到他调用了下级函数sub100001B80,显然这个sub100001B80函数是在对(unsigned int)(char)*(_DWORD *)a1进行移位为2的凯撒加密,首先他通过ascii码的范围判断是大写还是小写,以确保大小写字母都能够通用,然后对其进行凯撒移位
分析完了这几个函数和几个寄存器以后,可以把结构体补充完整了 如下 前四个就是通用用途寄存器,第五个就是标志寄存器,第六个是指令指针寄存器,其余的函数的功能就如其名称所示
现在返回去查看sub_100001F00函数里调用的第二个函数
可以看到此函数初始化了myeip指令指针寄存器将其指向 (__int64)&loc_100001980+4的地方 现在vm_cpu结构体搞清楚了,就可以照着结构体翻译 (__int64)&loc_100001980+4处的虚拟机opcode了 这里既然我们已经了解了这些opcode的功能,其实并不需要费力去提取出代码执行或者写脚本一句一句翻译了 他就是把一个立即数装入r1中,然后判断大小写,对其进行凯撒移位,我们直接在idapython里写一句脚本便出来了
当然也可以写个脚本把整个虚拟机字节码全部翻译出来,鉴于比赛时的时间限制,如今比赛已停止,可以看一下 首先可以用get_bytes(0x100001984,3000,0).encode("hex")把虚拟机字节码给提取出来,也可以用lazyida插件 然后解析代码如下
运行后效果如下,极大的增强了可读性
像这种伪代码不能运行,所以说实际比赛中还是分析好了像前一个方法一样,不一句一句翻译,直接写脚本解题比较快
02 Obfuscating macros
ubuntu64 + gdbserver + ida64调试环境
首先main函数里关键的函数只有两个,一个sub4069D6一个sub4013E6,先跟进第一个函数,在所有传入我们输入的变量的地方下断点 然后配置好远程gdb调试器,f9运行起来,随手输入ABCD,ida中触发断点,首先先观察寄存器的值,在堆栈中找到我们输入的字符串
然后一路f9观察对我们输入的字符串做了什么, f9几次后会发现他把我们输入的ABCD转换成了0xABCD
继而我们推测下一个函数会对0xABCD进一步处理或者与一些值进行比较,于是删除所有其他的断点,在0xABCD处下内存读写断点,然后f9运行,就触发了硬件断点
第一次 test al,al是检测输入是否为空,再按f9触发第二次断点,会发现如下代码
f8到0000000000405FC6处会发现他拿我们的0xAB与一个0x79进行了对比,这样的话我们只需要在sub这里下一个断点,一路f9就好了 他一开始没有验证长度,直接进行了对比,只要对比到不同就退出,因为我们输入了ABCD,观察到他对比一次就会退出了 这样我们就可以每次多输入两个字符,并且这两个字符只能是0到9和A到F,然后取出新的对比的字符再多输入两个字符直到他不在对比就得到flag了
这道题目还是说,如果没有先调试得到的知识背景,用pintool解题也会卡壳的,因为经过我们的分析,发现第一个函数把字符串ABCD转换成了0xABCD,这里四个字节变成了两个字节,如果用pintool解题需要两个字符两个字符得组合得测试,这个题目不是纯正的ollvm混淆的,大体的思路就是污点追踪,把所有的涉及到用户输入的地方都下断点,还有就是内存断点的使用,在必要时刻是很有用的。
03 题目链接
链接:https://pan.baidu.com/s/1yx9RlgITi5rjcHGBgZ1DDA 提取码:ql9p
04 相关操作学习
CTF-Reverse系列汇总:从最简单的逆向分析开始,循序渐进地讲解关键代码定位、算法分析、手工脱壳、病毒分析等实验,同时详细介绍了常见逆向分析工具的使用方法,点击前往 合天网安实验室,开始操作学习!
网友评论