Guide External & Internal Pattern Scanning Guide

Hexui Undetected CSGO Cheats Sinkicheat PUBG Cheat

Rake

Cesspool Admin
Administrator
Jan 21, 2014
11,574
78,998
2,317
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
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"
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.

if the code was:
Code:
mov eax, esi
mov esi, 0F43EA74
mov edi [ecx]
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 you 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;
}
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.

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;
}
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

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;
}
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.

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;
}
External Module Scan Wrapper
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);
}
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
https://guidedhacking.com/threads/universal-pattern-signature-parser.9588/

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);
}
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
 
Last edited:

Unknown Archetype

Banned
Silenced
Nov 27, 2013
3
373
0
.




I have a problem here, it always returns 0xDEADBEEF, like if it couldn't find the bytes



e.g.2.png




example.png



1.Initiating hooks

C++:
void InitiateHooks()
{
    DWORD ammoAddy = FindPattern("hl2.exe",
                                 "\x89\x3E\x5F\x8B\xC6\x5E\xC2\x04\x00\xCC\xCC\xCC\xCC\xCC",
                                 "xxxxxxxxxxxxxx");

    ammoAddy+=6;

    MsgBoxAddy(ammoAddy);
    char cad[1024];
    sprintf(cad, "Direccion:%02x", ammoAddy);
    MessageBox(NULL, cad, "", NULL);

    WriteToMemory(ammoAddy, AmmoOpCode, 2);
}






2. Finding patterns

C++:
DWORD FindPattern(char *module, char *pattern, char *mask)
{
    MODULEINFO mInfo = GetModuleInfo(module);

    DWORD base = (DWORD)mInfo.lpBaseOfDll;
    DWORD size = (DWORD)mInfo.SizeOfImage;

    DWORD patternLength = (DWORD)strlen(mask);

    for (DWORD i = 0; i < size - patternLength; i++)
    {
        bool found = true;

        for (DWORD j = 0; j < patternLength; j++)
        {
            found &= mask[j] == '?' || pattern[j] == *(char*)(base+i+j);
        }

        if (found)
            return base+i;
    }

    return 0xDEADBEEF;
}
 
Last edited:

squeenie

Hacker
Meme Tier VIP
Dank Tier Donator
Mar 6, 2013
677
5,478
37
Findpattern doesn't seem to work on source player classes. Try instead for a function that returns the class.
 

till0sch

Respected Hacker
Dank Tier VIP
Dank Tier Donator
Oct 14, 2012
1,107
12,593
51
But you are going internally, aren't you?

Try giving out the base of the dll and the end (the region you're scanning) and see if it's even where you find it in ollydbg.
 

Rake

Cesspool Admin
Administrator
Jan 21, 2014
11,574
78,998
2,317
I've been meaning to write this guide for years, here it is. Anything else important to note about pattern scanning?
 
  • Love
  • Like
Reactions: Kage and Nomade

zwaffel

zwaffel
Dank Tier Donator
Nov 1, 2019
7
278
0
I was just looking for info on how to scan for patterns.
Cant come at a better timing :). Thank you!
 

Kage

Dank Tier Donator
Nobleman
Jan 26, 2019
53
2,373
1
redefine :
PEB* GetPEB()
{
    PEB* peb;  <------------------ remove
    #ifdef _WIN64
    PEB* peb = (PEB*)__readgsword(0x60);

    #else
    PEB* peb = (PEB*)__readfsdword(0x30);
    #endif

    return peb;
}
I like the way to walk the module list in the PEB <3.
In the GetPEB() you define peb twic
 
  • Like
Reactions: Rake

BDKPlayer

No hack no life
Dank Tier VIP
Dank Tier Donator
Oct 31, 2013
342
10,363
31
Good stuff. Id also mention sigmaker plugin for OllyDbg and IDA.
 

Nomade

Newbie
Meme Tier VIP
Aug 23, 2017
139
3,948
14
I was bored and decided to try and find bugs on other people's code, here's what I found:

