Kenshoru
Kenshoru

Reputation: 35

Random number generator crashing in assembly

Found this code here on stackoverflow, I understood how it works and tried implementing it. It crashes at the INT 1AH instruction and I don't know why. When I run it in ollydbg, it stops at the same line.

I also tried the random number generator function rand(void) but it always gives me the same numbers whenever I rerun the code. (different 3 numbers if I call it 3 times in a row, but still the same ones with every rerun)

.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

Upvotes: 3

Views: 1672

Answers (1)

Michael Petch
Michael Petch

Reputation: 47573

If you are writing Win32 programs you can't call BIOS and DOS services like Int 1ah, Int 10h, Int 21h etc. That will crash your application as Win32 programs do not have access to those services.

The basic rand and srand in the Windows C library (MSVCRT.LIB) are based on a linear congruent generator (LCG) pseudo-random number generator (PRNG). This formula relies on a seed value to set the initial state of the PRNG. The initial state when your program executes will always be the same each time the program is restarted. Every call to rand will then reproduce a pseudo-random number, but the numbers will be the same sequence each time the program is run.

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 function with a NULL(0) parameter to get the number of seconds since midnight January 1, 1970. This value should be different as long as your program isn't run quickly in such a way it executes within the same second. This is generally good enough.

You can then pass the value returned by time(0) in EAX to srand to set the seed value. Only call srand once when your program starts. From that point on you should be able to call rand to get a new random number. rand returns value between 0 and RAND_MAX and RAND_MAX is 32767.

This sample code does srand(time(0)) to initialize the seed and then loops 10 times printing out a different random number retrieved by calls to rand. Each time you run the program the output should be different.

.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

Some other important changes. I use the C (CDECL) calling convention (via .model flat, C) which automatically handles decorating main PROC with an underscore in 32-bit code. I have also changed start to main and changed end start to just end. We don't want to use end main either because that directive will make main the entry point to our code and will skip the C runtime initialization that usually has to be done prior to main being called. Failure to have the C runtime initialization called may make C library function work unexpectedly or crash altogether.

When the code finishes I do a ret to return to the C startup code which will exit for us. The code also preserves the non-volatile (callee saved) registers. See the Microsoft 32-bit CDECL calling convention for more information.

Upvotes: 3

Related Questions