Blog. Just Blog

Системные генераторы случайных чисел

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Системные генераторы случайных чисел
Системные генераторы случайных чисел

На сайте уже выложено несколько различных алгоритмов генераторов псевдослучайных чисел. Какие-то генераторы лучше, какие-то похуже. Но когда надо сгенерировать всего пару-тройку чисел или результаты генерации не используются в критических участках кода, можно воспользоваться системными источниками псевдослучайных чисел.

Первые две функции - это RtlRandom и ее улучшенная версия RtlRandomEx. К сожалению, в MSDN ничего не сказано относительно того, какие алгоритмы используются для генерации в этих функциях. А использование очень простое, что-то типа:
  1.         ; Инициализация генератора
  2.         invoke  GetTickCount
  3.         mov     [random_seed],eax
  4.         ...
  5.         ; Сгенерировать псевдослучайное число
  6.         invoke  RtlRandom,random_seed
  7.         ; EAX - число
  8.         ...
  9.         ; Сгенерировать псевдослучайное число
  10.         invoke  RtlRandomEx,random_seed
  11.         ; EAX - число
В недрах системной библиотеки advapi32.dll закопана функция RtlGenRandom. Но просто так ее не вызвать, надо сперва получить адрес функции "SystemFunction036", под которым она скрывается. Впрочем, штатные средства описания импорта FASM также прекрасно справляются с этой задачей. RtlGenRandom хорошо использовать, когда надо заполнить буфер нужного объема случайными данными.
  1. dllname db 'advapi32.dll',0
  2. fname   db 'SystemFunction036',0
  3. RtlGenRandom dd ?
  4.         ...
  5.         ; Получить адрес функции RtlGenRandom
  6.         invoke  LoadLibrary,dllname
  7.         invoke  GetProcAddress,eax,fname
  8.         mov     [RtlGenRandom],eax
  9.  
  10.         ; Сгенерировать псевдослучайное число размером 4 байта
  11.         stdcall [RtlGenRandom],random,4
  12.         ; [random] -> псевдослучайное число
Динамическая библиотека msvcrt.dll также содержит генератор случайных чисел. Он состоит из двух функций: srand для инициализации и rand для получения псевдослучайного числа. Обе функции спокойно импортируются штатными средствами FASM. Результат возвращается независимо сразу в двух регистрах. В регистре EAX содержится значение в диапазоне [0..0xFFFF], а в регистре ECX значение в диапазоне [0..0xFFFFFFFF].
  1.         ; Инициализация генератора
  2.         invoke  GetTickCount
  3.         invoke  srand,eax
  4.  
  5.         ; Сгенерировать псевдослучайное число
  6.         invoke  rand
  7.         ; ECX -> псевдослучайное число DWORD
  8.         ; EAX -> псевдослучайное число WORD
Немного посложнее вариант с использованием системных криптографических функций. Наиболее универсальная библиотека WinCrypt, которая поддерживается даже на Windows XP. В самом начале при инициализации генератора с помощью функции CryptAcquireContext создается криптопровайдер, затем с помощью функции CryptGenRandom получаем нужное количество псевдослучайных чисел. Когда генератор больше не требуется, надо освободить хэндл его криптопровайдера с помощью функции CryptReleaseContext.
  1.         ...
  2.         ; Инициализация генератора
  3.         invoke  CryptAcquireContext,hProv,NULL,NULL,PROV_RSA_FULL,\
  4.                 CRYPT_VERIFYCONTEXT or CRYPT_SILENT
  5.         ...
  6.         ; Сгенерировать псевдослучайное число размером 4 байта
  7.         invoke  CryptGenRandom,[hProv],4,rnd
  8.         ; [rnd] -> псевдослучайное число
  9.         ...
  10.         ; Освободить хэндл
  11.         invoke  CryptReleaseContext,[hProv],0
На смену WinCrypt пришла новая библиотека - BCrypt. Если верить официальной документации, используемый алгоритм генерации соответствует требованиям Federal Information Processing Standards (FIPS 186-2, FIPS 140-2) и Национального института стандартов и технологий США (NIST SP 800-90). Как и в случае с предыдущей библиотекой, при инициализации генератора с помощью функции BCryptOpenAlgorithmProvider создаем криптопровайдер, затем с помощью функции BCryptGenRandom получаем нужное количество псевдослучайных чисел. Когда генератор больше не нужен, освобождаем хэндл криптопровайдера с помощью функции BCryptCloseAlgorithmProvider.
  1. szAlgId du 'RNG',0
  2.         ...
  3.         ; Инициализация генератора
  4.         invoke  BCryptOpenAlgorithmProvider,hAlgorithm,szAlgId,NULL,NULL
  5.         ...
  6.         ; Сгенерировать псевдослучайное число размером 4 байта
  7.         invoke  BCryptGenRandom,[hAlgorithm],rnd,4,NULL
  8.         ; [rnd] -> псевдослучайное число
  9.         ...
  10.         ; Освободить хэндл
  11.         invoke  BCryptCloseAlgorithmProvider,[hAlgorithm]
В современных процессорах есть встроенные инструкции для получения случайного числа, это RDRAND и RDSEED. По заверениям разработчиков процессоров, эти генераторы соответствуют стандартам безопасности и криптографическим стандартам, но в то же время есть мнение, что это пример уязвимого заведомо криптографически слабого элемента. Впрочем, для прикладных нужд результатов этих инструкций вполне достаточно, лишь бы их поддерживал процессор. Использование простейшее:
  1.         ; Проверить доступность команды RDRAND
  2.         mov     eax,1
  3.         cpuid
  4.         test    ecx,0x40000000
  5.         jz      loc_exit
  6.         ...
  7.         rdrand  eax
  8.         ; EAX -> случайное число
или
  1.         ; Проверить доступность команды RDSEED
  2.         xor     ecx,ecx
  3.         mov     eax,7
  4.         cpuid
  5.         test    ebx,0x40000
  6.         jz      loc_exit
  7.         ...
  8.         rdseed  eax
  9.         ; EAX -> случайное число
Ну а самый простой способ получить парочку случайных чисел - использовать ассемблерную инструкцию RDTSC. При вызове она возвращает в регистрах EDX:EAX количество тактов с момента последнего сброса процессора. Для работы лучше брать значение из регистра EAX, так как оно меняется чаще. Инициализировать ничего не надо, все делается единственной командой. Но надо иметь в виду, что последовательный вызов RDTSC вернет числа очень близкие по значению.
  1.         rdtsc
  2.         ; EDX:EAX -> случайное число
В приложении примеры программ с исходными текстами, которые используют функции из статьи для генерации псевдослучайных чисел.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

System.Random.Demo.zip (22,797 bytes)


Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 393 | Комментариев: 22

Комментарии

Отзывы посетителей сайта о статье
ManHunter (22.07.2021 в 10:05):
rndgen, а какое отношение это имеет к системным генераторам?

rdtsc меняет регистр EDX, а тут сохраняются и восстанавливаются только esi и ecx. edx вернется с непредсказуемым значением.

xor ecx,ecx
mov cl,al
xor eax,eax
mov al,byte [esi+ecx]

преобразуется в человеческое

movzx eax,al
mov   al,byte [esi+eax]

test al,al
je @1
То есть первый символ строки в принципе никогда не выпадет?

symbols   db 'qwertyuiopasdfghjklzxcvbnm0123456789',0
symlen    dd ($ - symbols - 1)
Смысл завершающего нуля, если он все равно вычеркивается при расчете длины?
Смысл резервировать DWORD для symlen, если он станет константой и будет использоваться в вызове?

symbols   db 'qwertyuiopasdfghjklzxcvbnm0123456789'
symlen    = $-symbols

stdcall RndNameGen,symbols,symlen

Садись, двойка.
rndgen (22.07.2021 в 10:03):
еще один вариант

symbols   db 'qwertyuiopasdfghjklzxcvbnm0123456789',0
symlen    dd ($ - symbols - 1)

;stdcall RndNameGen,symbols,[symlen]

proc RndNameGen pSym:DWORD, pLen:DWORD
push ecx esi
mov esi,[pSym]
mov ecx,[pLen]
@1:
rdtsc
@3:
cmp al,cl
jb @2
cmp al,ah
ja @4
shr al,1
jmp @3
@4:
test ah,ah
je @1
sub al,ah
jmp @3
@2:
test al,al
je @1
xor ecx,ecx
mov cl,al
xor eax,eax
mov al,byte [esi+ecx]
pop esi ecx
ret
endp
Petya (19.07.2021 в 13:03):
voffka, слова "EXE" в предыдущем сообщении не было. а "DLL" было.
Ладно, кончаю спорить, пока хозяину не надоело.
voffka (19.07.2021 в 12:37):
Petya, DEF - он же Export Definition File, при компиляции exe нахрен не упал ни на одном языке программирования. Он нужен при компиляции dll, чтоб указать какие процедуры должны экспортироваться.
Petya (19.07.2021 в 11:02):
voffka, Всё ещё не понимаю. И создать, и импортировать DLL FASM может и без этого, см. прилагающиеся к нему примеры.
voffka (16.07.2021 в 20:49):
Petya, def-ы нужны при создании dll, чтоб ты смог в FASM сделать
Цитатаimport advapi32,RtlGenRandom,'SystemFunction036'
Petya (16.07.2021 в 14:47):
Цитатаimport advapi32,RtlGenRandom,'SystemFunction036'

А теперь внимание, вопрос - зачем вообще нужны эти def-файлы, если FASM и без них даже лучше работает?
Цитатаsrand/rand

Функции, вообще-то, крошечные. Выдрать и носить с собой оптимальнее и по скорости, и по размеру, чем лишнюю библиотеку грузить.
Единственный бонус - некоторые версии msvcrt делают его потокобезопасным - каждой твари по ячейке памяти.
voffka (14.07.2021 в 23:17):
В msvcrt в eax оказывается ограничение не 0xFFFF, а 0x7FFF
        SHR EAX,010h
        AND EAX,07FFFh
        MOV ESP,EBP
        POP EBP
        RETN
