在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
答案 0 :(得分:3)
如果您正在编写Win32程序,则无法调用BIOS和DOS服务,例如Int 1ah
,Int 10h
,Int 21h
等。由于Win32程序无法访问,这将导致应用程序崩溃这些服务。
Windows C 库(MSVCRT.LIB)中的基本rand
和srand
基于线性同余生成器(LCG)伪随机数生成器(PRNG) )。此公式依赖于种子值来设置PRNG的初始状态。每次重新启动程序时,程序执行时的初始状态将始终相同。这样,每次对rand
的调用都会产生一个伪随机数,但是每次运行程序时,这些数字将具有相同的顺序。
srand
可用于更改PRNG的种子值。更改种子值将更改将产生的数字rand
,但是在给定相同种子的情况下,它们始终是相同的数字序列。您需要的是一种在每次运行程序时将种子值设置为不同值的机制。您可以将 C 库time
函数与NULL(0)参数一起使用,以获取自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
成为我们代码的入口点,并且将跳过通常必须先进行的 C 运行时初始化。 main
被调用。如果未调用 C 运行时初始化,可能会导致 C 库函数意外运行或完全崩溃。
代码完成后,我执行ret
返回到 C 启动代码,该代码将为我们退出。该代码还保留了非易失性(保存被调用者)寄存器。有关更多信息,请参见Microsoft 32-bit CDECL calling convention。