Узнайте, чем был засеян генератор случайных чисел в C++

У меня есть неуправляемое консольное приложение С++, в котором я использую srand() и rand(). Мне это не нужно для решения конкретной проблемы, но мне было любопытно: хранится ли исходное семя, переданное в srand(), где-то в памяти, к которому я могу запросить? Есть ли способ выяснить, что это за семя?


person Brian    schedule 27.08.2009    source источник


Ответы (4)


Семя не требуется сохранять, требуется только последнее возвращенное случайное число.

Вот пример из справочной страницы:

       static unsigned long next = 1;

       /* RAND_MAX assumed to be 32767 */
       int myrand(void) {
           next = next * 1103515245 + 12345;
           return((unsigned)(next/65536) % 32768);
       }

       void mysrand(unsigned seed) {
           next = seed;
       }
person P Shved    schedule 27.08.2009

Если у вас есть простой линейный конгруэнтный генератор, для которого у вас есть несколько значений, это дает систему уравнений:

 v1 = ( seed * a + b ) % m
 v2 = (   v1 * a + b ) % m;
 v3 = (   v2 * a + b ) % m;
... 

Если вы знаете первое значение, вы можете вернуться в последовательности назад:

seed = (v1 - b)/a (mod m)

Вы не знаете уникальное семя, вы знаете его только по модулю m (что обычно хорошо, поскольку (0 ‹ seed ‹ m) в любом случае). Если v1 - b отрицательное, вам нужно добавить m до тех пор, пока оно снова не станет положительным.

Вы также можете взглянуть на китайскую теорему об остатках, хотя это не точное совпадение.

person Justin    schedule 27.08.2009
comment
Вы только что сказали просто попробовать все 64-битные семена? Насколько быстрый у Вас компьютер? :D - person Andrew Coleson; 27.08.2009
comment
Попробовать все 64-битные семена? Это 18 446 744 073 709 551 616 различных возможностей — немного за пределами возможного. - person Eclipse; 27.08.2009
comment
Что вы можете сделать с небольшим количеством информации о том, откуда взялось семя, так это попробовать несколько миллионов предположений. Часто в srand указывается текущее время. Если вы приблизительно знаете, когда генератор был впервые заполнен, вы можете сделать довольно хорошее предположение о том, каким было значение time() в этот момент. - person Eclipse; 27.08.2009
comment
да, гм, 64, возможно, сейчас многовато, но вы определенно можете попробовать все 32-битные числа с современным оборудованием. Обратите внимание, что вам не нужно пробовать все значения 2^64, только эти ‹ m - person Justin; 28.08.2009
comment
Я думаю, вы можете восстановить a и b для линейного конгруэнтного генератора, и как только это станет известно, и вы узнаете v1 и m, последует начальное число. - person Calyth; 22.09.2009

Я не знаю, каков ваш уровень владения сборкой или есть ли у вас доступ к исходному коду/символам отладки для неуправляемого приложения, но за пределами такого рода ухищрений нет реального способа определить исходное начальное значение. Весь смысл генераторов случайных чисел состоит в том, чтобы придумать способ выдавать вам непредсказуемые числа — связь между любыми двумя заданными вызовами rand() не должна быть выводимой. В криптографически сильных генераторах псевдослучайных чисел будет считаться серьезным недостатком способность угадывать начальное число на основе сгенерированного случайного числа.

Самый простой способ сделать это - запустить приложение под отладчиком и установить точку останова, где вызывается srand(), а затем просто посмотреть на переданный параметр.

Далее будет дизассемблировать приложение и выяснить обстоятельства случайного звонка. Вполне возможно, что оно засеяно текущим временем — тогда вы можете попробовать несколько догадок (вероятно, вы можете сузить их до нескольких тысяч или около того) и посмотреть, дают ли они ту же последовательность случайных чисел, которую использует приложение. . (Конечно, это предполагает, что у вас есть какой-то способ узнать, каковы генерируемые случайные значения). Также возможно, что семя все время что-то глупое, например «0».

person Eclipse    schedule 27.08.2009
comment
-1. rand() не является криптографически безопасным; он почти всегда возвращает старшие биты LCG. Выходных данных пары вызовов rand() обычно достаточно, чтобы вывести текущее состояние и, таким образом, работать в обратном направлении. - person tc.; 27.09.2010

Теоретически нет — начальное значение используется для вычисления следующего случайного значения, и это значение (теоретически) используется для начального заполнения следующего случайного числа и так далее.

С точки зрения безопасности, возможность заглянуть в начальное число (будь то исходное или новое) является серьезной проблемой безопасности, поэтому я ожидаю, что вы не сможете заглянуть в него, даже если оно должно где-то храниться.

person Guss    schedule 27.08.2009
comment
Я предполагаю, что «должен» должен быть «не должен» (поскольку это не имеет большого смысла) - person Dan Walker; 27.08.2009
comment
Я не знаю, что использует ваша функция rand(), но большинство хороших из них поддерживают таблицу перемешивания, а не только «текущее» состояние. - person Justin; 27.08.2009
comment
-1. rand() вообще не является криптографически безопасным. Работать в обратном направлении очень легко. - person tc.; 27.09.2010
comment
Я никогда не утверждал, что rand() криптографически безопасен, но возможность заглянуть в семя проблематична, даже если вы не занимаетесь криптографией :-) - person Guss; 28.09.2010