- How long you been coding/hacking?
- 5 years
Pattern Scanning or Signature Scanning is the art of finding a sequence of bytes in memory which matches a sequence of bytes you already identified. You scan the good regions of memory byte by byte looking for the first byte. When the first byte is found, you compare each subsequent byte against the known pattern. If all the bytes are found, your pattern is found. If your comparison for 1 byte fails, you start over at the next byte.
You pattern scan for CODE, not DATA
Meaning, your signature should be for assembly instructions which are converted into bytes. Like "mov eax, [esi]". You do not pattern scan for the health integer 100. You pattern scan for the code which accesses this address. When you pattern scan returns the resulting address of the matching instructions, you then read the memory in that area where the address is hardcoded into the instructions. Or you hook and pull the value out of register. If you are pattern scanning for DATA you will get false positives. There are fringe cases when you will scan for DATA but that's rare.
Signature Pattern
Signatures can be written several ways, but they are always used as char arrays also known as string literals
The first 2 represent patterns with no known wild cards, if there is a mask as well, there may be wildcards defined there. The second two patterns are referred to as "combo patterns" because they are a mask and a pattern all in one. There are different schemes because different tools generate different signatures.
Wild Cards
Wildcards are represented by ? or ?? in almost every scheme. Why do we have wildcards? We use wildcards to replace addresses and offsets in the assembly code. When the game updates, these might change, but the assembly instructions do not change from a small update. If the update is very large, it's possible for the instructions or src & dst registers to also change.
if the code was:
If 0F43EA74 represents an address, you may want to use wildcards for those bytes.
Masks
Masks are used to identify which bytes represent wildcards in the pattern. If the pattern is "FF 45 B3" and the mask is "x?x" then the second byte, the "45" is a wildcard and should be skipped by the pattern scan function.
Only Scan Good Memory
It is important that you only scan proper memory. If you just scanned from 0x0 to 0xFFFFFFFFF it would take at least 5 seconds in most processes. You can skip bad regions of memory by checking the memory page settings by using VirtualQueryEx. This will retrieve a MEMORY_BASIC_INFORMATION which will define the state of that memory region.
If the MemoryBasicInformation.state is not MEM_COMMIT then it is bad memory
If the MBI.Protect is PAGE_NOACCESS you also want to skip this memory
Scan a modules address range, not the entire process
Whenever possible (99% of the time) only scan a module, by scanning from the beginning of the module to the end. These addresses can easily be found with ToolHelp32Snapshot or by parsing the Peb->Ldr-ModuleList
Limitations of the following code
This code has one limitation, if your pattern bridges 2 memory regions, this will fail. In theory this is possible, but in practice I have never had a problem. Too lazy to fix it when it hasn't been a problem.
Basic Byte Comparison
This forms the basis of all pattern scanning, when you have a buffer you can scan that buffer for your pattern using your pattern and mask. All your pattern scan functions will be wrappers for this function.
begin = address of buffer to scan
size = size of that buffer
Internal Scan Wrapper
This is a wrapper around the above function which checks the memory region state and skips bad regions. If you're scanning memory internally, use this.
Internal Module Scan Wrapper
I like to use ToolHelp32Snapshot for external, and when internal to walk the module list in the PEB. So this requires a bit more code. It uses undocumented structures, you can grab the LDR_DATA_TABLE_ENTRY structure from x64dbg github
External Wrapper
Any external pattern scan function should use this, it is a wrapper around the ScanBasic comparison function. This external wrapper, queries memory and skips the bad regions. It loops through the good regions, copies them to a local buffer using ReadProcessMemory and then calls ScanBasic on the buffer.
External Module Scan Wrapper
Use ToolHelp32Snapshot to get the moduleEntry struct, learn how here: Tutorial - Get Module Base Address Tutorial dwGetModuleBaseAddress
Universal Pattern Parser
This function will work convert all pattern types to pattern & mask combos. If it's ollydbg, Cheat Engine or IDA they all will work. You can use it in wrappers for your pattern scan functions
Source Code - Universal Pattern / Signature Parser
Using Universal Pattern Parser In Wrappers
If you want to use universal combo patterns instead of pattern/masks in your code, you can parse them in wrappers which looks something like this
Modify it to match your code.
So now you have wrappers for scanning modules internally and externally, that should cover 99% of your pattern scanning needs.
DON'T JUST USE THIS CODE, LEARN HOW IT WORKS, MODIFY IT TO FIT YOUR CODING STYLE
This is meant for beginners, @Nomade has posted some considerations regarding this code regarding some fringe cases where it won't work, so please read them here
You pattern scan for CODE, not DATA
Meaning, your signature should be for assembly instructions which are converted into bytes. Like "mov eax, [esi]". You do not pattern scan for the health integer 100. You pattern scan for the code which accesses this address. When you pattern scan returns the resulting address of the matching instructions, you then read the memory in that area where the address is hardcoded into the instructions. Or you hook and pull the value out of register. If you are pattern scanning for DATA you will get false positives. There are fringe cases when you will scan for DATA but that's rare.
Signature Pattern
Signatures can be written several ways, but they are always used as char arrays also known as string literals
C++:
"3B DE 7C 2A 2B DE"
"\x31\xF6\x90\x90\x90\x90"
"29 ?? 6C 01 00 00 48"
"7D 1D 29 B3 ? ? ? ? 8B 83"
Wild Cards
Wildcards are represented by ? or ?? in almost every scheme. Why do we have wildcards? We use wildcards to replace addresses and offsets in the assembly code. When the game updates, these might change, but the assembly instructions do not change from a small update. If the update is very large, it's possible for the instructions or src & dst registers to also change.
if the code was:
Code:
mov eax, esi
mov esi, 0F43EA74
mov edi [ecx]
Masks
Masks are used to identify which bytes represent wildcards in the pattern. If the pattern is "FF 45 B3" and the mask is "x?x" then the second byte, the "45" is a wildcard and should be skipped by the pattern scan function.
Only Scan Good Memory
It is important that you only scan proper memory. If you just scanned from 0x0 to 0xFFFFFFFFF it would take at least 5 seconds in most processes. You can skip bad regions of memory by checking the memory page settings by using VirtualQueryEx. This will retrieve a MEMORY_BASIC_INFORMATION which will define the state of that memory region.
If the MemoryBasicInformation.state is not MEM_COMMIT then it is bad memory
If the MBI.Protect is PAGE_NOACCESS you also want to skip this memory
Scan a modules address range, not the entire process
Whenever possible (99% of the time) only scan a module, by scanning from the beginning of the module to the end. These addresses can easily be found with ToolHelp32Snapshot or by parsing the Peb->Ldr-ModuleList
Limitations of the following code
This code has one limitation, if your pattern bridges 2 memory regions, this will fail. In theory this is possible, but in practice I have never had a problem. Too lazy to fix it when it hasn't been a problem.
Basic Byte Comparison
This forms the basis of all pattern scanning, when you have a buffer you can scan that buffer for your pattern using your pattern and mask. All your pattern scan functions will be wrappers for this function.
C++:
char* ScanBasic(char* pattern, char* mask, char* begin, intptr_t size)
{
intptr_t patternLen = strlen(mask);
for (int i = 0; i < size; i++)
{
bool found = true;
for (int j = 0; j < patternLen; j++)
{
if (mask[j] != '?' && pattern[j] != *(char*)((intptr_t)begin + i + j))
{
found = false;
break;
}
}
if (found)
{
return (begin + i);
}
}
return nullptr;
}
size = size of that buffer
Internal Scan Wrapper
This is a wrapper around the above function which checks the memory region state and skips bad regions. If you're scanning memory internally, use this.
C++:
char* ScanInternal(char* pattern, char* mask, char* begin, intptr_t size)
{
char* match{ nullptr };
MEMORY_BASIC_INFORMATION mbi{};
for (char* curr = begin; curr < begin + size; curr += mbi.RegionSize)
{
if (!VirtualQuery(curr, &mbi, sizeof(mbi)) || mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS) continue;
match = ScanBasic(pattern, mask, curr, mbi.RegionSize);
if (match != nullptr)
{
break;
}
}
return match;
}
I like to use ToolHelp32Snapshot for external, and when internal to walk the module list in the PEB. So this requires a bit more code. It uses undocumented structures, you can grab the LDR_DATA_TABLE_ENTRY structure from x64dbg github
C++:
char* TO_CHAR(wchar_t* string)
{
size_t len = wcslen(string) + 1;
char* c_string = new char[len];
size_t numCharsRead;
wcstombs_s(&numCharsRead, c_string, len, string, _TRUNCATE);
return c_string;
}
PEB* GetPEB()
{
#ifdef _WIN64
PEB* peb = (PEB*)__readgsword(0x60);
#else
PEB* peb = (PEB*)__readfsdword(0x30);
#endif
return peb;
}
LDR_DATA_TABLE_ENTRY* GetLDREntry(std::string name)
{
LDR_DATA_TABLE_ENTRY* ldr = nullptr;
PEB* peb = GetPEB();
LIST_ENTRY head = peb->Ldr->InMemoryOrderModuleList;
LIST_ENTRY curr = head;
while (curr.Flink != head.Blink)
{
LDR_DATA_TABLE_ENTRY* mod = (LDR_DATA_TABLE_ENTRY*)CONTAINING_RECORD(curr.Flink, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
if (mod->FullDllName.Buffer)
{
char* cName = TO_CHAR(mod->BaseDllName.Buffer);
if (_stricmp(cName, name.c_str()) == 0)
{
ldr = mod;
break;
}
delete[] cName;
}
curr = *curr.Flink;
}
return ldr;
}
char* ScanModIn(char* pattern, char* mask, std::string modName)
{
LDR_DATA_TABLE_ENTRY* ldr = GetLDREntry(modName);
char* match = ScanInternal(pattern, mask, (char*)ldr->DllBase, ldr->SizeOfImage);
return match;
}
Any external pattern scan function should use this, it is a wrapper around the ScanBasic comparison function. This external wrapper, queries memory and skips the bad regions. It loops through the good regions, copies them to a local buffer using ReadProcessMemory and then calls ScanBasic on the buffer.
C++:
char* ScanEx(char* pattern, char* mask, char* begin, intptr_t size, HANDLE hProc)
{
char* match{ nullptr };
SIZE_T bytesRead;
DWORD oldprotect;
char* buffer{ nullptr };
MEMORY_BASIC_INFORMATION mbi;
mbi.RegionSize = 0x1000;//
VirtualQueryEx(hProc, (LPCVOID)begin, &mbi, sizeof(mbi));
for (char* curr = begin; curr < begin + size; curr += mbi.RegionSize)
{
if (!VirtualQueryEx(hProc, curr, &mbi, sizeof(mbi))) continue;
if (mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS) continue;
delete[] buffer;
buffer = new char[mbi.RegionSize];
if (VirtualProtectEx(hProc, mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &oldprotect))
{
ReadProcessMemory(hProc, mbi.BaseAddress, buffer, mbi.RegionSize, &bytesRead);
VirtualProtectEx(hProc, mbi.BaseAddress, mbi.RegionSize, oldprotect, &oldprotect);
char* internalAddr = ScanBasic(pattern, mask, buffer, (intptr_t)bytesRead);
if (internalAddr != nullptr)
{
//calculate from internal to external
match = curr + (internalAddr - buffer);
break;
}
}
}
delete[] buffer;
return match;
}
Use ToolHelp32Snapshot to get the moduleEntry struct, learn how here: Tutorial - Get Module Base Address Tutorial dwGetModuleBaseAddress
C++:
char* ScanModEx(char* pattern, char* mask, MODULEENTRY32& modEntry, HANDLE hProc)
{
return Scan(pattern, mask, (char*)modEntry.modBaseAddr, modEntry.modBaseSize, hProc);
}
This function will work convert all pattern types to pattern & mask combos. If it's ollydbg, Cheat Engine or IDA they all will work. You can use it in wrappers for your pattern scan functions
Source Code - Universal Pattern / Signature Parser
Using Universal Pattern Parser In Wrappers
If you want to use universal combo patterns instead of pattern/masks in your code, you can parse them in wrappers which looks something like this
C++:
char* ScanModExCombo(char* combopattern, ModEx& mod)
{
char pattern[100]{};
char mask[100]{};
Pattern::Parse(combopattern, pattern, mask);
return Pattern::Ex::ScanMod(pattern, mask, mod);
}
So now you have wrappers for scanning modules internally and externally, that should cover 99% of your pattern scanning needs.
DON'T JUST USE THIS CODE, LEARN HOW IT WORKS, MODIFY IT TO FIT YOUR CODING STYLE
This is meant for beginners, @Nomade has posted some considerations regarding this code regarding some fringe cases where it won't work, so please read them here
Last edited: