Skip to content

nickm980/lcg-predictor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 

Repository files navigation

Java's Random class uses a linear congruential generator which are pseudo-random
and can be predicted because they are not truly random. The algorithm uses previous 
values to predict next pseudo-random numbers and is generated using the formula 
Xn+1 = (aXn+c) mod m with a, c, and m constant. The modulus keeps the next random 
number within bounds and the multiplier and addend generate the next number from 
the sequence. 

Internally Random uses nextseed = (oldseed * multiplier + addend) & mask where the 
bitwise & operator is equivalent to performing modulus 2^48 because both keep only 
the lower 48 bits.

Basic usage of Random to generate a random number
Random random = new Random();
random.nextInt();

The method below is the modified nextInt method from Random.java 
(https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/Random.java#L432)

protected int nextInt(long oldseed) {
  long nextseed = (oldseed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
  return (int)(nextseed >>> 16);
}

As you probably noticed with the bitwise shift, the last 16 bits are discarded to create 
values that more closely resemble random numbers. To 
predict the original seed a possible solution is a brute force loop through all the possible 
last 2^16 bits until we find the combination of numbers that matches from two consecutive 
seeds (nextInt results).

long result = 0;

for (int i = 0; i < (1 << 16); i++) {
  long nextseed = ((long) oldseed << 16) + i;
  if (next(seed) == nextseed) {
    res = nextseed;
    break;
  }
}

Now the result can be passed into Random to predict the same future values as the original Random.

There's one trick for getting the seed to work because when initializing a new instance of 
Random the seed is XOR with the multiplier. However this can be reversed by supplying a seed 
that is already XOR with the multiplier.

Random Initializer
public Random(long seed) {
  this.seed = (seed ^ multiplier) & mask;
}

So you would pass in your predicted seed like this
new Random(seed^multiplier);

About

Java Linear Congruental Generator seed finder

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages