QCTF-re-asong-纯静态详解

前言

时隔一月没写点东西,忙这忙那,物是人非,不说废话,看到题。

xman选拔赛asong

这道题真的很郁闷,在我的ubuntu上,open始终失败,无法调试,换了台机子还是一样,但学弟说他那没问题。–。–
没办法,只好纯静态的看了。

解题过程

0x0:

sub_400DB4(char *input,int len):

反汇编后,整理成c代码

int i;

char *a1;

int len;

unsigned int a1>>5;

for(i=0;len-1>a1;i++){

input[i]=8*input[i]|input[i+1]>>5;

}

result=&input[i];

*result=8*input[i]|input[0]>>5;

return result;

很明显这是不可逆的,但在已知最终结果的情况下可以爆破出input。每组爆破出来都是多解,先是尝试前面两位,第一位存在多解[125, 61, 93, 189, 29, 253, 221, 157],第二位为133
代码如下:

import json
result=[0xec,0x29,0xe3,0x41,0xe1,0xf7,0xaa,0x1d,0x29,0xed,0x29,0x99,0x39,0xf3,0xb7,0xa9,0xe7,0xac,0x2b,0xb7,0xab,0x40,0x9f,0xa9,0x31,0x35,0x2c,0x29,0xef,0xa8,0x3d,0x4b,0xb0,0xe9,0xe1,0x68,0x7b,0x41]
# print len(result)#28
k=0
st={}
for i in range(256):#input[i]
    for j in range(256):#input[i+1]
        fl=(8*i|j>>5)%256
        if fl==result[k]:
            if i in st:
                st[i].append(j)
            else:
                st[i]=[j]
            continue
# js = json.dumps(st, sort_keys=True, indent=4, separators=(',', ':'))# format json output
#print js
print "done!\n"
print st.keys()
n=1
sst={}
k=61
for i in st[k]:
    for j in range(256):
        fl=(8*i|j>>5)%256
        if fl==result[n]:
            if i in sst:
                sst[i].append(j)
            else:
                sst[i]=[j]
        continue
js = json.dumps(sst, sort_keys=True, indent=4, separators=(',', ':'))# format json output
print js

继续可以爆破出后面的
完整代码如下:

result=[0xec,0x29,0xe3,0x41,0xe1,0xf7,0xaa,0x1d,0x29,0xed,0x29,0x99,0x39,0xf3,0xb7,0xa9,0xe7,0xac,0x2b,0xb7,0xab,0x40,0x9f,0xa9,0x31,0x35,0x2c,0x29,0xef,0xa8,0x3d,0x4b,0xb0,0xe9,0xe1,0x68,0x7b,0x41]
def first_bit():
    k=0
    st={}
    for i in range(256):#input[i]
        for j in range(256):#input[i+1]
            fl=(8*i|j>>5)%256
            if fl==result[k]:
                if i in st:
                    st[i].append(j)
                else:
                    st[i]=[j]
                continue
    return st
def brup(st,k):
    sst={}
    for i in st.values()[0]:
        for j in range(256):
            fl=(8*i|j>>5)%256
            if fl==result[k]:
                if i in sst:
                    sst[i].append(j)
                else:
                    sst[i]=[j]
            continue
    return sst,sst.keys()[0]

if __name__ == '__main__':
    # st=
    s=[]
    st=first_bit()
    for i in range(1,len(result)):
        st,key=brup(st,i)
        s.append(key)
    print first_bit().keys()
    print st.values()[0]
    print s

[125, 61, 93, 189, 29, 253, 221, 157]
[32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]
[x,133, 60, 104, 60, 62, 245, 67, 165, 61, 165, 51, 39, 62, 118, 245, 60, 245, 133, 118, 245, 104, 19, 245, 38, 38, 165, 133, 61, 245, 7, 169, 118, 29, 60, 45, 15, 104,y]
但是仍然无法确定第一位和最后一位,无奈只得继续向上看。

0x1

sub_400D33(__int64 in)
同样格式化代码如下:

