伪随机数之梅森旋转算法的逆向求解。

本文介绍了一种针对MT19937随机数生成器的逆向工程方法,通过分析其内部状态转换过程,实现对已知输出的逆向求解,进而恢复初始种子并预测后续随机数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

工具包:来自这位dl的文章: 

Explore MT19937 | huangx607087's Blog

from Crypto.Util.number import *
from hashlib import md5
import random


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


class MT19937:
    def __init__(self, seed=0):
        self.mt = [0] * 624
        self.mt[0] = seed
        self.mti = 0
        for i in range(1, 624):
            self.mt[i] = _int32(1812433253 * (self.mt[i - 1] ^ self.mt[i - 1] >> 30) + i)

    def getstate(self, op=False):
        if self.mti == 0 and op == False:
            self.twist()
        y = self.mt[self.mti]
        y = y ^ y >> 11
        y = y ^ y << 7 & 2636928640
        y = y ^ y << 15 & 4022730752
        y = y ^ y >> 18
        self.mti = (self.mti + 1) % 624
        return _int32(y)

    def twist(self):
        for i in range(0, 624):
            y = _int32((self.mt[i] & 0x80000000) + (self.mt[(i + 1) % 624] & 0x7fffffff))
            self.mt[i] = (y >> 1) ^ self.mt[(i + 397) % 624]
            if y % 2 != 0:
                self.mt[i] = self.mt[i] ^ 0x9908b0df

    def inverse_right(self, res, shift, mask=0xffffffff, bits=32):
        tmp = res
        for i in range(bits // shift):
            tmp = res ^ tmp >> shift & mask
        return tmp

    def inverse_left(self, res, shift, mask=0xffffffff, bits=32):
        tmp = res
        for i in range(bits // shift):
            tmp = res ^ tmp << shift & mask
        return tmp

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

    def recover(self, y):
        y = self.inverse_right(y, 18)
        y = self.inverse_left(y, 15, 4022730752)
        y = self.inverse_left(y, 7, 2636928640)
        y = self.inverse_right(y, 11)
        return y & 0xffffffff

    def setstate(self, s):
        if (len(s) != 624):
            raise ValueError("The length of prediction must be 624!")
        for i in range(624):
            self.mt[i] = self.recover(s[i])
        # self.mt=s
        self.mti = 0

    def predict(self, s):
        self.setstate(s)
        self.twist()
        return self.getstate(True)

    def invtwist(self):
        high = 0x80000000
        low = 0x7fffffff
        mask = 0x9908b0df
        for i in range(623, -1, -1):
            tmp = self.mt[i] ^ self.mt[(i + 397) % 624]
            if tmp & high == high:
                tmp ^= mask
                tmp <<= 1
                tmp |= 1
            else:
                tmp <<= 1
            res = tmp & high
            tmp = self.mt[i - 1] ^ self.mt[(i + 396) % 624]
            if tmp & high == high:
                tmp ^= mask
                tmp <<= 1
                tmp |= 1
            else:
                tmp <<= 1
            res |= (tmp) & low
            self.mt[i] = res


# 题目
# flag = b'????????????'
# l = len(bin(bytes_to_long(flag))[2:])
# if l % 32 == 0:
#     l //= 32
# else:
#     l = l // 32 + 1
#
# rand = ''
# Rand = []
# for _ in range(l):
#     pro = random.getrandbits(32)
#     rand += bin(pro)[2:].zfill(32)
#     Rand.append(pro)
#
# s = bin(bytes_to_long(flag))[2:].zfill(len(rand))
# c = ''

# for i in range(len(s)):
#     c += str(int(s[i]) ^ int(rand[i]))
# print(c)


# def change(number):
#     number = number ^ (number >> 11)
#     number = number ^ ((number << 7) & 2636928640)  # 1001110100101100010101101 000 0000
#     number = number ^ ((number << 15) & 4022730752)  # 11101111110001100 000 0000 0000 0000
#     number = number ^ (number >> 18)
#     return number & 0xffffffff


Rand = [389902299, 3515959351, 2216779731, 2601284435, 514154742, 4172047173, 2921107804, 2217826537, 4248207905,
        1322376767]
c = '01101100010001011100100011111110110000101111000001100001110010001100111000110010111101011111100101011100111011110100001010100111100000010101100011001000011111110111111001010000010000111000101000011111011101001011110001010100100011001010001001111110011111100101111010000010101011100010001111011001010010001010001110001111'

if __name__ == '__main__':
    Demo = []

    # D.recover()方法就是逆向求原数字
    D = MT19937(48)  #这个里面填什么数字都没影响
    for i in range(len(Rand)):
        Demo.append(D.recover(Rand[i]))
    print('改变前的随机数: ', Demo)

    rand = ''
    for ch in Demo:
        rand += str(bin(ch)[2:]).zfill(32)  # 每个rand都是32位

    flag = ''
    for i in range(len(c)):
        flag += str(int(c[i]) ^ int(rand[i]))

    print('flag为: ', flag)
    # flag是一串0,1串,我不知道怎么转成二进制的形式,所有就先打印,再复制到下面,前面加上个0b
    print(long_to_bytes(
        0b01010011010110010100001101111011001100010111001100101101011100100110000101101110011001000011000001101101001011010110111101110010001011010110111001101111011101000010110101010100011010000110000101110100001011010110100100110101001011010110000100101101011100010111010100110011011100110111010001101001011011110110111001111101))

最后:

关于这个有一些博客讲的真不错。先挖个坑,以后再看。

谈谈梅森旋转:算法及其爆破 | 始终 (liam.page)   

Explore MT19937 | huangx607087's Blog

还有就是关于python的long_to_bytes()和bytes_to_long()方法的一点注意点:

加密用bytes_to_long()必须用字符串的二进制形式,才行,直接输入str会报错。

print(bytes_to_long(b'I am flag'))

解密的时候有的时候是用二进制的一串数字,千万记得二进制的时候一定前面加上0b,你要不加上0b他就会默认那个是十进制。这个细节注意不到也很头疼。

print(long_to_bytes(0b10010010010000001100001011011010010000001000110011011000110000101100111))
print(long_to_bytes(10010010010000001100001011011010010000001000110011011000110000101100111))

会发现结果截然不同,千万记得加上0b 

<think>嗯,用户想了解梅森旋转算法如何生成伪随机数,以及它的原理和实现。首先,我需要回忆梅森旋转算法的基本结构。记得它基于线性反馈移位寄存器(LFSR),但具体是怎么操作的? 梅森旋转算法的核心是使用一个大的状态数组,并通过某种递归关系来更新状态。比如MT19937,它的周期是2^19937-1,这个名字也来源于梅森素数。那状态数组的大小应该是624个32位整数,对吗?每个周期处理这624个状态,然后生成新的状态。 接下来,生成随机数的过程。当状态数组被初始化后,每次生成一个随机数的时候,会从当前的状态中提取,并经过一些处理,比如位移和掩码操作。可能涉及到“扭转”(twist)过程,当所有状态都被使用后,需要重新生成新的状态数组,这就是所谓的扭转步骤。 那具体怎么实现的呢?初始化的时候,需要种子,然后根据种子填充状态数组。种子可以是单个整数,或者数组。填充方法可能使用一个递公式,比如用前一个状态的值经过一些运算得到下一个值。比如,可能用异或、乘法和其他位操作。 然后是状态更新。每次生成一定数量的随机数后(比如624次),需要执行扭转操作。这个步骤可能涉及到将状态数组中的元素进行某种线性变换,可能包括左移、右移、与掩码异或等操作。比如,每个元素可能会被分解成高位和低位,然后低位经过变换后与高位结合,生成新的状态值。 在生成随机数的时候,每个状态元素会被进一步处理,比如通过右移、左移,与不同的掩码异或,最后可能还有一个乘法或加法步骤,以提高分布的均匀性。比如,MT19937的tempering步骤,包括位移和掩码操作,将状态中的数值打乱,生成最终的随机数。 用户可能需要具体的代码示例。这时候,我需要回忆Python中可能的实现方式。比如,状态数组的初始化,扭转函数的实现,以及如何从状态中提取随机数。要注意位操作的正确性,尤其是32位截断,可能需要使用掩码0xFFFFFFFF来保持数值在32位范围内。 另外,需要考虑梅森旋转算法的优缺点。比如,长周期和高维均匀分布,但状态空间较大,初始化较慢,不适合短随机数需求。还有,像种子必须正确初始化,否则可能影响随机性。 可能用户的问题还包括如何优化,或者与其他算法比较,但当前问题主要集中在原理和实现上。需要确保解释清晰,分步骤说明初始化、状态更新、随机数生成的各个阶段,并给出代码示例。同时,引用提供的参考资料中的内容,比如MT19937的参数和优点,来支持回答的正确性。 现在需要检查是否有错误的地方。比如,状态数组的大小是否正确,扭转的触发条件是否是索引到达数组末尾,以及tempering步骤的具体操作是否准确。此外,代码中的位操作是否正确处理了32位整数,避免溢出或错误计算。确保示例代码能够正确演示梅森旋转的关键步骤,即使可能简化了某些部分,但核心逻辑正确。</think>梅森旋转算法(Mersenne Twister)是一种基于线性反馈移位寄存器的伪随机数生成算法,其核心原理是通过二进制矩阵的线性递归操作生成均匀分布的数值序列。以下是其原理和实现的分步说明: --- ### **一、算法核心原理** 1. **状态数组结构** 算法维护一个长度为$n$的状态数组(MT19937中$n=624$),每个元素为32位整数,通过递归公式更新状态: $$x_{k+n} = x_{k+m} \oplus \left( (x_k^{\text{u}} \mid x_{k+1}^{\text{l}} ) \cdot A \right)$$ 其中$A$是常数矩阵,$\oplus$表示异或,$x^{\text{u}}$和$x^{\text{l}}$分别表示高低位截断[^3]。 2. **扭转(Twist)操作** 当状态数组遍历完毕后,通过非线性变换生成新状态,确保周期达到$2^{19937}-1$。变换包括位掩码、位移和矩阵乘法。 3. **输出处理(Tempering)** 从状态中提取数值后,需经过以下步骤增强分布均匀性: $$y = x \oplus ((x >> u) \& d)$$ $$y = y \oplus ((y << s) \& b)$$ $$y = y \oplus ((y << t) \& c)$$ $$y = y \oplus (y >> l)$$ 其中$u,s,t,l,d,b,c$为预设常数[^1]。 --- ### **二、Python实现示例** ```python class MersenneTwister: def __init__(self, seed): self.index = 624 self.MT = [0] * 624 self.MT[0] = seed for i in range(1, 624): self.MT[i] = 0xFFFFFFFF & (1812433253 * (self.MT[i-1] ^ (self.MT[i-1] >> 30)) + i) def extract_number(self): if self.index >= 624: self.twist() y = self.MT[self.index] y ^= (y >> 11) y ^= (y << 7) & 0x9D2C5680 y ^= (y << 15) & 0xEFC60000 y ^= (y >> 18) self.index += 1 return 0xFFFFFFFF & y def twist(self): for i in range(624): x = (self.MT[i] & 0x80000000) + (self.MT[(i+1)%624] & 0x7FFFFFFF) self.MT[i] = self.MT[(i+397)%624] ^ (x >> 1) if x % 2 != 0: self.MT[i] ^= 0x9908B0DF self.index = 0 ``` #### **代码说明** - **初始化**:通过种子生成初始状态数组,使用递公式确保多样性。 - **扭转函数**:通过位移和异或操作更新全部624个状态值。 - **输出处理**:通过四次位移和掩码操作优化数值分布。 --- ### **三、算法特性** | 特性 | 说明 | |--------------------|----------------------------------------------------------------------| | 周期长度 | $2^{19937}-1$,适用于长期连续随机数需求[^2] | | 高维均匀分布 | 在623维空间内均匀分布,适用于蒙特卡洛模拟 | | 局限性 | 状态空间较大(2.5KB),初始化速度慢于线性同余法 | --- ### **四、对比其他算法** - **线性同余法**:周期短(通常$2^{32}$),易预测,但实现简单。 - **Xorshift**:轻量快速,但周期和分布质量低于梅森旋转。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ad_m1n

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值