【问题标题】:how to seed a random number generator in assembly?如何在汇编中播种随机数生成器?
【发布时间】:2017-08-09 12:07:17
【问题描述】:

我试图在汇编中创建一个完整的随机数,但每次我启动程序时,它都会以相同的顺序给我相同的数字。 如果数字是 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

我可以怎样做才能在每次运行时获得不同的随机数?

【问题讨论】:

  • 您的程序是否在没有操作系统的情况下运行?
  • @James 你在操作系统中是什么意思?
  • 操作系统
  • 对于 PRNG,您可能希望使用更像 xorshift+ 的东西,以避免需要 32 位乘法。 OTOH,找到不适合 32 位版本的常量并非易事。 The reference version 用于 64 位整数。 (您可以只实现 64 位版本以获得相当高质量的 PRNG,尤其是如果您可以使用 386 SHRD / SHLD 来提高扩展精度移位的效率。)
  • @PeterCordes 该论文也有 32 位的常量。见,例如here

标签: assembly random x86 x86-16


【解决方案1】:

你需要用“随机”的东西初始化RNG_Seed。这在计算机等确定性机器上实际上有点问题。

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

因为你只希望它用于游戏,它并没有那么糟糕,但仍然有点棘手。

我只是猜测您处于实模式(因为您使用 16b 寄存器),因此请阅读 BIOS ticks-since-midnight 作为熵的来源之一:

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 * 4 位 = 总共 16 位)。然后再次将其“添加”(异或)到RNG_Seed

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


编辑:如 cmets 中所述,当混合不同的种子值时,确保它们不相关,或者使用 add 而不是 xor。我最初的想法是从午夜开始使用 BIOS 滴答声,xor-ed 针对您的变量的一些随机内存(可能为零,顺便说一句,在 exe 加载期间由操作系统清除),然后测量用户的击键时间,那些组成为 4 :4:4:4 位 4 次击键(无异或,只是相互附加,直到完整的 16b 准备好),并在主种子上使用带有xor 的最终值。由于击键与 BIOS 滴答声没有任何关系,因此这不会干扰,xor 在这种特殊情况下应该可以正常工作。

还有为什么 4:4:4:4 来自击键,而不是每次异或一次又一次的单个 16b 值。我希望你通过调用int 16h, ah=1 来实现那个击键延迟计数器,所以如果你在这个上无限循环,并增加一些计数器,它很可能会很快运行超过 16 个值(我猜不到千秒) .然后使用这种计数器的低 4 位几乎与用户击键的方式[快速] 相关。虽然在非常慢的计算机上使用完整的 16b 等待用户实际上可能会在高位中产生一些相似的位模式(即用户每次在计数器的 10000-13000 值之间的某个地方按下键 -> 前 2 位始终为零并且整个前 8 位位每次都会非常相似)。这就是我使用低 4 位的意思,将其中的四个连接在一起形成 16b 值。如果用户输入更长的名称,我什至不会使用那些进一步的击键来调整种子,只使用前四个。我可能会做一些调试来看看它在现实中是如何工作的,也许我对结果过于乐观并且有一些隐藏的类似同步的捕获。但我懒得实际编写它并尝试(它就像大约 20 行代码,但需要 dosbox 和一些 DOS 调试器)。

【讨论】:

  • 从(大部分)确定性机器中创建熵一直是我最喜欢的任务之一——非常有创意!写一个熵源的大列表以及它们的比特大小会很有趣(这可能很难证明)。例如,除了rdtscrdseedrdrand,到下一次垂直回撤的时间(以任何单位为单位)是一个很好的来源:)
  • @S.josh: RDRAND r16 应该在 16 位模式下工作,因为它不使用 VEX 编码(这就是为什么某些 BMI/BMI2 指令不使用的原因) t 在 16 位模式下可用)。但当然只有在支持它的 CPU 上,即使这样,特定 CPU 中的 RNG 硬件也可能出现故障,导致它返回 CF=0 非随机数据。
  • @Ped7g: xor 对于可能相关的事物来说是一个危险的混合函数。 As @Yakk says+ 通常更好:x+xx 中只燃烧 1 位熵,而 x^x 将它们全部燃烧,在您最终两次获得相同值的情况下。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-05
  • 1970-01-01
  • 2015-03-14
相关资源
最近更新 更多