SigScan - Documentation

  1. About / Why you would use it.
  2. Function Details
  3. Finding Signatures on your own.
  4. Examples in multiple languages.
  5. Remarks - Remember to cleanup.

About / Why you would use it

Author: Aikar@Windower.net
Upon joining the windower team one of the first things I learned about was the use of byte signatures to find memory addresses. Cliff originally wrote a function to do it called GetAddress that was very basic. The function required actual byte arrays, so it required more work to 'use', and therefor I decided to write my own implementation with more features and make it faster using better techniques to find the destined address, and now we have SigScan, which I am now providing as a DLL to do scans from a 3rd party exe.

The most popular form of reading memory from the game is finding the modules base address then using a static offset to the desired memory location. The problem with this is when changes are made to the DLL, most offsets will 'shift' as the new code is put into the binary. Now all of those static offsets are changed and no longer work.

However there is a technique called "Byte Signature Scanning" that can help you find your desired memory location without using static memlocs by "scanning" for it. This The idea is to scan over the binaries actual CODE, and look for a specific pattern of code that references your desired memory location that doesnt occur anywhere else in the binary.

For Example:

The very last line contains my desired Memory Address: 0x4C96E105

Every time the game loads, this same exact code will likely still be in the binary (unless the patch changed this specific code), and will be referencing the exact memory location i'm wanting to access. If you don't understand assembly, its ok, i'll give a brief overview of this data. The first column you can ignore, that was the memory addresses those instructions were at at the time of this paste. The 2nd column is the ACTUAL instructions/code in the binary. The 3rd column is just a textual representation of what those instructions are doing so you can read it easier if you program in assembly. On the last line you can see its moving a value of 0 into a byte, and the [5E1964C] is the address its storing to, and thats what I want. On the left hand side you can see that address is in the actual code too (note: hex is stored in reverse order in memory. This is what we are scanning for, the bytes in the middle column, up until the desired memory location (0x4C96E105). So instead of a static address, we can for the code and pull the memory address we want out of that data. From a 3rd party perspecive you can now use ReadProcessMemory on that address, or from an internal perspective, create a pointer to that address.
This greatly will increase the likelyhood your application will still work after the module is updated.

Function Details

The SigScan DLL relies on 3 functions to work.

InitializeSigScan( DWORD PID, const char* Module)

The first parameter is a 32bit unsigned integer, supplying the Process ID you are wanting to read, and the module to find a memory address in.

This function will connect to the process, find the base address, and if the process is a not the current process, copy the entire module to your programs local memory. This is required to achieve high speeds of scanning the memory, as doing it over ReadProcessMemory would be drastically slow.

SigScan(const char* Pattern, int Offset)

This is the primary function of the DLL, that does the actual scanning. You MUST call InitializeSigScan before using this function. It will automatically scan the last Process and Module initialized with InitializeSigScan(). This function accepts a string ascii based pattern of bytes. You must supply 2 characters per byte to check for as you normally would define as 0xXX. If its a single 0xF, you need "0f". So the strings length must be divisible by 2, where every 2 characters represents 1 byte.

Example: "83C408DFE0F6C4050F8A610100005FC605"
This will look for: 0x83 0xC4 0x08 0xDF 0xE0 0xF6 0xC4 0x05 0x0F 0x8A 0x61 0x01 0x00 0x00 0x5F 0xC6 0x05
By default, SigScan will use the next 4 bytes AFTER the end of the signature as the memory address you are wanting to find.
The Offset field is computed into this predefined location. So in most cases Offset should be 0. However, if you find a signature thats like 36 bytes before your desired memory location, you can set offset to 36 and it will look at 36 bytes AFTER the end of the signature. Offset will primarily be 0 in your scans, but is provided to you so you can use signatures a little more distant from the desired location without creating an insanely long signature or using a ton of wildcards.

There are 2 special characters that can be used in the pattern, one is address specification. As stated, by default the memory address it will return is at the end of the signature. But sometimes you may want to use a few bytes AFTER your desired memory location as part of your signature. If you wish to do this, you can specify WHERE in your signature your desired memory location is at with XXXXXXXX,
For example (not a real signature!): "3FBACD300200A1XXXXXXXXB1C4DA"
Now the position of the X's declares where the memory address is. Offset should be 0 when using this, as offset will modify the position, and will not returned your desired memory location!!!

Any unkown character such as ? and invalid hex characters are treated as wildcards, but still must be matched up in 2's.
For example (not a real signature!): "3AB2DFAB????????DEA1FA"
Sometimes the middle of your signature may have other unwanted memory addresses in them, and you need to wildcard those out to skip them. Wildcard anything that will potentially be different every time the game opens - that includes ALL memory addresses that you are not trying to retrieve, otherwise your signature will not be valid the next time the game opens and starts at a different base address.
This function also has special characters that can be used at the start of the pattern to change how it functions. Those however will not help much unless you are using a native language with a DLL loaded into the targets process to access those functions.

FinalizeSigScan()

This is a fairly simple function. It simply cleans up the memory used for the scanning when you are done. Always call this when your done scanning a module. Calling InitializeSigScan again will automatically call FinalizeSigScan so if you need to scan multiple modules you do not need to call it until you finish the last.

Finding Signatures on your own

Examples in multiple languages

C++

C++ has 2 different versions you can use. One is static library, so you may link the code directly into your program and not require a DLL to distribute with your application. This is recommended for C++ developers. However you may dynamically link still with SigScan.dll if you wish to use it over multiple applications.

Code for Static Linking (Header file sigscan.h or place in your own header file)


Code for Dynamic Linking (Header file sigscan.h or place in your own header file)


Once you have your headers set up, you may utilize the functions as so.



C#

One pitfall to .NET is not being able to statically link code, so you will have to import from the DLL and distribute it.
First you will need

Then you need these imports in your main class

Then you can use the DLL like so

VB.NET

One pitfall to .NET is not being able to statically link code, so you will have to import from the DLL and distribute it.
Note: VB.NET is not a real programming language, and needs to die in a fire. If you're using this, stop learning how to use signatures and learn another language please :(
First you will need

Then you need these imports in your main class

Then you can use the DLL like so

Remarks - Remember to cleanup

It's important you remember to call FinalizeSigScan() after finding all the signatures you need to find. If you do not do so, around 2.5mb of memory will be sitting there unused and wasted. You should clean up after you are done and save your users memory! This is automatically done when the DLL is unloaded, so if you do forgot itll clean up properly on application close, but you should do this when you are done always.

Also, note that this method of finding memory addresses is not 100% fullproof. If Square Enix changes some of the physical code in the area you built the signature off of... it will break! This does not happen much, but note you may need to repair/find a new signature after a patch one day. Don't expect there to never be problems ever again.