攻防世界-polyre

Writeup

IDA打开查看,可以看到使用了OLLVM中的一种代码保护方式——控制流平坦化。在IDA反汇编上显示出来就是使用while+switch的形式,经过平坦化之后的代码块大部分整齐地堆积在程序流图的下方,而这部分代码就是真正有意义的代码块,称之为相关块。

 

具体脚本编写思路可参考:https://security.tencent.com/index.php/blog/msg/112

或者直接网上找脚本(需要安装angr):https://github.com/cq674350529/deflat

脚本解完后,main函数里已经能看出大概逻辑了,不过仍有大量的while ture和不透明谓词,需要再写脚本处理这些虚假控制流,

具体符号执行脚本编写思路可参考:https://mp.weixin.qq.com/s/d8xoR3VdMf6lMfnETaQHZw

 我这里就针对题目简单写个IDA脚本,由调试这些可以知道,红框部分的判断恒为false,所以有大量代码块永远不会执行到,把这些不会执行到的代码块nop掉,条件判断的jnz改jmp。

 我这里IDA是7.5版本,和之前版本有些API函数变化,可参考官网:https://www.hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml

几个注意点:

这里JNZ操作码是0F 85,修改成JMP的E9后,需要再修改偏移量,JNZ这条指令长度为6,JMP长度为5,需要把addr+5处nop掉。

JMP偏移量=目标地址-(当前地址+当前指令长度)

只是要IDA能F5反编译结果的话,修改好JNZ成JMP就好了,那些不可达代码块不nop掉无所谓

IDA脚本如下

def NextAddr(addr):
    return addr+get_item_size(addr)
st=0x400620
ed=0x4020c7
addr=st
while addr<ed:
    if "ds:dword_603054" in generate_disasm_line(addr,0):
        while True:
            addr=NextAddr(addr)
            if "jnz" in generate_disasm_line(addr,0):
                dest=get_operand_value(addr,0)
                offset=dest-(addr+5)
                patch_byte(addr,0xe9)
                patch_byte(addr+5,0x90)    
                patch_dword(addr+1,offset)
                break
            if addr>=ed:
                break
    addr=NextAddr(addr)
View Code

再F5就能看到较清晰的代码逻辑了,对输入的字符串,分成6组,每组8个字符,

 解密脚本如下:

s=[0xBC8FF26D43536296, 0x520100780530EE16, 0x4DC0B5EA935F08EC,0x342B90AFD853F450, 0x8B250EBCAA2C3681, 0x55759F81A2C68AE4]
res=""
key=0xB0004B7679FA26B3
for item in s:
    for j in range(64):
        f=item&1
        if f==1:
            item=(item^key)//2
            item = item | 0x8000000000000000
        else:
            item=item//2
    n=hex(item)[2:]
    print(hex(item))
    for j in range(len(n)-1,-1,-2):
        res += chr(int("0x" + n[j-1:j + 1], 16))
print(res)

在原来的数左移一位之后,结果最后一位必然是0,如果原来的数大于0,那一次处理的结果最后一位必然是0。如果原来的数小于0,则在进行一步和0xB0004B7679FA26B3异或,异或后最后一位必然是1。所以可以根据结果的最后一位是1还是0,判断原来数是否小于0。如果小于0,负数右移一位会溢出成正数,在加上0x8000000000000000才能得到原来的数。

得到

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

posted @ 2021-11-17 15:45  写忧  阅读(310)  评论(0编辑  收藏  举报