【问题标题】:Random number generator crashing in assembly随机数生成器在装配中崩溃
【发布时间】:2019-05-23 20:57:32
【问题描述】:

在 stackoverflow 上找到此代码,我了解它的工作原理并尝试实现它。它在 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

【问题讨论】:

  • 当您尝试rand 时,您是否首先使用srand 为生成器播种?你是在 DOS 模拟器中运行的吗?
  • 旧的 BIOS 中断(如 int 1Ah)只能在 16 位实模式代码中工作。它没有 .flat 内存模型。因此,您肯定将其构建为 32 位程序,当您使用 int 时,这会立即死亡。
  • 您调用播种机srand 一次,然后连续调用rand 以生成随机数。查看rand的文档。
  • 我同意 Hans 和 Lurker 的观点,尽管我对标签感到困惑。显然,您正在混合 32 位 Windows 代码和 DOS 代码,这将失败。但是,您使用了 [x86-16] 标记,这表明您正在尝试创建 16 位程序。所以我会问这个。您是在尝试开发可以在 MSDOS 上运行的 16 位 DOS 应用程序,还是在尝试创建 32 位 Win32 应用程序?您提到建议 Windows 应用程序的 Ollydbg。 [x86-16] 标签有误吗?
  • 介绍性 CS 课程真的不应该教如何编写汇编语言 MS-DOS 程序。它没有帮助学生为任何实际使用的汇编做好准备(比如矢量化关键循环或编写设备驱动程序),它看起来不像现代程序中的asm 过程或以任何方式让学生准备写一个,它对任何实际目的都没有用,它甚至不比现代 ISA 简单。

标签: winapi assembly x86 masm msvcrt


【解决方案1】:

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

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

srand 可用于更改 PRNG 的种子值。更改种子值将改变rand 将产生的数字,但在给定相同种子的情况下,它们将始终是相同的数字序列。您需要的是一种机制,可以在每次运行程序时将种子值设置为不同的值。您可以使用带有 NULL(0) 参数的 Ctime 函数来获取自 1970 年 1 月 1 日午夜以来的秒数。只要您的程序不是,这个值应该不同以在同一秒内执行的方式快速运行。这通常已经足够了。

然后您可以将 EAX 中 time(0) 返回的值传递给 srand 以设置种子值。程序启动时只调用一次srand。从那时起,您应该可以调用rand 来获得一个新的随机数。 rand 返回 0 和 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成为我们代码的入口点,并将跳过通常必须在main之前完成的C运行时初始化被调用。未能调用 C 运行时初始化可能会使 C 库函数意外工作或完全崩溃。

当代码完成时,我执行ret 以返回将退出的C 启动代码。该代码还保留了非易失性(被调用者保存)寄存器。有关详细信息,请参阅 Microsoft 32-bit CDECL calling convention

【讨论】:

    猜你喜欢
    • 2012-10-24
    • 2017-02-24
    • 1970-01-01
    • 1970-01-01
    • 2011-05-07
    • 1970-01-01
    • 1970-01-01
    • 2020-05-21
    • 1970-01-01
    相关资源
    最近更新 更多