Inverse function of Java's Random function
Java的随机函数取一个种子并产生一个"pSuedo-No"序列的序列。(这是根据
它有反函数吗?也就是说,给定一个数字序列,是否可以用数学方法确定种子是什么?(也就是说,暴力强迫不算是有效的方法)
[编辑]这里似乎有很多评论…我想我会澄清我在找什么。
例如,函数
但是函数
现在回到我的问题上,如果你说函数是不可逆的,请解释为什么。
(我希望能从那些真正读过这篇文章并理解它的人那里得到答案。像"这是不可能的"这样的回答真的没有帮助)
如果我们讨论的是
1 | nextseed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1) |
这是一个非常简单的函数,如果通过计算知道种子的所有位,它就可以被反转。
1 | seed = ((nextseed - 0xBL) * 0xdfe05bcb1365L) & ((1L << 48) - 1) |
自
不过,
显然,对于48位种子,您必须观察至少48位的输出,或者您显然没有要处理的内射函数(因此是可逆的)。我们很幸运:
让我们具体看看
请注意,由于
我们有
1 | (b << 16) - 11 - (a << 16)*0x5DEECE66DL + (k<<48) |
对于某些
我们只需测试
演示方法的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | import java.util.Random; public class randhack { public static long calcSeed(long nextLong) { final long x = 0x5DEECE66DL; final long xinv = 0xdfe05bcb1365L; final long y = 0xBL; final long mask = ((1L << 48)-1); long a = nextLong >>> 32; long b = nextLong & ((1L<<32)-1); if((b & 0x80000000) != 0) a++; // b had a sign bit, so we need to restore a long q = ((b << 16) - y - (a << 16)*x) & mask; for(long k=0; k<=5; k++) { long rem = (x - (q + (k<<48))) % x; long d = (rem + x)%x; // force positive if(d < 65536) { long c = ((q + d) * xinv) & mask; if(c < 65536) { return ((((a << 16) + c) - y) * xinv) & mask; } } } throw new RuntimeException("Failed!!"); } public static void main(String[] args) { Random r = new Random(); long next = r.nextLong(); System.out.println("Next long value:" + next); long seed = calcSeed(next); System.out.println("Seed" + seed); // setSeed mangles the input, so demangle it here to get the right output Random r2 = new Random((seed ^ 0x5DEECE66DL) & ((1L << 48)-1)); System.out.println("Next long value from seed:" + r2.nextLong()); } } |
我通常不会只是链接文章…但我找到了一个网站,有人深入调查了这个网站,认为它值得张贴。http://jazzy.id.au/default/2010/09/20/cracking_random_number_generators_part_1.html
您似乎可以这样计算种子:
1 | seed = (seed * multiplier + addend) mod (2 ^ precision) |
其中乘数为25214903917,加数为11,精度为48(位)。你不能计算只有1个数字的种子是什么,但是你可以用2。
编辑:正如恩哈德所说,有一个第2部分,他深入研究种子背后的数学。
我想介绍一个实现来反转由
该程序将对
它可以很容易地扩展到
注意,有些情况下,结果与您在
我想感谢本文作者JamesRoper,他使下面的示例代码成为可能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | import java.util.Random; import java.util.Arrays; class TestRandomReverse { // The secret seed that we want to find private static long SEED = 782634283105L; // Number of random numbers to be generated private static int NUM_GEN = 5; private static int[] genNum(long seed) { Random rand = new Random(seed); int arr[] = new int[NUM_GEN]; for (int i = 0; i < arr.length; i++) { arr[i] = rand.nextInt(); } return arr; } public static void main(String args[]) { int arr[] = genNum(SEED); System.out.println(Arrays.toString(arr)); Long result = reverse(arr); if (result != null) { System.out.println(Arrays.toString(genNum(result))); } else { System.out.println("Seed not found"); } } private static long combine(int rand, int suffix) { return (unsignedIntToLong(rand) << 16) | (suffix & ((1L << 16) - 1)); } private static long unsignedIntToLong(int num) { return num & ((1L << 32) - 1); } // This function finds the seed of a sequence of integer, // generated by nextInt() // Can be easily modified to find the seed of a sequence // of long, generated by nextLong() private static Long reverse(int arr[]) { // Need at least 2 numbers. assert (arr.length > 1); int end = arr.length - 1; // Brute force lower 16 bits, then compare // upper 32 bit of the previous seed generated // to the previous number. for (int i = 0; i < (1 << 16); i++) { long candidateSeed = combine(arr[end], i); long previousSeed = getPreviousSeed(candidateSeed); if ((previousSeed >>> 16) == unsignedIntToLong(arr[end - 1])) { System.out.println("Testing seed:" + previousSeed +" -->" + candidateSeed); for (int j = end - 1; j >= 0; j--) { candidateSeed = previousSeed; previousSeed = getPreviousSeed(candidateSeed); if (j > 0 && (previousSeed >>> 16) == unsignedIntToLong(arr[j - 1])) { System.out.println("Verifying:" + previousSeed +" -->" + candidateSeed); } else if (j == 0) { // The XOR is done when the seed is set, need to reverse it System.out.println("Seed found:" + (previousSeed ^ MULTIPLIER)); return previousSeed ^ MULTIPLIER; } else { System.out.println("Failed"); break; } } } } return null; } private static long ADDEND = 0xBL; private static long MULTIPLIER = 0x5DEECE66DL; // Credit to James Roper // http://jazzy.id.au/default/2010/09/21/cracking_random_number_generators_part_2.html private static long getPreviousSeed(long currentSeed) { long seed = currentSeed; // reverse the addend from the seed seed -= ADDEND; // reverse the addend long result = 0; // iterate through the seeds bits for (int i = 0; i < 48; i++) { long mask = 1L << i; // find the next bit long bit = seed & mask; // add it to the result result |= bit; if (bit == mask) { // if the bit was 1, subtract its effects from the seed seed -= MULTIPLIER << i; } } return result & ((1L << 48) - 1); } } |