Silver

知识分子应该首先做好自己的专业,每个人有自己的领域,尽可能做好研究,
然后如果在这个社会有一些现象涉及到研究领域,你要出来发声,
因为别人不熟悉不了解,我们知识分子有义务传播自己的理念。
--李银河,《恶毒梁欢秀》第二季

RCTF 2015 Reverse 复盘

Last updated:Feb.24, 2016 CST 20:49:10

Note

这是一篇尚未完成的文章

Re100: NeoYang

not sequence:100

The sequence is not a sequence But RE is RE

祭出IDA,可以看到两个关键判断函数。

程序读入一长串数字后开始运算。第一个判断函数位于.text:0804867B,当输入正确的密码时,对其描述为:对password[i*(i+1)/2:i*(i+3)/2]中的元素集合A,满足sum(A)==2**(i+1)且A中的任意元素x满足x!=0时循环正常进行,其中i=0,1,2,..,19;该函数返回值即为等差数列个数i。不妨认为password是一个三角形的数列,每行为含有i+1项的斐波契那数列,即可满足要求。这样可以构造出一个可以通过该函数的样例:

fp=open('eg','w')
for i in range(20):
    for j in range(i+1):
        if (j<2):
            fp.write('1 ')
        else:
            fp.write(str(2**(j-1))+' ')
    fp.write('\n')
fp.write("0\n")
fp.close()

第二个函数位于.text:08048731,要求对于上面建立的三角数列,对于任意i=range(1,20)满足password[19][i]=sum(password[i-1][:18])。将上式修改为下面的帕斯卡三角的输出程序,则可得到正确结果:

def pas_triangles():
    a = [1]
    while True:
        yield a
        a = [sum(i) for i in zip([0] + a, a + [0])]

if __name__ == "__main__":
    fp=open('trig','w')
    g = pas_triangles()
    for n in range(20):
        i=next(g)
        for j in i:
            fp.write("%d "%(j))
        fp.write('\n')
    fp.write("0\n")
    fp.close()

Re100
按照要求即可得到flag。

Re200:tank

消息循环位于.text:00401700处,处理函数为.text:00402B50,在对结构体进行分析后可确定该结构体最后一个双字保存着当前所在等级。进行一些简单的操作后可以看到对等级进行判断以及将flag写入文件的函数,用0x39作为密钥,异或之后就可以得出结果了
Re200

Re300:crackMe

是一个Win32程序。将程序载入IDA后,可以看到大量的区域处于未识别状态。导入函数只有Kernel32.dll中的LoadLibraryAGetProcAddressVirtualProtectExitProcess,推断这个程序是被加壳过的,PEiD检查后证实了这是个UPX壳,在官网找到upx后,直接upx -d即可脱出来(手动怎么都脱不下来,以后慢慢研究,说不定又能混一篇文章)。用IDA加载脱壳之后的程序,可以看到一组花指令,如下图:
Re 300 flower~
Re 300 flower
可以看到这组指令执行的工作就是,调用压入栈中的子程序,并跳过这组指令其后的两条指令,继续执行。这样我们可以用下面的一组指令替换这些花指令:
Fix of bad guys
之后我们可以发现,该程序是在将程序末尾附加的另外一个文件读入内存并用0x07解密:
Small boy in the program
从内存中dump出来后,可以看到这才是真正的解Flag程序。同样用UPX脱壳后分析程序工作流程。这个程序使用的反分析技巧类似于部分恶意软件,将会把内存中部分执行后不需要用的代码随机加密,有至少三处采用了这样的做法(因此动态调试的时候请记住先调整好.text为可写),但这并不影响我们对其进行静态分析。程序直接从缓冲区读入字符串后,将其转化为十六进制性质的字符串,首先通过下图所示的comp_1()函数进行处理。这里首先从字符串生成一个类似于KMP算法中的“部分匹配表”,之后将每个字符与匹配表中的数据和25加和后的结果异或,之后再次将其转化为十六进制字符串。将每个字符存入一个二叉树之后,进行深度优先遍历,再将结果与一个表做匹配,如果内容相等,则认为输入正确。
这个二叉树的构造实在是骨骼精奇,我到现在还没解开它。但是有趣的是,涉及到二叉树建立的.text:401120.text:401160与输入并没有关系。因此我们只要将二叉树建立的这部分代码复制出来,然后构造一个1,2,3,4,...,...的输入,就可以将这个步骤转化成为一个简单的置换。下面的这个python脚本输出的结果,就是进入二叉树之前的数据:

slen=27
tree=[[0,0,0] for i in range(4*slen)]
ret=[i for i in range(4*slen)]
src=list("22722272222227272222727a2222222222272222272222222222cfdceeeebb9fdbcdbbedfdede7ce9bebe0bb1e2ceab9e2bbbdecf9d8")
dst=list(range(len(src)))
m=0
def sub_401120(index):
    global tree
    left=0
    if (tree[index][0]!=-1):
        left=sub_401120(tree[index][0])
    right=tree[index][2]
    if (right!=-1):
        left+=sub_401120(right)
    return (left+1)


def tree_connect(root,target):
    global tree
    result=root
    pos=root
    pos_idxL=tree[root][0]
    if (pos_idxL==-1):
        tree[result][0]=target
        return result
    else:
        while ( tree[pos][2] != -1 ):
            v5 = sub_401120(pos_idxL);
            if (sub_401120(tree[pos][2]) <= v5+4 ):
                result=tree[pos][2]
            else:
                result=tree[pos][0];
            pos=result
            pos_idxL=tree[result][0]
            if (pos_idxL == -1):
                tree[result][0]=target
                return result
        #result*=3
        tree[result][2]=target
        result*=3
    return result

def dfs(pos):
    global tree
    global m
    global ret
    flag=1
    while(flag or pos!=-1):
        flag=0
        if (tree[pos][0] != -1):
            dfs(tree[pos][0])
        ret[m]=tree[pos][1]
        m+=1
        pos=tree[pos][2]
    return pos


tree[0][0]=-1
tree[0][1]=0
tree[0][2]=-1
for i in range(1,4*slen):
    tree[i][0]=-1
    tree[i][1]=i
    tree[i][2]=-1
    tree_connect(0,i)
m=0
dfs(0)
print(ret)
for i in range(len(src)):
    dst[ret[i]]=src[i]
print(dst)
dst_decoded=str().join([chr(int("0x"+dst[i]+dst[i+1],16)) for i in range(0,len(dst),2)])
print(dst_decoded)
print(len(dst_decoded))
#Result:*y+.+{-~---~./++/,-~/,. .--~,+/+.).+.z,|++-~..+),(+!+)

BTree Image
接下来我们就要将这段字符串反向为原始的字符串。这里采用穷举法:

src="*y+.+{-~---~./++/,-~/,. .--~,+/+.).+.z,|++-~..+),(+!+)"
hexlist="1234567890ABCDEF"
temp=[i for i in range(54)]

for k in range(-25,30):
    temp=[ chr(ord(i)^(25+k)) for i in src]
    #decoded=str().join([chr(int("0x"+temp[i]+temp[i+1],16)) for i in range(0,len(temp),2)])
    decoded="".encode('ascii','ignore')
    for i in range(0,len(temp),2):
        #decoded=str().join([chr(int("0x"+temp[i]+temp[i+1],16)) for i in range(0,len(temp),2)])
        if temp[i].upper() in hexlist and temp[i+1].upper() in hexlist: 
            p=int("0x"+temp[i]+temp[i+1],16)
            if p in range(0x20,0x7f):
                ch=chr(p)
            else:
                ch=" "
        else:
            ch=" "
        #print("%d %d %c %c %c"%(k,i,temp[i], temp[i+1], ch))
        decoded+=ch.encode('ascii','ignore')
    print(decoded)

结果如下
Final Result
随便挑一个合适的就好啦
Succeed

先写这些吧 有时间接着写

Contact webmaster at:
[email protected]