char *in;
int i=0;
while(dword_6020a0[i]){
  in[i]=dword_6020a0[in[i]];//这是错误的,如果是这样那根本就得不到最后的输出
  i=dword_6020a0[i]
}
result=in[i];
in[i]=result;

正确的格式化代码如下:

char *in;
int i=0;
while(dword_6020a0[i]){
  in[i]=in[dword_6020a0[i]];
  i=dword_6020a0[i]
}
result=in[i];
in[i]=result;

dword_6020a0是一个置换表,简单正向推导两个,理清楚思路,先看一下能不能逆,如果不能逆,那就只能正向爆破了,但是爆破也要讲究效率。置换是肯定可逆的。

m=[0x16,0x00,0x06,0x02,0x1E,0x18,0x09,0x01,0x15,0x07,0x12,0x0A,0x08,0x0C,0x11,0x17,0x0D,0x04,0x03,0x0E,0x13,0x0B,0x14,0x10,0x0F,0x05,0x19,0x24,0x1B,0x1C,0x1D,0x25,0x1F,0x20,0x21,0x1A,0x22,0x23]
    i=1
    for j in range(len(m)):
        dword=m.index(i)
        i=m.index(dword)
        print i,dword

得出反向的置换序列[1, 7, 9, 6, 2,3 ,18,10, 11,21, 8,12, 13,16, 23,15, 24,5,25,26 ,35,37, 31,32, 33,34, 36,27, 28,29, 30,4, 17,14, 19,20, 22,0 ,1],根据反向的置换序列得到源数组,这时同样无法确定in[0],但是最后一位无影响,因此可以忽略,这里我先假设in[0]=0。

s=[0,133, 60, 104, 60, 62, 245, 67, 165, 61, 165, 51, 39, 62, 118, 245, 60, 245, 133, 118, 245, 104, 19, 245, 38, 38, 165, 133, 61, 245, 7, 169, 118, 29, 60, 45, 15, 104,1]
    fl={}
    n=[1, 7, 9, 6, 2,3 ,18,10, 11,21, 8,12, 13,16, 23,15, 24,5, 25,26 ,35,37, 31,32, 33,34, 36,27, 28,29, 30,4, 17,14, 19,20, 22,0 ,1]
    print len(m)#38
    print len(n)#39
    for j in range(len(n)-1):
        fl[n[j]]=s[n[j+1]]
    print fl.values()

[133, 67, 104, 133, 245, 38, 60, 61, 39, 245, 51, 104, 62, 60, 118, 38, 245, 118, 165, 245, 19, 165, 0, 245, 62, 165, 45, 61, 245, 7, 60, 118, 29, 60, 15, 104, 133, 169]
继续向上看!

0x2

sub_400936(char a1)
这个没什么可说的,把代码抄下来,用Python写一遍即可(当然以上所有脚本均可用c实现,而且c写起来直接copy IDA反编译的代码即可)。
我想部分同学可能会不明白a2是什么变量,其实a2是malloc的一块内存地址,观察汇编就是ds:0[],说白了就是指向了sub_400AAA处理之后的v3,v3是一个固定的数组,查看sub_400AAA代码,很容易看出这是一个进行简单变化后求词频的函数,最后的词频表即保存在ds:0[],也就是v3中。因此不需要关心,如果动态调试一遍就会非常清楚。

接下来重写一遍sub_400AAA,得到词频表。

def get_A():
    r=[]
    re={}
    fp=open("that_girl","r")
    while True:
        data=fp.read(1)
        if not data:
            break
        else:
            data=sub_400936(ord(data))
            if data in re:
                re[data]=re[data]+1
            else:
                re[data]=1
    return re
def table_():
    t={}
    for i in range(256):
        t[sub_400936(i)]=i
    return t

之后便是将结果带入词频进行查词,同时对in[0]进行爆破(其实就8个数,手动试一下也行),最终得到两组结果,根据语义进行抉择。QCTF{that_girl_saying_no_for_your_vindicate}

0x03

完整代码如下:

