├── 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); --------------------------------------------------------------------------------