随机数生成器在装配中崩溃

Random number generator crashing in assembly

在 Whosebug 上找到这段代码,我了解它的工作原理并尝试实现它。它在 INT 1AH 指令处崩溃,我不知道为什么。 当我 运行 它在 ollydbg 时,它停在同一行。

我还尝试了随机数生成器函数 rand(void),但每当我重新运行 代码时,它总是给我相同的数字。 (如果我连续调用它 3 次,则有 3 个不同的数字,但每次调用仍然相同 运行)

.386
.model flat, stdcall

includelib msvcrt.lib
extern exit: proc
extern printf: proc
extern rand: proc

public start


.data
decimal_format DB "%d",0ah

.code
start:
    mov ah, 00h
    int 1ah

    mov ax,dx
    mov dx,0
    mov cx,10
    div cx

    mov ah,2h
    int 21h

    push edx
    push offset decimal_format
    call printf
    add esp,8

    push 0
    call exit
end start

如果您正在编写 Win32 程序,则不能调用 BIOS 和 DOS 服务,例如 Int 1ahInt 10hInt 21h 等。这会使您的应用程序崩溃,因为 Win32 程序不会可以访问这些服务。

WindowsC库(MSVCRT.LIB)中的基本randsrand是基于线性全等的生成器 (LCG) pseudo-random 数字生成器 (PRNG)。此公式依赖于种子值来设置 PRNG 的初始状态。每次程序重新启动时,程序执行时的初始状态将始终相同。每次调用 rand 都会重新生成一个 pseudo-random 数字,但是每次程序 运行.

时数字的序列都是相同的

srand can be used to change the seed value of the PRNG. Changing the seed value will alter the numbers rand will produce but they will always be the same sequence of numbers given the same seed. What you need is a mechanism to set the seed value to a different value each time the program is run. You can use the C library time 带有 NULL(0) 参数的函数获取自 1970 年 1 月 1 日午夜以来的秒数。只要您的程序不是 运行 快速,这个值应该不同以这种方式它在同一秒内执行。这通常就足够了。

然后您可以将 return 由 time(0) 在 EAX 中编辑的值传递给 srand 以设置种子值。程序启动时只调用 srand 一次。从那时起,您应该可以调用 rand 来获得一个新的随机数。 rand return0 和 RAND_MAX 和 RAND_MAX 之间的值是 32767。

此示例代码 srand(time(0)) 初始化种子,然后循环 10 次打印出通过调用 rand 检索到的不同随机数。每次 运行 程序的输出都应该不同。

.386
.model flat, C

includelib msvcrt.lib
extern exit: proc
extern printf: proc
extern rand: proc
extern srand: proc
extern time: proc

.data
decimal_format DB "%d", 0ah, 0 
                             ; Ensure string is NUL(0) terminated

.code

main PROC
    push ebx                 ; Save callee saved (non-volatile) registers that we use.
                             ; EBX, EBP, ESI, EDI, ESP are non-volatile. For each
                             ; one we clobber we must save it and restore it before
                             ; returning from `main`

    push 0
    call time                ; EAX=time(0)
    add esp, 4
    push eax                 ; Use time as seed
    call srand               ; srand(time(0))
    add esp, 4

    mov ebx, 10              ; Loop 10 times

loopit:
    call rand                ; Get a random number between 0 and 32767 into EAX

    push eax
    push offset decimal_format
    call printf              ; Print the random number
    add esp,8

    dec ebx
    jnz loopit               ; Loop until the counter EBX reaches 0

    pop ebx                  ; Restore callee saved registers
    xor eax, eax             ; Return 0 from our program
    ret
main ENDP

END

其他一些重要的变化。我使用 C (CDECL) 调用约定(通过 .model flat, C)自动处理在 32 位代码中使用下划线装饰 main PROC。我还将 start 更改为 main,并将 end start 更改为 end。我们也不想使用 end main,因为该指令将使 main 成为我们代码的入口点,并将跳过 C 运行 时间通常必须在调用 main 之前完成的初始化。未能调用 C 运行time 初始化可能会使 C 库函数意外工作或完全崩溃。

代码完成后,我对 C 启动代码执行 ret 到 return,这将为我们退出。该代码还保留了 non-volatile(被调用方已保存)寄存器。有关详细信息,请参阅 Microsoft 32-bit CDECL calling convention