RoarCTF-2019-polyre(控制流平坦化+虚假控制流+CRC32校验)

二木 王者

拿到题目我们可以发现,这个是开了ollvm控制流平坦化的题目,优化有点高,不好直接进行分析。

image-20231017160156261

对于ollvm控制流平坦化,对于一般的开得比较少的,没有控制流没有那么复杂的,一般的的我们都在最后进行下断点,然后直接分析汇编代码即可,对于像这种,我们就需要使用脚本去除控制流,让我们的题目能更好的分析,对于脚本,使用的原理大都是使用的符号执行,例如angr等符号执行,先将整个程序跑一遍,将不执行的代码进行nop,将执行的代码进行Jmp等的链接。

使用的脚本为

deflat.py

am_graph.py

1
2
python deflat.py RoarCTF-2019-polyre1 0x400620    #python版本 + 脚本名 + 文件名 + 混淆起始地址”

image-20231017163633118

这里我们就解出了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 idaapi
import idc

st = 0x0000000000401117
end = 0x0000000000402144

def patch_nop(start, end):
for i in range(start, end):
idaapi.patch_byte(i, 0x90) # 修改指定地址处的指令 0x90是最简单的1字节nop

def next_instr(addr):
return addr + idaapi.get_item_size(addr) # get_item_size获取指令或数据长度,这个函数的作用就是去往下一条指令

addr = st
while addr < end:
next = next_instr(addr)
if "ds:dword_603054" in idc.GetDisasm(addr): # GetDisasm(addr)得到addr的反汇编语句
while True:
addr = next
next = next_instr(addr)
if "jnz" in idc.GetDisasm(addr):
dest = idc.get_operand_value(addr, 0) # 使用idc.get_operand_value来获取操作数
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


成功的可以看到了美丽的代码,真的,这个时候这个反汇编出的代码太美丽了,舒服多了,直接进行分析,干!

image-20231017163330517

最后我们可以发现,程序使用的是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 = ""

# 产生CRC32查表法所用的表
for s in secret:
for i in range(64):
sign = s & 1 # 数乘以二,必为偶数,再异或上奇数,得奇数 所以最后一位为1的数还原前为负。
if sign == 1:
s ^= key
s //= 2
# 防止负值除2,溢出为正值
if sign == 1:
s |= 0x8000000000000000 # 让数回到负数

j = 0
while j < 8:
flag += chr(s & 0xFF) # &0xff可以将高的24位置为0,低8位保持原样。
s >>= 8
j += 1
print(flag)

可以解出flag

flag{6ff29390-6c20-4c56-ba70-a95758e3d1f8}

 评论
目录