使用 MASM32 随机化装配中的数字
Posted
技术标签:
【中文标题】使用 MASM32 随机化装配中的数字【英文标题】:Randomizing Numbers in Assembly with MASM32 【发布时间】:2013-04-23 13:40:21 【问题描述】:如何使用带有 Masm32 的 Assembly 随机化一个数字?我可以使用什么来创建随机数生成器?
非常感谢!
【问题讨论】:
你想实现自己的伪随机函数还是只想知道如何从asm调用rand()
?
我想知道如何从 asm 调用 rand()
我使用了这个解决方案,它有效!调用 GetTickCount 调用 nseed,eax 调用 nrandom,10 调用 dwtoa,eax,偏移量 lpszNumber 调用 StdOut,偏移量 lpszNumber
crc32 %eax
可能是 SSE4.2 上可用的最简单的 PRNG。
【参考方案1】:
使用 MASM32 获取随机数
MASM32 SDK 附带了一些实现随机生成器的示例。将它们用于自己的目的并不是最糟糕的主意。以下示例只是示例,其中缺少错误处理。这些示例生成并生成范围为 [0..11] 的 30 个随机数。
具有 a=134775813 和 b=c(如 Delphi)的 linear congruential generator 位于 \masm32\examples\exampl03\lcd\lcd.asm。
.686
.MODEL flat, STDCALL
INCLUDE kernel32.inc ; GetStdHandle, WriteFile, ExitProcess
INCLUDELIB kernel32.lib
INCLUDE user32.inc ; wsprintf
INCLUDELIB user32.lib
NumberOfNumbers = 30 ; Number of random numbers to be generated and shown
RangeOfNumbers = 12 ; Range of the random numbers (0..RangeOfNumbers-1)
.DATA
RandSeed dd ?
.CODE
PseudoRandom PROC ; Deliver EAX: Range (0..EAX-1)
push edx ; Preserve EDX
imul edx,RandSeed,08088405H ; EDX = RandSeed * 0x08088405 (decimal 134775813)
inc edx
mov RandSeed, edx ; New RandSeed
mul edx ; EDX:EAX = EAX * EDX
mov eax, edx ; Return the EDX from the multiplication
pop edx ; Restore EDX
ret
ret
PseudoRandom ENDP ; Return EAX: Random number in range
main PROC
rdtsc
mov RandSeed, eax ; Initialize random generator
mov ecx, NumberOfNumbers ; Loop counter - show ECX random numbers
LL1:
push ecx ; Preserve loop counter
mov eax, RangeOfNumbers ; Range (0..RangeOfNumbers-1)
call PseudoRandom
call write_number ; printf ("%u ", EAX)
pop ecx ; Restore loop counter
loop LL1
invoke ExitProcess, 0
main ENDP
write_number PROC STDCALL USES ebx ; printf ("%u ", EAX)
LOCAL numstring[12]:BYTE, NumberOfBytesWritten:DWORD
.CONST
fmt db "%u ",0
.CODE
invoke wsprintf, ADDR numstring, ADDR fmt, eax
mov ebx, eax ; Preserve result - count of written bytes
invoke GetStdHandle, -11 ; Get STD_OUTPUT_HANDLE
mov edx, eax ; EAX will be used by the following INVOKE
invoke WriteFile, edx, ADDR numstring, ebx, ADDR NumberOfBytesWritten, 0
ret
write_number ENDP
END main
XORshifter 可以在 \masm32\examples\exampl04\pascal\pascal.asm 中找到。
.686
.MODEL flat, STDCALL
INCLUDE kernel32.inc ; GetStdHandle, WriteFile, ExitProcess
INCLUDELIB kernel32.lib
INCLUDE user32.inc ; wsprintf
INCLUDELIB user32.lib
NumberOfNumbers = 30 ; Number of random numbers to be generated and shown
RangeOfNumbers = 12 ; Range of the random numbers (0..RangeOfNumbers-1)
.CODE
Rnd3Bit Proc ; This procedure generates up to 20 random bits (EAX=0..20).
.DATA
RndInit dd 0A2F59C2Eh
.CODE
mov edx,RndInit
rl: rol edx, 1
jnc rs
xor edx, 0Ah
rs: dec eax
jne rl
mov eax, edx
rcr edx, 1
mov RndInit, edx
ret
Rnd3Bit EndP
main PROC
rdtsc ; Any number for the first seed
test eax, eax ; EAX == 0?
setz dl ; DL=1 if EAX==0, DL=0 if EAX>0
or eax, edx ; Not 0 under any circumstances
mov RndInit, eax ; Reinitialize random generator
mov ecx, NumberOfNumbers ; Loop counter - show ECX random numbers
LL1:
push ecx ; Preserve loop counter
mov eax, 20 ; Amount of bits
call Rnd3Bit
and eax, 11111111111111111111b ; 20 bits set = 1048575
mov ecx, RangeOfNumbers ; Range (0..RangeOfNumbers-1)
xor edx, edx ; Needed for DIV
div ecx ; EDX:EAX/ECX -> EAX remainder EDX
mov eax, edx ; Get the remainder
call write_number ; printf ("%u ", EAX)
pop ecx ; Restore loop counter
loop LL1
invoke ExitProcess, 0
main ENDP
write_number PROC STDCALL USES ebx ; printf ("%u ", EAX)
LOCAL numstring[12]:BYTE, NumberOfBytesWritten:DWORD
.CONST
fmt db "%u ",0
.CODE
invoke wsprintf, ADDR numstring, ADDR fmt, eax
mov ebx, eax ; Preserve count of written bytes
invoke GetStdHandle, -11 ; Get STD_OUTPUT_HANDLE
mov edx, eax ; EAX will be used by the following INVOKE
invoke WriteFile, edx, ADDR numstring, ebx, ADDR NumberOfBytesWritten, 0
ret
write_number ENDP
END main
Park-Miller-Algorithm 用在 \masm32\examples\exampl05\rpg\rpg.asm
.686
.MODEL flat, STDCALL
INCLUDE kernel32.inc ; GetStdHandle, WriteFile, ExitProcess
INCLUDELIB kernel32.lib
INCLUDE user32.inc ; wsprintf
INCLUDELIB user32.lib
NumberOfNumbers = 30 ; Number of random numbers to be generated and shown
RangeOfNumbers = 12 ; Range of the random numbers (0..RangeOfNumbers-1)
.DATA
rseed dd 0
range dd 0
.CODE
nrandom PROC
; ------------------
; NaN's nrandom algo (Park Miller random algorithm)
; ------------------
lpstart:
mov eax, rseed
test eax, 80000000h
jz @F
add eax, 7FFFFFFFh
@@:
xor edx, edx
mov ecx, 127773
div ecx
mov ecx, eax
mov eax, 16807
mul edx
mov edx, ecx
mov ecx, eax
mov eax, 2836
mul edx
sub ecx, eax
xor edx, edx
mov eax, ecx
mov rseed, ecx
div range
mov eax, edx ; Write DWORD result to return register
add rseed, 1 ; New value to rseed
ret
nrandom ENDP
main PROC
rdtsc
mov rseed, eax ; Reinitialize random generator
mov ecx, NumberOfNumbers ; Loop counter - show ECX random numbers
LL1:
push ecx ; Preserve loop counter
mov range, RangeOfNumbers ; Range (0..RangeOfNumbers-1)
call nrandom
call write_number ; printf ("%u ", EAX)
pop ecx ; Restore loop counter
loop LL1
invoke ExitProcess, 0
main ENDP
write_number PROC STDCALL USES ebx ; printf ("%u ", EAX)
LOCAL numstring[12]:BYTE, NumberOfBytesWritten:DWORD
.CONST
fmt db "%u ",0
.CODE
invoke wsprintf, ADDR numstring, ADDR fmt, eax
mov ebx, eax ; Preserve count of written bytes
invoke GetStdHandle, -11 ; Get STD_OUTPUT_HANDLE
mov edx, eax ; EAX will be used by the following INVOKE
invoke WriteFile, edx, ADDR numstring, ebx, ADDR NumberOfBytesWritten, 0
ret
write_number ENDP
END main
\masm32\examples\exampl07\shuflarr\sa.asm 使用nrandom
中内置的 MASM32。源代码位于 \masm32\m32lib\nrand.asm。和上面的 Park-Miller-Algorithm 是一样的。
.686
.MODEL flat, STDCALL
OPTION casemap:none
INCLUDE kernel32.inc ; GetStdHandle, WriteFile, ExitProcess
INCLUDELIB kernel32.lib
INCLUDE user32.inc ; wsprintf
INCLUDELIB user32.lib
INCLUDE masm32.inc ; nseed, nrandom
INCLUDELIB masm32.lib
NumberOfNumbers = 30 ; Number of random numbers to be generated and shown
RangeOfNumbers = 12 ; Range of the random numbers (0..RangeOfNumbers-1)
.CODE
main PROC
rdtsc
invoke nseed, eax ; Initialize nrandom_seed
mov ecx, NumberOfNumbers ; Loop counter - show ECX random numbers
LL1:
push ecx ; Preserve loop counter
invoke nrandom, RangeOfNumbers ; Range (0..RangeOfNumbers-1)
call write_number ; printf ("%u ", EAX)
pop ecx ; Restore loop counter
loop LL1
invoke ExitProcess, 0
main ENDP
write_number PROC STDCALL USES ebx ; printf ("%u ", EAX)
LOCAL numstring[12]:BYTE, NumberOfBytesWritten:DWORD
.CONST
fmt db "%u ",0
.CODE
invoke wsprintf, ADDR numstring, ADDR fmt, eax
mov ebx, eax ; Preserve result - count of written bytes
invoke GetStdHandle, -11 ; Get STD_OUTPUT_HANDLE
mov edx, eax ; EAX will be used by the following INVOKE
invoke WriteFile, edx, ADDR numstring, ebx, ADDR NumberOfBytesWritten, 0
ret
write_number ENDP
END main
有很多.lib
文件可以访问Windows 系统。微软建议使用CryptGenRandom
:
.686
.MODEL flat, STDCALL
OPTION casemap:none
INCLUDE kernel32.inc ; GetStdHandle, WriteFile, ExitProcess
INCLUDELIB kernel32.lib
INCLUDE user32.inc ; wsprintf
INCLUDELIB user32.lib
INCLUDE advapi32.inc ; CryptAcquireContext, CryptGenRandom, CryptReleaseContext
INCLUDELIB advapi32.lib
NumberOfNumbers = 30 ; Number of random numbers to be generated and shown
RangeOfNumbers = 12 ; Range of the random numbers (0..RangeOfNumbers-1)
.DATA
random_bytes dd 30 DUP (?)
hProvider dd ?
.CODE
main PROC
; https://msdn.microsoft.com/library/windows/desktop/aa379886.aspx
CRYPT_VERIFYCONTEXT = 0F0000000h
PROV_RSA_FULL = 1
invoke CryptAcquireContext, ADDR hProvider, 0, 0, PROV_RSA_FULL,CRYPT_VERIFYCONTEXT
; https://msdn.microsoft.com/library/windows/desktop/aa379942.aspx
invoke CryptGenRandom, hProvider, 30*4, ADDR random_bytes ; Generate 30 random DWORD (30*4)
; https://msdn.microsoft.com/library/windows/desktop/aa380268.aspx
invoke CryptReleaseContext, hProvider, 0
lea esi, random_bytes
mov ecx, NumberOfNumbers ; Loop counter - show ECX random numbers
@@:
push ecx ; Preserve loop counter
lodsd ; [ESI] -> EAX, ADD ESI, 4
; Adjust EAX to the range
mov ecx, RangeOfNumbers ; Range (0..RangeOfNumbers-1)
xor edx, edx ; Needed for DIV
div ecx ; EDX:EAX/ECX -> EAX remainder EDX
mov eax, edx ; Get the remainder
call write_number ; printf ("%u ", EAX)
pop ecx ; Restore loop counter
loop @B ; Loop the next @@ above
invoke ExitProcess, 0 ; Exit (0) = return 0
main ENDP
write_number PROC STDCALL USES ebx ; printf ("%u ", EAX)
LOCAL numstring[12]:BYTE, NumberOfBytesWritten:DWORD
.CONST
fmt db "%u ",0
.CODE
invoke wsprintf, ADDR numstring, ADDR fmt, eax
mov ebx, eax ; Preserve result - count of written bytes
invoke GetStdHandle, -11 ; Get STD_OUTPUT_HANDLE
mov edx, eax ; EAX will be used by the following INVOKE
invoke WriteFile, edx, ADDR numstring, ebx, ADDR NumberOfBytesWritten, 0
ret
write_number ENDP
END main
我在dnsapi.lib
中发现了一个未记录的函数Dns_GetRandomXid
。它使用CryptGenRandom
,有时使用C 函数rand()
,似乎是线程安全的。
.686
.MODEL flat, STDCALL
OPTION casemap:none
INCLUDE kernel32.inc ; GetStdHandle, WriteFile, ExitProcess
INCLUDELIB kernel32.lib
INCLUDE user32.inc ; wsprintf
INCLUDELIB user32.lib
INCLUDE dnsapi.inc ; Dns_GetRandomXid
INCLUDELIB dnsapi.lib
NumberOfNumbers = 30 ; Number of random numbers to be generated and shown
RangeOfNumbers = 12 ; Range of the random numbers (0..RangeOfNumbers-1)
.CODE
main PROC
mov ecx, NumberOfNumbers ; Loop counter - show ECX random numbers
LL1:
push ecx ; Preserve loop counter
invoke Dns_GetRandomXid, 0 ; Argument not used -> AX = random WORD
mov ecx, RangeOfNumbers ; Range (0..RangeOfNumbers-1)
xor edx, edx ; Needed for DIV
div ecx ; EDX:EAX/ECX -> EAX remainder EDX
mov eax, edx ; Get the remainder
call write_number ; printf ("%u ", EAX)
pop ecx ; Restore loop counter
loop LL1
invoke ExitProcess, 0
main ENDP
write_number PROC STDCALL USES ebx ; printf ("%u ", EAX)
LOCAL numstring[12]:BYTE, NumberOfBytesWritten:DWORD
.CONST
fmt db "%u ",0
.CODE
invoke wsprintf, ADDR numstring, ADDR fmt, eax
mov ebx, eax ; Preserve result - count of written bytes
invoke GetStdHandle, -11 ; Get STD_OUTPUT_HANDLE
mov edx, eax ; EAX will be used by the following INVOKE
invoke WriteFile, edx, ADDR numstring, ebx, ADDR NumberOfBytesWritten, 0
ret
write_number ENDP
END main
另一个未记录的函数是cryptdll.lib
中的CDGenerateRandomBits
。
.686
.MODEL flat, STDCALL
OPTION casemap:none
INCLUDE kernel32.inc ; GetStdHandle, WriteFile, ExitProcess
INCLUDELIB kernel32.lib
INCLUDE user32.inc ; wsprintf
INCLUDELIB user32.lib
INCLUDE cryptdll.inc ; CDGenerateRandomBits
INCLUDELIB cryptdll.lib
NumberOfNumbers = 30 ; Number of random numbers to be generated and shown
RangeOfNumbers = 12 ; Range of the random numbers (0..RangeOfNumbers-1)
.DATA
random_bytes dd 30 DUP (?)
.CODE
main PROC
invoke CDGenerateRandomBits, Addr random_bytes, (NumberOfNumbers*4) ; Generate 120 random bytes (30 DWORD à 4 BYTE)
lea esi, random_bytes
mov ecx, 30 ; Show 30 random numbers
LL1:
push ecx ; Preserve loop counter
lodsd ; [ESI] -> EAX; ESI += 4
; Adjust EAX to range
mov ecx, RangeOfNumbers ; Range (0..RangeOfNumbers-1)
xor edx, edx ; Needed for DIV
div ecx ; EDX:EAX/ECX -> EAX remainder EDX
mov eax, edx ; Get the remainder
call write_number ; printf ("%u ", EAX)
pop ecx ; Restore loop counter
loop LL1
invoke ExitProcess, 0
main ENDP
write_number PROC STDCALL USES ebx ; printf ("%u ", EAX)
LOCAL numstring[12]:BYTE, NumberOfBytesWritten:DWORD
.CONST
fmt db "%u ",0
.CODE
invoke wsprintf, ADDR numstring, ADDR fmt, eax
mov ebx, eax ; Preserve result - count of written bytes
invoke GetStdHandle, -11 ; Get STD_OUTPUT_HANDLE
mov edx, eax ; EAX will be used by the follwing INVOKE
invoke WriteFile, edx, ADDR numstring, ebx, ADDR NumberOfBytesWritten, 0
ret
write_number ENDP
END main
不应缺少 C 库中的旧 rand()
。
.686
.MODEL flat, C
INCLUDE msvcrt.inc ; crt_time, crt_srand, crt_rand, crt_printf,crt_exit
INCLUDELIB msvcrt.lib
NumberOfNumbers = 30 ; Number of random numbers to be generated and shown
RangeOfNumbers = 12 ; Range of the random numbers (0..RangeOfNumbers-1)
.DATA
fmt db "%u ", 0
.CODE
main PROC
sub esp, 8 ; Reserve place for the C arguments
; srand( time (NULL) )
mov DWORD PTR [esp], 0
call crt_time ; EAX = time(0)
mov [esp], eax
call crt_srand ; srand (EAX)
mov ebx, NumberOfNumbers ; Loop counter - show ECX random numbers
LL1:
call crt_rand ; EAX = rand()
; Adjust EAX to the range
mov ecx, RangeOfNumbers ; Range (0..RangeOfNumbers-1)
xor edx, edx ; Needed for DIV
div ecx ; EDX:EAX/ECX -> EAX remainder EDX
mov eax, edx ; Get the remainder
; printf ("%d\n", EAX )
mov [esp], OFFSET fmt
mov [esp+4], eax
call crt_printf ; printf (fmt,eax)
dec ebx
jne LL1
; exit (0) = return 0
mov DWORD PTR [esp], 0
call crt_exit ; exit (0) = return 0
main ENDP
END main
在现代处理器上(至少 Ivy Bridge - 自 2012 年以来)已实现指令 RDRAND
。它符合NIST SP 800-90A standard。 MASM32 SDK 中的 MASM 汇编器无法汇编该指令。一种解决方法是将指令作为一系列十六进制字节插入代码中。 MASM 会将它存储为给定的,处理器将根据需要执行它。
.686
.MODEL flat, STDCALL
INCLUDE kernel32.inc ; GetStdHandle, WriteFile, ExitProcess
INCLUDELIB kernel32.lib
INCLUDE user32.inc ; wsprintf
INCLUDELIB user32.lib
NumberOfNumbers = 30 ; Number of random numbers to be generated and shown
RangeOfNumbers = 12 ; Range of the random numbers (0..RangeOfNumbers-1)
.CONST
err_text db "ERR: RDRAND not supported.",10,0
.CODE
main PROC
mov eax, 01h ; Check for availability (EAX=1) -> ECX.30
cpuid
bt ecx, 30 ; CPUID.01H:ECX.RDRAND[bit 30] = 1 ?
jnc err_exit ; No (RDRAND not supported) -> err_exit
mov ecx, NumberOfNumbers ; Loop counter - generate and show ECX random numbers
LL1:
push ecx ; Preserve loop counter
@@:
db 0Fh, 0C7h, 0F0h ; rdrand eax
jnc @B ; Invalid number - try again
; Adjust EAX to the range
mov ecx, RangeOfNumbers ; Range (0..RangeOfNumbers-1)
xor edx, edx ; Needed for DIV
div ecx ; EDX:EAX/ECX -> EAX remainder EDX
mov eax, edx ; Get the remainder
call write_number
pop ecx ; Restore loop counter
loop LL1
invoke ExitProcess, 0 ; Returncode = 0
err_exit:
invoke GetStdHandle, -11 ; Get STD_OUTPUT_HANDLE
push eax ; Place for WriteFile.NumberOfBytesWritten
invoke WriteFile, eax, ADDR err_text, LENGTHOF err_text, esp, 0
invoke ExitProcess, 1 ; Returncode = 1
main ENDP
write_number PROC STDCALL USES ebx ; printf ("%u ", EAX)
LOCAL numstring[20]:BYTE, NumberOfBytesWritten:DWORD
.CONST
fmt db "%u ",0
.CODE
invoke wsprintf, ADDR numstring, ADDR fmt, eax
mov ebx, eax ; Preserve count of written bytes
invoke GetStdHandle, -11 ; Get STD_OUTPUT_HANDLE
mov edx, eax ; EAX will be used by the following INVOKE
invoke WriteFile, edx, ADDR numstring, ebx, ADDR NumberOfBytesWritten, 0
ret
write_number ENDP
END main
“全新”是处理器指令 RDSEED
(至少 Broadwell - 自 2014 年以来)。它符合 NIST SP 800-90B/C 标准。
.686
.MODEL flat, STDCALL
INCLUDE kernel32.inc ; GetStdHandle, WriteFile, ExitProcess
INCLUDELIB kernel32.lib
INCLUDE user32.inc ; wsprintf
INCLUDELIB user32.lib
NumberOfNumbers = 30 ; Number of random numbers to be generated and shown
RangeOfNumbers = 12 ; Range of the random numbers (0..RangeOfNumbers-1)
.CONST
err_text db "ERR: RDSEED not supported.",10,0
.CODE
main PROC
mov eax, 07h ; Check for availability (EAX=7, ECX=0) -> EBX.18
xor ecx, ecx
cpuid
bt ebx, 18 ; CPUID (EAX=07H, ECX=0H):EBX.RDSEED[bit 18] = 1 ?
jnc err_exit ; No (RDSEED not supported) -> err_exit
mov ecx, NumberOfNumbers ; Loop counter - generate and show ECX random numbers
LL1:
push ecx ; Preserve loop counter
@@:
db 0Fh, 0C7h, 0F8h ; rdseed eax
jnc @B ; Invalid number - try again
; Adjust EAX to the range
mov ecx, RangeOfNumbers ; Range (0..RangeOfNumbers-1)
xor edx, edx ; Needed for DIV
div ecx ; EDX:EAX/ECX -> EAX remainder EDX
mov eax, edx ; Get the remainder
call write_number
pop ecx ; Restore loop counter
loop LL1
invoke ExitProcess, 0 ; Returncode = 0
err_exit:
invoke GetStdHandle, -11 ; Get STD_OUTPUT_HANDLE
push eax ; Place for WriteFile.NumberOfBytesWritten
invoke WriteFile, eax, ADDR err_text, LENGTHOF err_text, esp, 0
invoke ExitProcess, 1 ; Returncode = 1
main ENDP
write_number PROC STDCALL USES ebx ; printf ("%u ", EAX)
LOCAL numstring[20]:BYTE, NumberOfBytesWritten:DWORD
.CONST
fmt db "%u ",0
.CODE
invoke wsprintf, ADDR numstring, ADDR fmt, eax
mov ebx, eax ; Preserve count of written bytes
invoke GetStdHandle, -11 ; Get STD_OUTPUT_HANDLE
mov edx, eax ; EAX will be used by the following INVOKE
invoke WriteFile, edx, ADDR numstring, ebx, ADDR NumberOfBytesWritten, 0
ret
write_number ENDP
END main
【讨论】:
【参考方案2】:你需要实现一个Pseudorandom number generator,就像这个答案:
Pseudorandom generator in Assembly Language
【讨论】:
【参考方案3】:如果你想在 Assembly 中获取随机数,我猜有两种方法:
如果允许在汇编中调用 C 函数,则可以使用 rand()。 我们在大学用NASM,有一个命令 (rdtsc) 在 NASM 中读取 CPU 时钟并将其放在 寄存器。你可以把这个数除以随机数。我不了解 MASM,但 Paul Carter 的 NASM tutorial 很棒。
【讨论】:
以上是关于使用 MASM32 随机化装配中的数字的主要内容,如果未能解决你的问题,请参考以下文章