Issues in ScanBasic()
- The size of the positive part of the variable i is 2147483647, which is less than the maximum size of a page. Solution: use size_t.

Issues in ScanInternal()
- If the pattern string in memory is scanned, it's always gonna be a match. Solution: Check if match != pattern when returning.
- Scanning pages with PAGE_GUARD protection raises exception, if not handled it crashes. Solution: Don't scan those pages or use VirtualProtect().
 
Last edited:

mambda

headass
Escobar Tier VIP
Trump Tier Donator
Jun 25, 2014
2,279
37,938
268
I was bored and decided to try and find bugs on other people's code, here's what I found:

Issues in ScanBasic()
- The size of the positive part of the variable i is 2147483647, which is less than the maximum size of a page. Solution: use size_t.

Issues in ScanInternal()
- If the pattern string in memory is scanned, it's always gonna be a match. Solution: Check if match != pattern when returning.
- Scanning pages with PAGE_GUARD protection raises exception, if not handled it crashes. Solution: Don't scan those pages or use VirtualProtect().
a page is size 0x1000 bytes, so using a signed integer lets you search through 524,287 identical pages (of which youd never find in practice)

as for scan internal:
if you're scanning the entirety of process memory, yes thats possible, but since you point it at a starting address and a given size (unless that somehow runs into your module), this would also never happen in practice

guard page stuff sure, dont need to vprotect, can just wrap in try/except, whatever your preference is
 
  • Like
Reactions: Nomade

Rake

Cesspool Admin
Administrator
Jan 21, 2014
11,574
78,998
2,317
I appreciate you looking at this code, I want the code in the main guides to be the best they can be while still being approachable to beginners, so your input is valuable.

size_t
I stopped using unsigned ints based on some videos and articles I have read

https://www.learncpp.com/cpp-tutorial/unsigned-integers-and-why-to-avoid-them/
https://wesmckinney.com/blog/avoid-unsigned-integers/

"In particular, do not use unsigned types to say a number will never be negative. Instead, use assertions for this." -
Google C++ Style Guide

There are also several stack overflow posts debating this topic with most arguments in favor of avoiding unsigned ints

Diagnosing integer problems is easier with signed integers because checking for negative values is easy, diagnosing an overflow is more difficult to catch if it's never negative.

2147483647 is > 2GB, in practice is a page ever > 2GB in size? I don't think so but I never really checked

Issues in ScanInternal()
- If the pattern string in memory is scanned, it's always gonna be a match. Solution: Check if match != pattern when returning.
In the context of this guide I want people to scan modules not the entire memory so this wouldn't happen. My personal framework takes this issue into account in the non-mdule based scan functions. I purposely left out all my other functions for this reason

Issues in ScanInternal()
- Scanning pages with PAGE_GUARD protection raises exception, if not handled it crashes. Solution: Don't scan those pages or use VirtualProtect().
I have never encountered a PAGE_GUARD page in my experience, is it common? In what context is it used?

I stopped using VirtualProtect() in my example because I wanted it to be easier to understand for beginners and after testing it was never required.

You bring up some important points, so thank you, it's good to have this information available in this thread for reference.

Just to explain a the difference between me and everyone else:

Everything I do here is for one singular purpose: To make game hacking as beginner friendly as humanly possible. All my code is written with the #1 priority being readability and ease of use. For everyone else here, you all want to make dank hacks and dank code. I modify everything I release to make it easy for beginners to use without over complicated everything. Every line of code I ever posted is for consumption by beginners.

If people are manual mapping and bypassing anticheat which requires very specific considerations they should be writing all this code themselves because my code is not for those people.

Based on my current understanding I think this code is still fine and doesn't require modification for it's intended purpose. If I am wrong let me know
 
Last edited:
  • Like
Reactions: Kekz and Nomade

Rake

