Creating Signatures
This is user documentation.
This document explains how to create signatures for code and data patterns in games.
Signatures are string representations of the bytes you want to locate in a game's memory.
They consist of hex bytes and wildcards that match bytes.
Signature Format
A signature looks like this (hex)
89 15 ?? ?? ?? ?? EB ?? A3 ?? ?? ?? ??
- Each
byte
is represented by its hexadecimal value (e.g.89
,15
). - Wildcards are represented by
??
, which matches any byte.
Signature Format (Bit-Level)
A signature can also be defined at the bit level:
10001001 ???????? ???????? 11101011 ???????? 10100011 ???????? ????????
Where ?
represents a bit that can be either 0
or 1
.
Identifying Signatures
To create a signature, you need to identify the bytes that make up the code or data you want to find.
This usually involves the following steps:
-
Locate the code or data in a disassembler: Use a tool like IDA Pro, Ghidra, or Binary Ninja to find the function or variable you're interested in.
-
Identify unique bytes: Find a unique sequence of code bytes at that address.
This process can be automated with with Signature Creation Tools.
The theory is to find a sequence of bytes that is unique to the code or data you want to locate, and while masking out the bytes that may change between different game builds from the assembly, i.e. memory addresses, jump targets, etc.
It's suggested to only use this technique to scan read only memory.
This can mean one of the following:
- Code (usually in
.text
segment) - Read-only data (usually in
.rdata
segment)
Most commonly, you will use this to create a signature for the start of a
function you intend to hook.
However, it can also be used to find variables in memory.
Signature Creation Tools
There are tools available to help automate the process of creating signatures
- SigMaker for IDA: A plugin for IDA Pro that can generate and test signatures.
- SigMakerEx for IDA: An alternative plugin for IDA Pro that generates signatures.
- Creates signatures only, but is more flexible than SigMaker for that purpose.
- MakeSig for Ghidra: A script for Ghidra that creates signatures for functions and code snippets.
SigMaker for IDA
We have merged SigMaker and SigMakerEx docs into a single section.
Since using these two plugins is very similar.
You can find SigMaker for IDA here. You can find SigMakerEx here.
To install, simply drop the plugin into the plugins
directory.
You can then access SigMaker
(Ex) via Edit -> Plugins -> SigMaker
(Ex) or by pressing Ctrl + Alt + S
.
We recommend using SigMakerEx
for creating signatures and older SigMaker
for testing them.
To create a function signature perform the following steps
- Navigate to the code you want to create a signature for.
- Press
Ctrl + Alt + S
to open SigMakerEx. - Click
Function
. - Copy the signature from the
Output
window. - Test the pattern in original SigMaker (non-EX) by clicking
Test IDA Pattern
.
- Select the code you want to create a signature for.
- Press
Ctrl + Alt + S
to open SigMaker. - Click
Create IDA Pattern from Selection
. - Copy the signature from the
Output
window. - Test the pattern by clicking
Test IDA Pattern
. - If there are multiple results, select more code and repeat the process until there is only one result.
MakeSig for Ghidra
You can find MakeSig for Ghidra here.
To install, drop the makesig.py
script into your ghidra_scripts
directory.
To create a signature:
- Open the
Script Manager
window by clickingWindow -> Script Manager
or using the Script Manager button in the toolbar. - Find the
makesig
script and double-click it to run. - Select the function or instruction you want to create a signature for.
- The signature will be printed to the console output.
Example of Finding a Static Variable via Signature Scanning
An example of finding a variable in memory with signature scanning.
More specifically, we want to find a variable levelId
in read-write memory (.data
).
The assembly code (commented below) accesses the levelId
variable.
// Instruction // Bytes
mov levelId, edx // 89 15 [?? ?? ?? ??] <= Level ID Address
jmp loc_4354A1 // EB ??
mov someOtherVariable, eax // A3 ?? ?? ?? ??
The bytes make up the signature 89 15 ?? ?? ?? ?? EB ?? A3 ?? ?? ?? ??
, with the
4 bytes after 89 15
being the static address of the levelId
variable.
We scan for the above signature, and add 2
to the found address to get the location of where
levelId
is stored in memory. We then read from that location to find out where levelId
is:
// Scan for the signature
var result = scanner.FindPattern("89 15 ?? ?? ?? ?? EB ?? A3 ?? ?? ?? ??");
if (!result.Found)
throw new Exception("Signature not found");
// Get the address of the instruction
var instructionAddress = (byte*)result.Address;
// Get the address of the levelId variable
var levelIdAddress = (int*)(instructionAddress + 2);
// Read the value of levelId
var levelId = *levelIdAddress;
// Scan for the signature
let result = scanner.find_pattern("89 15 ?? ?? ?? ?? EB ?? A3 ?? ?? ?? ??");
if !result.found {
panic!("Signature not found");
}
// Get the address of the instruction
let instruction_address = result.address as *const u8;
// Get the address of the levelId variable
let level_id_address = unsafe { instruction_address.offset(2) as *const i32 };
// Read the value of levelId
let level_id = unsafe { *level_id_address };
// Scan for the signature
auto result = scanner.FindPattern("89 15 ?? ?? ?? ?? EB ?? A3 ?? ?? ?? ??");
if (!result.Found)
throw std::runtime_error("Signature not found");
// Get the address of the instruction
auto instructionAddress = reinterpret_cast<uint8_t*>(result.Address);
// Get the address of the levelId variable
auto levelIdAddress = reinterpret_cast<int*>(instructionAddress + 2);
// Read the value of levelId
auto levelId = *levelIdAddress;