mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-24 04:53:08 +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 cmp = a << 16;
|
||||||
uint x = b << 16;
|
uint x = b << 16;
|
||||||
for (uint i = 0; i <= 0xFFFF; i++)
|
return method.RecoverLower16Bits(x, cmp);
|
||||||
{
|
|
||||||
var seed = x | i;
|
|
||||||
if ((method.Next(seed) & 0xFFFF0000) == cmp)
|
|
||||||
yield return method.Prev(seed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
private static IEnumerable<uint> GetSeedsFromIVs(RNG method, uint a, uint b)
|
private static IEnumerable<uint> GetSeedsFromIVs(RNG method, uint a, uint b)
|
||||||
{
|
{
|
||||||
uint cmp = a << 16 & 0x7FFF0000;
|
uint cmp = a << 16 & 0x7FFF0000;
|
||||||
uint x = b << 16 & 0x7FFF0000;
|
uint x = b << 16 & 0x7FFF0000;
|
||||||
for (uint i = 0; i <= 0xFFFF; i++)
|
return method.RecoverLower16Bits(x ^ 0x00000000, cmp ^ 0x00000000).Concat(
|
||||||
{
|
method.RecoverLower16Bits(x ^ 0x80000000, cmp ^ 0x00000000).Concat(
|
||||||
var seed = x | i;
|
method.RecoverLower16Bits(x ^ 0x00000000, cmp ^ 0x80000000).Concat(
|
||||||
if ((method.Next(seed) & 0x7FFF0000) != cmp)
|
method.RecoverLower16Bits(x ^ 0x80000000, cmp ^ 0x80000000))));
|
||||||
continue;
|
|
||||||
var prev = method.Prev(seed);
|
|
||||||
yield return prev;
|
|
||||||
yield return prev ^ 0x80000000;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
namespace PKHeX.Core
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace PKHeX.Core
|
||||||
{
|
{
|
||||||
public class RNG
|
public class RNG
|
||||||
{
|
{
|
||||||
|
@ -7,12 +9,41 @@
|
||||||
public static readonly RNG ARNG = new RNG(0x6C078965, 0x00000001, 0x9638806D, 0x69C77F93);
|
public static readonly RNG ARNG = new RNG(0x6C078965, 0x00000001, 0x9638806D, 0x69C77F93);
|
||||||
|
|
||||||
private readonly uint Mult, Add, rMult, rAdd;
|
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)
|
private RNG(uint f_mult, uint f_add, uint r_mult, uint r_add)
|
||||||
{
|
{
|
||||||
Mult = f_mult;
|
Mult = f_mult;
|
||||||
Add = f_add;
|
Add = f_add;
|
||||||
rMult = r_mult;
|
rMult = r_mult;
|
||||||
rAdd = r_add;
|
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;
|
public uint Next(uint seed) => seed * Mult + Add;
|
||||||
|
@ -56,5 +87,26 @@
|
||||||
}
|
}
|
||||||
return ivs;
|
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