莲城杯MT19937_64

题解

考的是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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值