lcg随机数破解

题目链接

  • 根据线性同余生成器lcg可得:lcg=(a*x+c)%m
  • 根据题目,当输入数字时,可以得到多个连续随机数,根据伪随机数生成的机制,可以了解到,随机数的生成只要知道随机数种子就可以破解
  • 类推到线性同余方程组,也就是说当知道a,c,m时即可知道随机数的整个序列
  • 例如:假设我们知道lcg0,那么lcg1=(alcg0+c)%m,同理lcg2=(alcg1+c)%m
  • 此时我们随意提交多次,可以从中得到连续的随机数,类比于lcg0,lcg1,lcg2的关系
  • lcg0=32081 lcg1=23815 lcg2=1237 lcg3=2387
  • 爆破模数module,每次moudle值改变时,都进行下面的操作:
lcg1=(a*lcg0+c)%m       lcg2=(a*lcg1+c)%m

两式相减   左边为:lcg2-lcg1   右边为:(a*lcg1+c)-(a*lcg0+c)

化简为: [lcg2-lcg1=a*(lcg1-lcg0) ] %m

根据求余运算规则可知  
[a=(lcg2-lcg1)*gmpy2.invert(lcg1-lcg0,m)]%m

此时代入lcg0,lcg1,lcg2之后可求得a

代入lcg1=(a*lcg0+c)%m,把c当作未知数放到等式左边后,式子为:
c=[lcg1-(a*lcg0)]%m

类推lcg3_guess=(a*lcg2+c)%m
将求得地lcg3_guess与lcg3比较,如果相等,证明求得的a,c,m为正确值
否则,m=m+1,继续验证

注意:
1、这个式子成立条件是两边同时对m求余
2、gmpy2.invert(x1,x2)是求与x1%x2同余的最小数,二者在求余操作后等价,因为余数相等,该式表示余数,类似7%5=2%5
  • 求得正确的a,c,m之后,线性同余方程为 lcg[i]=(a*lcg[i-1]+c)%m
  • 多次提交成功之后,获得flag

注意:下面的exp有失败的机率,多次运行得到flag
失败原因:在得到四个随机数的时候,如果恰好四个随机数都比较小,那么很有可能得到错误的三个参数。

exp

import requests
import gmpy2
from bs4 import BeautifulSoup

# LCG solver
def lcg(m, a, c, x=0):
    return (a * x + c) % m

def rlcg(m, a, c, x=0):
    ainv = gmpy2.invert(a, m)
    return ainv * (x - c) % m

def solve_a(lcg0, lcg1, lcg2, m):
    a = (lcg2 - lcg1) * gmpy2.invert(lcg1 - lcg0, m) % m
    return a

def solve_c(lcg0, lcg1, m, a):
    c = (lcg1 - a * lcg0) % m
    return c

session = requests.session()

# API functions
def guess(num=0):
	url = f'http://192.168.233.128/?guess={num}'
	res = session.get(url=url)

	soup = BeautifulSoup(res.text, 'html.parser')
	for paragraph in soup.select('form > p'):
		paragraph = paragraph.get_text(strip=True, separator=" ")

		if 'The Monolith desired:' in paragraph:
			desired = paragraph.split('The Monolith desired: ', 1)[1].split(' ', 1)[0]
			desired = int(desired.strip())

		print('// ' + paragraph)
	
	return desired


def main():
	# Get lcg values original
	lcg_0 = guess()
	lcg_1 = guess()
	lcg_2 = guess()
	lcg_3 = guess()
	print(f'Retrieved:')
	print(f'>> lcg_0: {lcg_0}')
	print(f'>> lcg_1: {lcg_1}')
	print(f'>> lcg_2: {lcg_2}')
	print(f'>> lcg_3: {lcg_3}')
	print()

	# Find the mod value
	modulus = -1
	for mod_value in reversed(range(30000, 2**16)):
		try:
			a = solve_a(lcg_0, lcg_1, lcg_2, mod_value)
			c = solve_c(lcg_0, lcg_1, mod_value, a)
			next = lcg(mod_value, a, c, lcg_2)

			if next == lcg_3:
				modulus = mod_value
				break
		except ZeroDivisionError:
			pass

	# Now we have all the parameters
	print(f'Found parameters:')
	print(f'>> mod: {modulus}')
	print(f'>> a: {a}')
	print(f'>> c: {c}')
	print()

	print(f'Doing guesses:')
	lcg_last = lcg_3
	for x in range(30):
		lcg_last = lcg(mod_value, a, c, lcg_last)
		print(f'>> next {x}: {lcg_last}')
		guess(lcg_last)



if __name__ == '__main__':
	main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值