如何在程序集中播种随机数生成器?

时间:2017-08-09 12:07:18

标签: assembly random x86 x86-16

我试图在程序集中创建一个完整的随机数,但是每次启动程序时它都会以相同的顺序给出相同的数字。 如果数字是12,132,4113或其他什么,那么每次我启动代码时它都会重复它们。

我试图制作的节目类似于猜谜游戏。

    IDEAL
MODEL small
STACK 100h
DATASEG
;vars here
RNG_Seed dw ? 

CODESEG
; Generates a pseudo-random 15-bit number.
; Parameters: <none>
; Clobbers:   AX, DX
; Returns:    AX contains the random number
proc GenerateRandNum
   push bx
   push cx
   push si
   push di


   ; 32-bit multiplication in 16-bit mode (DX:AX * CX:BX == SI:DI)
   mov  ax, [RNG_Seed]
   xor  dx, dx
   mov  cx, 041C6h
   mov  bx, 04E6Dh
   xor  di, di
   push ax
   mul  bx
   mov  si, dx
   xchg di, ax
   mul  bx
   add  si, ax
   pop  ax
   mul  cx
   add  si, ax


   ; Do addition
   add  di, 3039h
   adc  si, 0


   ; Save seed
   mov [RNG_Seed], di


   ; Get result and mask bits
   mov  ax, si
   and  ah, 07Fh


   pop  di
   pop  si
   pop  cx
   pop  bx
   ret
endp GenerateRandNum

我可以做些什么来每次运行获得不同的随机数?

1 个答案:

答案 0 :(得分:4)

您需要使用“随机”内容初始化RNG_Seed。这对于计算机等确定性机器来说实际上有点问题。

特别是如果你想让随机性足够强大以进行加密,那么你需要几个小时来研究当前的解决方案,一些行业解决方案甚至涉及带有白噪声发生器的特殊硬件芯片,用作随机值发生器。

正如你想要的只是一场比赛,它并没有那么糟糕,但仍然有点棘手。

我只是猜测你处于实模式(因为你使用了16b寄存器),所以读取BIOS滴答 - 从午夜开始作为一个熵源:

xor ah,ah  ; ah = 0
int 1Ah    ; returns in cx:dx ticks since midnight (18.2Hz ticks)
; let's mix the cx:dx a bit together to get a bit more entropy out of it
rol cx,8
xor dx,cx
xor [RNG_Seed],dx ; "add" that entropy to the original seed

这会稍微改善一下,但它仍然有点差的解决方案(同时运行游戏可能很容易产生相同的随机值),所以这里有另一个“廉价”熵源的建议:

要求玩家输入名称(至少3个字符),并在等待键时始终按inc counter计时每次击键,每次保持低4位计数器(3个字符+输入= 4 * 4bits =总共16位)。然后再次“添加”(xor)到RNG_Seed

这两件事应该为游戏RNG产生足够的熵(但不足以达到安全目的,比如加密)。

编辑:如评论中所述,在混合不同的种子值时,请确保它们不相关,或者使用add而不是xor。我最初的想法是从午夜开始使用BIOS滴答,xor - 针对变量的一些随机存储器(可能为零,BTW,在exe加载期间由OS清除),然后测量用户击键时间,组成的那些作为4:4:4:4比特的4次击键(没有xor,只是相互附加,直到完全16b准备就绪),并在主要种子上使用xor的最终值。由于击键与BIOS滴答无关,因此不应该干扰xor在这种特殊情况下应该正常工作。

另外为什么4:4:4:4来自键击,而不是每次xor-ed反复出现单个16b值。我希望你通过调用int 16h, ah=1来实现键击延迟计数器,所以如果你将无限循环遍历这个,并增加一些计数器,它很可能会非常快地运行超过16个值(低于数千秒)猜测)。然后使用这种计数器的低4位几乎与用户击中键的方式无关。虽然在非常慢的计算机上使用完全16b的等待用户实际上可能在高位中产生稍微类似的位模式(即,用户每次在10000-13000的计数器值之间的某个点击 - >前2位总是为零并且整个顶部每次8位非常相似)。所以我的意思是使用低4位,将它们中的四个连接在一起形成16b值。如果用户输入更长的名称,我甚至不会使用那些进一步的击键进行种子调整,只有前四个。我可能会做一些调试,看看它在现实中是如何工作的,也许我对结果过于乐观,并且有一些隐藏的同步捕获。但是我实在太懒了,试着写它(就像~20行代码,但需要dosbox和一些DOS调试器)。