2023安洵杯 re复现
ez_cpp
这个题目看了两种办法,受益匪浅
by Arr3stY0u
打开去花得到主函数,反编译后看到单字节判断,可以进行单字节爆破
首先改掉不对就跳到wrong这个逻辑,判断错误后直接跳到exit函数上,然后改掉exit的参数,让它的参数变成ecx,也就是判断正确了几次
反编译后就变成了这样
那么就可以利用这样一个知识点
在main中:
return v; 与 exit(v); 的效果相同。
返回值就是j,可以捕获这个j,来当成单字节爆破的判断依据
师傅的脚本如下
import string
import os
import time
table = string.ascii_letters+string.digits+'!-{}'
# table = string.printable
# 'SYC{Y3S-yE5-y0u-S0Ve-Th3-C9P!!!}'
theflag = ''
while len(theflag) < 32:
for ch in table:
flag = (theflag+ch).ljust(32, '#')
exitcode = os.system(f"echo {flag} | ez_cpp.exe 1>&0")
if exitcode >= len(theflag) + 1:
theflag += ch
print(theflag, exitcode)
break
else:
print('not found')
time.sleep(0.1)
拼接flag部分并把flag输入到程序里,exitcode就是返回值,也就是ecx的值,如果ecx>=输入的flag的前几位+1,那么这几位就是部分正确的flag,接下来继续拼接即可
SYC{Y3S-yE5-y0u-S0Ve-Th3-C9P!!!}
by Blu3e
这个思路就是直接逆向
依然是去花,然后爆破凯撒加密之后的密文
result=[]
opcode=[0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
cmp=[0x22, 0x00, 0x00, 0x00, 0xA2, 0xFF, 0xFF, 0xFF, 0x72, 0x00,
0x00, 0x00, 0xE6, 0xFF, 0xFF, 0xFF, 0x52, 0x00, 0x00, 0x00,
0x8C, 0xFF, 0xFF, 0xFF, 0xF2, 0xFF, 0xFF, 0xFF, 0xD4, 0xFF,
0xFF, 0xFF, 0xA6, 0xFF, 0xFF, 0xFF, 0x0A, 0x00, 0x00, 0x00,
0x3C, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xA6, 0xFF,
0xFF, 0xFF, 0x9C, 0xFF, 0xFF, 0xFF, 0x86, 0xFF, 0xFF, 0xFF,
0x24, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0xD4, 0xFF,
0xFF, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xB6, 0xFF, 0xFF, 0xFF,
0x14, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0xCE, 0xFF,
0xFF, 0xFF, 0xAC, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x00, 0x00,
0x6A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x7C, 0x00,
0x00, 0x00, 0xE4, 0xFF, 0xFF, 0xFF, 0xE4, 0xFF, 0xFF, 0xFF,
0xE4, 0xFF, 0xFF, 0xFF, 0x1E, 0x00, 0x00, 0x00]
for i in range(32):
for j in range(0x100):
if i<=16:
if i>=16:
a=j^4
else:
if opcode[i*4]==1:
a=j^9
else:
a=j-2
else:
if opcode[i*4]==1:
a=j^6
else:
a=j-5
v2=0
v3=0
v4=7
a2=8
while v3<a2:
v2|=((a>>v3)&1)<<v4
v3+=1
v4-=1
a=1^(v2+1)
if a==cmp[i*4]:
result.append(j)
print(bytes(result))
ezr3
by Blu3e
拖进ida,看见函数很少,于是拖进winhex,看见了类似UPX的标志
全部改成UPX,然后脱壳
把得到的程序拖进ida,看见了两个可疑函数p和v
在主函数里面也能找到这两个函数的调用
并且没有其他条件,那么我们直接逆
先逆v
做了异或操作,逆一下是这样的
v2 = 0
for i in range(0,len(auth),6):
for j in range(6):
auth[i + j] ^= mere[v2+j*12]
v2 += 2
再逆p,先进行了一次对前四位和后四位的转换,然后进行异或,最后进行验证
逆向脚本如下
v2 = 0
for i in range(0,len(auth),6):
for j in range(6):
flag.append(auth[i + j]//mere[(v2+j*6)*2])
v2 += 1
for i in range(len(flag)):
flag[len(flag)-1-i] ^= flag[i]
flag[len(flag)-1-i] = (flag[len(flag)-1-i]>>4|flag[len(flag)-1-i]<<4)&0xff
所以总的脚本如下
auth = [0x0003BC69, 0x000D3FA0, 0x0003A94A, 0x00044AFF, 0x00045254, 0x0000CDD1, 0x00001815, 0x00003B08, 0x00070868, 0x000C6560, 0x00065662, 0x000855C8, 0x0000DCF6, 0x00004CE6, 0x0014EEC2, 0x0002CFD6, 0x00032766, 0x0014F6BA, 0x00025E69, 0x0006A9A3, 0x00121EBD, 0x0005991C, 0x00050016, 0x00004A3D, 0x00097485, 0x0008D0A0, 0x0003B916, 0x00054C58, 0x00096F94, 0x00010334, 0x000DAD22, 0x0004B234, 0x0002FE96, 0x000F33CC, 0x0012C1E8, 0x00148F9E]
mere = [0x00000D21, 0x00000000, 0x0000009D, 0x00000000, 0x0000094B, 0x00000000, 0x000003C9, 0x00000000, 0x00000C3F, 0x00000000, 0x000017E9, 0x00000000, 0x0000130E, 0x00000000, 0x00000088, 0x00000000, 0x00000486, 0x00000000, 0x0000202F, 0x00000000, 0x00002230, 0x00000000, 0x000024B4, 0x00000000, 0x000008B1, 0x00000000, 0x00000A9F, 0x00000000, 0x00001AD2, 0x00000000, 0x000023EB, 0x00000000, 0x00000C7E, 0x00000000, 0x0000042B, 0x00000000, 0x000005BF, 0x00000000, 0x0000113C, 0x00000000, 0x00000449, 0x00000000, 0x00001751, 0x00000000, 0x00000ACE, 0x00000000, 0x00001894, 0x00000000, 0x0000208A, 0x00000000, 0x00000E82, 0x00000000, 0x000006BD, 0x00000000, 0x00000CEE, 0x00000000, 0x00002386, 0x00000000, 0x000013D4, 0x00000000, 0x00000111, 0x00000000, 0x00000D1C, 0x00000000, 0x0000238E, 0x00000000, 0x00001759, 0x00000000, 0x0000012B, 0x00000000, 0x0000214D, 0x00000000, 0x00000040]
v2 = 0
flag = []
for i in range(0,len(auth),6):
for j in range(6):
auth[i + j] ^= mere[v2+j*12]
v2 += 2
v2 = 0
for i in range(0,len(auth),6):
for j in range(6):
flag.append(auth[i + j]//mere[(v2+j*6)*2])
v2 += 1
for i in range(len(flag)):
flag[len(flag)-1-i] ^= flag[i]
flag[len(flag)-1-i] = (flag[len(flag)-1-i]>>4|flag[len(flag)-1-i]<<4)&0xff
print(bytes(flag))
babythread
by ATC Team
进入main函数,查看,发现是一个RC4
调试不动,查看TLS回调函数里有反调试,去掉
然后进行调试,找到RC4的密文Buf2和密钥v12
dump下来后写脚本,这个是RC4脚本
def KSA(key):
key_length = len(key)
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % key_length]) % 256
S[i], S[j] = S[j], S[i]
return S
def PRGA(S):
i = 0
j = 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]
yield K
def RC4(key):
S = KSA(key)
keystream = PRGA(S)
return keystream
# 使用示例
if __name__ == '__main__':
key = [0x01, 0xE5, 0xD5, 0x40, 0xC3, 0xD5, 0x76, 0x36, 0xFE, 0x66, 0x2D, 0x05, 0xC9, 0xFB, 0x50, 0xE7]
Buf1 = [0xDE, 0x1C, 0x22, 0x27, 0x1D, 0xAE, 0xAD, 0x65, 0xAD, 0xEF, 0x6E, 0x41, 0x4C, 0x34, 0x75, 0xF1, 0x16, 0x50, 0x50, 0xD4, 0x48, 0x69, 0x6D, 0x93, 0x36, 0x1C, 0x86, 0x3B, 0xBB, 0xD0, 0x4C, 0x91]
# 解密
plaintext2 = bytearray()
keystream = RC4(key)
for b in Buf1:
plaintext2.append(b ^ next(keystream)) # 异或操作
print(plaintext2)