Сбой генератора случайных чисел в сборке - PullRequest
2 голосов
/ 23 мая 2019

Нашел этот код здесь на 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

1 Ответ

3 голосов
/ 24 мая 2019

Если вы пишете программы для Win32, вы не можете вызывать службы BIOS и DOS, такие как Int 1ah, Int 10h, Int 21h и т. Д. Это приведет к сбою приложения, поскольку программы Win32 не имеют доступа к этим службам.

Базовые rand и srand в библиотеке Windows C (MSVCRT.LIB) основаны на генераторе псевдослучайных чисел с линейным конгруэнтным генератором (LCG) (PRNG). Эта формула основывается на начальном значении для установки начального состояния PRNG. Начальное состояние при выполнении вашей программы всегда будет одинаковым при каждом перезапуске программы. Каждый вызов rand будет воспроизводить псевдослучайное число, но номера будут иметь одинаковую последовательность при каждом запуске программы.

srand можно использовать для изменения начального значения PRNG. Изменение начального значения приведет к изменению чисел rand, но они всегда будут иметь одинаковую последовательность чисел с одним и тем же начальным числом. Вам нужен механизм для установки начального значения на другое значение при каждом запуске программы. Вы можете использовать функцию C library time с параметром NULL (0), чтобы получить количество секунд с полуночи 1 января 1970 года. Это значение должно отличаться, пока Ваша программа не запускается так быстро, как она выполняется в течение одной секунды. Как правило, это достаточно хорошо.

Затем вы можете передать значение, возвращаемое time(0) в EAX, на 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), которое автоматически обрабатывает оформление main PROC подчеркиванием в 32-битном коде. Я также изменил start на main и изменил end start на end. Мы также не хотим использовать end main, потому что эта директива сделает main точкой входа в наш код и пропустит инициализацию C во время выполнения, которую обычно необходимо выполнить до того, как main будет называется. Невозможность вызвать инициализацию C во время выполнения может привести к неожиданной работе библиотечной функции C или к аварийному завершению работы.

Когда код закончится, я делаю ret, чтобы вернуться к C коду запуска, который выйдет для нас. Код также сохраняет энергонезависимые (сохраняемые) регистры. См. Microsoft 32-битное соглашение о вызовах CDECL для получения дополнительной информации.

...