之前看到过一次平坦化的题,不是很复杂,就纯分析,这次这个实在太狠了。所以学习了一下脚本的使用。
https://github.com/cq674350529/deflat
这个angr安装可能会踩很多坑,我已经忘了怎么踩得了,就不过多讲解了,自行安装。
然后使用defalt.py进行去除平坦化,
成功去除后,代码看着舒服多了。
但是会发现有很多永远为真的跳转,迷惑人的,叫虚假控制流,无效指令直接Patch掉。学习一下这个去除虚假控制流的脚本吧。
#coding:utf-8
def patch_nop(start,end):
for i in range(start,end):
PatchByte(i, 0x90) #修改指定地址处的指令 0x90是最简单的1字节nop
def next_instr(addr):
return addr+ItemSize(addr) #ItemSize获取指令或数据长度,这个函数的作用就是去往下一条指令
st = 0x0000000000401117
end = 0x0000000000402144
addr = st
while(addr<end):
next = next_instr(addr)
if "ds:dword_603054" in GetDisasm(addr): #GetDisasm(addr)得到addr的反汇编语句
while(True):
addr = next
next = next_instr(addr)
if "jnz" in GetDisasm(addr):
dest = GetOperandValue(addr, 0) #得到操作数,就是指令后的数
PatchByte(addr, 0xe9)
PatchByte(addr+5, 0x90)
offset = dest - (addr + 5)
PatchDword(addr + 1, offset)
print("patch bcf: 0x%x"%addr)
addr = next
break
else:
addr = next
还是要好好学习IDApython啊,
这样代码看着就更舒服了。
然后程序算法也不难,但是比赛的时候不会写脚本,没跑出来,我哭了。。。。
思路:
观察代码逻辑,首先获取输入,然后把回车用\0替换。
接下来一段代码需要仔细识别。取输入每八个字节作为一个Qword,然后比较他的正负。注意这里其实是取最高位,正的话最高位为0,负最高位为1.
然后先左移1位,如果为负(最高位为1)则异或常量,否则继续。
如此循环64次。
据说是CRC64加密,我压根不知道。。。。
[CRC RevEng](https://sourceforge.net/projects/reveng/),它可以用来计算CRC的逆。
CRC算法这里不讲解,因为俺也不会23333333
看一个解题脚本吧,因为自己没写出来,太菜了。。。
s = ['96','62','53','43','6D','F2','8F','BC','16','EE','30','05','78','00','01','52','EC','08','5F','93','EA','B5','C0','4D','50','F4','53','D8','AF','90','2B','34','81','36','2C','AA','BC','0E','25','8B','E4','8A','C6','A2','81','9F','75','55']
flag = ''
a = 0
for i in range(6):
s1 = '0x' + s[a+7] + s[a+6] + s[a+5] + s[a+4] + s[a+3] + s[a+2] + s[a+1] + s[a]
s2 = int(s1, 16)
s4 = s2
for j in range(64):
s3 = int(hex(s4), 16)
s3 = s3 & 1
if s3 == 0:
s4 = s4 >> 1
else:
s4 = s4 ^ int('0xB0004B7679FA26B3', 16)
s4 = s4 >> 1
s4 |= 0x8000000000000000
print(hex(s4)[2:])
flag += hex(s4)[2:]
a += 8
解释一下 s3 & 1和 s4 |= 0x8000000000000000 吧,因为我写脚本卡到这里了。
当16进制数大于 0x8000000000000000会溢出。
源代码是这样
如果有符号16进制数小于0就说明它的二进制第一位是1,所以左移一位时就会溢出,而源代码在小于0时(溢出时),会和0xB0004B7679FA26B3异或,异或后肯定是奇数,因为左移一位后的二进制数的最后一位是0,而0xB0004B7679FA26B3的二进制数最后一位为1所以必定是奇数。所以我们的脚本就可以根据最后一位的奇偶性判断是否需要异或。如果有溢出,在右移一位后加上0x8000000000000000才能得到原来的数。大家可以手动计算一下。
最后的flag需要每8个倒序。