前言
时隔一月没写点东西,忙这忙那,物是人非,不说废话,看到题。
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))
总结
静态的看,验证的工作需要自己写代码完成,而动态来说,利用程序本身进行验证,真是全靠想象啊!