Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine the seed of C# Random Instance

Out of interest, I am playing around with the Random class that comes with C#. I am trying to predict the future. As it is pseudo random, there must be some way to predict the numbers. The only way I can think of so far is brute force (Getting all possible seeds and finding the pattern of the random numbers in them) but I believe it will be too much processing power as the seed can be anything from -2,147,483,647 to 2,147,483,647.

So far I have determined that:

new Random() == new Random(Environment.TickCount)

and that both these classes will provide the same random numbers in the same order.

Is it in any way possible to determine the seed of the Random class instance? If you could know the Environment.TickCount when the Random class was instantiated, the random numbers could be predicted, but I have no idea whether it could be done.

like image 877
Corne Avatar asked Dec 27 '22 01:12

Corne


2 Answers

As it is pseudo random, there must be some way to predict the numbers.

Indeed; if you know the internal state (in particular inext,inextp and SeedArray - so 58 int values in total), and you know the exact operations that are going to be requested in the exact order (for example, asking for Next(), Next(), NextBytes() is very different to asking for Next(), NextBytes(), Next()) - then you could make entirely accurate forward guesses. That is the entire point of a seeded PRNG - it allows for repeatable randomness, which is useful in many scenarios when you need to be able to replay events.

So: I don't think you could ever get back the original seed, but to predict the future (rather than the past) you don't need the seed - you just need 58 int values.

However! Anything where the randomness matters should be using the cryptographic random providers - and those are not repeatable or guessable.

For example:

static class Program {
    static Random Clone(this Random source)
    {
        var clone = new Random();
        var type = typeof(Random);
        var field = type.GetField("inext",
            BindingFlags.Instance | BindingFlags.NonPublic);
        field.SetValue(clone, field.GetValue(source));
        field = type.GetField("inextp",
            BindingFlags.Instance | BindingFlags.NonPublic);
        field.SetValue(clone, field.GetValue(source));
        field = type.GetField("SeedArray",
            BindingFlags.Instance | BindingFlags.NonPublic);
        int[] arr = (int[])field.GetValue(source);
        field.SetValue(clone, arr.Clone());
        return clone;
    }
    static void Main()
    {
        Random rand = new Random();
        var clone = rand.Clone();
        Console.WriteLine("My predictions:");
        Console.WriteLine(clone.Next());
        Console.WriteLine(clone.Next());
        Console.WriteLine(clone.Next());
        Console.WriteLine("Actual:");
        Console.WriteLine(rand.Next());
        Console.WriteLine(rand.Next());
        Console.WriteLine(rand.Next());
    }
}
like image 53
Marc Gravell Avatar answered Jan 11 '23 19:01

Marc Gravell


The implementation of Random doesn't store the seed anywhere - it uses a seed array instead. So unfortunately there's no way to determine the seed.

The constructor for Random which takes a seed looks like this:

public Random(int Seed)
{
    this.SeedArray = new int[0x38];
    int num4 = (Seed == 0x80000000) ? 0x7fffffff : Math.Abs(Seed);
    int num2 = 0x9a4ec86 - num4;
    this.SeedArray[0x37] = num2;
    int num3 = 0x1;
    for (int i = 0x1; i < 0x37; i++)
    {
        int index = (0x15 * i) % 0x37;
        this.SeedArray[index] = num3;
        num3 = num2 - num3;
        if (num3 < 0x0)
        {
            num3 += 0x7fffffff;
        }
        num2 = this.SeedArray[index];
    }
    for (int j = 0x1; j < 0x5; j++)
    {
        for (int k = 0x1; k < 0x38; k++)
        {
            this.SeedArray[k] -= this.SeedArray[0x1 + ((k + 0x1e) % 0x37)];
            if (this.SeedArray[k] < 0x0)
            {
                this.SeedArray[k] += 0x7fffffff;
            }
        }
    }
    this.inext = 0x0;
    this.inextp = 0x15;
    Seed = 0x1;
}

You can see that it doesn't store the seed anywhere.

(An odd thing about this is that it assigns 1 to the Seed variable right at the end for some strange reason - but that's not really relevant; it's just a bit weird.)

You could write your own wrapper class for Random which retains the seed value for later retrieval. The implementation would just remember Seed:

public class MyRandom: Random
{
    public MyRandom() : this(Environment.TickCount)
    {
    }

    public MyRandom(int seed) : base(seed)
    {
        this.seed = seed;
    }

    public int Seed
    {
        get { return seed; }
    }

    private readonly int seed;
}
like image 39
Matthew Watson Avatar answered Jan 11 '23 21:01

Matthew Watson