MT19937伪随机

MT19937伪随机

大佬博客:MT19937_mt19937predictor-CSDN博客

1、原理:梅森旋转算法,该算法生成的随机数有周期性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
random.seed(1)
print(hex(random.getrandbits(32))[2:])#getrandbits(a)是生成a位二进制的随机数
print(hex(random.getrandbits(32))[2:])
#2265b1f5
#91b7584a

random.seed(1)
print(hex(random.getrandbits(64))[2:])#64位的是两个32位的拼接,但是是倒序拼接
#91b7584a2265b1f5

注意8bit和16bit和24bit是 截取前8、16、24bit位:
random.seed(1)
print(hex(random.getrandbits(8))[2:])
print(hex(random.getrandbits(8))[2:])
# 22 91
random.seed(1)
print(hex(random.getrandbits(16))[2:])
print(hex(random.getrandbits(16))[2:])
# 2265 91b7
random.seed(1)
print(hex(random.getrandbits(24))[2:])
print(hex(random.getrandbits(24))[2:])
# 2265b1 91b758

注意一下生成的随机字节:
random.seed(1)
# print(random.randbytes(8))#ranbytes(a)是生成a个字节的数据
print(hex(bytes_to_long(random.randbytes(8)))[2:])#正好是getrandbits生成的数据的倒序
#f5b165224a58b791

2.逆向temper(从随机数中得到state):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
o = 2080737669

