Welcome to hackvshack.net Forum!
Download Free HvH CS2/CS:GO Cheats, CFG, LUA/JS Scripts, and More!
Register

Miscellaneous Core Logic Beginner Pattern Scanning (Signature Scanning)

vix

hhh
Ex Staff
User ID:
68947
Messages:
66
Reactions:
23
Badges:
12
REP:
−0/3+
Level:
88
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.

Example:
Expand Collapse Copy
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:
Expand Collapse Copy
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:
  • Like
Reactions: legacypaster
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.

Example:
Expand Collapse Copy
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:
Expand Collapse Copy
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.
off by one issue:
Expand Collapse Copy
// this never checks last
for (size_t Index = 0; Index < Size - Length; Index++)

// correct version should have an equal sign
for (size_t Index = 0; Index <= Size - Length; Index++)
safety check:
Expand Collapse Copy
// we should add this check on the top to not overflow
if (Length == 0 || Length > Size)
    return nullptr;

Also this code is relatively slow, and windows-only, take a look at for example
 
off by one issue:
Expand Collapse Copy
// this never checks last
for (size_t Index = 0; Index < Size - Length; Index++)

// correct version should have an equal sign
for (size_t Index = 0; Index <= Size - Length; Index++)
safety check:
Expand Collapse Copy
// we should add this check on the top to not overflow
if (Length == 0 || Length > Size)
    return nullptr;

Also this code is relatively slow, and windows-only, take a look at for example
back from being dead to hatin 😳
 
back from being dead to hatin 😳
I js gave you feedback gang 🥲🥲 also I'm back to check out coding posts and open source the best cheat in the world giuseppehook/cumhook/prometheus with private p100 hitting since 2019 method for defensive fix with future tick intersection in 48-72 hours 30th February tomorrow 4pm 🔥 (sorry I love larping)
 

Who has read this thread (Total: 0) in last 1 hours View details