ByteCTF MordernCPP WriteUp
2021-10-19
通过Ghidra分析可以看出main函数里面有大量try catch结构。整个程序的核心控制逻辑都是由try catch来控制的。因此动调的时候尽量都跟进函数里,先分析出程序的大体执行流程。
可以识别几处函数的跳转(图片来自Socular师傅)
动调过后可以发现,两个关键函数为sub_40211C
和sub_4024A4
。通过常数可以轻松识别出来,sub_4024A4
是TEA加密,密钥可以调试得来为byte-ctfwecome~
。密文与从0x536800
开始的40字节对比。因此可以直接写出解密脚本。
解得原文为
[0x0c, 0xf0, 0x69, 0xd8, 0x4a, 0x32, 0xfb, 0x62, 0x8e, 0xa4, 0xcc, 0x0c, 0xc0, 0x22, 0x63, 0xe5, 0xb6, 0xfd, 0x07, 0x5e, 0xe6, 0xfe, 0xc6, 0x8d, 0xfd, 0x8d, 0x51, 0xad, 0xe4, 0x68, 0xfa, 0x14, 0x78]
sub_40211C
函数先逐字符将flag转化为01串(每个字符对应的01串长度不一定相等),然后把这个01串8个一组,变成二进制数组,传入TEA加密。
由于这个对应表可能是c++的某种hash表储存,难以直接找到。因此我写了一个IDA的条件断点来输出对应的01串。
执行会有如下输出
因此多来几次,把所有的可能字符都打出表来,再遍历即可
l=[0x0c, 0xf0, 0x69, 0xd8, 0x4a, 0x32, 0xfb, 0x62, 0x8e, 0xa4, 0xcc, 0x0c, 0xc0, 0x22, 0x63, 0xe5, 0xb6, 0xfd, 0x07, 0x5e, 0xe6, 0xfe, 0xc6, 0x8d, 0xfd, 0x8d, 0x51, 0xad, 0xe4, 0x68, 0xfa, 0x14, 0x78]
ss="".join([bin(b)[2:].zfill(8) for b in l])
table={
'!': '00110110',
'#': '101001',
'%': '00010',
'&': '10100011100',
'(': '010011',
')': '111100',
'*': '0110010',
'+': '10111',
'-': '10100010',
';': '1010000',
'=': '000000',
'@': '1110011',
'[': '110011',
']': '00100',
'^': '01111',
'_': '01010',
'{': '10001',
'}': '1010001111',
'a': '100101',
'b': '00001',
'c': '01110',
'd': '11011',
'e': '0011010',
'f': '010010',
'g': '111011',
'h': '01000',
'i': '10110',
'j': '00110111',
'k': '1111010',
'l': '110010',
'm': '00011',
'n': '10000',
'o': '10100011101',
'p': '0110011',
'q': '011000',
'r': '111110',
's': '01011',
't': '11000',
'u': '11110110',
'v': '000001',
'w': '111000',
'x': '00101',
'y': '10011',
'z': '101000110',
'1': '100100',
'2': '111111',
'3': '01101',
'4': '11010',
'5': '11110111',
'6': '001100',
'7': '111010',
'8': '00111',
'9': '10101',
'0': '1110010'
}
states=[(ss,'')]
while len(states):
s,result=states.pop(0)
for ch,bi in table.items():
if s.startswith(bi):
states.append((s[len(bi):],result+ch))
break
else:
break
print(result)
最后flag为bytectf{autofp=[=](m0d3rnc++juejuezi){};}
。(是c++的lambda表达式