Originally Posted by
XORReverseEngineering
also you can search for current working pattern in ghidra (write the name of the function, save the work) and then bindeff with the new version, then you can use cheat engine with SigMaker to generate the new pattern on that address (that's for static patterns), or if you find a software that is updated faster then GameHelper, you can IL the dll and translate it into C#
so after I revisited the function Pattern and this is what I learned:
based on this code
Code:
public struct Pattern
{
public readonly string Name;
public readonly byte[] Data;
public readonly bool[] Mask;
public readonly int BytesToSkip;
private static (byte[], bool[]) ParseArrayOfHexBytes(List<string> arrayOfHexBytes)
{
List<bool> list = new List<bool>();
List<byte> list2 = new List<byte>();
for (int i = 0; i < arrayOfHexBytes.Count; i++)
{
string text = arrayOfHexBytes[i];
if (text.StartsWith("?"))
{
list2.Add(0);
list.Add(item: false);
}
else
{
list2.Add(byte.Parse(text, NumberStyles.HexNumber));
list.Add(item: true);
}
}
return (list2.ToArray(), list.ToArray());
}
public Pattern(string name, string arrayOfHexBytes)
{
Name = name;
List<string> list = arrayOfHexBytes.Split(new string[2] { " ", "," }, StringSplitOptions.RemoveEmptyEntries).ToList();
BytesToSkip = list.FindIndex("^".Equals);
(Data, Mask) = ParseArrayOfHexBytes(list.Where((string hex) => hex != "^").ToList());
}
public Pattern(string name, string arrayOfHexBytes, int bytesToSkip)
{
Name = name;
BytesToSkip = bytesToSkip;
(Data, Mask) = ParseArrayOfHexBytes(arrayOfHexBytes.Split(new string[2] { " ", "," }, StringSplitOptions.RemoveEmptyEntries).ToList());
}
public override string ToString()
{
string text = "Name: " + Name + " Pattern: ";
for (int i = 0; i < Data.Length; i++)
{
text = ((!Mask[i]) ? (text + "?? ") : (text + $"0x{Data[i]:X} "));
}
return text + $"BytesToSkip: {BytesToSkip}";
}
}
we took as example "GameStates" "41 ?? 48 ?? ?? ?? 48 ?? ?? 33 ?? 48 ?? ?? ^ ?? ?? ?? ?? 0f 85 ?? ?? ?? ??"
Code:
41 56 PUSH R14
48 83 ec 40 SUB RSP,0x40
48 8b f1 MOV RSI,param_1
33 ed XOR EBP,EBP
48 39 2d CMP qword ptr [DAT_7ff695aabe80],RBP
fd 61 ac 03
0f 85 6a JNZ LAB_7ff691fe5df3
01 00 00
Code:
Before ^:
The bytes before ^ (41 ?? 48 ?? ?? ?? 48 ?? ?? 33 ?? 48 ?? ??) are used for pattern matching.
The memory scanner looks for these bytes (with wildcards where ?? is present) to identify the start of the signature in memory.
After ^:
The bytes after ^ (?? ?? ?? ?? 0f 85 ?? ?? ?? ??) are used to calculate the offset for data extraction or processing.
This section typically includes dynamic or relative information:
For example, 0F 85 is a conditional jump (JNZ) instruction in x86 assembly. Its operand may contain the offset or address of the jump target.
These bytes may be parsed to determine specific data locations, offsets, or branch behavior.
Code:
BytesToSkip:
The constructor identifies the position of ^ and calculates the number of bytes before it.
In this case, BytesToSkip is 14 (the position of ^ in the pattern).
Data and Mask Arrays:
Bytes before ^ are used to create the pattern's data and mask arrays:
Data: [0x41, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x33, 0x00, 0x48, 0x00, 0x00]
Mask: [true, false, true, false, false, false, true, false, false, true, false, true, false, false]
Bytes after ^ (?? ?? ?? ?? 0f 85 ?? ?? ?? ??) are ignored during initial matching but are available for further interpretation.