分析代码,可以知道关键部分:
def encrypt(plaintext):
global USERNAME
global key
plaintext = USERNAME + ": " + plaintext
out = [random.randint(0, 9999), random.randint(0, 999)]
for i in range(len(plaintext)):
out.append( ( out[i+1] + ( (out[i] * ord(plaintext[i])) ^ (key+out[i+1]) ) ) ^ (key*out[i]) )
因为每次发送的消息都有固定头部,所以可以利用这一特征求出 key。利用z3构造多个方程求解。由于^运算的参与,不能使用Int(‘x’),需使用比特流BitVec(‘x’,bit)。由key的生成过程可知 key 最多 76bits。
from z3 import *
from tqdm import tqdm
conv = open(r'conversation','rb').readlines()
text = []
for i in range(1,len(conv),2):
out = conv[i][9:-1].split()
out = [int(j) for j in out]
text.append(out)
know = 'Houdini: '
out = text[0]
for bit in tqdm(range(1, 78)):
x = BitVec('x', bit)
S = Solver()
for i in range(len(know)):
S.add( (out[i+1] + ((out[i] * ord(know[i])) ^ (x+out[i+1]))) ^ (x*out[i]) == out[i+2] )
S.check()
result = S.model()
key = result[x].as_long()
message = ''
for each in text:
for k in range(len(each)-2):
try:
ch = (((each[k+2] ^ (key*each[k])) - each[k+1]) ^ (key + each[k+1]))//each[k]
message += chr(ch)
except:
pass
if 'flag' in message or 'watevr' in message:
print(message)