我给出的任务是连续50次使用针对此RockPaperScissor-PythonServer的自编客户端
import SocketServer,threading,os,string
import random, time
f = open('secret.txt')
offset = int(f.readline().strip())
choices = {
'r': 'rock',
'p': 'paper',
's': 'scissors'
}
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
class MyTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
rnd = random.Random()
# Initialize the random number generator to some secret value
# Note: the value of offset is too big to guess/bruteforce you need to find a better way :)
rnd.seed(int(time.time() + offset))
self.request.sendall("Rock paper scissors is back\n")
win_count = 0
play_again = True
while play_again:
while win_count < 50:
self.request.sendall("choose one [r] rock, [p] paper, [s] scissors: ")
your_choice = self.request.recv(1024).strip()
if not your_choice in 'rps':
continue
self.request.sendall("Your choice %s\n" % choices.get(your_choice))
my_choice = rnd.choice("rps")
self.request.sendall("My choice %s\n" % choices.get(my_choice))
if my_choice == your_choice:
self.request.sendall("Its a tie, sorry you need to win 50 times in a row, a tie is simply not good enough.\nWho ever said life was fair?\n")
break
if ((my_choice == 'r' and your_choice == 'p') or
(my_choice == 'p' and your_choice == 's') or
(my_choice == 's' and your_choice == 'r')):
win_count += 1
self.request.sendall("Arghhh. you beat me %s times\n" % win_count)
else:
self.request.sendall("You loose!\n")
break
if win_count == 50:
self.request.sendall("50 times in a row?!? are you some kind of mind reader?\n")
return
else:
win_count = 0
answer = ''
while answer not in ('y','n'):
self.request.sendall("Play again? (y/n): ")
answer = self.request.recv(1024).strip().lower()
if answer == 'n':
return
SocketServer.TCPServer.allow_reuse_address = True
server = ThreadedTCPServer(("0.0.0.0", 1178), MyTCPHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
server.serve_forever()
我已经在python的random.py文档和各种网站上读过pythons随机类使用的核心随机数生成器(MersenneTwister)不适合安全相关的东西,因为当攻击者设法获得624时它是可预测的连续数字。
我已经有一个播放624次摇滚的客户端,并且在每一轮中检测服务器选择,将其转换为[rps]中的相应数组索引并将该数字写入文件。所以最后还有一个长文件,其中包含许多0&1,1&2和2&#39; s
0
1
0
2
2
0
....
对我来说服务器代码中最重要的一行显然是
my_choice = rnd.choice("rps")
实现为(从random.py中提取):
def choice(self, seq):
"""Choose a random element from a non-empty sequence."""
return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty
Here我读到为了预测下一个数字我需要记录624个连续数字并通过反转/撤消某些变换来恢复状态,但是我认为直接核心rng输出,这是一个浮点数[0.0,1.0],是必需的......
要从序列索引中获取核心rng输出,似乎我只需要完全颠倒函数的上述代码&#34; choice()&#34;,这就像
seq_value = seq[int(core_rng_out * len(seq))]
seq_index = int(core_rng_out * len(seq))
int^-1(seq_index) = core_rng_out * len(seq)
int^-1(seq_index) / len(seq) = core_rng_out
core_rng_out = int^-1(seq_index) / 3
以上应该是解决某个变量的数学方程式。除以3,因为序列是3大小的(&#34; rps&#34;), 但是pythons int(...)函数的反函数是什么?!?上面我尝试通过使它成为^ -1来抽象地将其标记为反向。
此外甚至可以获得rng浮点数?!?,因为在pythons int-doc中它表示当int(...)被赋予一个浮点数时,某些截断将会/可能发生......?!
或者这可能是一种完全错误的方法,我可以更轻松地击败服务器?
答案 0 :(得分:1)
在我看来,你可以通过同时启动两个连接来欺骗服务器(在同一秒内)。
如果它们在同一秒内启动,则随机种子将是相同的(由于此行:SELECT id, eid, date, pid, COUNT(pid) AS NumOccurrences
FROM `mytable`
HAVING ( COUNT(pid) > 1 )
。
因此,服务器将为两个客户端生成相同的选择。
所以需要做的就是让一个客户端采用任何(可能正在失败的)策略,记录计算机的前50个选择。然后,知道这些,你可以在另一个连接上完全赢得胜利。