# right shift inverse
def inverse_right(res, shift, bits=32):
tmp = res
for i in range(bits // shift):
tmp = res ^ tmp >> shift
return tmp


# right shift with mask inverse
def inverse_right_mask(res, shift, mask, bits=32):
tmp = res
for i in range(bits // shift):
tmp = res ^ tmp >> shift & mask
return tmp

# left shift inverse
def inverse_left(res, shift, bits=32):
tmp = res
for i in range(bits // shift):
tmp = res ^ tmp <​<​ shift
return tmp


# left shift with mask inverse
def inverse_left_mask(res, shift, mask, bits=32):
tmp = res
for i in range(bits // shift):
tmp = res ^ tmp <​<​ shift & mask
return tmp


def extract_number(y):
y = y ^ y >> 11
y = y ^ y <​<​ 7 & 2636928640
y = y ^ y <​<​ 15 & 4022730752
y = y ^ y >> 18
return y&0xffffffff

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&0xffffffff#保证是32位

y = extract_number(o)
print(recover(y) == o)


3.逆向twist(每生成624个伪随机后会进行一次twist函数后再生成第625个随机数):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def backtrace(cur):
high = 0x80000000
low = 0x7fffffff
mask = 0x9908b0df
state = cur
for i in range(623,-1,-1): ## 要理解这俩行
tmp = state[i]^state[(i+397)%624]
# recover Y,tmp = Y
if tmp & high == high:
tmp ^= mask
tmp <​<​= 1
tmp |= 1
else:
tmp <​<​=1
# recover highest bit
res = tmp&high
# recover other 31 bits,when i =0,it just use the method again it so beautiful!!!!
tmp = state[i-1]^state[(i+396)%624]
# recover Y,tmp = Y
if tmp & high == high:
tmp ^= mask
tmp <​<​= 1
tmp |= 1
else:
tmp <​<​=1
res |= (tmp)&low
state[i] = res
return state

4.逆向init(得到seed):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 原理代码:
def _int32(x):
#保证32位
return int(0xFFFFFFFF & x)

def __init__(self, seed):
self.mt = [0] * 624
self.mt[0] = seed#这里的mt[]数组就是我们的状态state
self.mti = 0#类似于一个计数器
for i in range(1, 624):
self.mt[i] = _int32(1812433253 * (self.mt[i - 1] ^ self.mt[i - 1] >> 30) + i)

注意到_int32只是为了保证位数,相当于mod 2**32 ,所以我们完全可以通过求逆元得到(self.mt[i - 1] ^ self.mt[i - 1] >> 30) 的值

下面是脚本,从state(内部状态数组)中的每个数都能推出seed:
from gmpy2 import invert

def _int32(x):
return int(0xFFFFFFFF & x)

def init(seed):
mt = [0] * 624
mt[0] = seed
for i in range(1, 624):
mt[i] = _int32(1812433253 * (mt[i - 1] ^ mt[i - 1] >> 30) + i)
return mt

seed = 2080737669

def invert_right(res,shift):
tmp = res
for i in range(32//shift):
res = tmp^res>>shift
return _int32(res)

def recover(last):
n = 1<​<​32
inv = invert(1812433253,n)#求逆元
for i in range(623,0,-1):
last = ((last-i)*inv)%n
last = invert_right(last,30)
return last

state = init(seed)

print(recover(state[-1]) == seed)

5.randcrack库

局限性:randcrack只能提交624次,且必须是32位数,位数多了少了都不行,这样预测出的结果才是正确的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from hashlib import md5
from randcrack import RandCrack

f = open("random.txt",'r').readlines()
prng = []
j=0
for i in f:
i = i.strip('\n')
if(j%3==0):#分割数字,我们要按照32的位数来
prng.append(int(i))
elif(j%3==1):#将生成两次随机数的两个随机数分离出来,倒序的
prng.append(int(i)& (2 ** 32 - 1))
prng.append(int(i)>> 32)
else:#将生成三次随机数的三个随机数分离出来
prng.append(int(i)& (2 ** 32 - 1))
prng.append(int(i)& (2 ** 64 - 1) >> 32)
prng.append(int(i)>>64)
j+=1

rc = RandCrack()
for i in prng:
rc.submit(i)#传入624位的数据
flag = rc.predict_getrandbits(32) # 在给出的随机数数量多时,predict_getrandbits()可以预测下一个随机数
print('GKCTF{' + md5(str(flag).encode()).hexdigest() + '}')
########################################################################
import random, time
from randcrack import RandCrack

random.seed(time.time())

rc = RandCrack()

for i in range(624):
rc.submit(random.getrandbits(32))
# Could be filled with random.randint(0,4294967294) or random.randrange(0,4294967294)

print("Random result: {}\nCracker result: {}"
.format(random.randrange(0, 4294967295), rc.predict_randrange(0, 4294967295)))

6.MT19937 Predictor库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import random  
from mt19937predictor import MT19937Predictor

predictor = MT19937Predictor()

# 首先用624个32位随机数来初始化预测器
for _ in range(624):
predictor.setrandbits(random.getrandbits(32), 32)

# 然后使用正确的方法名来预测随机数
for _ in range(1024):
assert predictor.getrandbits(32) == random.getrandbits(32)
assert predictor.getrandbits(64) == random.getrandbits(64)
assert predictor.getrandbits(128) == random.getrandbits(128)
assert predictor.getrandbits(256) == random.getrandbits(256)
print("123")

7题目:

7.1.seedrand(XSWCTF初赛)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 题目
from hashlib import sha256
import random
from Crypto.Util.number import *
import os
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

with open("/home/ctf/flag", "r") as f:
flag = f.read().strip().encode()

class prng:
def __init__(self, seed, a, b, c, d, e, p):
self.state = seed
self.a = a
self.b = b
self.c = c
self.d = d
self.e = e
self.p = p

def next(self, i):
if i % 2 == 0:
self.state = (self.a * self.state**2 + self.b * self.state + self.c) % self.p
else:
self.state = (self.d * self.state + self.e) % self.p
return self.state

def show(self):
return (self.a, self.b, self.c, self.d, self.e, self.p)

p = getPrime(256)
[a, b, c, d, e] = [random.randint(1, p) for _ in range(5)]

logo = '''
█ █ █████ █ █ █████ ███████ ███████
█ █ ░ █ ░░░░░ █░ █░ █ ░░░░░ ░░█░░░░ █░░░░░░░
█ █ ░ ░█░ ░░░░░ █░░█ █░░█░ ░░░░░ ░█░░░░░█░░░░░░░░
█ ░ ░ ░█████░░░█░░█░ █░░█░░ ░░░░░ █░░░░░█████░░░░░
█ █ ░ ░ ░░░░█ █░█░█░█░░█░░░ █░░░ █░░░░░█ ░ █ ░ ░░░█░ ██░░ ██░░█░░░ █░░░ █░░░░░░ █ ░ ░ █ █████░░░█░░░░ █░░░█████ █░░░ █░░░░░░░
░ ░ ░ ░ ░░░░░ ░░░░░ ░ ░░░ ░░░░░ ░░░ ░░░
░ ░ ░ ░░░░░ ░ ░░░ ░░ ░░░░░ ░░ ░░
░ ░ ░░░░░ ░ ░ ░░░░░ ░ ░ '''
menu = '''
[1] Show PRNG parameters
[2] Give me your seed
[3] Generate Challenge
[4] Exit
'''

if __name__ == "__main__":
print(logo)
while True:
print(menu)
choice = input("Your choice: ")
if choice == '1':
print(f"PRNG parameters:\na = {a}\nb = {b}\nc = {c}\nd = {d}\ne = {e}\np = {p}\n")
elif choice == '2':
seed_input = input("Enter your seed (integer): ")
try:
user_seed = int(seed_input)
user_prng = prng(user_seed, a, b, c, d, e, p)
print("Seed accepted!\n")
except ValueError:
print("Invalid seed. Please enter an integer.\n")
elif choice == '3':
if 'user_prng' not in locals():
print("Please provide your seed first (option 2).\n")
continue
tmp = user_seed
rng = random.Random()
rng.seed(os.urandom(16))
gift = []
for i in range(320):
random_number = rng.getrandbits(256)
usr_number = user_prng.next(i)
if usr_number == tmp:
gift.append(random_number)

key = long_to_bytes(rng.getrandbits(128))
cipher = AES.new(key, AES.MODE_ECB)
c = cipher.encrypt(pad(flag, 16))
print(f"Encrypted flag (hex): {c.hex()}")
print(f"gift = {gift}\n")
break
elif choice == '4':
print("Exiting. Goodbye!")
break
else:
print("Invalid choice. Please try again.\n")

**该题目因个人原因,我要用三个脚本(依次执行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/usr/bin/env python3
from sage.all import *
from Crypto.Util.number import *
from pwn import *
from tqdm import *

io = remote("127.0.0.1", 43481)
io.recvuntil(b"Your choice: ")
io.sendline(b'1')
io.recvuntil(b"a = ")
a = int(io.recvline().strip())
io.recvuntil(b"b = ")
b = int(io.recvline().strip())
io.recvuntil(b"c = ")
c = int(io.recvline().strip())
io.recvuntil(b"d = ")
d = int(io.recvline().strip())
io.recvuntil(b"e = ")
e = int(io.recvline().strip())
io.recvuntil(b"p = ")
p = int(io.recvline().strip())

PR = PolynomialRing(Zmod(p), 'x')
x = PR.gen()
f = d*(a*x**2 + b*x + c) + e - x
g = a*x**2 + b*x + c - x
seed1 = int(f.roots(multiplicities=False)[0])
print("seed = ",seed1)

io.recvuntil(b"Your choice: ")
io.sendline(b'2')
io.recvuntil(b"Enter your seed (integer): ")
io.sendline(str(seed1).encode())
io.recvuntil(b"Seed accepted!\n")
io.recvuntil(b"Your choice: ")
io.sendline(b'3')
io.recvuntil(b"Encrypted flag (hex): ")
c = io.recvline().strip()
c = bytes.fromhex(c.decode())
io.recvuntil(b"gift = ")
gift = eval(io.recvline().strip().decode())
print("c = ",c)
print("gift = ",gift)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
这是第二个脚本,也是最重要的一个脚本,后面就是得到key了
# python3.11 xxx.py
#!/usr/bin/env python3

from gf2bv import LinearSystem
from gf2bv.crypto.mt import MT19937

def mt19937(bs, out):
    lin = LinearSystem([32] * 624)
    mt = lin.gens()
    rng = MT19937(mt)
    zeros = []
    for o in out:
        rng.getrandbits(256)
        zeros.append((rng.getrandbits(256)) ^ o)
    zeros +=[mt[0] ^ 0x80000000] # MT19937 的状态数组首元素 mt[0] 的第 31 位(MSB)永远是 1。没有这个条件,会破坏预期周期性。也就无法求解
   
    print("solving...")
    sol = lin.solve_one(zeros)
    rng = MT19937(sol)
    pyrand = rng.to_python_random()
    ans = 0
    for i in range(160):
        pyrand.getrandbits(256)
        veri = pyrand.getrandbits(256)
        if veri == out[i]:
            ans += 1
    print(f"Correct outputs: {ans}/160")
    if ans != 160:
        return        
    key = (pyrand.getrandbits(128))
    print("key")
    print(key)

gift =  [104394689524...]
mt19937(256, gift)

7.2.高斯求逆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# [2022DASCTF MAY 出题人挑战赛]Yusa的密码学课堂——一见如故
def forward(x):
x = int(x)
x = (x ^ ((x <​<​ 11) ^ (x >> 21)) ^ ((x <​<​ 15) ^ (x >> 17))) & 0xffffffff
y = (x ^ ((x >> 7) ^ (x <​<​ 25)) ^ ((x >> 19) ^ (x <​<​ 13))) & 0xffffffff
return y

# 预计算逆变换(只需运行一次)
def build_inverse():
# 构建正向变换的 32x32 GF(2) 矩阵并求逆
M = [[0]*32 for _ in range(32)]
for j in range(32):
y = forward(1 <​<​ j)
for i in range(32):
if (y >> i) & 1:
M[i][j] = 1

# 高斯-约当消元求逆
n = 32
aug = [row + [1 if i==j else 0 for j in range(n)] for i, row in enumerate(M)]
# [M | I] → 行变换 → [I | M⁻¹]
for col in range(n):
pivot = next(r for r in range(col, n) if aug[r][col])
aug[col], aug[pivot] = aug[pivot], aug[col]
for r in range(n):
if r != col and aug[r][col]:
for k in range(2*n):
aug[r][k] ^= aug[col][k]
Minv = [row[n:] for row in aug]

# x = M⁻¹ × y (在GF(2)上)
def invert(y):
y = int(y)
x = 0
for i in range(32):
bit = 0
for j in range(32):
if (y >> j) & 1:
bit ^= Minv[i][j]
if bit:
x |= (1 <​<​ i)
return x
return invert

# 创建逆函数
invert_forward = build_inverse()

x = 123456
y = forward(x)
x2 = invert_forward(y)
print(x, y, x2, x == x2) # 应输出 True

MT19937伪随机
https://baymax-fools.github.io/2026/02/19/crypto/MT19937伪随机/
Author
Baymax
Posted on
February 19, 2026
Updated on
March 10, 2026
Licensed under