第4天:RoarCTF-Polyre学习

之前看到过一次平坦化的题,不是很复杂,就纯分析,这次这个实在太狠了。所以学习了一下脚本的使用。

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个倒序。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值