mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-23 20:43:07 +00:00
Drastically speed up rand() pairs -> seed search
Meet in the middle attack trades some RAM (2^16 flag/byte array) to reduce future searches by a factor of 1:2^8 profiling yielded >100x speed improvements, even a 2x would have been impressive ;) knocks the biggest cpu hog (when searching db) out of the race!
This commit is contained in:
parent
64919cb8c1
commit
3123f0df2f
2 changed files with 58 additions and 16 deletions
|
@ -467,26 +467,16 @@ namespace PKHeX.Core
|
|||
{
|
||||
uint cmp = a << 16;
|
||||
uint x = b << 16;
|
||||
for (uint i = 0; i <= 0xFFFF; i++)
|
||||
{
|
||||
var seed = x | i;
|
||||
if ((method.Next(seed) & 0xFFFF0000) == cmp)
|
||||
yield return method.Prev(seed);
|
||||
}
|
||||
return method.RecoverLower16Bits(x, cmp);
|
||||
}
|
||||
private static IEnumerable<uint> GetSeedsFromIVs(RNG method, uint a, uint b)
|
||||
{
|
||||
uint cmp = a << 16 & 0x7FFF0000;
|
||||
uint x = b << 16 & 0x7FFF0000;
|
||||
for (uint i = 0; i <= 0xFFFF; i++)
|
||||
{
|
||||
var seed = x | i;
|
||||
if ((method.Next(seed) & 0x7FFF0000) != cmp)
|
||||
continue;
|
||||
var prev = method.Prev(seed);
|
||||
yield return prev;
|
||||
yield return prev ^ 0x80000000;
|
||||
}
|
||||
return method.RecoverLower16Bits(x ^ 0x00000000, cmp ^ 0x00000000).Concat(
|
||||
method.RecoverLower16Bits(x ^ 0x80000000, cmp ^ 0x00000000).Concat(
|
||||
method.RecoverLower16Bits(x ^ 0x00000000, cmp ^ 0x80000000).Concat(
|
||||
method.RecoverLower16Bits(x ^ 0x80000000, cmp ^ 0x80000000))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
namespace PKHeX.Core
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public class RNG
|
||||
{
|
||||
|
@ -7,12 +9,41 @@
|
|||
public static readonly RNG ARNG = new RNG(0x6C078965, 0x00000001, 0x9638806D, 0x69C77F93);
|
||||
|
||||
private readonly uint Mult, Add, rMult, rAdd;
|
||||
|
||||
// Bruteforce cache for searching seeds
|
||||
private const int cacheSize = 1 << 16;
|
||||
private readonly uint k2;
|
||||
private readonly byte[] low8 = new byte[cacheSize];
|
||||
private readonly bool[] flags = new bool[cacheSize];
|
||||
|
||||
private RNG(uint f_mult, uint f_add, uint r_mult, uint r_add)
|
||||
{
|
||||
Mult = f_mult;
|
||||
Add = f_add;
|
||||
rMult = r_mult;
|
||||
rAdd = r_add;
|
||||
|
||||
// Set up bruteforce utility
|
||||
k2 = Mult << 8;
|
||||
PopulateMeetMiddleArrays();
|
||||
}
|
||||
|
||||
private void PopulateMeetMiddleArrays()
|
||||
{
|
||||
for (uint i = 0; i <= byte.MaxValue; i++)
|
||||
{
|
||||
uint right = Mult * i;
|
||||
ushort val = (ushort)(right >> 16);
|
||||
flags[val] = true;
|
||||
low8[val] = (byte)i;
|
||||
|
||||
// when calculating the left side, sometimes the low bits might not carry (not considered in calc)
|
||||
// since LCGs are linear, there are no collisions if we mark the next adjacent with the prior val
|
||||
// we do this now so that the search only has to access the array once per loop.
|
||||
++val;
|
||||
flags[val] = true;
|
||||
low8[val] = (byte)i;
|
||||
}
|
||||
}
|
||||
|
||||
public uint Next(uint seed) => seed * Mult + Add;
|
||||
|
@ -56,5 +87,26 @@
|
|||
}
|
||||
return ivs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recovers sets of lower 16 bit seeds, then returns the origin seed.
|
||||
/// </summary>
|
||||
/// <param name="first">First rand() call, 16 bits, already shifted left 16 bits.</param>
|
||||
/// <param name="second">Second rand() call, 16 bits, already shifted left 16 bits.</param>
|
||||
/// <remarks>
|
||||
/// Use a meet-in-the-middle attack to reduce the search space to 2^8 instead of 2^16
|
||||
/// flag/2^8 tables are precomputed and constant (unrelated to rand pairs)
|
||||
/// </remarks>
|
||||
/// <returns>Possible origin seeds that generate the 2 random numbers</returns>
|
||||
internal IEnumerable<uint> RecoverLower16Bits(uint first, uint second)
|
||||
{
|
||||
uint k1 = second - (first * Mult + Add);
|
||||
for (uint i = 0, k3 = k1; i <= 255; ++i, k3 -= k2)
|
||||
{
|
||||
ushort val = (ushort)(k3 >> 16);
|
||||
if (flags[val])
|
||||
yield return Prev(first | i << 8 | low8[val]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue