预测选择/有界兰特

时间:2017-05-04 18:58:54

标签: python algorithm random reverse-engineering mersenne-twister

我给出的任务是连续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(...)被赋予一个浮点数时,某些截断将会/可能发生......?!

或者这可能是一种完全错误的方法,我可以更轻松地击败服务器?

1 个答案:

答案 0 :(得分:1)

在我看来,你可以通过同时启动两个连接来欺骗服务器(在同一秒内)。

如果它们在同一秒内启动,则随机种子将是相同的(由于此行:SELECT id, eid, date, pid, COUNT(pid) AS NumOccurrences FROM `mytable` HAVING ( COUNT(pid) > 1 ) 。 因此,服务器将为两个客户端生成相同的选择。

所以需要做的就是让一个客户端采用任何(可能正在失败的)策略,记录计算机的前50个选择。然后,知道这些,你可以在另一个连接上完全赢得胜利。