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

工具包:来自这位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 

猜你喜欢

转载自blog.csdn.net/hacker_zrq/article/details/121419286