result=[0xec,0x29,0xe3,0x41,0xe1,0xf7,0xaa,0x1d,0x29,0xed,0x29,0x99,0x39,0xf3,0xb7,0xa9,0xe7,0xac,0x2b,0xb7,0xab,0x40,0x9f,0xa9,0x31,0x35,0x2c,0x29,0xef,0xa8,0x3d,0x4b,0xb0,0xe9,0xe1,0x68,0x7b,0x41]
def first_bit():
    k=0
    st={}
    for i in range(256):#input[i]
        for j in range(256):#input[i+1]
            fl=(8*i|j>>5)%256
            if fl==result[k]:
                if i in st:
                    st[i].append(j)
                else:
                    st[i]=[j]
                continue
    return st
def brup(st,k):
    sst={}
    for i in st.values()[0]:
        for j in range(256):
            fl=(8*i|j>>5)%256
            if fl==result[k]:
                if i in sst:
                    sst[i].append(j)
                else:
                    sst[i]=[j]
            continue
    return sst,sst.keys()[0]
def sub_400936(a1):
    result=a1-10
    if a1==10:
        result=a1+35
    elif a1==34:
        result = (a1 + 10) 
    elif a1==39:
        result = (a1 + 2) 
    elif a1==44:
        result = (a1 - 4) 
    elif a1==46:
        result = (a1 - 7) 
    elif a1==59:
        result =(a1 - 21) 
    elif a1==63:
        result = (a1 - 27) 
    elif a1==95:
        result = (a1 - 49) 
    else:
        if (a1<=47 ) or (a1>57):
            if (a1<=64 or a1>90):
                if a1>96 and a1<=122:
                    result=a1-87
            else:
                result=a1-55
        else:
            result=a1-48
    return result
def get_A():
    r=[]
    re={}
    fp=open("that_girl","r")
    while True:
        data=fp.read(1)
        if not data:
            break
        else:
            data=sub_400936(ord(data))
            if data in re:
                re[data]=re[data]+1
            else:
                re[data]=1
    return re
def table_():
    t={}
    for i in range(256):
        t[sub_400936(i)]=i
    return t
# logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.basicConfig(level=logging.INFO)
logger=logging.getLogger(__name__)
if __name__ == '__main__':
    s=[]
    flag=[]
    st=first_bit()
    for i in range(1,len(result)):
        st,key=brup(st,i)
        s.append(key)
    logger.info(first_bit().keys())
    # print st.values()[0]
    # print s
    fl=[]
    m=[0x16,0x00,0x06,0x02,0x1E,0x18,0x09,0x01,0x15,0x07,0x12,0x0A,0x08,0x0C,0x11,0x17,0x0D,0x04,0x03,0x0E,0x13,0x0B,0x14,0x10,0x0F,0x05,0x19,0x24,0x1B,0x1C,0x1D,0x25,0x1F,0x20,0x21,0x1A,0x22,0x23]
    i=1
    for j in range(len(m)):
        dword=m.index(i)
        i=m.index(dword)
        # print dword,i
    s=[61,133, 60, 104, 60, 62, 245, 67, 165, 61, 165, 51, 39, 62, 118, 245, 60, 245, 133, 118, 245, 104, 19, 245, 38, 38, 165, 133, 61, 245, 7, 169, 118, 29, 60, 45, 15, 104,1]
    #
    fl={}
    n=[1, 7, 9, 6, 2,3 ,18,10, 11,21, 8,12, 13,16, 23,15, 24,5, 25,26 ,35,37, 31,32, 33,34, 36,27, 28,29, 30,4, 17,14, 19,20, 22,0 ,1]
    for j in range(len(n)-1):
        fl[n[j]]=s[n[j+1]]
    logger.info(fl.values())
    A=get_A()
    A_={v:k for k,v in A.items()}
    logger.info(A_)
    for i in fl.values():
        flag.append(chr(table_()[A_[i]]))
    logger.info("".join(flag)) 

总结

静态的看,验证的工作需要自己写代码完成,而动态来说,利用程序本身进行验证,真是全靠想象啊!

猜你喜欢

转载自blog.csdn.net/qq_33438733/article/details/81084295