题解
考的是MT19937,从输出中弹出了第375个随机数,每个随机数为64bit,是足够还原states的,一般情况下对这种问题直接通过624个32bit的out推出用来生成他们的states,然后依据states推出下一轮的states用以生成下一轮的out,发现少了哪个补上哪个就好。但是在这里情况稍有不同,原来用的预测脚本如下:
from random import Random
o = 9999999999999999999999999999999999999999999999999999999999999
# right shift inverse
def inverse_right(res, shift):
tmp = res
bits = len(bin(res)[2:])
for i in range(bits // shift):
tmp = res ^ tmp >> shift
return tmp
# right shift with mask inverse
def inverse_right_mask(res, shift, mask):
tmp = res
bits = len(bin(res)[2:])
for i in range(bits // shift):
tmp = res ^ tmp >> shift & mask
return tmp
# left shift inverse
def inverse_left(res, shift):
tmp = res
bits = len(bin(res)[2:])
for i in range(bits // shift):
tmp = res ^ tmp << shift
return tmp
# left shift with mask inverse
def inverse_left_mask(res, shift, mask):
tmp = res
bits = len(bin(res)[2:])
for i in range(bits // shift):
tmp = res ^ tmp << shift & mask
return tmp
def recover(y):
y = inverse_right(y, 18)
y = inverse_left_mask(y, 15, 4022730752)
y = inverse_left_mask(y, 7, 2636928640)
y = inverse_right(y, 11)
return y
def clone_mt(record):
state = [recover(i) for i in record]
gen = Random()
gen.setstate((3, tuple(state + [0]), None))
return gen ,state
def twist(state):
for i in range(0, 624):
y = ((state[i] & 0x80000000) + (state[(i + 1) % 624] & 0x7fffffff))&0xffffffff
state[i] = (y >> 1) ^ state[(i + 397) % 624]
if y % 2 != 0:
state[i] = state[i] ^ 0x9908b0df
return state
prng = []
for i in range(312):
prng.append(s[i]&0xffffffff)
prng.append(s[i]>>32)
g,state = clone_mt(prng[:624])
for i in range(374):
print(g.getrandbits(64))
print(s[i])
key = g.getrandbits(64)
from hashlib import *
flag = md5(str(key).encode()).hexdigest()
print(flag)
但是在这里不知道为什么可以推出前面的312个64bit的out,但是到第二轮就不对了,这里大概意识到了MT19937_64似乎并不是单纯的MT19937以64位为单元的形式,通过进一步的搜集(资料是真的少,这里不是特别清楚两者是否有本质区别,毕竟之前代码前面生成的还是对的)MT19937_64的预测代码如下:
class mt19937_64:
def __init__(self):
self._n = 312
self._i = 0
self._state = [0] * self._n
def _untemper(self, value: int) -> int:
value ^= value >> 43
value ^= (value << 37) & 0xfff7eee000000000
_value = value
for i in range(64):
value = _value ^ ((value << 17) & 0x71d67fffeda60000)
value ^= (value >> 29) & 0x5555555555555555
return value
def from_output(self, output: List[int]) -> None:
assert len(output) >= self._n
output = output[:self._n]
self._state = list(map(self._untemper, output))
def random(self) -> int:
j = (self._i + 1) % self._n
mask = (1 << 31) - 1
inv_mask = (1 << 64) - 1 - mask
yp = (self._state[self._i] & inv_mask) | (self._state[j] & mask)
k = (self._i + 156) % self._n
self._state[self._i] = self._state[k] ^ (yp >> 1) ^ (
0xb5026f5aa96619e9 * (yp & 1))
z = self._state[self._i] ^ (
(self._state[self._i] >> 29) & 0x5555555555555555)
self._i = j
z ^= (z << 17) & 0x71d67fffeda60000
z ^= (z << 37) & 0xfff7eee000000000
return z ^ (z >> 43)
if __name__ == '__main__':
import subprocess
import os
import random
res = s
pre = mt19937_64()
pre.from_output(list(map(int, res[:312])))
i=0
for rand in res[312:]:
print(pre.random())
print(s[i+312])
if(312+i==376):
print("注意\n")
break
i+=1
运行可以很明显的发现被弹出的字符。md5加密下就是flag。