工具包:来自这位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))
最后:
关于这个有一些博客讲的真不错。先挖个坑,以后再看。
还有就是关于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