Cesspool Admin
Administrator
Jan 21, 2014
11,574
78,998
2,317
I'm gonna add a link to your comments to the main post
 
  • Love
Reactions: Nomade

Rake

Cesspool Admin
Administrator
Jan 21, 2014
11,574
78,998
2,317
and I'm actually going to make a new pattern scanning video using this code, so if there are any other issues or considerations let me know so I can include them in the video
 
  • Like
Reactions: Kekz and Nomade

Nomade

Newbie
Meme Tier VIP
Aug 23, 2017
139
3,948
14
a page is size 0x1000 bytes, so using a signed integer lets you search through 524,287 identical pages (of which youd never find in practice)
Sorry, I meant to say memory region of pages.

I made a practical example:
The size of the positive part of the variable i is 2147483648(0x80000000), so if a memory region had the size 0x80001000(one more page), the i variable would overflow to the negative side, which makes the whole page never be completely scanned, and it would probably make the function read invalid memory regions, also if that doesn't happen it'd be stuck in an infinite loop.

Allocating memory and copying pattern.
1579810713316.png


Screenshot of i just before overflow to negative side.(line 9 is there just so I can easily break before the event)
1579810841035.png


Screenshot of i just after overflow to negative side.(I changed from hex to decimal view so it's easier to see).
1579810948462.png


Screenshot showing a crash because now i is negative, so we're doing 'begin+ (-2147487744)' which is an invalid memory region.
1579810997630.png


size_t
I stopped using unsigned ints based on some videos and articles I have read

https://wesmckinney.com/blog/avoid-unsigned-integers/
According to my demo above, I believe it's safe to say that this is a situation that definitely requires the use of size_t. Sure, it's not really common for a module to have that size...

if you're scanning the entirety of process memory, yes thats possible, but since you point it at a starting address and a given size (unless that somehow runs into your module), this would also never happen in practice
Yeah, fair point. As long as you don't scan your .dll module or the entire process it's not gonna be a problem.

I have never encountered a PAGE_GUARD page in my experience, is it common? In what context is it used?
TBH I only know of its existence because of the MSDN documentation, so I added it to my PatternScan() implementation.
Here's a page creating one/explaining its uses.
https://docs.microsoft.com/en-us/windows/win32/memory/creating-guard-pages

guard page stuff sure, dont need to vprotect, can just wrap in try/except, whatever your preference is
I stopped using VirtualProtect() in my example because I wanted it to be easier to understand for beginners and after testing it was never required.
I said VirtualProtect() as a possibility, but I wouldn't recommend using it. It's easier just to add PAGE_GUARD to the check like this:
C++:
if (!VirtualQuery(curr, &mbi, sizeof(mbi)) || mbi.State != MEM_COMMIT || mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD) continue;
Because I brought VirtualProtect() to the discussion I'd also like to mention that there's the possiblity that a process is storing useful bytes in commited memory regions with PAGE_NOACCESS or PAGE_GUARD protection, so VirtualProtect() would apply here. That'd be quite inefficient because the process would have to call VirtualProtect() every time it interacts with the memory region, so this is a possible but never gonna happen situation which is why I don't use VirtualProtect() on my implementation.
 

Nomade

Newbie
Meme Tier VIP
Aug 23, 2017
139
3,948
14
and I'm actually going to make a new pattern scanning video using this code, so if there are any other issues or considerations let me know so I can include them in the video
Yes, it's not really an issue, but something that doesn't make any difference(maybe that's considered an issue idk).

On ScanEx()
  • The following piece of code(one lines 8-10) makes no difference.
C++:
mbi.RegionSize = 0x1000;//

VirtualQueryEx(hProc, (LPCVOID)begin, &mbi, sizeof(mbi));
  • There's no reason to use VirtualProtect() at all because ReadProcessMemory() fails if it can't read the memory region(and literally all memory protection constants except for PAGE_NOACCESS / PAGE_GUARD are readable).
  • The following check(line 15) is useless because of the same reason as VirtualProtect().
    C++:
    if (mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS) continue;
 

XtremeCoder

Newbie
Full Member
Jan 21, 2016
32
309
1
and I'm actually going to make a new pattern scanning video using this code, so if there are any other issues or considerations let me know so I can include them in the video
did you actually made one ? kinda lost with all those threads
 

XtremeCoder

Newbie
Full Member
Jan 21, 2016
32
309
1
Hello @Rake
wouldn't be useful to use SSE & AVX Registers ? to make specific scanning method for each CPU
Coding Games and Programming Challenges to Code Better


C:
static struct support_instructions t;


__attribute__((constructor))
static void support_instructions_init()
{
    unsigned long __eax, __ebx, __ecx, __edx;

    __cpuid(0x00000000, __eax, __ebx, __ecx, __edx);
    const long nIds = __eax;

    if (nIds >= 0x00000001) {
        __cpuid(0x00000001, __eax, __ebx, __ecx, __edx);
        t.MMX = (__edx & bit_MMX) != 0;
        t.SSE = (__edx & bit_SSE) != 0;
        t.SSE2 = (__edx & bit_SSE2) != 0;
        t.SSE3 = (__ecx & bit_SSE3) != 0;
        t.SSSE3 = (__ecx & bit_SSSE3) != 0;
        t.SSE41 = (__ecx & (1 << 19)) != 0;
        t.SSE42 = (__ecx & (1 << 20)) != 0;
        t.OSXSAVE = (__ecx & bit_OSXSAVE) != 0;
        t.AVX = (__ecx & bit_AVX) != 0;
    }

    if (nIds >= 0x00000007) {
        __cpuid_count(0x00000007, 0, __eax, __ebx, __ecx, __edx);
        t.AVX2 = (__ebx & bit_AVX2) != 0;
    }

    if (!(t.OSXSAVE && t.AVX && (_xgetbv(0) & 0x6) != 0))
        t.AVX = t.AVX2 = 0;
}

C:
static ADDRESS (*scanner_signatures_ptr) (ADDRESS, ADDRESS, const void*, const struct Signature*);
static void scanner_signatures_ptr_init()
{
    const struct support_instructions *inst = get_support_instructions();

    if (inst->AVX2) {
        scanner_signatures_ptr = scanner_signatures_avx_ymm;
    } else if (inst->AVX) {
        scanner_signatures_ptr = scanner_signatures_avx_xmm;
    } else if (inst->SSE2) {
        scanner_signatures_ptr = scanner_signatures_sse2;
    } else if (inst->SSE) {
        scanner_signatures_ptr = scanner_signatures_sse;
    } else {
        scanner_signatures_ptr = scanner_signatures_primitive;
    }
}
btw this was build on GCC
 

Attachments

  • Love
Reactions: Nomade

Rake

Cesspool Admin
Administrator
Jan 21, 2014
11,574
78,998
2,317
Hello @Rake
wouldn't be useful to use SSE & AVX Registers ? to make specific scanning method for each CPU
The purpose of everything I do is to help beginners learn game hacking as easy as possible. My code is always written in an easy to read way for this reason. I avoid over complicating things for the benefit of people trying to learn.

and no I did not make a new pattern scanning video
 
  • Like
Reactions: Nomade

KF1337

Dank Tier Donator
Full Member
Nobleman
Jan 30, 2020
85
2,773
0
I did something:
sigscan.h:
#pragma once
#include <string>
#include <vector>
#include <Windows.h>
#include <Psapi.h>

/*
- simple class that exposes the Find() function to search for patterns in memory (internal usage)
- helper functions are declared private
- resources used:
    - https://guidedhacking.com/threads/external-internal-pattern-scanning-guide.14112/
    - https://github.com/guided-hacking/GH-Offset-Dumper
    - https://github.com/frk1/hazedumper-rs/blob/master/config.json
*/

struct signature
{
    const std::string pattern;
    const std::wstring modname;
    const std::vector<int> offsets;
    const int extra;
};

class SigScan
{
public:
    static BYTE* Find(signature sig);

private:
    SigScan() = delete;
    ~SigScan() = delete;

    static std::vector<BYTE> Parse(const char* sig, size_t len);
    static BYTE* Compare(std::vector<BYTE> pattern, BYTE* start, size_t len);
    static void ResolveOffsets(BYTE* &addr, std::vector<int> offsets);
    static MODULEINFO GetModule(const wchar_t* mod);
};
sigscan.cpp:
#include "PatternScan.h"

BYTE* SigScan::Find(signature sig)
{
    //Init variables
    std::vector<BYTE> pattern = SigScan::Parse(sig.pattern.c_str(), sig.pattern.size());
    MODULEINFO modinfo = SigScan::GetModule(sig.modname.c_str());
    MEMORY_BASIC_INFORMATION mbi{};
    BYTE* match{ nullptr };

    //Cycle through module
    for (BYTE* addr = (BYTE*)modinfo.lpBaseOfDll; addr < ((BYTE*)modinfo.lpBaseOfDll + modinfo.SizeOfImage); addr += mbi.RegionSize)
    {
        if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
            continue;
        if (mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS)
            continue;

        match = SigScan::Compare(pattern, addr, mbi.RegionSize);

        if (match != nullptr)
        {
            SigScan::ResolveOffsets(match, sig.offsets);

            if (sig.extra)
                match += sig.extra;

            return match;
        }

    }

    return nullptr;
}

std::vector<BYTE> SigScan::Parse(const char* sig, size_t len)
{
    std::vector<BYTE> result;

    for (unsigned short i{ 0 }; i < len - 2; ++i)
    {
        if (sig[i] == '?')
            result.push_back('?');
        if (sig[i] != '?' && sig[i] != ' ' && sig[i + 1] != '?' && sig[i + 1] != ' ')
            result.push_back((BYTE)strtol(&sig[i], 0, 16));
    }

    return result;
}

BYTE* SigScan::Compare(std::vector<BYTE> pattern, BYTE* regionStart, size_t len)
{
    bool found{ true };

    for (unsigned int i{ 0 }; i < len; i++)
    {
        found = true;

        for (unsigned int j{ 0 }; j < pattern.size(); j++)
        {
            if (pattern[j] == '?')
                continue;

            if (pattern[j] != *(BYTE*)((uintptr_t)regionStart + i + j))
            {
                found = false;
                break;
            }
        }
        if (found)
            return (regionStart + i);
    }

    return nullptr;
}

void SigScan::ResolveOffsets(BYTE* &addr, std::vector<int> offsets)
{
    uintptr_t result{ (uintptr_t)addr };
    if (offsets.size())
    {
        for (int off : offsets)
        {
            result += off;
            result = *(uintptr_t*)result;
        }

        addr = (BYTE*)result;
    }

    return;
}

MODULEINFO SigScan::GetModule(const wchar_t* mod)
{
    MODULEINFO modinfo;
    GetModuleInformation(GetCurrentProcess(), GetModuleHandle(mod), &modinfo, sizeof(modinfo));

    return modinfo;
}
This uses a similar mechanism as offset dumpers, just populate the signature struct and invoke SigScan::Find()
I wanted this to be easy to use, targeted for internal cheats.
 
Attention! Before you post:

Read the How to Ask Questions Guide
99% of questions are answered in the Beginner's Guide, do it before asking a question.

No Hack Requests. Post in the correct section.  Search the forum first. Read the rules.

How to make a good post:

  • Fill out the form correctly
  • Tell us the game name & coding language
  • Post everything we need to know to help you
  • Ask specific questions, be descriptive
  • Post errors, line numbers & screenshots
  • Post code snippets using code tags
  • If it's a large project, zip it up and attach it

If you do not comply, your post may be deleted.  We want to help, please make a good post and we will do our best to help you.

Community Mods League of Legends Accounts