├── PatternScanExTutorial.vcxproj
├── PatternScanExTutorial.vcxproj.filters
├── README.md
├── main.cpp
├── memhack.cpp
├── memhack.h
├── patternscan.h
├── patterscan.cpp
├── processtools.cpp
└── processtools.h
/PatternScanExTutorial.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {B3A6C404-C29A-4672-9339-FBFA5FB123A0}
23 | Win32Proj
24 | PatternScanExTutorial
25 | 8.1
26 |
27 |
28 |
29 | Application
30 | true
31 | v140
32 | Unicode
33 |
34 |
35 | Application
36 | false
37 | v140
38 | true
39 | Unicode
40 |
41 |
42 | Application
43 | true
44 | v140
45 | Unicode
46 |
47 |
48 | Application
49 | false
50 | v140
51 | true
52 | Unicode
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | true
74 |
75 |
76 | true
77 |
78 |
79 | false
80 |
81 |
82 | false
83 |
84 |
85 |
86 |
87 |
88 | Level3
89 | Disabled
90 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
91 | true
92 |
93 |
94 | Console
95 | true
96 |
97 |
98 |
99 |
100 |
101 |
102 | Level3
103 | Disabled
104 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
105 | true
106 |
107 |
108 | Console
109 | true
110 |
111 |
112 |
113 |
114 | Level3
115 |
116 |
117 | MaxSpeed
118 | true
119 | true
120 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
121 | true
122 |
123 |
124 | Console
125 | true
126 | true
127 | true
128 |
129 |
130 |
131 |
132 | Level3
133 |
134 |
135 | MaxSpeed
136 | true
137 | true
138 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
139 | true
140 |
141 |
142 | Console
143 | true
144 | true
145 | true
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/PatternScanExTutorial.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 | Source Files
23 |
24 |
25 | Source Files
26 |
27 |
28 | Source Files
29 |
30 |
31 |
32 |
33 | Header Files
34 |
35 |
36 | Header Files
37 |
38 |
39 | Header Files
40 |
41 |
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # C++ Extrenal Pattern Scanning
2 | This repository is dedicated to a C++ based implementation of Pattern Scanning, often used for creating external hacks in various applications. Let's discuss the basics of these concepts below.
3 |
4 | # What is Pattern Scanning?
5 | Pattern scanning, also known as signature scanning, is a technique used to find or 'scan' a sequence of bytes that forms a certain 'pattern' within a process's memory. This technique is widely used in software engineering, game hacking, reverse engineering and more. The sequence of bytes, or 'signature', can refer to anything from a hard-coded string, a set of instructions, or a piece of data within a program.
6 |
7 | # What is an External Hack?
8 | An external hack is a type of software that influences another software or game externally, i.e., it does not inject or modify the code of the target process directly. Instead, it reads and writes the memory of another process. This is often used in video game cheating tools, to manipulate the state of a game without being detected by anti-cheat systems.
9 |
10 | # Project Overview
11 | This project provides a practical example of a C++ pattern scanning implementation that can be used for creating external hacks. The code has been designed to be simple, efficient, and easily understandable.
12 | My first github project, my mum would be so proud.
13 |
14 | # Development History
15 | This was originally created for a Youtube tutorial for Guided Hacking
16 |
17 | Our original [internal pattern scanning tutorial](https://guidedhacking.com/threads/c-signature-scan-pattern-scanning-tutorial.3981/) was made by Fleep and but it's pretty poorly made.
18 |
19 | So we decided to make an updated video tutorial showing [external pattern scanning](https://guidedhacking.com/threads/c-external-signature-scanning-pattern-scanning-tutorial.8255/) as well.
20 |
21 | There ended up being a few bugs in the source code so I posted this repo to GitHub so it's easier for people to get the updated version.
22 |
23 | The code is for Part 2 of this Youtube Tutorial and contains code intended for teaching beginners.
24 | I also include code to showcase tips and tricks I have to share that may not relate to the core purpose of this project.
25 |
26 | ### More Signature Scanning Tutorials
27 | - [External & Internal Pattern Scanning Guide](https://guidedhacking.com/threads/external-internal-pattern-scanning-guide.14112/)
28 | - [How to Keep Cheats Updated with Signature Scanning](https://guidedhacking.com/threads/how-to-keep-cheats-updated-with-signature-scanning.19951/)
29 | - [Universal Pattern / Signature Parser](https://guidedhacking.com/threads/universal-pattern-signature-parser.9588/)
30 | - [C++ External Pattern Scanning Tutorial](https://guidedhacking.com/threads/c-external-signature-scanning-pattern-scanning-tutorial.8255/)
31 | - [FASM x86 Signature Scanning](https://guidedhacking.com/threads/fasm-x86-signature-scanning-noping-full-source.8896/)
32 | - [External AoB Pattern Scan](https://guidedhacking.com/threads/external-aob-pattern-scan.9469/)
33 | - [C# Pattern Scanning Library](https://guidedhacking.com/threads/c-memory-editing-library.11707/)
34 | - [Python Pattern Scanning](https://guidedhacking.com/threads/lightning-fast-pattern-scanning-in-python-as-fast-as-ce.16176/)
35 |
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "process.h"
3 | #include "patternscan.h"
4 | #include "memhack.h"
5 |
6 | //String Conversion Snippet for the sake of knowledge
7 | void StringConversions()
8 | {
9 | char* c_string = "ac_client.exe";
10 | wchar_t* wc_converted = TO_WCHAR_T(c_string);
11 | delete wc_converted;
12 |
13 | wchar_t * wc_string = L"ac_client.exe";
14 | char * c_converted = TO_CHAR(wc_string);
15 | delete c_converted;
16 |
17 | }
18 |
19 | int main()
20 | {
21 | //String Conversion Snippet
22 | StringConversions();
23 |
24 | //Get procEntry
25 | Process ac_clientProc = Process(TEXT("ac_client.exe"));
26 |
27 | //Get handle by OpenProcess
28 | ac_clientProc.Attach();
29 |
30 | Module ac_clientMod = Module(&ac_clientProc, TEXT("ac_client.exe"));
31 |
32 | if (ac_clientMod.bValid)
33 | {
34 | //PatternScan for pattern in ac_client.exe module of ac_client.exe process
35 | char* healthDecAddress = Pattern::Ex::Mod("\x29\x7b\x00\x8b\xc7", "xx?xx", &ac_clientMod);
36 |
37 | //Scan all modules in process
38 | healthDecAddress = Pattern::Ex::AllMods("\x29\x7b\x00\x8b\xc7", "xx?xx", &ac_clientProc);
39 |
40 | //Scan module using combo pattern
41 | healthDecAddress = Pattern::Ex::Mod("29 7b ?? 8b c7", &ac_clientMod);
42 |
43 | //Scan all modules using combo pattern
44 | healthDecAddress = Pattern::Ex::AllMods("29 7b ?? 8b c7", &ac_clientProc);
45 |
46 | //Nop the instructions
47 | NopEx(healthDecAddress, 5, ac_clientProc.handle);
48 | }
49 |
50 | return 0;
51 | }
--------------------------------------------------------------------------------
/memhack.cpp:
--------------------------------------------------------------------------------
1 | #include "memhack.h"
2 |
3 | //internal patch
4 | void Patch(char* dst, char* src, unsigned int size)
5 | {
6 | DWORD oldprotect;
7 | VirtualProtect(dst, size, PAGE_READWRITE, &oldprotect);
8 | memcpy(dst, src, size);
9 | VirtualProtect(dst, size, oldprotect, &oldprotect);
10 | }
11 |
12 | //external
13 | void PatchEx(char* dst, char* src, unsigned int size, HANDLE hProcess)
14 | {
15 | DWORD oldprotect;
16 | VirtualProtectEx(hProcess, dst, size, PAGE_READWRITE, &oldprotect);
17 | WriteProcessMemory(hProcess, dst, src, size, NULL);
18 | VirtualProtectEx(hProcess, dst, size, oldprotect, &oldprotect);
19 | }
20 |
21 | //Internal Nop
22 | void Nop(char* dst, unsigned int size)
23 | {
24 | DWORD oldprotect;
25 | VirtualProtect(dst, size, PAGE_READWRITE, &oldprotect);
26 | memset(dst, 0x90, size);
27 | VirtualProtect(dst, size, oldprotect, &oldprotect);
28 | }
29 |
30 |
31 | //External
32 | void NopEx(char* dst, unsigned int size, HANDLE hProcess)
33 | {
34 | byte* nopArray = new byte[size];
35 | memset(nopArray, 0x90, size);
36 |
37 | DWORD oldprotect;
38 | VirtualProtectEx(hProcess, dst, size, PAGE_READWRITE, &oldprotect);
39 | WriteProcessMemory(hProcess, dst, nopArray, size, NULL);
40 | VirtualProtectEx(hProcess, dst, size, oldprotect, &oldprotect);
41 | delete[] nopArray;
42 | }
--------------------------------------------------------------------------------
/memhack.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | //internal patch
5 | void Patch(char* dst, char* src, unsigned int size);
6 |
7 | //external
8 | void PatchEx(char* dst, char* src, unsigned int size, HANDLE hProcess);
9 |
10 | //Internal Nop
11 | void Nop(char* dst, unsigned int size);
12 |
13 | //External
14 | void NopEx(char* dst, unsigned int size, HANDLE hProcess);
--------------------------------------------------------------------------------
/patternscan.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include "processtools.h"
5 |
6 | namespace Pattern
7 | {
8 | //Split combo pattern into mask/pattern
9 | void Parse(char* combo, char* pattern, char* mask);
10 |
11 | namespace In
12 | {
13 | //Internal Pattern Scan
14 | char* Scan(char* pattern, char* mask, char* begin, unsigned int size);
15 |
16 | char* Mod(char *combopattern, Module* module);
17 |
18 | char* AllMods(char* combopattern);
19 |
20 | char* Proc(char* combopattern);
21 | }
22 |
23 | namespace Ex
24 | {
25 | //External Wrapper
26 | char* Scan(char* pattern, char* mask, char* begin, char* end, Process* process);
27 |
28 | //Scan just a module
29 | char* Mod(char* pattern, char* mask, Module* module);
30 | //Overloaded Function for combopattern
31 | char* Mod(char* combopattern, Module* module);
32 |
33 | //Scan all modules from process
34 | char* AllMods(char* pattern, char* mask, Process* process);
35 | //Overloaded Function for combopattern
36 | char* AllMods(char* combopattern, Process* process);
37 |
38 | //Scan entire process
39 | char* Proc(char* combopattern, Process* process);
40 |
41 | }
42 | }
--------------------------------------------------------------------------------
/patterscan.cpp:
--------------------------------------------------------------------------------
1 | #include "patternscan.h"
2 |
3 | //Split combo pattern into mask/pattern
4 | void Pattern::Parse(char* combo, char* pattern, char* mask)
5 | {
6 | unsigned int patternLen = (strlen(combo) + 1) / 3;
7 |
8 | for (unsigned int i = 0; i < strlen(combo); i++)
9 | {
10 | if (combo[i] == ' ')
11 | {
12 | continue;
13 | }
14 |
15 | else if (combo[i] == '?')
16 | {
17 | mask[(i + 1) / 3] = '?';
18 | i += 2;
19 | }
20 |
21 | else
22 | {
23 | char byte = (char)strtol(&combo[i], 0, 16);
24 | pattern[(i + 1) / 3] = byte;
25 | mask[(i + 1) / 3] = 'x';
26 | i += 2;
27 | }
28 | }
29 | pattern[patternLen] = '\0';
30 | mask[patternLen] = '\0';
31 | }
32 |
33 | //Internal Pattern Scan
34 | char* Pattern::In::Scan(char* pattern, char* mask, char* begin, unsigned int size)
35 | {
36 | unsigned int patternLength = strlen(pattern);
37 |
38 | for (unsigned int i = 0; i < size - patternLength; i++)
39 | {
40 | bool found = true;
41 | for (unsigned int j = 0; j < patternLength; j++)
42 | {
43 | if (mask[j] != '?' && pattern[j] !=*(begin + i + j))
44 | {
45 | found = false;
46 | break;
47 | }
48 | }
49 | if (found)
50 | {
51 | return (begin + i);
52 | }
53 | }
54 | return nullptr;
55 | }
56 |
57 | char* Pattern::In::Mod(char *combopattern, Module* module)
58 | {
59 |
60 |
61 | }
62 |
63 | char* Pattern::In::AllMods(char* combopattern)
64 | {
65 |
66 | }
67 |
68 | char* Pattern::In::Proc(char* combopattern)
69 | {
70 |
71 | }
72 |
73 | //External Wrapper
74 | char* Pattern::Ex::Scan(char* pattern, char* mask, char* begin, char* end, Process* process)
75 | {
76 | char* currentChunk = begin;
77 | SIZE_T bytesRead;
78 |
79 | while (currentChunk < end)
80 | {
81 | char buffer[4096]; //char * buffer[4096];?
82 |
83 | DWORD oldprotect;
84 | VirtualProtectEx(process->handle, currentChunk, sizeof(buffer), PROCESS_VM_READ, &oldprotect);
85 | ReadProcessMemory(process->handle, currentChunk, &buffer, sizeof(buffer), &bytesRead);
86 | VirtualProtectEx(process->handle, currentChunk, sizeof(buffer), oldprotect, NULL);
87 |
88 | if (bytesRead == 0)
89 | {
90 | return nullptr;
91 | }
92 |
93 | char* internalAddress = In::Scan(pattern, mask, (char*)&buffer, bytesRead);
94 |
95 | if (internalAddress != nullptr)
96 | {
97 | //calculate from internal to external
98 | uintptr_t offsetFromBuffer = internalAddress - (char*)&buffer;
99 | return (currentChunk + offsetFromBuffer);
100 | }
101 | else
102 | {
103 | //advance to next chunk
104 | currentChunk = currentChunk + bytesRead;
105 | }
106 | }
107 | return nullptr;
108 | }
109 |
110 | //Module wrapper for external pattern scan
111 | char* Pattern::Ex::Mod(char* pattern, char* mask, Module* module)
112 | {
113 | return Scan(pattern, mask, (char*)module->modBaseAddr, (char*)module->modBaseAddr + module->modBaseSize, module->process);
114 | }
115 |
116 | //loops through all modules and scans them
117 | char* Pattern::Ex::AllMods(char* pattern, char* mask, Process* process)
118 | {
119 | MODULEENTRY32 modEntry = { 0 };
120 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, process->th32ProcessID);
121 | if (hSnapshot != INVALID_HANDLE_VALUE)
122 | {
123 | modEntry.dwSize = sizeof(MODULEENTRY32);
124 | if (Module32First(hSnapshot, &modEntry))
125 | {
126 | do
127 | {
128 | char* patternAddress = In::Scan(pattern, mask, (char*)modEntry.modBaseAddr, modEntry.dwSize);
129 | if (patternAddress != nullptr)
130 | {
131 | CloseHandle(hSnapshot);
132 | return patternAddress;
133 | }
134 | } while (Module32Next(hSnapshot, &modEntry));
135 | }
136 | CloseHandle(hSnapshot);
137 | }
138 | return nullptr;
139 | }
140 |
141 | //Combo External Module
142 | char* Pattern::Ex::Mod(char* combopattern, Module* module)
143 | {
144 | unsigned int patternLen = ((strlen(combopattern) + 1) / 3) + 1;
145 | char* pattern = new char[patternLen];
146 | char* mask = new char[patternLen];
147 |
148 | Parse(combopattern, pattern, mask);
149 |
150 | char* match = Pattern::Ex::Mod(pattern, mask, module);
151 |
152 | delete[] pattern;
153 | delete[] mask;
154 | return match;
155 | }
156 |
157 | //Combo External Process
158 | char* Pattern::Ex::AllMods(char* combopattern, Process* process)
159 | {
160 | unsigned int patternLen = ((strlen(combopattern) + 1) / 3) + 1;
161 | char* pattern = new char[patternLen];
162 | char* mask = new char[patternLen];
163 |
164 | Parse(combopattern, pattern, mask);
165 |
166 | char* match = AllMods(pattern, mask, process);
167 |
168 | delete[] pattern;
169 | delete[] mask;
170 | return match;
171 | }
--------------------------------------------------------------------------------
/processtools.cpp:
--------------------------------------------------------------------------------
1 | #include "processtools.h"
2 | #include
3 | #include
4 |
5 | //Convert char* to wchar_t*
6 | wchar_t* TO_WCHAR_T(char* string)
7 | {
8 | unsigned int len = strlen(string) + 1;
9 | wchar_t* wc_string = new wchar_t[len];
10 | unsigned int numCharsRead;
11 | mbstowcs_s(&numCharsRead, wc_string, len, string, _TRUNCATE);
12 | return wc_string;
13 | }
14 |
15 | //Convert wchar_t* to char*
16 | char* TO_CHAR(wchar_t* string)
17 | {
18 | unsigned int len = wcslen(string) + 1;
19 | char* c_string = new char[len];
20 | unsigned int numCharsRead;
21 | wcstombs_s(&numCharsRead, c_string, len, string, _TRUNCATE);
22 | return c_string;
23 | }
24 |
25 | //Get Process by name
26 | bool Process::Get(TCHAR* exeName, PROCESSENTRY32& procEntry)
27 | {
28 | procEntry = { 0 };
29 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
30 |
31 | if (hSnapshot)
32 | {
33 | procEntry.dwSize = sizeof(procEntry);
34 |
35 | if (Process32First(hSnapshot, &procEntry))
36 | {
37 | do
38 | {
39 | if (!_tcscmp(procEntry.szExeFile, exeName))
40 | {
41 | CloseHandle(hSnapshot);
42 | bValid = true;
43 | return true;
44 | }
45 | } while (Process32Next(hSnapshot, &procEntry));
46 | }
47 | }
48 | CloseHandle(hSnapshot);
49 | return false;
50 | }
51 |
52 | Process::Process(TCHAR* exeName)
53 | {
54 | this->name = exeName;
55 | Get(exeName, *this);
56 | }
57 |
58 | bool Process::Attach()
59 | {
60 | handle = OpenProcess(PROCESS_ALL_ACCESS, NULL, th32ProcessID);
61 |
62 | if (handle)
63 | {
64 | return true;
65 | }
66 | else return false;
67 | }
68 |
69 | Module::Module(Process* process, TCHAR* moduleName)
70 | {
71 | this->name = moduleName;
72 | this->process = process;
73 | Get(process, moduleName, *this);
74 | }
75 |
76 | bool Module::Get(Process* process, TCHAR* moduleName, MODULEENTRY32 &modEntry)
77 | {
78 | modEntry = { 0 };
79 |
80 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, process->th32ProcessID);
81 |
82 | if (hSnapshot != INVALID_HANDLE_VALUE)
83 | {
84 | modEntry.dwSize = sizeof(MODULEENTRY32);
85 |
86 | if (Module32First(hSnapshot, &modEntry))
87 | {
88 | do
89 | {
90 | if (!_tcscmp(modEntry.szModule, moduleName))
91 | {
92 | CloseHandle(hSnapshot);
93 | bValid = true;
94 | return true;
95 | }
96 | } while (Module32Next(hSnapshot, &modEntry));
97 | }
98 | CloseHandle(hSnapshot);
99 | }
100 | bValid = false;
101 | return false;
102 | }
--------------------------------------------------------------------------------
/processtools.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 |
6 | class Module;
7 |
8 | class Process : public PROCESSENTRY32
9 | {
10 | public:
11 | TCHAR* name;
12 | HANDLE handle = 0;
13 | bool bValid = false;
14 |
15 | //std::vector* modules;
16 |
17 | Process(TCHAR* exeName);
18 | bool Get(TCHAR* exeName, PROCESSENTRY32 &procEntry);
19 | bool Attach();
20 | };
21 |
22 | class Module : public MODULEENTRY32
23 | {
24 | public:
25 | TCHAR* name;
26 | bool bValid = false;
27 | Process * process;
28 |
29 | Module(Process* process, TCHAR* moduleName);
30 | bool Get(Process* process, TCHAR* moduleName, MODULEENTRY32 &modEntry);
31 | };
32 |
33 |
34 | //String conversion snippets
35 |
36 | //Convert char* to wchar_t*
37 | wchar_t* TO_WCHAR_T(char* string);
38 |
39 | //Convert wchar_t* to char*
40 | char* TO_CHAR(wchar_t* string);
--------------------------------------------------------------------------------