SMaSm-94 (14.07.2021 в 01:53):
ManHunter, да всё так, за исключением того, что я не удостоверился в наличии данной функции в "Окнах" версии ниже 8.1. На данный момент семёрки под рукой не имею, но вот в 8.1 и 10 функция "ProcessPrng" присутствует. Думаю, она была реализована в Win8; хотя не факт - чекнуть нужно. Использовать, или нет - дело каждого.
ManHunter (13.07.2021 в 23:19):
Да я вообще был удивлен, что мой стационарный рабочий комп внезапно не вывез RDSEED по железу. Что уж тут говорить про поддержку всего этого паноптикума на уровне операционки.
voffka (13.07.2021 в 23:10):
ManHunter, В 7 нет, в 10 уже есть. Так что длл не постоянная и лучше ее не юзать.
ManHunter (13.07.2021 в 23:08):
Добавил srand/rand, архив обновлен.
ManHunter (13.07.2021 в 22:47):
SMaSm-94, не то, чтоб я сомневался, но факты - упрямая штука. У bcryptprimitives.dll экспорт не содержит ProcessPrng, соответственно, код типа

dllname db 'bcryptprimitives.dll',0
fname   db 'ProcessPrng',0
ProcessPrng dd ?

        invoke  LoadLibrary,dllname
        invoke  GetProcAddress,eax,fname
        mov     [ProcessPrng],eax

возвращает ошибку (Win7 x86).
Пруф: https://ibb.co/kqxPkZ8

ЧЯДНТ?
SMaSm-94 (13.07.2021 в 20:59):
ADVAPI32.DLL:SystemFunction036(RtlGenRandom) вызывает CRYPTBASE.DLL:SystemFunction036, которая в свою очередь вызывает BCryptPrimitives.DLL:ProcessPrng, поэтому лучше в целях экономии процессорного времени использовать напрямую последнюю. Вот пример:
...
szBCryptPrimitivesLibrary TCHAR 'BCryptPrimitives.dll',0
align 4
szProcessPrng db 'ProcessPrng',0
pProcessPrng dd ?
bRandBuffer rb 4
sizeof.bRandBuffer = $ - bRandBuffer
...
invoke  LoadLibrary, szBCryptPrimitivesLibrary
invoke  GetProcAddress, eax, szProcessPrng
mov     [pProcessPrng], eax
...
stdcall [pProcessPrng], bRandBuffer, sizeof.bRandBuffer
...
Ещё для генерации псевдослучайных чисел можно воспользоваться функцией CPGenRandom из RSAENH.DLL. Но она по сути является лишь "надстройкой" над ProcessPrng.
voffka (13.07.2021 в 19:50):
В msvcrt
srand(seed) идет как init
rand() возвращает радомные числа в eax число до 0xFFFF, в ecx до 0xFFFFFFFF
ManHunter (13.07.2021 в 16:48):
Добавил примеры из камментов в статью, архив обновлен. Всем большое спасибо за подсказки!
ManHunter (13.07.2021 в 16:02):
В FASM работает конструкция:

section '.idata' import data readable writeable

library advapi32,'advapi32.dll'
import advapi32,RtlGenRandom,'SystemFunction036'

и вызывается invoke RtlGenRandom,buf,[cnt]
voffka (13.07.2021 в 15:57):
Это я знаю. Просто как в песенке - Жопа есть, а слова нет. Есть api, у него есть название, а прилинковать невозможно т.к. в dll оно под кодовым названием SystemFunction036.
ManHunter (13.07.2021 в 15:42):
voffka, это RtlGenRandom
https://docs.microsoft.com/en-...rtlgenrandom
voffka (13.07.2021 в 15:39):
Еще один не очевидный виндовый генератор
invoke LoadLibrary,SADD("Advapi32.dll")
invoke GetProcAddress,eax,SADD("SystemFunction036")
push 100
push offset buffer
call eax
voffka (13.07.2021 в 15:17):
SMaSm, Тоже самое, что и BCrypt, только BCrypt появился в висте, а wincrypt в Advapi32.dll намного раньше еще в win2000 вроде.
SMaSm (13.07.2021 в 13:58):
; вот ещё, как вариант :
; --------------------
RAND_BUFFER_LENGTH = MAX_PATH ; длинна буфера
...
hProv rd 1 ; хэндл CSP
bRandBuffer rb RAND_BUFFER_LENGTH ; буфер
...
invoke  CryptAcquireContext, hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT or CRYPT_SILENT
test    al, al
jz      .cryptapi_context_acquirement_error
invoke  CryptGenRandom, [hProv], RAND_BUFFER_LENGTH, bRandBuffer
test    al, al
jz      .cryptapi_random_data_generation_error
...
invoke  CryptReleaseContext, [hProv], 0

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2021
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.005 сек.) / Память: 5 Mb
Наверх