拿到题目我们可以发现,这个是开了ollvm控制流平坦化的题目,优化有点高,不好直接进行分析。
对于ollvm控制流平坦化,对于一般的开得比较少的,没有控制流没有那么复杂的,一般的的我们都在最后进行下断点,然后直接分析汇编代码即可,对于像这种,我们就需要使用脚本去除控制流,让我们的题目能更好的分析,对于脚本,使用的原理大都是使用的符号执行,例如angr等符号执行,先将整个程序跑一遍,将不执行的代码进行nop,将执行的代码进行Jmp等的链接。
使用的脚本为
deflat.py
am_graph.py
1 2 python deflat.py RoarCTF-2019 -polyre1 0x400620 #python版本 + 脚本名 + 文件名 + 混淆起始地址”
这里我们就解出了ollvm的混淆,但是我们将生成的文件放入IDA中分析,我们可以发现,还是很花,有着许多if与while无从下手,这个就是加了虚假控制流的混淆。基本上还是能看了,如果头铁可以直接进行分析了,但是。。。。有点恼火。
所以我们写个脚本来自动去除虚假控制流。在idapython中书写以下脚本运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import idaapiimport idcst = 0x0000000000401117 end = 0x0000000000402144 def patch_nop (start, end ): for i in range (start, end): idaapi.patch_byte(i, 0x90 ) def next_instr (addr ): return addr + idaapi.get_item_size(addr) addr = st while addr < end: next = next_instr(addr) if "ds:dword_603054" in idc.GetDisasm(addr): while True : addr = next next = next_instr(addr) if "jnz" in idc.GetDisasm(addr): dest = idc.get_operand_value(addr, 0 ) idaapi.patch_byte(addr, 0xe9 ) idaapi.patch_byte(addr + 5 , 0x90 ) offset = dest - (addr + 5 ) idaapi.patch_dword(addr + 1 , offset) print ("patch bcf: 0x%x" % addr) addr = next break else : addr = next
成功的可以看到了美丽的代码,真的,这个时候这个反汇编出的代码太美丽了,舒服多了,直接进行分析,干!
最后我们可以发现,程序使用的是CRC32的加密校验的方法
我们可以找到此题加密的CRC32的源码原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void GenerateTable (uint32_t polynomial) { for (int byte = 0 ; byte < 256 ; ++byte) { uint32_t crc = byte; for (int bit = 32 ; bit > 0 ; --bit) { if (crc & 0x80000000 ) { crc = (crc << 1 ) ^ polynomial; } else { crc <<= 1 ; } } crcTable[byte] = crc; } }
CRC校验(2):CRC32查表法详解、代码实现和CRC反转-CSDN博客
基于正向代码,我们可以写出逆向代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 secret = [0xBC8FF26D43536296 , 0x520100780530EE16 , 0x4DC0B5EA935F08EC , 0x342B90AFD853F450 , 0x8B250EBCAA2C3681 , 0x55759F81A2C68AE4 ] key = 0xB0004B7679FA26B3 flag = "" for s in secret: for i in range (64 ): sign = s & 1 if sign == 1 : s ^= key s //= 2 if sign == 1 : s |= 0x8000000000000000 j = 0 while j < 8 : flag += chr (s & 0xFF ) s >>= 8 j += 1 print (flag)
可以解出flag
flag{6ff29390-6c20-4c56-ba70-a95758e3d1f8}