Pattern Scanning is just finding bytes you already know inside a module. Some bytes never change, others change every update.
That is why you write signatures like "48 8B 05 ? ? ? ?".
Normal hex like 48 or 8B = this byte must match
? = this byte can be anything, we ignore it
You give it a module name and a signature string. It finds the module, turns your string into a byte pattern + a mask, then walks the module byte by byte. If the mask says x and the byte does not match, it starts over at the next position. If the mask says ?, it does not care and keeps going.
You found mov rax,[rip+...] in your debugger. The first three bytes are always 48 8B 05 but the offset changes every patch. You just call it like this:
That is it. The x spots are the static bytes that never change. The ? spots are the bytes that change every update. The scanner only checks the x spots and ignores the ? spots. Everything else is just walking bytes.
That is why you write signatures like "48 8B 05 ? ? ? ?".
Normal hex like 48 or 8B = this byte must match
? = this byte can be anything, we ignore it
You give it a module name and a signature string. It finds the module, turns your string into a byte pattern + a mask, then walks the module byte by byte. If the mask says x and the byte does not match, it starts over at the next position. If the mask says ?, it does not care and keeps going.
Example:
char* Scan(const char* ModuleName, const char* Signature)
{
// Find the module and get its size
HMODULE HMod = GetModuleHandleA(ModuleName);
if (!HMod) return nullptr;
char* Base = (char*)HMod;
PIMAGE_DOS_HEADER Dos = (PIMAGE_DOS_HEADER)Base;
PIMAGE_NT_HEADERS Nt = (PIMAGE_NT_HEADERS)(Base + Dos->e_lfanew);
size_t Size = Nt->OptionalHeader.SizeOfImage;
// Turn the signature string into a byte pattern and a mask
// "48 8B 05 ? ? ? ?" becomes:
// Pattern = { 0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00 }
// Mask = "xxx????"
char Pattern[128] = {0};
char Mask[128] = {0};
int Length = 0;
const char* P = Signature;
while (*P)
{
if (*P == ' ') { P++; continue; }
if (*P == '?')
{
Pattern[Length] = 0;
Mask[Length] = '?';
Length++;
while (*P == '?') P++;
}
else
{
char Hex[3] = { P[0], P[1], 0 };
Pattern[Length] = (char)strtol(Hex, 0, 16);
Mask[Length] = 'x';
Length++;
P += 2;
}
}
// Walk the module byte by byte
for (size_t Index = 0; Index < Size - Length; Index++)
{
bool bFound = true;
for (int Offset = 0; Offset < Length; Offset++)
{
// If mask is 'x', the byte MUST match the pattern
// If mask is '?', we skip this byte completely
if (Mask[Offset] == 'x' && Pattern[Offset] != *(Base + Index + Offset))
{
bFound = false;
break;
}
}
if (bFound)
return Base + Index;
}
return nullptr;
}
You found mov rax,[rip+...] in your debugger. The first three bytes are always 48 8B 05 but the offset changes every patch. You just call it like this:
Example:
char* pMatch = Scan("client.dll", "48 8B 05 ? ? ? ?");
That is it. The x spots are the static bytes that never change. The ? spots are the bytes that change every update. The scanner only checks the x spots and ignores the ? spots. Everything else is just walking bytes.
Last edited:
