SUSCTF2022的tttree逆向wp

本文介绍了作者通过分析Treap树和汇编代码解决竞赛难题的过程。首先,从Wikipedia中了解Treap的概念,然后解析全汇编代码,通过过滤垃圾指令得到关键逻辑。在理解汇编代码逻辑后,构建了Treap树并实现了验证条件的函数。最终,通过遍历树结构还原原始输入字符串。解题过程中涉及到了二叉树、堆、伪随机序列和ASCII编码等知识。
摘要由CSDN通过智能技术生成

这题最终有5个队伍解出来,我比赛的时候感觉不算很难,不过比赛的那两天正好有事,大致看了下觉得有希望,但是没什么时间所以没做出来。然后过了几天再做,没想到花了近一周的时间才搞定,确实超出我的能力了。

题目给了一个附件和提示:https://en.m.wikipedia.org/wiki/Treap
看了这个,知道了Treap就是二叉树和堆的结合体,和平衡二叉树有点类似。

代码基本是全汇编写的,采用代码片段实现业务逻辑的单条指令的执行
在这里插入图片描述
这里是2个代码片段,实际实现的代码就这两句:

lea	rcx,[rsp+40h]
inc	eax

按照官方的wp,这里的垃圾代码应该用IDA直接patch掉,变成跳转语句,这样可以用IDA直接显示业务逻辑,我当时没有patch,是用了个笨办法,把指令提取出来,然后输出到文件里,然后对这些垃圾指令进行过滤,然后得到比较清晰的汇编代码,过滤的操作代码是这样的

with open(r'g:\share\20220226\tttree\tree.log','r') as f:
    g=open(r'g:\share\20220226\tttree\tree1.log','w')
    lines=f.readlines()
    frame=0
    skip=0
    for l in range(len(lines)):
        line=lines[l].split()
        if line[2]=="retn":
            frame+=1
            skip=0
        elif line[2]=="push" and line[3]=="rax" and "pushfq" in lines[l+1]:
            skip=1
        elif line[2]=="popfq":
            skip=0
        elif skip==0:
            g.write(lines[l])
g.close()
with open(r'g:\share\20220226\tttree\tree1.log','r') as f:
    g=open(r'g:\share\20220226\tttree\tree2.log','w')
    lines=f.readlines()
    frame=0
    skip=0
    for l in range(len(lines)-1):
        line=lines[l].split()
        line2=lines[l+1].split()
        if line[2]=="push" and line[3]==line2[3] and line2[2]=="pop":
            skip=1
        elif skip==0:
            g.write(lines[l])
        else:
            skip=0
g.close()

得到的汇编代码是这样的:
在这里插入图片描述
根据这些汇编代码,结合Treap的文档,经过大量的脑力活动,得到代码的逻辑如下:
首先根据输入的字符串,判断前后缀是不是SUSCTF{},然后用中间的32个字符生成Treap树,每个插入的节点权重是伪随机序列生成的随机数,每个节点的值是伪随机数+对应的字符ASCII码,根节点的权重最小。

a=[0x0A2, 0x0AF, 0x09D, 0x0B7, 0x0D2, 0x0CB, 0x0C7, 0x0C6, 0x0B0, 0x0D5, 0x0DA, 0x0E3, 0x0E6, 0x0E8, 0x0E9, 0x0F3, 0x0F4, 0x0EF, 0x0EE, 0x0F7, 0x0F9, 0x0FF, 0x101, 0x0F5, 0x109, 0x11F, 0x11A, 0x146, 0x124, 0x10F, 0x106, 0x0DF]
b=[0x0A8, 0x131, 0x113, 0x047, 0x09E, 0x03B, 0x03A, 0x0BF, 0x092, 0x0F0, 0x174, 0x0C3, 0x289, 0x104, 0x260, 0x04D, 0x2FB, 0x09E, 0x191, 0x158, 0x07D, 0x04A, 0x1E9, 0x101, 0x0D0, 0x0FC, 0x070, 0x11F, 0x345, 0x162, 0x2A4, 0x092]
c=[0x0AC, 0x0FD, 0x247, 0x115, 0x0D4, 0x2B5, 0x1FC, 0x28B, 0x14A, 0x04C, 0x08E, 0x0E9, 0x055, 0x12C, 0x0F5, 0x0E3, 0x081, 0x2E2, 0x1A8, 0x117, 0x152, 0x101, 0x03A, 0x1D0, 0x0A8, 0x0CC, 0x149, 0x137, 0x300, 0x1EC, 0x276, 0x247]
addkey=[0xC1,0xA7,0xC3,0x64,0x77,0x7A,0x7F,0x73,0xAE,0xB2,0x66,0x86,0x99,0x90,0x75,0xB0,0xBC,0x8E,0xAD,0x87,0xBB,0x70,0x65,0x68,0xC0,0x7E,0xA9,0x80,0xC8,0xA5,0x9C,0x98]

最后程序验证这个树是否满足下面的条件:
判断规则:
1、所有子节点,从左->右开始遍历,每个节点的值可以和a列表对应上
2、如果第i个节点是其父节点的左节点,则第i+1个b列表的值为父节点的输入位置×23+父节点的输入字符ASCII
3、如果第i个节点是其父节点的右节点,则第i+1个c列表的值为父节点的输入位置×23+父节点的输入字符ASCII

根据这些规则首先把a列表还原成目标树,我一开始的时候完全是手工完成的,后来看了官方的wp才会根据数据所在的位置来判断树的形状
然后得到树结构以后,再根据列表b和c,从根节点开始逐个生成子节点,直至还原所有的字符。
完整的解题代码如下:

class TreapNode(object):
    def __init__(self, value):
        self.value=value
        self.left = None
        self.right = None
        self.p = None
        self.weight=None
        self.slot=None
        self.ans=None

def create_tree(inorder,postorder):
    if len(postorder)==0:
        return None
    x=postorder[-1]
    pos=inorder.index(x)
    root=TreapNode(x)
    l=len(postorder)
    #print(len(inorder),inorder,postorder,pos,l)
    root.left=create_tree(inorder[:pos],postorder[:pos])
    root.right=create_tree(inorder[pos+1:],postorder[pos:l-1])
    return root

flag=[' ']*32
def walk(node):
    global flag
    flag[node.slot]=node.ans
    if node.left:
        i=(b[a.index(node.value)]-ord(node.ans))
        if i%23>0:
            print(hex(node.left.value),a.index(node.value),node.ans,i,'error')
            return
        i=i//23-1
        node.left.weight=rands[i]
        node.left.slot=i
        node.left.ans=chr(node.left.value-addkey[i])
        walk(node.left)
    if node.right:
        i=(c[a.index(node.value)]-ord(node.ans))
        if i%23>0:
            print(hex(node.right.value),a.index(node.value),node.ans,i,'error')
            return
        i=i//23-1
        node.right.weight=rands[i]
        node.right.slot=i
        node.right.ans=chr(node.right.value-addkey[i])
        walk(node.right)

root=create_tree(d,a)
root.weight=min(rands)
root.slot=rands.index(root.weight)
root.ans=chr(root.value-addkey[root.slot])
walk(root)
print(''.join(flag))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值