├── .gitignore ├── XLivelessAddon.sln └── XLivelessAddon ├── XLivelessAddon.vcxproj ├── XLivelessAddon.vcxproj.filters ├── dllmain.cpp └── includes ├── IniReader ├── .gitattributes ├── IniReader.h ├── LICENSE.txt ├── README.md └── ini_parser.hpp ├── hooking ├── .gitattributes ├── Hooking.Patterns.cpp ├── Hooking.Patterns.h ├── LICENSE.md └── README.md └── injector ├── LICENSE ├── assembly.hpp ├── calling.hpp ├── gvm ├── gvm.hpp └── translator.hpp ├── hooking.hpp ├── injector.hpp └── utility.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.userosscache 43 | *.sln.docstates 44 | 45 | # User-specific files (MonoDevelop/Xamarin Studio) 46 | *.userprefs 47 | 48 | # Build results 49 | [Dd]ebug/ 50 | [Dd]ebugPublic/ 51 | [Rr]elease/ 52 | [Rr]eleases/ 53 | x64/ 54 | x86/ 55 | bld/ 56 | [Bb]in/ 57 | [Oo]bj/ 58 | [Ll]og/ 59 | 60 | # Visual Studio 2015 cache/options directory 61 | .vs/ 62 | # Uncomment if you have tasks that create the project's static files in wwwroot 63 | #wwwroot/ 64 | 65 | # MSTest test Results 66 | [Tt]est[Rr]esult*/ 67 | [Bb]uild[Ll]og.* 68 | 69 | # NUNIT 70 | *.VisualState.xml 71 | TestResult.xml 72 | 73 | # Build Results of an ATL Project 74 | [Dd]ebugPS/ 75 | [Rr]eleasePS/ 76 | dlldata.c 77 | 78 | # DNX 79 | project.lock.json 80 | artifacts/ 81 | 82 | *_i.c 83 | *_p.c 84 | *_i.h 85 | *.ilk 86 | *.meta 87 | *.obj 88 | *.pch 89 | *.pdb 90 | *.pgc 91 | *.pgd 92 | *.rsp 93 | *.sbr 94 | *.tlb 95 | *.tli 96 | *.tlh 97 | *.tmp 98 | *.tmp_proj 99 | *.log 100 | *.vspscc 101 | *.vssscc 102 | .builds 103 | *.pidb 104 | *.svclog 105 | *.scc 106 | 107 | # Chutzpah Test files 108 | _Chutzpah* 109 | 110 | # Visual C++ cache files 111 | ipch/ 112 | *.aps 113 | *.ncb 114 | *.opendb 115 | *.opensdf 116 | *.sdf 117 | *.cachefile 118 | *.db 119 | 120 | # Visual Studio profiler 121 | *.psess 122 | *.vsp 123 | *.vspx 124 | *.sap 125 | 126 | # TFS 2012 Local Workspace 127 | $tf/ 128 | 129 | # Guidance Automation Toolkit 130 | *.gpState 131 | 132 | # ReSharper is a .NET coding add-in 133 | _ReSharper*/ 134 | *.[Rr]e[Ss]harper 135 | *.DotSettings.user 136 | 137 | # JustCode is a .NET coding add-in 138 | .JustCode 139 | 140 | # TeamCity is a build add-in 141 | _TeamCity* 142 | 143 | # DotCover is a Code Coverage Tool 144 | *.dotCover 145 | 146 | # NCrunch 147 | _NCrunch_* 148 | .*crunch*.local.xml 149 | nCrunchTemp_* 150 | 151 | # MightyMoose 152 | *.mm.* 153 | AutoTest.Net/ 154 | 155 | # Web workbench (sass) 156 | .sass-cache/ 157 | 158 | # Installshield output folder 159 | [Ee]xpress/ 160 | 161 | # DocProject is a documentation generator add-in 162 | DocProject/buildhelp/ 163 | DocProject/Help/*.HxT 164 | DocProject/Help/*.HxC 165 | DocProject/Help/*.hhc 166 | DocProject/Help/*.hhk 167 | DocProject/Help/*.hhp 168 | DocProject/Help/Html2 169 | DocProject/Help/html 170 | 171 | # Click-Once directory 172 | publish/ 173 | 174 | # Publish Web Output 175 | *.[Pp]ublish.xml 176 | *.azurePubxml 177 | # TODO: Comment the next line if you want to checkin your web deploy settings 178 | # but database connection strings (with potential passwords) will be unencrypted 179 | *.pubxml 180 | *.publishproj 181 | 182 | # NuGet Packages 183 | *.nupkg 184 | # The packages folder can be ignored because of Package Restore 185 | **/packages/* 186 | # except build/, which is used as an MSBuild target. 187 | !**/packages/build/ 188 | # Uncomment if necessary however generally it will be regenerated when needed 189 | #!**/packages/repositories.config 190 | # NuGet v3's project.json files produces more ignoreable files 191 | *.nuget.props 192 | *.nuget.targets 193 | 194 | # Microsoft Azure Build Output 195 | csx/ 196 | *.build.csdef 197 | 198 | # Microsoft Azure Emulator 199 | ecf/ 200 | rcf/ 201 | 202 | # Microsoft Azure ApplicationInsights config file 203 | ApplicationInsights.config 204 | 205 | # Windows Store app package directories and files 206 | AppPackages/ 207 | BundleArtifacts/ 208 | Package.StoreAssociation.xml 209 | _pkginfo.txt 210 | 211 | # Visual Studio cache files 212 | # files ending in .cache can be ignored 213 | *.[Cc]ache 214 | # but keep track of directories ending in .cache 215 | !*.[Cc]ache/ 216 | 217 | # Others 218 | ClientBin/ 219 | ~$* 220 | *~ 221 | *.dbmdl 222 | *.dbproj.schemaview 223 | *.pfx 224 | *.publishsettings 225 | node_modules/ 226 | orleans.codegen.cs 227 | 228 | # RIA/Silverlight projects 229 | Generated_Code/ 230 | 231 | # Backup & report files from converting an old project file 232 | # to a newer Visual Studio version. Backup files are not needed, 233 | # because we have git ;-) 234 | _UpgradeReport_Files/ 235 | Backup*/ 236 | UpgradeLog*.XML 237 | UpgradeLog*.htm 238 | 239 | # SQL Server files 240 | *.mdf 241 | *.ldf 242 | 243 | # Business Intelligence projects 244 | *.rdl.data 245 | *.bim.layout 246 | *.bim_*.settings 247 | 248 | # Microsoft Fakes 249 | FakesAssemblies/ 250 | 251 | # GhostDoc plugin setting file 252 | *.GhostDoc.xml 253 | 254 | # Node.js Tools for Visual Studio 255 | .ntvs_analysis.dat 256 | 257 | # Visual Studio 6 build log 258 | *.plg 259 | 260 | # Visual Studio 6 workspace options file 261 | *.opt 262 | 263 | # Visual Studio LightSwitch build output 264 | **/*.HTMLClient/GeneratedArtifacts 265 | **/*.DesktopClient/GeneratedArtifacts 266 | **/*.DesktopClient/ModelManifest.xml 267 | **/*.Server/GeneratedArtifacts 268 | **/*.Server/ModelManifest.xml 269 | _Pvt_Extensions 270 | 271 | # Paket dependency manager 272 | .paket/paket.exe 273 | 274 | # FAKE - F# Make 275 | .fake/ 276 | 277 | # JetBrains Rider 278 | .idea/ 279 | *.sln.iml 280 | 281 | ############# 282 | ## Windows detritus 283 | ############# 284 | 285 | # Windows image file caches 286 | Thumbs.db 287 | ehthumbs.db 288 | 289 | # Folder config file 290 | Desktop.ini 291 | 292 | # Recycle Bin used on file shares 293 | $RECYCLE.BIN/ 294 | 295 | # Mac crap 296 | .DS_Store 297 | 298 | 299 | ############# 300 | ## Python 301 | ############# 302 | 303 | *.py[co] 304 | 305 | # Packages 306 | *.egg 307 | *.egg-info 308 | dist/ 309 | build/ 310 | eggs/ 311 | parts/ 312 | var/ 313 | sdist/ 314 | develop-eggs/ 315 | .installed.cfg 316 | 317 | # Installer logs 318 | pip-log.txt 319 | 320 | # Unit test / coverage reports 321 | .coverage 322 | .tox 323 | 324 | #Translations 325 | *.mo 326 | 327 | #Mr Developer 328 | .mr.developer.cfg 329 | 330 | #Executables and IDA files 331 | *.exe 332 | *.idb -------------------------------------------------------------------------------- /XLivelessAddon.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XLivelessAddon", "XLivelessAddon\XLivelessAddon.vcxproj", "{EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Debug|x64.ActiveCfg = Debug|x64 17 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Debug|x64.Build.0 = Debug|x64 18 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Debug|x86.ActiveCfg = Debug|Win32 19 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Debug|x86.Build.0 = Debug|Win32 20 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Release|x64.ActiveCfg = Release|x64 21 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Release|x64.Build.0 = Release|x64 22 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Release|x86.ActiveCfg = Release|Win32 23 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /XLivelessAddon/XLivelessAddon.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 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3} 23 | Win32Proj 24 | XLivelessAddon 25 | 10.0 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v142 32 | MultiByte 33 | 34 | 35 | DynamicLibrary 36 | false 37 | v142 38 | true 39 | MultiByte 40 | 41 | 42 | DynamicLibrary 43 | true 44 | v142 45 | Unicode 46 | 47 | 48 | DynamicLibrary 49 | false 50 | v142 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 | .asi 75 | $(Configuration)\ 76 | 77 | 78 | true 79 | 80 | 81 | false 82 | .asi 83 | $(Configuration)\ 84 | $(SolutionDir)$(Configuration)\plugins\ 85 | 86 | 87 | false 88 | 89 | 90 | 91 | 92 | 93 | Level3 94 | Disabled 95 | WIN32;_DEBUG;_WINDOWS;_USRDLL;XLIVELESSADDON_EXPORTS;%(PreprocessorDefinitions) 96 | /std:c++latest %(AdditionalOptions) 97 | 98 | 99 | Windows 100 | true 101 | 102 | 103 | call "$(SolutionDir)Debug\copy.bat" 104 | 105 | 106 | 107 | 108 | 109 | 110 | Level3 111 | Disabled 112 | _DEBUG;_WINDOWS;_USRDLL;XLIVELESSADDON_EXPORTS;%(PreprocessorDefinitions) 113 | 114 | 115 | Windows 116 | true 117 | 118 | 119 | 120 | 121 | Level3 122 | 123 | 124 | MaxSpeed 125 | true 126 | true 127 | WIN32;NDEBUG;_WINDOWS;_USRDLL;XLIVELESSADDON_EXPORTS;%(PreprocessorDefinitions) 128 | MultiThreaded 129 | /std:c++latest %(AdditionalOptions) 130 | 131 | 132 | Windows 133 | true 134 | true 135 | true 136 | 137 | 138 | 139 | 140 | Level3 141 | 142 | 143 | MaxSpeed 144 | true 145 | true 146 | NDEBUG;_WINDOWS;_USRDLL;XLIVELESSADDON_EXPORTS;%(PreprocessorDefinitions) 147 | 148 | 149 | Windows 150 | true 151 | true 152 | true 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /XLivelessAddon/XLivelessAddon.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 | -------------------------------------------------------------------------------- /XLivelessAddon/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include ".\includes\IniReader\IniReader.h" 4 | #include ".\includes\injector\injector.hpp" 5 | #include ".\includes\injector\calling.hpp" 6 | #include ".\includes\injector\hooking.hpp" 7 | #include ".\includes\injector\assembly.hpp" 8 | #include ".\includes\injector\utility.hpp" 9 | #include ".\includes\hooking\Hooking.Patterns.h" 10 | 11 | bool bDelay; 12 | char* pszPath; 13 | std::string szCustomSavePath; 14 | std::string szCustomSettingsPath; 15 | 16 | #define WM_ROM (43858) 17 | 18 | const static uint8_t staticData[] = { 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0xF1, 19 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xF0, 0x00, 20 | 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 21 | 0x00, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 22 | 0xF3, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 23 | 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x05, 0x00, 24 | 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 25 | 0x00 26 | }; 27 | 28 | struct dataStruct 29 | { 30 | char a[16]; 31 | int action; 32 | char* act100Addr; 33 | char* act18Addr; 34 | }; 35 | 36 | // change savefile path to "%USERPROFILE%\Documents\Rockstar Games\GTA IV\savegames\" 37 | void getSavefilePath(int __unused, char * pBuffer, char * pszSaveName) 38 | { 39 | if (szCustomSavePath.empty()) 40 | { 41 | strcpy_s(pBuffer, 256, pszPath); 42 | strcat_s(pBuffer, 256, "savegames"); 43 | } 44 | else 45 | { 46 | szCustomSavePath.copy(pBuffer, 256); 47 | pBuffer[szCustomSavePath.length()] = '\0'; 48 | } 49 | 50 | // check path and create directory if necessary 51 | DWORD attrs = GetFileAttributes(pBuffer); 52 | if (attrs == INVALID_FILE_ATTRIBUTES) 53 | CreateDirectory(pBuffer, NULL); 54 | else if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) 55 | { 56 | strcpy_s(pBuffer, 256, pszSaveName); 57 | return; 58 | } 59 | 60 | if (pszSaveName) 61 | { 62 | strcat_s(pBuffer, 256, "\\"); 63 | strcat_s(pBuffer, 256, pszSaveName); 64 | } 65 | } 66 | 67 | HANDLE CreateFileHook(_In_ LPCSTR lpFileName, _In_ DWORD dwDesiredAccess, _In_ DWORD dwShareMode, _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, _In_ DWORD dwCreationDisposition, _In_ DWORD dwFlagsAndAttributes, _In_opt_ HANDLE hTemplateFile) 68 | { 69 | return CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); 70 | } 71 | 72 | BOOL CloseHandleHook(_In_ HANDLE hObject) 73 | { 74 | __try 75 | { 76 | return CloseHandle(hObject); 77 | } 78 | __except (EXCEPTION_CONTINUE_EXECUTION) 79 | {} 80 | 81 | return TRUE; 82 | } 83 | 84 | BOOL GetFileSizeExHook(HANDLE hFile, PLARGE_INTEGER lpFileSize) 85 | { 86 | return GetFileSizeEx(hFile, lpFileSize); 87 | } 88 | 89 | BOOL ReadFileHook(HANDLE hFile, LPVOID pBuffer, DWORD nNumberOfBytesRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) 90 | { 91 | return ReadFile(hFile, pBuffer, nNumberOfBytesRead, lpNumberOfBytesRead, lpOverlapped); 92 | } 93 | 94 | DWORD SetFilePointerHook(HANDLE hFile, LONG dtm, PLONG dtmHigh, DWORD mm) 95 | { 96 | return SetFilePointer(hFile, dtm, dtmHigh, mm); 97 | } 98 | 99 | static LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 100 | { 101 | if (uMsg == WM_ROM) 102 | { 103 | dataStruct* str = (dataStruct*)lParam; 104 | 105 | switch (str->action) 106 | { 107 | case 100: 108 | *(DWORD*)(str->act100Addr) = 1; 109 | break; 110 | case 18: 111 | memcpy(str->act18Addr, staticData, sizeof(staticData)); 112 | break; 113 | case 51: 114 | return (LRESULT)(str->act100Addr + (DWORD)str->act18Addr); 115 | } 116 | 117 | return 0; 118 | } 119 | 120 | return 1; 121 | } 122 | 123 | static HWND CreateROMWindow() 124 | { 125 | WNDCLASSEXW wc; 126 | memset(&wc, 0, sizeof(wc)); 127 | wc.cbSize = sizeof(wc); 128 | 129 | wc.style = 3; 130 | wc.lpfnWndProc = WndProc; 131 | wc.lpszClassName = L"banana"; 132 | wc.hInstance = GetModuleHandle(NULL); 133 | wc.hbrBackground = (HBRUSH)5; 134 | wc.hCursor = LoadCursor(0, MAKEINTRESOURCE(0x7F00)); 135 | 136 | RegisterClassExW(&wc); 137 | 138 | HWND hWnd = CreateWindowExW(0, L"banana", L"", 0xCC00000, 0, 0, 0, 0, 0, 0, GetModuleHandle(NULL), 0); 139 | 140 | return hWnd; 141 | } 142 | 143 | void __stdcall SendMessageFakie(int, int, int, int) 144 | { 145 | 146 | } 147 | 148 | int __cdecl dfaInit(int, int, int) 149 | { 150 | return 1; 151 | } 152 | 153 | injector::hook_back hbsub_7870A0; 154 | void __cdecl sub_7870A0(int a1) 155 | { 156 | static bool bOnce = false; 157 | if (!bOnce) 158 | { 159 | if (a1 == 0) 160 | { 161 | bool bNoLoad = (GetAsyncKeyState(VK_SHIFT) & 0xF000) != 0; 162 | if (!bNoLoad) 163 | a1 = 6; 164 | 165 | bOnce = true; 166 | } 167 | } 168 | return hbsub_7870A0.fun(a1); 169 | } 170 | 171 | injector::hook_back hbGetVidMem; 172 | int32_t GetVidMem() 173 | { 174 | auto v = hbGetVidMem.fun(); 175 | return v <= 0 ? INT_MAX : v; 176 | } 177 | 178 | LRESULT WINAPI DefWindowProcAProxy(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) 179 | { 180 | static bool bOnce = false; 181 | if (!bOnce) 182 | { 183 | SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) & ~WS_OVERLAPPEDWINDOW); 184 | bOnce = true; 185 | } 186 | 187 | return DefWindowProcA(hWnd, Msg, wParam, lParam); 188 | } 189 | 190 | LRESULT WINAPI DefWindowProcWProxy(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) 191 | { 192 | static bool bOnce = false; 193 | if (!bOnce) 194 | { 195 | SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) & ~WS_OVERLAPPEDWINDOW); 196 | bOnce = true; 197 | } 198 | 199 | return DefWindowProcW(hWnd, Msg, wParam, lParam); 200 | } 201 | 202 | HKEY Key; 203 | std::string SubKey; 204 | LSTATUS WINAPI CustomRegOpenKeyA(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) 205 | { 206 | if (strstr(lpSubKey, "Rockstar Games") != NULL) 207 | { 208 | Key = hKey; 209 | SubKey = lpSubKey; 210 | *phkResult = NULL; 211 | return ERROR_SUCCESS; 212 | } 213 | else 214 | return ::RegOpenKeyA(hKey, lpSubKey, phkResult); 215 | } 216 | 217 | LSTATUS WINAPI CustomRegOpenKeyExA(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult) 218 | { 219 | if (strstr(lpSubKey, "Rockstar Games") != NULL) 220 | { 221 | Key = hKey; 222 | SubKey = lpSubKey; 223 | *phkResult = NULL; 224 | return ERROR_SUCCESS; 225 | } 226 | else 227 | return ::RegOpenKeyExA(hKey, lpSubKey, ulOptions, samDesired, phkResult); 228 | } 229 | 230 | LSTATUS WINAPI CustomRegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData) 231 | { 232 | if (hKey == NULL) 233 | { 234 | if (strstr(lpValueName, "InstallFolder") != NULL) 235 | { 236 | char path[MAX_PATH]; 237 | GetModuleFileName(GetModuleHandle(NULL), path, MAX_PATH); 238 | auto ptr = strrchr(path, '\\'); 239 | *(ptr + 1) = '\0'; 240 | auto szPath = std::string_view(path); 241 | 242 | if (lpData != NULL) 243 | { 244 | std::string_view str((char*)lpData, *lpcbData); 245 | *lpcbData = min(szPath.length(), str.length()); 246 | szPath.copy((char*)str.data(), *lpcbData, 0); 247 | lpData[*lpcbData] = '\0'; 248 | } 249 | else 250 | { 251 | *lpcbData = szPath.length(); 252 | } 253 | } 254 | else 255 | { 256 | ::RegOpenKeyExA(Key, SubKey.c_str(), 0, KEY_READ, &hKey); 257 | return ::RegQueryValueExA(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData); 258 | } 259 | return ERROR_SUCCESS; 260 | } 261 | else 262 | return ::RegQueryValueExA(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData); 263 | } 264 | 265 | DWORD WINAPI Init(LPVOID) 266 | { 267 | if(GetConsoleWindow()) 268 | FreeConsole(); 269 | 270 | auto pattern = hook::pattern("B0 04 B2 18 B1 20"); 271 | if (!(pattern.size() > 0) && !bDelay) 272 | { 273 | bDelay = true; 274 | CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&Init, NULL, 0, NULL); 275 | return 0; 276 | } 277 | 278 | if (bDelay) 279 | { 280 | while (!(pattern.size() > 0)) 281 | pattern = hook::pattern("B0 04 B2 18 B1 20"); 282 | } 283 | 284 | CIniReader iniReader(""); 285 | szCustomSavePath = iniReader.ReadString("MAIN", "CustomSavePath", ""); 286 | szCustomSettingsPath = iniReader.ReadString("MAIN", "CustomSettingsPath", ""); 287 | bool bRemoveRegistryPathDependency = iniReader.ReadInteger("MAIN", "RemoveRegistryPathDependency", 1) != 0; 288 | bool bSkipWebConnect = iniReader.ReadInteger("MAIN", "SkipWebConnect", 0) != 0; 289 | bool bSkipIntro = iniReader.ReadInteger("MAIN", "SkipIntro", 0) != 0; 290 | bool bSkipMenu = iniReader.ReadInteger("MAIN", "SkipMenu", 0) != 0; 291 | bool bDoNotPauseOnMinimize = iniReader.ReadInteger("MAIN", "DoNotPauseOnMinimize", 0) != 0; 292 | bool bBorderlessWindowed = iniReader.ReadInteger("MAIN", "BorderlessWindowed", 0) != 0; 293 | bool bVRAMFix = iniReader.ReadInteger("MAIN", "VRAMFix", 1) != 0; 294 | 295 | // Unprotect image - make .text and .rdata section writeable 296 | // get load address of the exe 297 | DWORD dwLoadOffset = (DWORD)GetModuleHandle(NULL); 298 | BYTE * pImageBase = reinterpret_cast(dwLoadOffset); 299 | PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast (dwLoadOffset); 300 | PIMAGE_NT_HEADERS pNtHeader = reinterpret_cast (pImageBase + pDosHeader->e_lfanew); 301 | PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHeader); 302 | 303 | for (int iSection = 0; iSection < pNtHeader->FileHeader.NumberOfSections; ++iSection, ++pSection) 304 | { 305 | char * pszSectionName = reinterpret_cast(pSection->Name); 306 | if (!strcmp(pszSectionName, ".text") || !strcmp(pszSectionName, ".rdata")) 307 | { 308 | DWORD dwPhysSize = (pSection->Misc.VirtualSize + 4095) & ~4095; 309 | DWORD oldProtect; 310 | DWORD newProtect = (pSection->Characteristics & IMAGE_SCN_MEM_EXECUTE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; 311 | if (!VirtualProtect(reinterpret_cast (dwLoadOffset + pSection->VirtualAddress), dwPhysSize, newProtect, &oldProtect)) 312 | { 313 | ExitProcess(0); 314 | } 315 | } 316 | } 317 | 318 | //static bool isEFLC = false; 319 | //pattern = hook::pattern("68 ? ? ? ? E8 ? ? ? ? 8B F0 83 C4 ? 85 F6 0F 84 ? ? ? ? 6A 04"); 320 | //if (pattern.size() > 0) 321 | //{ 322 | // if (strstr(*(char**)pattern.get(0).get(1), "EFLC") != NULL) 323 | // { 324 | // isEFLC = true; 325 | // } 326 | //} 327 | 328 | auto gv = [&dwLoadOffset]() -> uint32_t 329 | { 330 | DWORD signature = *(DWORD*)(0x608C34 + dwLoadOffset - 0x400000); 331 | if (signature == 0xC25DE58B) 332 | return 1000; 333 | else if (signature == 0x831F7518) 334 | return 1010; 335 | else if (signature == 0xC483FFE4) 336 | return 1020; 337 | else if (signature == 0x280F0000) 338 | return 1030; 339 | else if (signature == 0x110FF300) 340 | return 1040; 341 | else if (signature == 0xF3385058) 342 | return 1050; 343 | else if (signature == 0x00A42494) 344 | return 1060; 345 | else if (signature == 0x1006E857) 346 | return 1070; 347 | else if (signature == 0x404B100F) 348 | return 1080; 349 | else if (signature == 0x5C0FF301) 350 | return 1100; 351 | else if (signature == 0x0F14247C) 352 | return 1110; 353 | else if (signature == 0x0D5C0FF3) 354 | return 1120; 355 | else if (signature == 0x04C1F600) 356 | return 1130; 357 | else 358 | return UINT_MAX; 359 | }; 360 | 361 | auto game_version = gv(); 362 | auto gv_not = [&game_version](std::initializer_list v) -> bool 363 | { 364 | if (std::find(std::begin(v), std::end(v), game_version) != std::end(v)) 365 | return false; 366 | 367 | return true; 368 | }; 369 | 370 | // savegames [v1000 - v1008] 371 | { 372 | pattern = hook::pattern("8A 10 83 C0 01 84 D2 75 F7 56 57 BF ? ? ? ? 2B C1 8B F1 83 C7 FF 8A 4F 01 83 C7 01"); 373 | if (pattern.count(3).size() > 0) 374 | { 375 | auto range_end = (uintptr_t)pattern.get(2).get(0); 376 | auto tmp = hook::pattern(range_end - 0x80, range_end, "8A 88 ? ? ? ? 88 88 ? ? ? ? 83 C0 01"); 377 | pszPath = *tmp.get(0).get(2); 378 | 379 | pattern = hook::pattern("75 ? 83 C1 01 83 C0 01 83 F9 04"); 380 | if (pattern.size() > 0) 381 | injector::MakeNOP(pattern.get(0).get(0), 2, true); // NOP - save file CRC32 check 382 | 383 | pattern = hook::pattern("55 8B EC 83 E4 F8 83 EC 4C 56"); 384 | if (pattern.size() > 0) 385 | injector::MakeJMP(pattern.get(0).get(0), getSavefilePath, true); // replace getSavefilePath 386 | } 387 | } 388 | 389 | //settings [v1001-v1008] 390 | if (!szCustomSettingsPath.empty()) 391 | { 392 | pattern = hook::pattern("68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 8D 4C 24 04 E8 ? ? ? ? 5B 83 C4 08 C3"); 393 | if (pattern.size() > 0) 394 | { 395 | static auto buf = *pattern.count(4).get(1).get(1); 396 | static auto unk_1003C10 = *pattern.count(4).get(1).get(6); 397 | struct SettingsPath 398 | { 399 | void operator()(injector::reg_pack& regs) 400 | { 401 | std::string s; 402 | if (szCustomSettingsPath.find(":") == std::string::npos) 403 | { 404 | char path[MAX_PATH]; 405 | GetModuleFileName(GetModuleHandle(NULL), path, MAX_PATH); 406 | auto ptr = strrchr(path, '\\'); 407 | *(ptr + 1) = '\0'; 408 | s += path; 409 | } 410 | 411 | if (s.back() != '\\') 412 | s += "\\"; 413 | 414 | s += szCustomSettingsPath + "\\" + (char*)regs.ebx + "\\"; 415 | 416 | if (GetFileAttributes(s.c_str()) == INVALID_FILE_ATTRIBUTES) 417 | CreateDirectory(s.c_str(), NULL); 418 | 419 | if (GetFileAttributes(s.c_str()) & FILE_ATTRIBUTE_DIRECTORY) 420 | { 421 | s.copy(buf, 256); 422 | buf[s.length()] = '\0'; 423 | } 424 | 425 | regs.ecx = unk_1003C10; 426 | } 427 | }; 428 | injector::MakeInline(pattern.count(4).get(1).get(5)); 429 | injector::MakeInline(pattern.count(4).get(2).get(5)); 430 | } 431 | } 432 | 433 | // process patches 434 | uintptr_t* jmp_efc = nullptr; 435 | pattern = hook::pattern("68 ? ? ? ? 89 35 ? ? ? ? E8 ? ? ? ? 83 C4 04 84 C0 0F 84"); 436 | if (pattern.size() > 0) 437 | jmp_efc = pattern.get(0).get(0); 438 | 439 | // disable sleep [v1002 - v1008] 440 | pattern = hook::pattern("68 88 13 00 00 FF 15"); 441 | if (pattern.size() > 0) 442 | injector::WriteMemory(pattern.get(0).get(1), 1, true); 443 | 444 | // RETN - enable debugger in error menu (don't load WER.dll) [v1000 - v1008] 445 | pattern = hook::pattern("81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? 00 00 53 56 8B 35 ? ? ? ? 68"); 446 | if (pattern.size() > 0) 447 | injector::WriteMemory(pattern.get(0).get(0), 0xC3, true); 448 | 449 | // RETN 8 - certificates check [v1002 - v1008] 450 | pattern = hook::pattern("81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 53 8B 9C 24 ? ? ? ? 55 8B AC ? ? ? ? ? 8B 45 00"); 451 | if (pattern.size() > 0) 452 | injector::MakeRET(pattern.get(0).get(0), 8, true); 453 | 454 | // RETN - skip files.txt hash check [v1002 - v1005] 455 | pattern = hook::pattern("81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 83 3D ? ? ? ? ? 56 57 0F 85 ? ? ? ? 83 3D"); 456 | if (pattern.size() > 0) 457 | injector::MakeRET(pattern.get(0).get(0), 0, true); 458 | 459 | // RETN - skip files.txt hash check [v1000 - v1001] 460 | pattern = hook::pattern("81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 83 3D ? ? ? ? ? 0F 85 ? ? ? ? 83 3D"); 461 | if (pattern.size() > 0) 462 | injector::MakeRET(pattern.get(0).get(0), 0, true); 463 | 464 | // RETN - skip files.txt hash check [v1000 - v1001] 465 | pattern = hook::pattern("81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 56 68 ? ? ? ? 68 ? ? ? ? 68"); 466 | if (pattern.size() > 0) 467 | injector::MakeRET(pattern.get(0).get(0), 0, true); 468 | 469 | // JMP [v1002 - v1004] 470 | pattern = hook::pattern("3B 56 20 74 19"); 471 | if (pattern.size() > 0) 472 | { 473 | injector::WriteMemory(pattern.get(0).get(3), 0xE9, true); 474 | injector::WriteMemory(pattern.get(0).get(4), 0x16, true); 475 | } 476 | 477 | // NOP - another files.txt hash check [v1000 - v1001] 478 | pattern = hook::pattern("74 F7 8B CE"); 479 | if (pattern.size() > 0) 480 | injector::MakeNOP(pattern.get(0).get(0), 2, true); 481 | 482 | // RETN - remove connect to the RGSC [v1000 - v1001] 483 | pattern = hook::pattern("83 EC 68 A1 ? ? ? ? 33 C4 89 44 24 60 53 56 57 8B F1 68 ? ? ? ? 8D 44 24 2C 8D 4C 24 3C 68"); 484 | if (pattern.size() > 0) 485 | injector::MakeRET(pattern.get(0).get(0), 0, true); 486 | 487 | // RETN 4 - remove connect to the RGSC [v1002 - v1005] 488 | pattern = hook::pattern("83 EC 64 A1 ? ? ? ? 33 C4 89 44 24 60 8B 44 24 68 53 56 8B F1 8B 08 3B 4E 0C 57 0F 85"); 489 | if (pattern.size() > 0) 490 | injector::MakeRET(pattern.get(0).get(0), 4, true); 491 | 492 | // data integrity checks VDS102 [v1002 - v1004] 493 | pattern = hook::pattern("C7 06 ? ? ? ? 74 19"); 494 | if (pattern.size() > 0) 495 | injector::WriteMemory(pattern.get(0).get(6), 0xEB, true); //jmp 496 | 497 | // data integrity checks VDS102 [v1005 - v1008] 498 | pattern = hook::pattern("83 C4 ? 3B 46 20"); 499 | if (pattern.size() > 0) 500 | injector::WriteMemory(pattern.get(0).get(12), 0xEB, true); //jmp 501 | 502 | // data integrity checks VDS100 [v1006 - v1008] 503 | pattern = hook::pattern("75 28 6A 00 6A 00 68 ? ? ? ? E8 ? ? ? ? 83 C4 0C"); 504 | if (pattern.size() > 0) 505 | injector::WriteMemory(pattern.get(0).get(0), 0xEB, true); //jmp 506 | 507 | // NOP - RGSC initialization check [v1000 - v1001] 508 | pattern = hook::pattern("74 EC 38 5E 06 74 E7 68"); 509 | if (pattern.size() > 0) 510 | { 511 | injector::MakeNOP(pattern.get(0).get(0), 2, true); 512 | injector::MakeNOP(pattern.get(0).get(5), 2, true); 513 | } 514 | 515 | // NOP - RGSC initialization check [v1002 - v1004] 516 | pattern = hook::pattern("74 C4 8B 07 3B 46 18 75 BD 68"); 517 | if (pattern.size() > 0) 518 | injector::MakeNOP(pattern.get(0).get(0), 2, true); 519 | 520 | // NOP - last RGSC init check [v1002 - v1004] 521 | pattern = hook::pattern("3B 56 18 0F 85"); 522 | if (pattern.size() > 0) 523 | injector::MakeNOP(pattern.get(0).get(3), 6, true); 524 | 525 | // NOP - last RGSC init check [v1005] 526 | pattern = hook::pattern("3B 4E 18 0F 85"); 527 | if (pattern.size() > 0) 528 | injector::MakeNOP(pattern.get(0).get(3), 6, true); 529 | 530 | // NOP - last RGSC init check [v1006 - v1008] 531 | pattern = hook::pattern("0F 85 ? ? ? ? 8B 8C 24 ? ? ? ? 5F 5E 5B 33 CC C6"); 532 | if (pattern.size() > 0) 533 | injector::MakeNOP(pattern.get(0).get(0), 6, true); 534 | 535 | // RGSC initialization check [v1002 - v1004] 536 | pattern = hook::pattern("3B 46 18 75 BD"); 537 | if (pattern.size() > 0) 538 | { 539 | injector::WriteMemory(pattern.get(0).get(3), 0xC033, true); //XOR eax, eax 540 | injector::WriteMemory(pattern.get(0).get(10), 0xA390, true); //NOP; MOV [], eax 541 | } 542 | 543 | // EFC20 [v1003 - v1004] 544 | pattern = hook::pattern("57 51 C7 44 24 ? ? ? ? ? C7 44 24 ? ? ? ? ? C7 44 24"); 545 | if (pattern.size() > 0) 546 | injector::MakeNOP(pattern.get(0).get(1), 0x1BF, true); 547 | 548 | // fix messed sequences 549 | { 550 | pattern = hook::pattern("A1 ? ? ? ? 85 C0 8B 0D ? ? ? ? 75"); 551 | if (pattern.size() > 0) 552 | { 553 | injector::WriteMemory(pattern.get(0).get(0), 0x90C301B0, true); // 0xBAC160 mov al, 1; retn 554 | injector::WriteMemory(pattern.get(1).get(0), 0x90C301B0, true); // 0xBAC190 mov al, 1; retn 555 | } 556 | 557 | pattern = hook::pattern("33 C0 83 3D ? ? ? ? 01 0F 94 C0 C3"); 558 | if (pattern.size() > 0) 559 | { 560 | injector::WriteMemory(pattern.get(pattern.size() - 2).get(0), 0x90C301B0, true); // 0xBAC180 mov al, 1; retn 561 | injector::WriteMemory(pattern.get(pattern.size() - 1).get(0), 0x90C301B0, true); // 0xBAC1C0 mov al, 1; retn 562 | } 563 | } 564 | 565 | // skip RGSC connect and EFC checks [v1005 - v1008] 566 | pattern = hook::pattern("8B 56 1C 3B 56 20 ? ? 6A 00 6A 00"); 567 | if (pattern.size() > 0 && jmp_efc != nullptr) 568 | { 569 | injector::WriteMemory(pattern.get(0).get(0), 0xC033, true); // xor eax, eax - address of the RGSC object 570 | injector::MakeJMP(pattern.get(0).get(2), jmp_efc, true); 571 | } 572 | 573 | // NOP; MOV [g_rgsc], eax [v1000 - v1008] 574 | pattern = hook::pattern("89 35 ? ? ? ? E8 ? ? ? ? 83 C4 04 84 C0"); 575 | if (pattern.size() > 0) 576 | { 577 | if (gv_not({ 1000, 1010 })) 578 | injector::WriteMemory(pattern.get(0).get(0), 0xA390, true); 579 | else 580 | injector::WriteMemory(pattern.get(0).get(1), 0x1D, true); 581 | } 582 | 583 | //skip missing tests [v1005] 584 | { 585 | pattern = hook::pattern("8B 50 2C 3B 50 30"); 586 | if (pattern.size() > 0) 587 | { 588 | injector::WriteMemory(pattern.get(0).get(0), 0xC033, true); 589 | injector::MakeNOP(pattern.get(0).get(2), 4, true); 590 | } 591 | 592 | pattern = hook::pattern("8B 48 2C 83 EC 78"); 593 | if (pattern.size() > 0) 594 | { 595 | injector::MakeNOP(pattern.get(0).get(0), 3, true); 596 | injector::MakeNOP(pattern.get(0).get(6), 11, true); 597 | 598 | } 599 | 600 | pattern = hook::pattern("8B 48 2C 83 EC 74"); 601 | if (pattern.size() > 0) 602 | { 603 | injector::MakeNOP(pattern.get(0).get(0), 3, true); 604 | injector::MakeNOP(pattern.get(0).get(6), 11, true); 605 | 606 | } 607 | 608 | pattern = hook::pattern("8B 48 2C 3B 48 30 74 03"); 609 | if (pattern.size() > 0) 610 | injector::MakeNOP(pattern.get(0).get(0), 11, true); 611 | 612 | pattern = hook::pattern("8B 48 2C 3B 48 30 53"); 613 | if (pattern.size() > 0) 614 | injector::MakeNOP(pattern.get(0).get(0), 32, true); 615 | 616 | pattern = hook::pattern("8B 48 2C 3B 48 30 74 11"); 617 | if (pattern.size() > 0) 618 | injector::MakeNOP(pattern.get(0).get(0), 25, true); 619 | 620 | pattern = hook::pattern("8B 48 2C 3B 48 30 75 DC 8D 54 24 18"); 621 | if (pattern.size() > 0) 622 | injector::MakeNOP(pattern.get(0).get(0), 8, true); 623 | 624 | pattern = hook::pattern("8B 48 2C 3B 48 30 75 DC 8D 54 24 1C"); 625 | if (pattern.size() > 0) 626 | injector::MakeNOP(pattern.get(0).get(0), 8, true); 627 | 628 | pattern = hook::pattern("8B 0D ? ? ? ? 8B 51 2C"); 629 | if (pattern.size() > 0) 630 | injector::MakeNOP(pattern.get(0).get(0), 14, true); 631 | } 632 | 633 | //skip missing tests [v1003 - v1005] 634 | pattern = hook::pattern("51 53 55 56 57 8B 3D ? ? ? ? 8B 47 2C"); 635 | if (pattern.size() > 0) 636 | injector::MakeRET(pattern.get(0).get(0), 0, true); 637 | 638 | //skip missing tests [v1005 - v1008] 639 | pattern = hook::pattern("3B C7 0F 85 ? ? ? ? 85 C0"); 640 | if (pattern.size() > 0) 641 | injector::MakeNOP(pattern.get(0).get(0), 16, true); 642 | 643 | //skip missing tests [v1005 - v1008] 644 | pattern = hook::pattern("0F 85 ? ? ? ? 85 C0 0F 84 ? ? ? ? E8 ? ? ? ? 85 D2"); 645 | if (pattern.size() > 0) 646 | injector::MakeNOP(pattern.get(0).get(0), 14, true); 647 | 648 | // skip missing tests [v1006 - v1007] 649 | pattern = hook::pattern("83 EC 24 A1 ? ? ? ? 8B 48 14 53 55 56 57"); 650 | if (pattern.size() > 0) 651 | injector::WriteMemory(pattern.get(0).get(0), 0x90C3C033, true); 652 | 653 | // skip missing tests [v1006 - v1007] 654 | pattern = hook::pattern("83 EC 08 8B 0D ? ? ? ? 8B 51 14 8D 04 24 50 6A 00 52"); 655 | if (pattern.size() > 0) 656 | injector::WriteMemory(pattern.get(0).get(0), 0x90C3C033, true); 657 | 658 | /* 659 | //is this needed? I'm not sure 660 | //patchEflc1(); 661 | memset((BYTE*)(0x49412C + dwLoadOffset), 0x90, 24); 662 | // >> TEST 663 | *(DWORD*)(0x474FD0 + dwLoadOffset) = 0x90C3C033; // xor eax, eax; retn 664 | *(BYTE*)(0x7CAD20 + dwLoadOffset) = 0xC3; 665 | 666 | //patchEflc2(); 667 | memset((BYTE*)(0x493D4C + dwLoadOffset), 0x90, 24); 668 | */ 669 | 670 | // DLC 671 | // token activation [v1006 - v1007] 672 | pattern = hook::pattern("8B 54 24 04 52 A3"); 673 | if (pattern.size() > 0) 674 | injector::WriteMemory(pattern.get(0).get(0), 0xC9EB, true); 675 | 676 | // token activation [v1006 - v1007] 677 | pattern = hook::pattern("83 F8 01 75 30 A3"); 678 | if (pattern.size() > 0) 679 | { 680 | injector::WriteMemory(pattern.get(0).get(0), 0xB8, true); 681 | injector::WriteMemory(pattern.get(0).get(1), 1, true); 682 | } 683 | 684 | // skip first-time play ask [v1006 - v1008] 685 | pattern = hook::pattern("C7 05 ? ? ? ? 03 00 00 00 33 C0 5E 59 C3 C7"); 686 | if (pattern.size() > 0) 687 | injector::WriteMemory(pattern.get(0).get(0), 0x0DEB, true); 688 | 689 | // DFA (EFLC 1100/1110: Unhandled exception at 0x00426440 in EFLC.exe: 0xC0000005: Access violation reading location 0x00000014.) 690 | if (gv_not({ 1100, 1110 })) 691 | { 692 | //[v1006 - v1008] 693 | pattern = hook::pattern("E8 ? ? ? ? 83 C4 0C 83 F8 01 74 11 6A 00 6A 00"); 694 | if (pattern.size() > 0) 695 | injector::MakeCALL(pattern.get_first(0), dfaInit, true); 696 | 697 | //[v1005 - v1008] 698 | pattern = hook::pattern("55 8B EC 6A 00 E8"); 699 | if (pattern.size() > 0) 700 | injector::MakeJMP(pattern.get(0).get(0), CreateFileHook, true); 701 | 702 | //[v1005 - v1008] 703 | pattern = hook::pattern("6A 00 E8 ? ? ? ? 83 F8 01 59 75 17 FF 15 ? ? ? ? 50 B9 ? ? ? ? E8 ? ? ? ? 8B 40 08 FF 60 0C 33 C0 C3"); 704 | if (pattern.size() > 0) 705 | injector::MakeJMP(pattern.get(0).get(0), CloseHandleHook, true); 706 | 707 | //[v1005 - v1008] 708 | pattern = hook::pattern("6A 00 E8 ? ? ? ? 83 F8 01 59 75 17 FF 15 ? ? ? ? 50 B9 ? ? ? ? E8 ? ? ? ? 8B 40 08 FF 60 ? 83 C8 FF C3"); 709 | if (pattern.size() > 0) 710 | injector::MakeJMP(pattern.get(0).get(0), SetFilePointerHook, true); 711 | 712 | //[v1005 - v1008] 713 | pattern = hook::pattern("56 8B 74 24 0C 8D 46 04 50 FF 74 24 0C E8 ? ? ? ? 59 59 33 C9 83 F8 FF 0F 95 C1 89 06 5E 8B C1 C3"); 714 | if (pattern.size() > 0) 715 | injector::MakeJMP(pattern.get(0).get(0), GetFileSizeExHook, true); 716 | 717 | //[v1005 - v1008] 718 | pattern = hook::pattern("55 8B EC 51 51 53 56 8B C5"); 719 | if (pattern.size() > 0) 720 | injector::MakeJMP(pattern.get(0).get(0), ReadFileHook, true); 721 | 722 | //[v1006 - v1008] 723 | pattern = hook::pattern("A1 ? ? ? ? B9 A7 FA DC 5C F7 E1 8B 0D ? ? ? ? 33 F6"); 724 | if (pattern.size() > 0) 725 | { 726 | injector::MakeJMP(pattern.get(0).get(0), hook::pattern("80 BC 24 ? ? ? ? ? 6A 00").get(0).get(0), true); 727 | injector::MakeJMP(pattern.get(1).get(0), hook::pattern("6A 00 68 80 00 00 00 6A 03 6A 00 6A 01 68 00 00 00 80 57").get(0).get(0), true); 728 | } 729 | } 730 | 731 | // windows message id [v1000 - v1008] 732 | pattern = hook::pattern("A1 ? ? ? ? 8B 0D ? ? ? ? 68 ? ? ? ? 53 50 51 FF 15"); 733 | injector::WriteMemory(*pattern.get_first(1), WM_ROM, true); 734 | injector::WriteMemory(*pattern.get_first(7), CreateROMWindow(), true); // window handle 735 | 736 | // SendMessage call to this window that ends up freezing the audio update thread and resulting in a deadlock [v1000 - v1008] 737 | pattern = hook::pattern("FF 15 ? ? ? ? E8 ? ? ? ? E8 ? ? ? ? E8 ? ? ? ? E8 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 83 7C 24"); 738 | injector::MakeNOP(pattern.get_first(0), 6, true); 739 | injector::MakeCALL(pattern.get_first(0), SendMessageFakie, true); 740 | 741 | if (bSkipWebConnect) 742 | { 743 | // [v1000 - v1001] 744 | pattern = hook::pattern("FF 15 ? ? ? ? 85 C0 75 18"); 745 | if (pattern.size() > 0) 746 | injector::MakeNOP(pattern.get(0).get(8), 2, true); //InternetGetConnectedState 747 | 748 | // [v1002 - v1005] 749 | pattern = hook::pattern("FF 15 ? ? ? ? 85 C0 75 04 32"); 750 | if (pattern.size() > 0) 751 | injector::MakeNOP(pattern.get(0).get(8), 2, true); //InternetGetConnectedState 752 | 753 | // [v1006 - v1008] 754 | pattern = hook::pattern("FF 15 ? ? ? ? 85 C0 75 07 A0"); 755 | if (pattern.size() > 0) 756 | injector::MakeNOP(pattern.get(0).get(8), 2, true); //InternetGetConnectedState 757 | 758 | // [v1003 - v1008] 759 | pattern = hook::pattern("81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 53 ? 6A 3C 33 DB 8D 44 24 ? 53 50 E8"); // health check 760 | if (pattern.size() > 0) 761 | injector::WriteMemory(pattern.get(0).get(0), 0xC3, true); 762 | } 763 | 764 | if (bSkipIntro) 765 | { 766 | //if (isEFLC) 767 | //{ 768 | // pattern = hook::pattern("74 ? 80 3D ? ? ? ? 00 74 ? E8 ? ? ? ? 0F"); 769 | // injector::WriteMemory(pattern.get(0).get(0), 0xEB, true); 770 | //} 771 | //else 772 | { 773 | // [v1000 - v1008] 774 | //since iv hangs with the code above, I'll just zero the duration of loadscreens 775 | pattern = hook::pattern("89 91 ? ? ? ? 8D 44 24 ? 68 ? ? ? ? 50"); 776 | struct Loadsc 777 | { 778 | void operator()(injector::reg_pack& regs) 779 | { 780 | *(int32_t*)®s.ecx *= 400; 781 | if (regs.edx < 8000) 782 | regs.edx = 0; 783 | } 784 | }; injector::MakeInline(pattern.get_first(-6), pattern.get_first(0)); 785 | } 786 | } 787 | 788 | if (bSkipMenu) 789 | { 790 | pattern = hook::pattern("83 F8 03 75 ? A1 ? ? ? ? 80 88 ? ? ? ? ? 84 DB 74 0A 6A 00 E8 ? ? ? ? 83 C4 04 5F 5E"); 791 | if (pattern.size() > 0) 792 | { 793 | // [v1002 - v1008] 794 | hbsub_7870A0.fun = injector::MakeCALL(pattern.get_first(23), sub_7870A0).get(); 795 | } 796 | else 797 | { 798 | // [v1000] 799 | pattern = hook::pattern("6A 00 E8 ? ? ? ? 83 C4 04 8B 8C 24 ? ? ? ? 5F 5E 5D 5B 33 CC E8 ? ? ? ? 81 C4 ? ? ? ? C3"); 800 | if (pattern.size() > 0) 801 | hbsub_7870A0.fun = injector::MakeCALL(pattern.get(0).get(2), sub_7870A0).get(); 802 | else 803 | { 804 | // [v1001] 805 | pattern = hook::pattern("6A 00 E8 ? ? ? ? 83 C4 04 8B 8C 24 ? ? ? ? 5F 5E 5B 33 CC E8 ? ? ? ? 8B E5 5D C3"); 806 | if (pattern.size() > 0) 807 | hbsub_7870A0.fun = injector::MakeCALL(pattern.get(0).get(2), sub_7870A0).get(); 808 | } 809 | } 810 | } 811 | 812 | if (bDoNotPauseOnMinimize) 813 | { 814 | // [v1002 - v1008] 815 | pattern = hook::pattern("75 ? 8B 0D ? ? ? ? 51 FF 15 ? ? ? ? 85 C0 75 ? 8B 15"); //0x402D5A 816 | if (pattern.size() > 0) 817 | injector::MakeNOP(pattern.get(0).get(0), 2, true); 818 | } 819 | 820 | if (bBorderlessWindowed) 821 | { 822 | // [v1000 - v1008] 823 | pattern = hook::pattern("FF 15 ? ? ? ? 5F 5E 5D 5B 83 C4 10 C2 10 00"); 824 | injector::MakeNOP(pattern.count(2).get(0).get(0), 6, true); 825 | injector::MakeCALL(pattern.count(2).get(0).get(0), DefWindowProcWProxy, true); 826 | injector::MakeNOP(pattern.count(2).get(1).get(0), 6, true); 827 | injector::MakeCALL(pattern.count(2).get(1).get(0), DefWindowProcAProxy, true); 828 | } 829 | 830 | if (bRemoveRegistryPathDependency) 831 | { 832 | IMAGE_IMPORT_DESCRIPTOR* pImports = (IMAGE_IMPORT_DESCRIPTOR*)(dwLoadOffset + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); 833 | size_t nNumImports = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1; 834 | 835 | auto pRegOpenKeyA = (size_t)GetProcAddress(GetModuleHandle(TEXT("ADVAPI32.DLL")), "RegOpenKeyA"); 836 | auto pRegQueryValueExA = (size_t)GetProcAddress(GetModuleHandle(TEXT("ADVAPI32.DLL")), "RegQueryValueExA"); 837 | auto pRegOpenKeyExA = (size_t)GetProcAddress(GetModuleHandle(TEXT("ADVAPI32.DLL")), "RegOpenKeyExA"); 838 | 839 | static auto getSection = [](const PIMAGE_NT_HEADERS nt_headers, unsigned section) -> PIMAGE_SECTION_HEADER 840 | { 841 | return reinterpret_cast( 842 | (UCHAR*)nt_headers->OptionalHeader.DataDirectory + 843 | nt_headers->OptionalHeader.NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY) + 844 | section * sizeof(IMAGE_SECTION_HEADER)); 845 | }; 846 | 847 | static auto getSectionEnd = [](IMAGE_NT_HEADERS* ntHeader, size_t inst) -> auto 848 | { 849 | auto sec = getSection(ntHeader, ntHeader->FileHeader.NumberOfSections - 1); 850 | auto secSize = max(sec->SizeOfRawData, sec->Misc.VirtualSize); 851 | auto end = inst + max(sec->PointerToRawData, sec->VirtualAddress) + secSize; 852 | return end; 853 | }; 854 | 855 | auto PatchIAT = [&](size_t start, size_t end, size_t exe_end) 856 | { 857 | for (size_t i = 0; i < nNumImports; i++) 858 | { 859 | if (dwLoadOffset + (pImports + i)->FirstThunk > start && !(end && dwLoadOffset + (pImports + i)->FirstThunk > end)) 860 | end = dwLoadOffset + (pImports + i)->FirstThunk; 861 | } 862 | 863 | if (!end) { end = start + 0x100; } 864 | if (end > exe_end) 865 | { 866 | start = dwLoadOffset; 867 | end = exe_end; 868 | } 869 | 870 | for (auto i = start; i < end; i += sizeof(size_t)) 871 | { 872 | DWORD dwProtect[2]; 873 | VirtualProtect((size_t*)i, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dwProtect[0]); 874 | 875 | auto ptr = *(size_t*)i; 876 | if (!ptr) 877 | continue; 878 | 879 | if (ptr == pRegOpenKeyA) 880 | { 881 | *(size_t*)i = (size_t)CustomRegOpenKeyA; 882 | } 883 | else if (ptr == pRegQueryValueExA) 884 | { 885 | *(size_t*)i = (size_t)CustomRegQueryValueExA; 886 | } 887 | else if (ptr == pRegOpenKeyExA) 888 | { 889 | *(size_t*)i = (size_t)CustomRegOpenKeyExA; 890 | } 891 | 892 | VirtualProtect((size_t*)i, sizeof(size_t), dwProtect[0], &dwProtect[1]); 893 | } 894 | }; 895 | 896 | auto end = getSectionEnd(pNtHeader, dwLoadOffset); 897 | 898 | for (size_t i = 0; i < nNumImports; i++) 899 | { 900 | if ((size_t)(dwLoadOffset + (pImports + i)->Name) < end) 901 | { 902 | if (!_stricmp((const char*)(dwLoadOffset + (pImports + i)->Name), "ADVAPI32.DLL")) 903 | { 904 | PatchIAT(dwLoadOffset + (pImports + i)->FirstThunk, 0, end); 905 | break; 906 | } 907 | } 908 | } 909 | } 910 | 911 | if (bVRAMFix) 912 | { 913 | // workaround for vram detection, so settings won't be locked out [v1000 - v1007] 914 | pattern = hook::pattern("E8 ? ? ? ? D9 E8 85 C0"); 915 | if (pattern.size() > 0) 916 | hbGetVidMem.fun = injector::MakeCALL(pattern.get_first(0), GetVidMem).get(); 917 | 918 | pattern = hook::pattern("E8 ? ? ? ? F3 0F 10 0D ? ? ? ? F3 0F 5C 0D"); 919 | if (pattern.size() > 0) 920 | hbGetVidMem.fun = injector::MakeCALL(pattern.get_first(0), GetVidMem).get(); 921 | 922 | pattern = hook::pattern("E8 ? ? ? ? F3 0F 10 05 ? ? ? ? 89 44 24 08 6A 01 8D 44 24"); 923 | if (pattern.size() > 0) 924 | hbGetVidMem.fun = injector::MakeCALL(pattern.get_first(0), GetVidMem).get(); 925 | } 926 | 927 | return 0; 928 | } 929 | 930 | 931 | BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD reason, LPVOID /*lpReserved*/) 932 | { 933 | if (reason == DLL_PROCESS_ATTACH) 934 | { 935 | Init(NULL); 936 | } 937 | return TRUE; 938 | } 939 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/IniReader/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/IniReader/IniReader.h: -------------------------------------------------------------------------------- 1 | #ifndef INIREADER_H 2 | #define INIREADER_H 3 | #include "ini_parser.hpp" 4 | #include 5 | #include 6 | 7 | /* 8 | * String comparision functions, with case sensitive option 9 | */ 10 | 11 | using std::strcmp; 12 | 13 | inline int strcmp(const char* str1, const char* str2, bool csensitive) 14 | { 15 | return (csensitive ? ::strcmp(str1, str2) : ::_stricmp(str1, str2)); 16 | } 17 | 18 | inline int strcmp(const char* str1, const char* str2, size_t num, bool csensitive) 19 | { 20 | return (csensitive ? ::strncmp(str1, str2, num) : ::_strnicmp(str1, str2, num)); 21 | } 22 | 23 | inline int compare(const std::string& str1, const std::string& str2, bool case_sensitive) 24 | { 25 | if (str1.length() == str2.length()) 26 | return strcmp(str1.c_str(), str2.c_str(), case_sensitive); 27 | return (str1.length() < str2.length() ? -1 : 1); 28 | } 29 | 30 | inline int compare(const std::string& str1, const std::string& str2, size_t num, bool case_sensitive) 31 | { 32 | if (str1.length() == str2.length()) 33 | return strcmp(str1.c_str(), str2.c_str(), num, case_sensitive); 34 | return (str1.length() < str2.length() ? -1 : 1); 35 | } 36 | 37 | inline int compare(const char* str1, const char* str2, bool case_sensitive) 38 | { 39 | return strcmp(str1, str2, case_sensitive); 40 | } 41 | 42 | inline int compare(const char* str1, const char* str2, size_t num, bool case_sensitive) 43 | { 44 | return strcmp(str1, str2, num, case_sensitive); 45 | } 46 | 47 | inline bool starts_with(const char* str, const char* prefix, bool case_sensitive) 48 | { 49 | while (*prefix) 50 | { 51 | bool equal; 52 | if (case_sensitive) 53 | equal = (*str++ == *prefix++); 54 | else 55 | equal = (::tolower(*str++) == ::tolower(*prefix++)); 56 | 57 | if (!equal) return false; 58 | } 59 | return true; 60 | } 61 | 62 | inline bool ends_with(const char* str, const char* prefix, bool case_sensitive) 63 | { 64 | auto str2 = &str[strlen(str) - 1]; 65 | auto prefix2 = &prefix[strlen(prefix) - 1]; 66 | 67 | while (prefix2 >= prefix) 68 | { 69 | bool equal; 70 | if (case_sensitive) 71 | equal = (*str2-- == *prefix2--); 72 | else 73 | equal = (::tolower(*str2--) == ::tolower(*prefix2--)); 74 | 75 | if (!equal) return false; 76 | } 77 | return true; 78 | } 79 | 80 | class CIniReader 81 | { 82 | private: 83 | std::string m_szFileName; 84 | 85 | public: 86 | linb::ini data; 87 | 88 | CIniReader() 89 | { 90 | SetIniPath(""); 91 | } 92 | 93 | CIniReader(char* szFileName) 94 | { 95 | SetIniPath(szFileName); 96 | } 97 | 98 | CIniReader(const char* szFileName) 99 | { 100 | SetIniPath((char*)szFileName); 101 | } 102 | 103 | CIniReader(std::stringstream& ini_mem) 104 | { 105 | data.load_file(ini_mem); 106 | } 107 | 108 | bool operator==(CIniReader& ir) 109 | { 110 | if (data.size() != ir.data.size()) 111 | return false; 112 | 113 | for (auto& section : data) 114 | { 115 | for (auto& key : data[section.first]) 116 | { 117 | if (key.second != ir.data[section.first][key.first]) 118 | return false; 119 | } 120 | } 121 | return true; 122 | } 123 | 124 | bool operator!=(CIniReader& ir) 125 | { 126 | return !(*this == ir); 127 | } 128 | 129 | bool CompareBySections(CIniReader& ir) 130 | { 131 | if (data.size() != ir.data.size()) 132 | return false; 133 | 134 | for (auto& section : data) 135 | { 136 | if (ir.data.find(section.first) == ir.data.end()) 137 | return false; 138 | 139 | if (section.second.size() != ir.data.find(section.first)->second.size()) 140 | return false; 141 | 142 | if (section.first != ir.data.find(section.first)->first) 143 | return false; 144 | } 145 | return true; 146 | } 147 | 148 | bool CompareByValues(CIniReader& ir) 149 | { 150 | return *this == ir; 151 | } 152 | 153 | const std::string& GetIniPath() 154 | { 155 | return m_szFileName; 156 | } 157 | 158 | void SetIniPath() 159 | { 160 | SetIniPath(""); 161 | } 162 | 163 | void SetIniPath(char* szFileName) 164 | { 165 | char buffer[MAX_PATH]; 166 | HMODULE hm = NULL; 167 | GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)&ends_with, &hm); 168 | GetModuleFileNameA(hm, buffer, sizeof(buffer)); 169 | std::string modulePath = buffer; 170 | 171 | if (strchr(szFileName, ':') != NULL) 172 | { 173 | m_szFileName = szFileName; 174 | } 175 | else if (std::string(szFileName).length() == 0) 176 | { 177 | m_szFileName = modulePath.substr(0, modulePath.find_last_of('.')) + ".ini"; 178 | } 179 | else 180 | { 181 | m_szFileName = modulePath.substr(0, modulePath.rfind('\\') + 1) + szFileName; 182 | } 183 | 184 | data.load_file(m_szFileName); 185 | } 186 | 187 | int ReadInteger(char* szSection, char* szKey, int iDefaultValue) 188 | { 189 | try { 190 | auto str = data.get(szSection, szKey, std::to_string(iDefaultValue)); 191 | return std::stoi(str, nullptr, starts_with(str.c_str(), "0x", false) ? 16 : 10); 192 | } 193 | catch (...) { 194 | return iDefaultValue; 195 | } 196 | } 197 | 198 | float ReadFloat(char* szSection, char* szKey, float fltDefaultValue) 199 | { 200 | try { 201 | return (float)atof(data.get(szSection, szKey, std::to_string(fltDefaultValue)).c_str()); 202 | } 203 | catch (...) { 204 | return fltDefaultValue; 205 | } 206 | } 207 | 208 | bool ReadBoolean(char* szSection, char* szKey, bool bolDefaultValue) 209 | { 210 | try { 211 | auto& config = data[szSection]; 212 | if (config.count(szKey)) 213 | { 214 | if (config[szKey].size() == 1) return config[szKey].front() != '0'; 215 | return !!compare(config[szKey], "false", false); 216 | } 217 | } 218 | catch (...) { 219 | return bolDefaultValue; 220 | } 221 | } 222 | 223 | char* ReadString(char* szSection, char* szKey, const char* szDefaultValue) 224 | { 225 | char* szResult = new char[255]; 226 | try { 227 | auto& config = data[szSection]; 228 | if (config.count(szKey)) 229 | { 230 | if (config[szKey].at(0) == '\"' || config[szKey].at(0) == '\'') 231 | config[szKey].erase(0, 1); 232 | 233 | if (config[szKey].at(config[szKey].size() - 1) == '\"' || config[szKey].at(config[szKey].size() - 1) == '\'') 234 | config[szKey].erase(config[szKey].size() - 1); 235 | 236 | strcpy(szResult, config[szKey].c_str()); 237 | return szResult; 238 | } 239 | } 240 | catch (...) { } 241 | strcpy(szResult, szDefaultValue); 242 | return szResult; 243 | } 244 | 245 | std::string ReadString(char* szSection, char* szKey, std::string szDefaultValue) 246 | { 247 | char* str = ReadString(szSection, szKey, szDefaultValue.c_str()); 248 | std::string* szResult = new std::string(str); 249 | return *szResult; 250 | } 251 | 252 | void WriteInteger(char* szSection, char* szKey, int iValue, bool useparser = false) 253 | { 254 | if (useparser) 255 | { 256 | data.set(szSection, szKey, std::to_string(iValue)); 257 | data.write_file(m_szFileName); 258 | } 259 | else 260 | { 261 | char szValue[255]; 262 | _snprintf_s(szValue, 255, "%s%d", " ", iValue); 263 | WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str()); 264 | } 265 | } 266 | 267 | void WriteFloat(char* szSection, char* szKey, float fltValue, bool useparser = false) 268 | { 269 | if (useparser) 270 | { 271 | data.set(szSection, szKey, std::to_string(fltValue)); 272 | data.write_file(m_szFileName); 273 | } 274 | else 275 | { 276 | char szValue[255]; 277 | _snprintf_s(szValue, 255, "%s%f", " ", fltValue); 278 | WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str()); 279 | } 280 | } 281 | 282 | void WriteBoolean(char* szSection, char* szKey, bool bolValue, bool useparser = false) 283 | { 284 | if (useparser) 285 | { 286 | data.set(szSection, szKey, std::to_string(bolValue)); 287 | data.write_file(m_szFileName); 288 | } 289 | else 290 | { 291 | char szValue[255]; 292 | _snprintf_s(szValue, 255, "%s%s", " ", bolValue ? "True" : "False"); 293 | WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str()); 294 | } 295 | } 296 | 297 | void WriteString(char* szSection, char* szKey, char* szValue, bool useparser = false) 298 | { 299 | if (useparser) 300 | { 301 | data.set(szSection, szKey, szValue); 302 | data.write_file(m_szFileName); 303 | } 304 | else 305 | { 306 | WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str()); 307 | } 308 | } 309 | }; 310 | 311 | #endif //INIREADER_H 312 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/IniReader/LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 Denilson das Mercês Amorim 3 | * 4 | * This software is provided 'as-is', without any express or implied 5 | * warranty. In no event will the authors be held liable for any damages 6 | * arising from the use of this software. 7 | * 8 | * Permission is granted to anyone to use this software for any purpose, 9 | * including commercial applications, and to alter it and redistribute it 10 | * freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you must not 13 | * claim that you wrote the original software. If you use this software 14 | * in a product, an acknowledgment in the product documentation would be 15 | * appreciated but is not required. 16 | * 17 | * 2. Altered source versions must be plainly marked as such, and must not be 18 | * misrepresented as being the original software. 19 | * 20 | * 3. This notice may not be removed or altered from any source 21 | * distribution. 22 | * 23 | */ -------------------------------------------------------------------------------- /XLivelessAddon/includes/IniReader/README.md: -------------------------------------------------------------------------------- 1 | IniReader 2 | ---------------- 3 | Sample: 4 | 5 | #include "stdafx.h" 6 | #include "IniReader\IniReader.h" 7 | #include 8 | #include 9 | #include 10 | 11 | int main() 12 | { 13 | CIniReader ini1("ini1.ini"); 14 | ini1.WriteInteger("MAIN", "INTEGER", 33); 15 | ini1.WriteFloat("MAIN", "FLOAT", 35.5f); 16 | ini1.WriteBoolean("MAIN", "BOOL", false); 17 | ini1.WriteString("MAIN", "STRING", "text"); 18 | 19 | CIniReader ini2("ini2.ini"); 20 | ini2.WriteInteger("MAIN", "INTEGER", 33); 21 | ini2.WriteFloat("MAIN", "FLOAT", 35.5f); 22 | ini2.WriteBoolean("MAIN", "BOOL", false); 23 | ini2.WriteString("MAIN", "STRING", "text"); 24 | 25 | std::cout << ini1.ReadInteger("MAIN", "INTEGER", 0) << std::endl; 26 | std::cout << ini2.ReadString("MAIN", "STRING", "") << std::endl; 27 | 28 | if (ini1 == ini2) 29 | std::cout << "ini1 and ini2 are the same." << std::endl; 30 | 31 | std::ifstream file("ini1.ini", std::ios::in); 32 | std::stringstream ini_mem; 33 | ini_mem << file.rdbuf(); 34 | 35 | CIniReader mem_ini(ini_mem); 36 | 37 | mem_ini.WriteInteger("MAIN", "INTEGER", 0); 38 | 39 | if (!mem_ini.CompareByValues(ini2)) 40 | std::cout << "mem_ini and ini2 are different." << std::endl; 41 | 42 | return 0; 43 | } 44 | 45 | Result: 46 | 47 | ![](http://i.imgur.com/LyqVYN7.png) -------------------------------------------------------------------------------- /XLivelessAddon/includes/IniReader/ini_parser.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 Denilson das Mercês Amorim 3 | * Copyright (c) 2017 ThirteenAG 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any damages 7 | * arising from the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software 15 | * in a product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source 22 | * distribution. 23 | * 24 | */ 25 | #ifndef LINB_INI_PARSER_HPP 26 | #define LINB_INI_PARSER_HPP 27 | 28 | /* 29 | * STL-like INI Container 30 | */ 31 | 32 | #include // for std::string 33 | #include // for std::map 34 | #include // for std::FILE 35 | #include // for std::find_if 36 | #include // for std::function 37 | #include // for std::vector 38 | #include 39 | #include 40 | 41 | namespace linb 42 | { 43 | template< 44 | class CharT = char, /* Not compatible with other type here, since we're using C streams */ 45 | class StringType = std::basic_string, 46 | class KeyContainer = std::map, 47 | class SectionContainer = std::map 48 | > class basic_ini 49 | { 50 | public: 51 | typedef CharT char_type; 52 | typedef StringType string_type; 53 | typedef KeyContainer key_container; 54 | typedef SectionContainer section_container; 55 | 56 | // Typedef container values types 57 | typedef typename section_container::value_type value_type; 58 | typedef typename section_container::key_type key_type; 59 | typedef typename section_container::mapped_type mapped_type; 60 | 61 | // Typedef common types 62 | typedef typename section_container::size_type size_type; 63 | typedef typename section_container::difference_type difference_type; 64 | 65 | // Typedef iterators 66 | typedef typename section_container::iterator iterator; 67 | typedef typename section_container::const_iterator const_iterator; 68 | typedef typename section_container::reverse_iterator reverse_iterator; 69 | typedef typename section_container::const_reverse_iterator const_reverse_iterator; 70 | 71 | // typedef References and pointers 72 | typedef typename section_container::reference reference; 73 | typedef typename section_container::const_reference const_reference; 74 | typedef typename section_container::pointer pointer; 75 | typedef typename section_container::const_pointer const_pointer; 76 | 77 | private: 78 | section_container data; 79 | 80 | public: 81 | 82 | basic_ini() 83 | { } 84 | 85 | basic_ini(const char_type* filename) 86 | { this->read_file(filename); } 87 | 88 | /* Iterator methods */ 89 | iterator begin() 90 | { return data.begin(); } 91 | const_iterator begin() const 92 | { return data.begin(); } 93 | iterator end() 94 | { return data.end(); } 95 | const_iterator end() const 96 | { return data.end(); } 97 | const_iterator cbegin() const 98 | { return data.cbegin(); } 99 | const_iterator cend() const 100 | { return data.cend(); } 101 | 102 | /* Reverse iterator methods */ 103 | reverse_iterator rbegin() 104 | { return data.rbegin(); } 105 | const_reverse_iterator rbegin() const 106 | { return data.rbegin(); } 107 | reverse_iterator rend() 108 | { return data.rend(); } 109 | const_reverse_iterator rend() const 110 | { return data.rend(); } 111 | const_reverse_iterator crbegin() const 112 | { return data.crbegin(); } 113 | const_reverse_iterator crend() const 114 | { return data.crend(); } 115 | 116 | /* Acessing index methods */ 117 | mapped_type& operator[](const string_type& sect) 118 | { return data[sect]; } 119 | mapped_type& operator[](string_type&& sect) 120 | { return data[std::forward(sect)]; } 121 | mapped_type& at( const string_type& sect) 122 | { return data.at(sect); } 123 | const mapped_type& at(const string_type& sect) const 124 | { return data.at(sect); } 125 | 126 | /* Capacity information */ 127 | bool empty() const 128 | { return data.empty(); } 129 | size_type size() const 130 | { return data.size(); } 131 | size_type max_size() const 132 | { return data.max_size(); } 133 | 134 | /* Modifiers */ 135 | void clear() 136 | { return data.clear(); } 137 | 138 | /* Lookup */ 139 | size_type count(const string_type& sect) 140 | { return data.count(sect); } 141 | iterator find(const string_type& sect) 142 | { return data.find(sect); } 143 | 144 | /* Gets a value from the specified section & key, default_value is returned if the sect & key doesn't exist */ 145 | string_type get(const string_type& sect, const key_type& key, const string_type& default_value) 146 | { 147 | auto it = this->find(sect); 148 | if(it != this->end()) 149 | { 150 | auto itv = it->second.find(key); 151 | if(itv != it->second.end()) 152 | return itv->second; 153 | } 154 | return default_value; 155 | } 156 | 157 | /* Sets the value of a value in the ini */ 158 | void set(const string_type& sect, const key_type& key, const string_type& value) 159 | { 160 | (*this)[sect][key] = value; // no emplace since overwrite! 161 | } 162 | 163 | /* Too lazy to continue this container... If you need more methods, just add it */ 164 | 165 | 166 | bool read_file(std::stringstream& ini_mem) 167 | { 168 | if(ini_mem.rdbuf()->in_avail()) 169 | { 170 | key_container* keys = nullptr; 171 | string_type line; 172 | string_type key; 173 | string_type value; 174 | string_type null_string; 175 | size_type pos; 176 | 177 | // Trims an string 178 | auto trim = [](string_type& s, bool trimLeft, bool trimRight) -> string_type& 179 | { 180 | if(s.size()) 181 | { 182 | // Ignore UTF-8 BOM 183 | while(s.size() >= 3 && s[0] == (char)(0xEF) && s[1] == (char)(0xBB) && s[2] == (char)(0xBF)) 184 | s.erase(s.begin(), s.begin() + 3); 185 | 186 | if(trimLeft) 187 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::function(::isspace)))); 188 | if(trimRight) 189 | s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::function(::isspace))).base(), s.end()); 190 | } 191 | return s; 192 | }; 193 | 194 | // Start parsing 195 | while (std::getline(ini_mem, line)) 196 | { 197 | // Find comment and remove anything after it from the line 198 | if((pos = line.find_first_of(';')) != line.npos) 199 | line.erase(pos); 200 | 201 | if ((pos = line.rfind(" //")) != line.npos) 202 | line.erase(pos); 203 | 204 | // Trim the string, and if it gets empty, skip this line 205 | if(trim(line, true, true).empty()) 206 | continue; 207 | 208 | // Find section name 209 | if(line.front() == '[' && line.back() == ']') 210 | { 211 | pos = line.length() - 1; //line.find_first_of(']'); 212 | if(pos != line.npos) 213 | { 214 | trim(key.assign(line, 1, pos-1), true, true); 215 | keys = &data[std::move(key)]; // Create section 216 | } 217 | else 218 | keys = nullptr; 219 | } 220 | else 221 | { 222 | // Find key and value positions 223 | pos = line.find_first_of('='); 224 | if(pos == line.npos) 225 | { 226 | // There's only the key 227 | key = line; // No need for trim, line is already trimmed 228 | value.clear(); 229 | } 230 | else 231 | { 232 | // There's the key and the value 233 | trim(key.assign(line, 0, pos), false, true); // trim the right 234 | trim(value.assign(line, pos + 1, line.npos), true, false); // trim the left 235 | } 236 | 237 | // Put the key/value into the current keys object, or into the section "" if no section has been found 238 | #if __cplusplus >= 201103L || _MSC_VER >= 1800 239 | (keys ? *keys : data[null_string]).emplace(std::move(key), std::move(value)); 240 | #else 241 | (keys ? *keys : data[null_string])[key] = value; 242 | key.clear(); value.clear(); 243 | #endif 244 | } 245 | } 246 | 247 | return true; 248 | } 249 | return false; 250 | } 251 | 252 | bool read_file(const char_type* filename) 253 | { 254 | std::ifstream file(filename, std::ios::in); 255 | if (file.is_open()) 256 | { 257 | std::stringstream ss; 258 | ss << file.rdbuf(); 259 | file.close(); 260 | return read_file(ss); 261 | } 262 | return false; 263 | } 264 | 265 | /* 266 | * Dumps the content of this container into an ini file 267 | */ 268 | bool write_file(const char_type* filename) 269 | { 270 | if(FILE* f = fopen(filename, "w")) 271 | { 272 | bool first = true; 273 | for(auto& sec : this->data) 274 | { 275 | fprintf(f, first? "[%s]\n" : "\n[%s]\n", sec.first.c_str()); 276 | first = false; 277 | for(auto& kv : sec.second) 278 | { 279 | if(kv.second.empty()) 280 | fprintf(f, "%s\n", kv.first.c_str()); 281 | else 282 | fprintf(f, "%s = %s\n", kv.first.c_str(), kv.second.c_str()); 283 | } 284 | } 285 | fclose(f); 286 | return true; 287 | } 288 | return false; 289 | } 290 | 291 | 292 | /* 293 | */ 294 | bool load_file(const char_type* filename) 295 | { 296 | return read_file(filename); 297 | } 298 | 299 | bool load_file(const StringType& filename) 300 | { 301 | return load_file(filename.c_str()); 302 | } 303 | 304 | bool load_file(std::stringstream& filename) 305 | { 306 | return read_file(filename); 307 | } 308 | 309 | bool write_file(const StringType& filename) 310 | { 311 | return write_file(filename.c_str()); 312 | } 313 | }; 314 | 315 | 316 | /* Use default basic_ini 317 | * 318 | * Limitations: 319 | * * Not unicode aware 320 | * * Case sensitive 321 | * * Sections must have unique keys 322 | */ 323 | typedef basic_ini<> ini; 324 | } 325 | 326 | #endif 327 | 328 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/hooking/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/hooking/Hooking.Patterns.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the CitizenFX project - http://citizen.re/ 3 | * 4 | * See LICENSE and MENTIONS in the root of the source tree for information 5 | * regarding licensing. 6 | */ 7 | 8 | #include "Hooking.Patterns.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN 11 | #define NOMINMAX 12 | #include 13 | #include 14 | 15 | #if PATTERNS_USE_HINTS 16 | #include 17 | #endif 18 | 19 | 20 | #if PATTERNS_USE_HINTS 21 | 22 | // from boost someplace 23 | template 24 | struct basic_fnv_1 25 | { 26 | std::uint64_t operator()(std::string_view text) const 27 | { 28 | std::uint64_t hash = OffsetBasis; 29 | for (auto it : text) 30 | { 31 | hash *= FnvPrime; 32 | hash ^= it; 33 | } 34 | 35 | return hash; 36 | } 37 | }; 38 | 39 | static constexpr std::uint64_t fnv_prime = 1099511628211u; 40 | static constexpr std::uint64_t fnv_offset_basis = 14695981039346656037u; 41 | 42 | typedef basic_fnv_1 fnv_1; 43 | 44 | #endif 45 | 46 | namespace hook 47 | { 48 | ptrdiff_t baseAddressDifference; 49 | 50 | // sets the base to the process main base 51 | void set_base() 52 | { 53 | set_base((uintptr_t)GetModuleHandle(nullptr)); 54 | } 55 | 56 | 57 | #if PATTERNS_USE_HINTS 58 | static auto& getHints() 59 | { 60 | static std::multimap hints; 61 | return hints; 62 | } 63 | #endif 64 | 65 | static void TransformPattern(std::string_view pattern, std::basic_string& data, std::basic_string& mask) 66 | { 67 | uint8_t tempDigit = 0; 68 | bool tempFlag = false; 69 | 70 | auto tol = [](char ch) -> uint8_t 71 | { 72 | if (ch >= 'A' && ch <= 'F') return uint8_t(ch - 'A' + 10); 73 | if (ch >= 'a' && ch <= 'f') return uint8_t(ch - 'a' + 10); 74 | return uint8_t(ch - '0'); 75 | }; 76 | 77 | for (auto ch : pattern) 78 | { 79 | if (ch == ' ') 80 | { 81 | continue; 82 | } 83 | else if (ch == '?') 84 | { 85 | data.push_back(0); 86 | mask.push_back(0); 87 | } 88 | else if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) 89 | { 90 | uint8_t thisDigit = tol(ch); 91 | 92 | if (!tempFlag) 93 | { 94 | tempDigit = thisDigit << 4; 95 | tempFlag = true; 96 | } 97 | else 98 | { 99 | tempDigit |= thisDigit; 100 | tempFlag = false; 101 | 102 | data.push_back(tempDigit); 103 | mask.push_back(0xFF); 104 | } 105 | } 106 | } 107 | } 108 | 109 | class executable_meta 110 | { 111 | private: 112 | uintptr_t m_begin; 113 | uintptr_t m_end; 114 | 115 | public: 116 | template 117 | TReturn* getRVA(TOffset rva) 118 | { 119 | return (TReturn*)(m_begin + rva); 120 | } 121 | 122 | explicit executable_meta(uintptr_t module) 123 | : m_begin(module), m_end(0) 124 | { 125 | static auto getSection = [](const PIMAGE_NT_HEADERS nt_headers, unsigned section) -> PIMAGE_SECTION_HEADER 126 | { 127 | return reinterpret_cast( 128 | (UCHAR*)nt_headers->OptionalHeader.DataDirectory + 129 | nt_headers->OptionalHeader.NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY) + 130 | section * sizeof(IMAGE_SECTION_HEADER)); 131 | }; 132 | 133 | PIMAGE_DOS_HEADER dosHeader = getRVA(0); 134 | PIMAGE_NT_HEADERS ntHeader = getRVA(dosHeader->e_lfanew); 135 | 136 | for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++) 137 | { 138 | auto sec = getSection(ntHeader, i); 139 | auto secSize = sec->SizeOfRawData != 0 ? sec->SizeOfRawData : sec->Misc.VirtualSize; 140 | if (sec->Characteristics & IMAGE_SCN_MEM_EXECUTE) 141 | m_end = m_begin + sec->VirtualAddress + secSize; 142 | 143 | if ((i == ntHeader->FileHeader.NumberOfSections - 1) && m_end == 0) 144 | m_end = m_begin + sec->PointerToRawData + secSize; 145 | } 146 | } 147 | 148 | executable_meta(uintptr_t begin, uintptr_t end) 149 | : m_begin(begin), m_end(end) 150 | { 151 | } 152 | 153 | inline uintptr_t begin() const { return m_begin; } 154 | inline uintptr_t end() const { return m_end; } 155 | }; 156 | 157 | void pattern::Initialize(std::string_view pattern) 158 | { 159 | // get the hash for the base pattern 160 | #if PATTERNS_USE_HINTS 161 | m_hash = fnv_1()(pattern); 162 | #endif 163 | 164 | // transform the base pattern from IDA format to canonical format 165 | TransformPattern(pattern, m_bytes, m_mask); 166 | 167 | #if PATTERNS_USE_HINTS 168 | // if there's hints, try those first 169 | #if PATTERNS_CAN_SERIALIZE_HINTS 170 | if (m_rangeStart == reinterpret_cast(GetModuleHandle(nullptr))) 171 | #endif 172 | { 173 | auto range = getHints().equal_range(m_hash); 174 | 175 | if (range.first != range.second) 176 | { 177 | std::for_each(range.first, range.second, [&](const auto& hint) 178 | { 179 | ConsiderHint(hint.second); 180 | }); 181 | 182 | // if the hints succeeded, we don't need to do anything more 183 | if (!m_matches.empty()) 184 | { 185 | m_matched = true; 186 | return; 187 | } 188 | } 189 | } 190 | #endif 191 | } 192 | 193 | void pattern::EnsureMatches(uint32_t maxCount) 194 | { 195 | if (m_matched || (!m_rangeStart && !m_rangeEnd)) 196 | return; 197 | 198 | // scan the executable for code 199 | executable_meta executable = m_rangeStart != 0 && m_rangeEnd != 0 ? executable_meta(m_rangeStart, m_rangeEnd) : executable_meta(m_rangeStart); 200 | 201 | auto matchSuccess = [&](uintptr_t address) 202 | { 203 | #if PATTERNS_USE_HINTS 204 | getHints().emplace(m_hash, address); 205 | #else 206 | (void)address; 207 | #endif 208 | 209 | return (m_matches.size() == maxCount); 210 | }; 211 | 212 | const uint8_t* pattern = m_bytes.data(); 213 | const uint8_t* mask = m_mask.data(); 214 | const size_t maskSize = m_mask.size(); 215 | const size_t lastWild = m_mask.find_last_not_of(uint8_t(0xFF)); 216 | 217 | ptrdiff_t Last[256]; 218 | 219 | std::fill(std::begin(Last), std::end(Last), lastWild == std::string::npos ? -1 : static_cast(lastWild)); 220 | 221 | for (ptrdiff_t i = 0; i < static_cast(maskSize); ++i) 222 | { 223 | if (Last[pattern[i]] < i) 224 | { 225 | Last[pattern[i]] = i; 226 | } 227 | } 228 | 229 | __try 230 | { 231 | for (uintptr_t i = executable.begin(), end = executable.end() - maskSize; i <= end;) 232 | { 233 | uint8_t* ptr = reinterpret_cast(i); 234 | ptrdiff_t j = maskSize - 1; 235 | 236 | while ((j >= 0) && pattern[j] == (ptr[j] & mask[j])) j--; 237 | 238 | if (j < 0) 239 | { 240 | m_matches.emplace_back(ptr); 241 | 242 | if (matchSuccess(i)) 243 | { 244 | break; 245 | } 246 | i++; 247 | } 248 | else i += std::max(ptrdiff_t(1), j - Last[ptr[j]]); 249 | } 250 | } 251 | __except ((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) 252 | { } 253 | 254 | m_matched = true; 255 | } 256 | 257 | bool pattern::ConsiderHint(uintptr_t offset) 258 | { 259 | uint8_t* ptr = reinterpret_cast(offset); 260 | 261 | #if PATTERNS_CAN_SERIALIZE_HINTS 262 | const uint8_t* pattern = m_bytes.data(); 263 | const uint8_t* mask = m_mask.data(); 264 | 265 | for (size_t i = 0, j = m_mask.size(); i < j; i++) 266 | { 267 | if (pattern[i] != (ptr[i] & mask[i])) 268 | { 269 | return false; 270 | } 271 | } 272 | #endif 273 | 274 | m_matches.emplace_back(ptr); 275 | 276 | return true; 277 | } 278 | 279 | #if PATTERNS_USE_HINTS && PATTERNS_CAN_SERIALIZE_HINTS 280 | void pattern::hint(uint64_t hash, uintptr_t address) 281 | { 282 | auto& hints = getHints(); 283 | 284 | auto range = hints.equal_range(hash); 285 | 286 | for (auto it = range.first; it != range.second; ++it) 287 | { 288 | if (it->second == address) 289 | { 290 | return; 291 | } 292 | } 293 | 294 | hints.emplace(hash, address); 295 | } 296 | #endif 297 | } -------------------------------------------------------------------------------- /XLivelessAddon/includes/hooking/Hooking.Patterns.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the CitizenFX project - http://citizen.re/ 3 | * 4 | * See LICENSE and MENTIONS in the root of the source tree for information 5 | * regarding licensing. 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #pragma warning(push) 15 | #pragma warning(disable:4201) 16 | 17 | namespace hook 18 | { 19 | extern ptrdiff_t baseAddressDifference; 20 | 21 | // sets the base address difference based on an obtained pointer 22 | inline void set_base(uintptr_t address) 23 | { 24 | #ifdef _M_IX86 25 | uintptr_t addressDiff = (address - 0x400000); 26 | #elif defined(_M_AMD64) 27 | uintptr_t addressDiff = (address - 0x140000000); 28 | #endif 29 | 30 | // pointer-style cast to ensure unsigned overflow ends up copied directly into a signed value 31 | baseAddressDifference = *(ptrdiff_t*)&addressDiff; 32 | } 33 | 34 | // sets the base to the process main base 35 | void set_base(); 36 | 37 | inline uintptr_t getRVA(uintptr_t rva) 38 | { 39 | set_base(); 40 | #ifdef _M_IX86 41 | return static_cast(baseAddressDifference + 0x400000 + rva); 42 | #elif defined(_M_AMD64) 43 | return static_cast(baseAddressDifference + 0x140000000 + rva); 44 | #endif 45 | } 46 | 47 | class pattern_match 48 | { 49 | private: 50 | void* m_pointer; 51 | 52 | public: 53 | inline pattern_match(void* pointer) 54 | : m_pointer(pointer) 55 | { 56 | } 57 | 58 | template 59 | T* get(ptrdiff_t offset = 0) const 60 | { 61 | char* ptr = reinterpret_cast(m_pointer); 62 | return reinterpret_cast(ptr + offset); 63 | } 64 | }; 65 | 66 | class pattern 67 | { 68 | private: 69 | std::basic_string m_bytes; 70 | std::basic_string m_mask; 71 | 72 | #if PATTERNS_USE_HINTS 73 | uint64_t m_hash; 74 | #endif 75 | 76 | std::vector m_matches; 77 | 78 | bool m_matched = false; 79 | 80 | uintptr_t m_rangeStart; 81 | uintptr_t m_rangeEnd; 82 | 83 | private: 84 | void Initialize(std::string_view pattern); 85 | 86 | bool ConsiderHint(uintptr_t offset); 87 | 88 | void EnsureMatches(uint32_t maxCount); 89 | 90 | inline pattern_match _get_internal(size_t index) const 91 | { 92 | return m_matches[index]; 93 | } 94 | 95 | inline pattern(uintptr_t module) 96 | : pattern(module, 0) 97 | { 98 | } 99 | 100 | inline pattern(uintptr_t begin, uintptr_t end) 101 | : m_rangeStart(begin), m_rangeEnd(end) 102 | { 103 | } 104 | 105 | public: 106 | pattern() 107 | { 108 | } 109 | 110 | pattern(std::string_view pattern) 111 | : pattern(getRVA(0)) 112 | { 113 | Initialize(std::move(pattern)); 114 | } 115 | 116 | inline pattern(void* module, std::string_view pattern) 117 | : pattern(reinterpret_cast(module)) 118 | { 119 | Initialize(std::move(pattern)); 120 | } 121 | 122 | inline pattern(uintptr_t begin, uintptr_t end, std::string_view pattern) 123 | : m_rangeStart(begin), m_rangeEnd(end) 124 | { 125 | Initialize(std::move(pattern)); 126 | } 127 | 128 | inline pattern&& count(uint32_t expected) 129 | { 130 | EnsureMatches(expected); 131 | assert(m_matches.size() == expected); 132 | return std::forward(*this); 133 | } 134 | 135 | inline pattern&& count_hint(uint32_t expected) 136 | { 137 | EnsureMatches(expected); 138 | return std::forward(*this); 139 | } 140 | 141 | inline pattern&& clear(void* module = nullptr) 142 | { 143 | if (module) 144 | { 145 | this->m_rangeStart = reinterpret_cast(module); 146 | this->m_rangeEnd = 0; 147 | } 148 | 149 | m_matches.clear(); 150 | m_matched = false; 151 | return std::forward(*this); 152 | } 153 | 154 | inline size_t size() 155 | { 156 | EnsureMatches(UINT32_MAX); 157 | return m_matches.size(); 158 | } 159 | 160 | inline bool empty() 161 | { 162 | return size() == 0; 163 | } 164 | 165 | inline pattern_match get(size_t index) 166 | { 167 | EnsureMatches(UINT32_MAX); 168 | return _get_internal(index); 169 | } 170 | 171 | inline pattern_match get_one() 172 | { 173 | return std::forward(*this).count(1)._get_internal(0); 174 | } 175 | 176 | template 177 | inline auto get_first(ptrdiff_t offset = 0) 178 | { 179 | return get_one().get(offset); 180 | } 181 | 182 | template 183 | inline Pred for_each_result(Pred&& pred) 184 | { 185 | EnsureMatches(UINT32_MAX); 186 | for (auto it : m_matches) 187 | { 188 | std::forward(pred)(it); 189 | } 190 | return std::forward(pred); 191 | } 192 | 193 | public: 194 | #if PATTERNS_USE_HINTS && PATTERNS_CAN_SERIALIZE_HINTS 195 | // define a hint 196 | static void hint(uint64_t hash, uintptr_t address); 197 | #endif 198 | 199 | friend class range_pattern; 200 | friend class module_pattern; 201 | }; 202 | 203 | class range_pattern : public pattern 204 | { 205 | public: 206 | inline range_pattern(uintptr_t begin, uintptr_t end, std::string_view bytes) : pattern(begin, end) 207 | { 208 | Initialize(std::move(bytes)); 209 | } 210 | }; 211 | 212 | class module_pattern : public pattern 213 | { 214 | public: 215 | inline module_pattern(void* module, std::string_view bytes) : pattern(reinterpret_cast(module)) 216 | { 217 | Initialize(std::move(bytes)); 218 | } 219 | }; 220 | 221 | inline pattern make_module_pattern(void* module, std::string_view bytes) 222 | { 223 | return pattern(module, std::move(bytes)); 224 | } 225 | 226 | inline pattern make_range_pattern(uintptr_t begin, uintptr_t end, std::string_view bytes) 227 | { 228 | return pattern(begin, end, std::move(bytes)); 229 | } 230 | 231 | template 232 | inline auto get_pattern(std::string_view pattern_string, ptrdiff_t offset = 0) 233 | { 234 | return pattern(std::move(pattern_string)).get_first(offset); 235 | } 236 | } 237 | 238 | #pragma warning(pop) -------------------------------------------------------------------------------- /XLivelessAddon/includes/hooking/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Bas Timmer/NTAuthority et al. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/hooking/README.md: -------------------------------------------------------------------------------- 1 | Hooking.Patterns 2 | ---------------- 3 | Sample: 4 | 5 | ```cpp 6 | #include "stdafx.h" 7 | #include 8 | #include "Hooking.Patterns.h" 9 | 10 | int main() 11 | { 12 | auto pattern = hook::pattern("54 68 69 73 20 70 72 6F 67 72"); 13 | if (!pattern.count_hint(1).empty()) 14 | { 15 | auto text = pattern.get(0).get(0); 16 | MessageBoxA(0, text, text, 0); 17 | } 18 | return 0; 19 | } 20 | ``` 21 | 22 | Result: 23 | 24 | ![MessageBox](http://i.imgur.com/Tuijf2I.png) 25 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/injector/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012-2014 LINK/2012 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | 22 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/injector/assembly.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Useful Assembly Stuff 3 | * 4 | * Copyright (C) 2012-2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | 28 | // This header is very restrict about compiler and architecture 29 | #ifndef _MSC_VER // MSVC is much more flexible when we're talking about inline assembly 30 | #error Cannot use this header in another compiler other than MSVC 31 | #endif 32 | #ifndef _M_IX86 33 | #error Supported only in x86 34 | #endif 35 | 36 | // 37 | #include "injector.hpp" 38 | 39 | namespace injector 40 | { 41 | struct reg_pack 42 | { 43 | // The ordering is very important, don't change 44 | // The first field is the last to be pushed and first to be poped 45 | 46 | // PUSHAD/POPAD -- must be the lastest fields (because of esp) 47 | union 48 | { 49 | uint32_t arr[8]; 50 | struct { uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; }; 51 | }; 52 | 53 | // PUSHFD / POPFD 54 | uint32_t ef; 55 | 56 | enum reg_name { 57 | reg_edi, reg_esi, reg_ebp, reg_esp, reg_ebx, reg_edx, reg_ecx, reg_eax 58 | }; 59 | 60 | enum ef_flag { 61 | carry_flag = 0, parity_flag = 2, adjust_flag = 4, zero_flag = 6, sign_flag = 7, 62 | direction_flag = 10, overflow_flag = 11 63 | }; 64 | 65 | uint32_t& operator[](size_t i) 66 | { return this->arr[i]; } 67 | const uint32_t& operator[](size_t i) const 68 | { return this->arr[i]; } 69 | 70 | template // bit starts from 0, use ef_flag enum 71 | bool flag() 72 | { 73 | return (this->ef & (1 << bit)) != 0; 74 | } 75 | 76 | bool jnb() 77 | { 78 | return flag() == false; 79 | } 80 | }; 81 | 82 | // Lowest level stuff (actual assembly) goes on the following namespace 83 | // PRIVATE! Skip this, not interesting for you. 84 | namespace injector_asm 85 | { 86 | // Wrapper functor, so the assembly can use some templating 87 | template 88 | struct wrapper 89 | { 90 | static void call(reg_pack* regs) 91 | { 92 | T fun; fun(*regs); 93 | } 94 | }; 95 | 96 | // Constructs a reg_pack and calls the wrapper functor 97 | template // where W is of type wrapper 98 | inline void __declspec(naked) make_reg_pack_and_call() 99 | { 100 | _asm 101 | { 102 | // Construct the reg_pack structure on the stack 103 | pushfd // Pushes EFLAGS to reg_pack 104 | pushad // Pushes general purposes registers to reg_pack 105 | add dword ptr[esp+12], 8 // Add 4 to reg_pack::esp 'cuz of our return pointer, let it be as before this func is called 106 | 107 | // Call wrapper sending reg_pack as parameter 108 | push esp 109 | call W::call 110 | add esp, 4 111 | 112 | // Destructs the reg_pack from the stack 113 | sub dword ptr[esp+12], 8 // Fix reg_pack::esp before popping it (doesn't make a difference though) (+4 because eflags) 114 | popad 115 | popfd // Warning: Do not use any instruction that changes EFLAGS after this (-> sub affects EF!! <-) 116 | 117 | // Back to normal flow 118 | ret 119 | } 120 | } 121 | }; 122 | 123 | 124 | /* 125 | * MakeInline 126 | * Makes inline assembly (but not assembly, an actual functor of type FuncT) at address 127 | */ 128 | template 129 | void MakeInline(memory_pointer_tr at) 130 | { 131 | typedef injector_asm::wrapper functor; 132 | if(false) functor::call(nullptr); // To instantiate the template, if not done _asm will fail 133 | MakeCALL(at, injector_asm::make_reg_pack_and_call); 134 | } 135 | 136 | /* 137 | * MakeInline 138 | * Same as above, but it NOPs everything between at and end (exclusive), then performs MakeInline 139 | */ 140 | template 141 | void MakeInline(memory_pointer_tr at, memory_pointer_tr end) 142 | { 143 | MakeRangedNOP(at, end); 144 | MakeInline(at); 145 | } 146 | 147 | /* 148 | * MakeInline 149 | * Same as above, but (at,end) are template parameters. 150 | * On this case the functor can be passed as argument since there will be one func instance for each at,end not just for each FuncT 151 | */ 152 | template 153 | void MakeInline(FuncT func) 154 | { 155 | static std::unique_ptr static_func; 156 | static_func.reset(new FuncT(std::move(func))); 157 | 158 | // Encapsulates the call to static_func 159 | struct Caps 160 | { 161 | void operator()(reg_pack& regs) 162 | { (*static_func)(regs); } 163 | }; 164 | 165 | // Does the actual MakeInline 166 | return MakeInline(lazy_pointer::get(), lazy_pointer::get()); 167 | } 168 | 169 | /* 170 | * MakeInline 171 | * Same as above, but (end) is calculated by the length of a call instruction 172 | */ 173 | template 174 | void MakeInline(FuncT func) 175 | { 176 | return MakeInline(func); 177 | } 178 | }; 179 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/injector/calling.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Function Calls Using Variadic Templates 3 | * 4 | * Copyright (C) 2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | #include "injector.hpp" 28 | #include 29 | #include 30 | 31 | #if __cplusplus >= 201103L || _MSC_VER >= 1800 // MSVC 2013 32 | #else 33 | #error "This feature is not supported on this compiler" 34 | #endif 35 | 36 | namespace injector 37 | { 38 | template 39 | struct cstd; 40 | 41 | template 42 | struct cstd 43 | { 44 | // Call function at @p returning @Ret with args @Args 45 | static Ret call(memory_pointer_tr p, Args... a) 46 | { 47 | auto fn = (Ret(*)(Args...)) p.get(); 48 | return fn(std::forward(a)...); 49 | } 50 | 51 | template // Uses lazy pointer 52 | static Ret call(Args... a) 53 | { 54 | return call(lazy_pointer::get(), std::forward(a)...); 55 | } 56 | }; 57 | 58 | template 59 | struct stdcall; 60 | 61 | template 62 | struct stdcall 63 | { 64 | // Call function at @p returning @Ret with args @Args 65 | static Ret call(memory_pointer_tr p, Args... a) 66 | { 67 | auto fn = (Ret(__stdcall *)(Args...)) p.get(); 68 | return fn(std::forward(a)...); 69 | } 70 | 71 | template // Uses lazy pointer 72 | static Ret call(Args... a) 73 | { 74 | return call(lazy_pointer::get(), std::forward(a)...); 75 | } 76 | }; 77 | 78 | template 79 | struct fastcall; 80 | 81 | template 82 | struct fastcall 83 | { 84 | // Call function at @p returning @Ret with args @Args 85 | static Ret call(memory_pointer_tr p, Args... a) 86 | { 87 | auto fn = (Ret(__fastcall *)(Args...)) p.get();; 88 | return fn(std::forward(a)...); 89 | } 90 | 91 | template // Uses lazy pointer 92 | static Ret call(Args... a) 93 | { 94 | return call(lazy_pointer::get(), std::forward(a)...); 95 | } 96 | }; 97 | 98 | template 99 | struct thiscall; 100 | 101 | template 102 | struct thiscall 103 | { 104 | // Call function at @p returning @Ret with args @Args 105 | static Ret call(memory_pointer_tr p, Args... a) 106 | { 107 | auto fn = (Ret(__thiscall *)(Args...)) p.get(); 108 | return fn(std::forward(a)...); 109 | } 110 | 111 | // Call function at the index @i from the vtable of the object @a[0] 112 | template 113 | static Ret vtbl(Args... a) 114 | { 115 | auto obj = raw_ptr(std::get<0>(std::forward_as_tuple(a...))); 116 | auto p = raw_ptr( (*obj.template get()) [i] ); 117 | return call(p, std::forward(a)...); 118 | } 119 | 120 | template // Uses lazy pointer 121 | static Ret call(Args... a) 122 | { 123 | return call(lazy_pointer::get(), std::forward(a)...); 124 | } 125 | }; 126 | } 127 | 128 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/injector/gvm/gvm.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Base Header 3 | * 4 | * Copyright (C) 2012-2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | #include 28 | #include 29 | #include 30 | 31 | namespace injector 32 | { 33 | 34 | #if 1 // GVM and Address Translator, Not very interesting for the users, so skip reading those... 35 | 36 | /* 37 | * game_version_manager 38 | * Detects the game, the game version and the game region 39 | * This assumes the executable is decrypted, so, Silent's ASI Loader is recommended. 40 | */ 41 | #ifndef INJECTOR_OWN_GVM 42 | #ifndef INJECTOR_GVM_DUMMY 43 | class game_version_manager 44 | { 45 | public: 46 | // Set this if you would like that MessagesBox contain PluginName as caption 47 | const char* PluginName; 48 | 49 | private: 50 | char game, region, major, minor, majorRevision, minorRevision, cracker, steam; 51 | 52 | public: 53 | game_version_manager() 54 | { 55 | #ifdef INJECTOR_GVM_PLUGIN_NAME 56 | PluginName = INJECTOR_GVM_PLUGIN_NAME; 57 | #else 58 | PluginName = "Unknown Plugin Name"; 59 | #endif 60 | 61 | this->Clear(); 62 | } 63 | 64 | 65 | // Clear any information about game version 66 | void Clear() 67 | { 68 | game = region = major = minor = majorRevision = minorRevision = cracker = steam = 0; 69 | } 70 | 71 | // Checks if I don't know the game we are attached to 72 | bool IsUnknown() { return game == 0; } 73 | // Checks if this is the steam version 74 | bool IsSteam() { return steam != 0; } 75 | // Gets the game we are attached to (0, '3', 'V', 'S', 'I', 'E') 76 | char GetGame() { return game; } 77 | // Gets the region from the game we are attached to (0, 'U', 'E'); 78 | char GetRegion() { return region; } 79 | // Get major and minor version of the game (e.g. [major = 1, minor = 0] = 1.0) 80 | int GetMajorVersion() { return major; } 81 | int GetMinorVersion() { return minor; } 82 | int GetMajorRevisionVersion() { return majorRevision; } 83 | int GetMinorRevisionVersion() { return minorRevision; } 84 | 85 | bool IsHoodlum() { return cracker == 'H'; } 86 | 87 | // Region conditions 88 | bool IsUS() { return region == 'U'; } 89 | bool IsEU() { return region == 'E'; } 90 | 91 | // Game Conditions 92 | bool IsIII() { return game == '3'; } 93 | bool IsVC () { return game == 'V'; } 94 | bool IsSA () { return game == 'S'; } 95 | bool IsIV () { return game == 'I'; } 96 | bool IsEFLC(){ return game == 'E'; } 97 | 98 | // Detects game, region and version; returns false if could not detect it 99 | bool Detect(); 100 | 101 | // Gets the game version as text, the buffer must contain at least 32 bytes of space. 102 | char* GetVersionText(char* buffer) 103 | { 104 | if(this->IsUnknown()) 105 | { 106 | strcpy(buffer, "UNKNOWN GAME"); 107 | return buffer; 108 | } 109 | 110 | const char* g = this->IsIII() ? "III" : this->IsVC() ? "VC" : this->IsSA() ? "SA" : this->IsIV() ? "IV" : this->IsEFLC() ? "EFLC" : "UNK"; 111 | const char* r = this->IsUS()? "US" : this->IsEU()? "EURO" : "UNK_REGION"; 112 | const char* s = this->IsSteam()? "Steam" : ""; 113 | sprintf(buffer, "GTA %s %d.%d.%d.%d %s%s", g, major, minor, majorRevision, minorRevision, r, s); 114 | return buffer; 115 | } 116 | 117 | 118 | public: 119 | // Raises a error saying that you could not detect the game version 120 | void RaiseCouldNotDetect() 121 | { 122 | MessageBoxA(0, 123 | "Could not detect the game version\nContact the mod creator!", 124 | PluginName, MB_ICONERROR 125 | ); 126 | } 127 | 128 | // Raises a error saying that the exe version is incompatible (and output the exe name) 129 | void RaiseIncompatibleVersion() 130 | { 131 | char buf[128], v[32]; 132 | sprintf(buf, 133 | "An incompatible exe version has been detected! (%s)\nContact the mod creator!", 134 | GetVersionText(v) 135 | ); 136 | MessageBoxA(0, buf, PluginName, MB_ICONERROR); 137 | } 138 | }; 139 | #else // INJECTOR_GVM_DUMMY 140 | class game_version_manager 141 | { 142 | public: 143 | bool Detect() { return true; } 144 | }; 145 | #endif // INJECTOR_GVM_DUMMY 146 | #endif // INJECTOR_OWN_GVM 147 | 148 | 149 | /* 150 | * address_manager 151 | * Address translator from 1.0 executables to other executables offsets 152 | * Inherits from game_version_manager ;) 153 | */ 154 | class address_manager : public game_version_manager 155 | { 156 | private: 157 | address_manager() 158 | { 159 | this->Detect(); 160 | } 161 | 162 | // You could implement your translator for the address your plugin uses 163 | // If not implemented, the translator won't translate anything, just return the samething as before 164 | #ifdef INJECTOR_GVM_HAS_TRANSLATOR 165 | void* translator(void* p); 166 | #else 167 | void* translator(void* p) { return p; } 168 | #endif 169 | 170 | public: 171 | // Translates address p to the running executable pointer 172 | void* translate(void* p) 173 | { 174 | return translator(p); 175 | } 176 | 177 | 178 | public: 179 | // Address manager singleton 180 | static address_manager& singleton() 181 | { 182 | static address_manager m; 183 | return m; 184 | } 185 | 186 | // Static version of translate() 187 | static void* translate_address(void* p) 188 | { 189 | return singleton().translate(p); 190 | } 191 | 192 | // 193 | static void set_name(const char* modname) 194 | { 195 | singleton().PluginName = modname; 196 | } 197 | 198 | public: 199 | // Functors for memory translation: 200 | 201 | // Translates aslr translator 202 | struct fn_mem_translator_aslr 203 | { 204 | void* operator()(void* p) const 205 | { 206 | static uintptr_t module = (uintptr_t)GetModuleHandle(NULL); 207 | return (void*)((uintptr_t)(p)-(0x400000 - module)); 208 | } 209 | }; 210 | 211 | // Translates nothing translator 212 | struct fn_mem_translator_nop 213 | { 214 | void* operator()(void* p) const 215 | { return p; } 216 | }; 217 | 218 | // Real translator 219 | struct fn_mem_translator 220 | { 221 | void* operator()(void* p) const 222 | { return translate_address(p); } 223 | }; 224 | }; 225 | 226 | #endif // #if 1 227 | 228 | 229 | } -------------------------------------------------------------------------------- /XLivelessAddon/includes/injector/gvm/translator.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Address Translation Management 3 | * 4 | * Copyright (C) 2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | 28 | #if !defined(INJECTOR_GVM_HAS_TRANSLATOR) 29 | #error Missing INJECTOR_GVM_HAS_TRANSLATOR on compiler definitions 30 | #endif 31 | 32 | /* 33 | * This is a quick solution for address translations if you're too lazy to implement a proper address_manager::translator by yourself 34 | * So, just call address_translator_manager::singleton().translate(p) from your address_manager::translator and that's it. 35 | * It'll translate addresses based on 'address_translator' objects, when one gets constructed it turns into a possible translator. 36 | * At the constructor of your derived 'address_translator' make the map object to have [addr_to_translate] = translated_addr; 37 | * There's also the virtual method 'fallback' that will get called when the translation wasn't possible, you can do some fallback stuff here 38 | * (such as return the pointer as is or output a error message) 39 | */ 40 | 41 | #include "../injector.hpp" 42 | #include 43 | #include 44 | #include 45 | 46 | namespace injector 47 | { 48 | /* 49 | * address_translator 50 | * Base for an address translator 51 | */ 52 | class address_translator 53 | { 54 | private: 55 | bool enabled; 56 | void add(); 57 | void remove(); 58 | 59 | protected: 60 | friend class address_translator_manager; 61 | std::map map; 62 | 63 | public: 64 | address_translator() : enabled(true) 65 | { 66 | // Must have bounds filled with min ptr and max ptr to have search working properly 67 | map.insert(std::make_pair(raw_ptr(0x00000000u), raw_ptr(0x00000000u))); 68 | map.insert(std::make_pair(raw_ptr(0xffffffffu), raw_ptr(0xffffffffu))); 69 | add(); 70 | } 71 | 72 | ~address_translator() 73 | { 74 | remove(); 75 | } 76 | 77 | virtual void* fallback(void*) const 78 | { 79 | return nullptr; 80 | } 81 | 82 | 83 | // Enables or disables this translator 84 | void enable(bool enable_it) 85 | { 86 | if(enable_it) this->enable(); 87 | else this->disable(); 88 | } 89 | 90 | // Enables this translator 91 | void enable() 92 | { 93 | this->enabled = true; 94 | } 95 | 96 | // Disables this translator 97 | void disable() 98 | { 99 | this->enabled = false; 100 | } 101 | 102 | // Checks if this translator is enabled 103 | bool is_enabled() const 104 | { 105 | return enabled; 106 | } 107 | }; 108 | 109 | /* 110 | * address_translator_manager 111 | * Manages the address_translator objects 112 | */ 113 | class address_translator_manager 114 | { 115 | protected: 116 | friend class address_manager; 117 | friend class address_translator; 118 | 119 | std::list translators; 120 | 121 | void add(const address_translator& t) 122 | { 123 | translators.push_front(&t); 124 | } 125 | 126 | void remove(const address_translator& t) 127 | { 128 | translators.remove(&t); 129 | } 130 | 131 | public: 132 | // Translates the address p 133 | void* translator(void* p); 134 | 135 | // Singleton object 136 | static address_translator_manager& singleton() 137 | { 138 | static address_translator_manager mgr; 139 | return mgr; 140 | } 141 | }; 142 | 143 | 144 | 145 | inline void* address_translator_manager::translator(void* p_) 146 | { 147 | static const size_t max_ptr_dist = 7; 148 | 149 | // Tries to find an address in a translator map 150 | auto try_map = [](const std::map& map, memory_pointer_raw p) -> memory_pointer_raw 151 | { 152 | memory_pointer_raw result = nullptr; 153 | 154 | // Find first element in the map that is greater than or equal to p 155 | auto it = map.lower_bound(p); 156 | if(it != map.end()) 157 | { 158 | // If it's not exactly the address, get back one position on the table 159 | if(it->first != p) --it; 160 | 161 | auto diff = (p - it->first).as_int(); // What's the difference between p and that address? 162 | if(diff <= max_ptr_dist) // Could we live with this difference in hands? 163 | result = it->second + raw_ptr(diff); // Yes, we can! 164 | } 165 | 166 | return result; 167 | }; 168 | 169 | 170 | // 171 | memory_pointer_raw result = nullptr; 172 | 173 | // Try to find translation for this pointer 174 | auto& mgr = address_translator_manager::singleton().translators; 175 | for(auto it = mgr.begin(); result == nullptr && it != mgr.end(); ++it) 176 | { 177 | auto& t = **it; 178 | if(t.is_enabled()) result = try_map(t.map, p_); 179 | } 180 | 181 | // If we couldn't translate the address, notify and try to fallback 182 | if(result.is_null()) 183 | { 184 | for(auto it = mgr.begin(); result == nullptr && it != mgr.end(); ++it) 185 | { 186 | auto& t = **it; 187 | if(t.is_enabled()) result = t.fallback(p_); 188 | } 189 | } 190 | 191 | return result.get(); 192 | } 193 | 194 | inline void address_translator::add() 195 | { 196 | address_translator_manager::singleton().add(*this); 197 | } 198 | 199 | inline void address_translator::remove() 200 | { 201 | address_translator_manager::singleton().remove(*this); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/injector/hooking.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Classes for making your hooking life easy 3 | * 4 | * Copyright (C) 2013-2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | #include "injector.hpp" 28 | #include 29 | #include 30 | #include // for std::shared_ptr 31 | #include 32 | 33 | namespace injector 34 | { 35 | /* 36 | * scoped_base 37 | * Base for any scoped hooking type 38 | * !!!! NOTICE !!!! --> Any derived which implements/reimplements restore() should implement a destructor calling it 39 | */ 40 | class scoped_base 41 | { 42 | public: 43 | virtual ~scoped_base() {} 44 | virtual void restore() = 0; 45 | }; 46 | 47 | /* 48 | * scoped_basic 49 | * Base for scoped types which will need a buffer to save/restore stuff 50 | */ 51 | template // TODO specialize bufsize=0 to be dynamic 52 | class scoped_basic : public scoped_base 53 | { 54 | private: 55 | uint8_t buf[bufsize];// Saved content 56 | memory_pointer_raw addr; // Data saved from this address 57 | size_t size; // Size saved 58 | bool saved; // Something saved? 59 | bool vp; // Virtual protect? 60 | 61 | public: 62 | 63 | static const bool is_dynamic = false; 64 | 65 | // Restore the previosly saved data 66 | // Problems may arise if someone else hooked the same place using the same method 67 | virtual void restore() 68 | { 69 | #ifndef INJECTOR_SCOPED_NOSAVE_NORESTORE 70 | if(this->saved) 71 | { 72 | WriteMemoryRaw(this->addr, this->buf, this->size, this->vp); 73 | this->saved = false; 74 | } 75 | #endif 76 | } 77 | 78 | // Save buffer at @addr with @size and virtual protect @vp 79 | virtual void save(memory_pointer_tr addr, size_t size, bool vp) 80 | { 81 | #ifndef INJECTOR_SCOPED_NOSAVE_NORESTORE 82 | assert(size <= bufsize); // Debug Safeness 83 | this->restore(); // Restore anything we have saved 84 | this->saved = true; // Mark that we have data save 85 | this->addr = addr.get(); // Save address 86 | this->size = size; // Save size 87 | this->vp = vp; // Save virtual protect 88 | ReadMemoryRaw(addr, buf, size, vp); // Save buffer 89 | #endif 90 | } 91 | 92 | public: 93 | // Constructor, initialises 94 | scoped_basic() : saved(false) 95 | {} 96 | 97 | ~scoped_basic() 98 | { 99 | this->restore(); 100 | } 101 | 102 | // No copy construction, we can't do this! Sure we can move construct :) 103 | scoped_basic(const scoped_basic&) = delete; 104 | scoped_basic(scoped_basic&& rhs) 105 | { 106 | *this = std::move(rhs); 107 | } 108 | 109 | scoped_basic& operator=(const scoped_basic& rhs) = delete; 110 | scoped_basic& operator=(scoped_basic&& rhs) 111 | { 112 | if(this->saved = rhs.saved) 113 | { 114 | assert(bufsize >= rhs.size); 115 | 116 | this->addr = rhs.addr; 117 | this->size = rhs.size; 118 | this->vp = rhs.vp; 119 | memcpy(buf, rhs.buf, rhs.size); 120 | 121 | rhs.saved = false; 122 | } 123 | return *this; 124 | } 125 | }; 126 | 127 | /* 128 | * RAII wrapper for memory writes 129 | * Can save only basic and POD types 130 | */ 131 | template 132 | class scoped_write : public scoped_basic 133 | { 134 | public: 135 | // Save buffer at @addr with @size and virtual protect @vp and then overwrite it with @value 136 | void write(memory_pointer_tr addr, void* value, size_t size, bool vp) 137 | { 138 | this->save(addr, size, vp); 139 | return WriteMemoryRaw(addr, value, size, vp); 140 | } 141 | 142 | // Save buffer at @addr with size sizeof(@value) and virtual protect @vp and then overwrite it with @value 143 | template 144 | void write(memory_pointer_tr addr, T value, bool vp = false) 145 | { 146 | this->save(addr, sizeof(T), vp); 147 | return WriteMemory(addr, value, vp); 148 | } 149 | 150 | // Constructors, move constructors, assigment operators........ 151 | scoped_write() = default; 152 | scoped_write(const scoped_write&) = delete; 153 | scoped_write(scoped_write&& rhs) : scoped_basic(std::move(rhs)) {} 154 | scoped_write& operator=(const scoped_write& rhs) = delete; 155 | scoped_write& operator=(scoped_write&& rhs) 156 | { scoped_basic::operator=(std::move(rhs)); return *this; } 157 | }; 158 | 159 | /* 160 | * RAII wrapper for filling 161 | */ 162 | template 163 | class scoped_fill : public scoped_basic 164 | { 165 | public: 166 | // Fills memory at @addr with value @value and size @size and virtual protect @vp 167 | void fill(memory_pointer_tr addr, uint8_t value, size_t size, bool vp) 168 | { 169 | this->save(addr, size, vp); 170 | return MemoryFill(addr, value, size, vp); 171 | } 172 | 173 | // Constructors, move constructors, assigment operators........ 174 | scoped_fill() = default; 175 | scoped_fill(const scoped_fill&) = delete; 176 | scoped_fill(scoped_fill&& rhs) : scoped_basic(std::move(rhs)) {} 177 | scoped_fill& operator=(const scoped_fill& rhs) = delete; 178 | scoped_fill& operator=(scoped_fill&& rhs) 179 | { scoped_basic::operator=(std::move(rhs)); return *this; } 180 | 181 | scoped_fill(memory_pointer_tr addr, uint8_t value, size_t size, bool vp) 182 | { fill(addr, value, vp); } 183 | }; 184 | 185 | /* 186 | * RAII wrapper for nopping 187 | */ 188 | template 189 | class scoped_nop : public scoped_basic 190 | { 191 | public: 192 | // Makes NOP at @addr with value @value and size @size and virtual protect @vp 193 | void make_nop(memory_pointer_tr addr, size_t size = 1, bool vp = true) 194 | { 195 | this->save(addr, size, vp); 196 | return MakeNOP(addr, size, vp); 197 | } 198 | 199 | // Constructors, move constructors, assigment operators........ 200 | scoped_nop() = default; 201 | scoped_nop(const scoped_nop&) = delete; 202 | scoped_nop(scoped_nop&& rhs) : scoped_basic(std::move(rhs)) {} 203 | scoped_nop& operator=(const scoped_nop& rhs) = delete; 204 | scoped_nop& operator=(scoped_nop&& rhs) 205 | { scoped_basic::operator=(std::move(rhs)); return *this; } 206 | 207 | scoped_nop(memory_pointer_tr addr, size_t size = 1, bool vp = true) 208 | { make_nop(addr, size, vp); } 209 | }; 210 | 211 | /* 212 | * RAII wrapper for MakeJMP 213 | */ 214 | class scoped_jmp : public scoped_basic<5> 215 | { 216 | public: 217 | // Makes NOP at @addr with value @value and size @size and virtual protect @vp 218 | memory_pointer_raw make_jmp(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 219 | { 220 | this->save(at, 5, vp); 221 | return MakeJMP(at, dest, vp); 222 | } 223 | 224 | // Constructors, move constructors, assigment operators........ 225 | scoped_jmp() = default; 226 | scoped_jmp(const scoped_jmp&) = delete; 227 | scoped_jmp(scoped_jmp&& rhs) : scoped_basic<5>(std::move(rhs)) {} 228 | scoped_jmp& operator=(const scoped_jmp& rhs) = delete; 229 | scoped_jmp& operator=(scoped_jmp&& rhs) 230 | { scoped_basic<5>::operator=(std::move(rhs)); return *this; } 231 | 232 | scoped_jmp(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 233 | { make_jmp(at, dest, vp); } 234 | }; 235 | 236 | /* 237 | * RAII wrapper for MakeCALL 238 | */ 239 | class scoped_call : public scoped_basic<5> 240 | { 241 | public: 242 | // Makes NOP at @addr with value @value and size @size and virtual protect @vp 243 | memory_pointer_raw make_call(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 244 | { 245 | this->save(at, 5, vp); 246 | return MakeCALL(at, dest, vp); 247 | } 248 | 249 | // Constructors, move constructors, assigment operators........ 250 | scoped_call() = default; 251 | scoped_call(const scoped_call&) = delete; 252 | scoped_call(scoped_call&& rhs) : scoped_basic<5>(std::move(rhs)) {} 253 | scoped_call& operator=(const scoped_call& rhs) = delete; 254 | scoped_call& operator=(scoped_call&& rhs) 255 | { scoped_basic<5>::operator=(std::move(rhs)); return *this; } 256 | 257 | scoped_call(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 258 | { make_call(at, dest, vp); } 259 | }; 260 | 261 | 262 | #if __cplusplus >= 201103L || _MSC_VER >= 1800 // C++11 or MSVC 2013 required for variadic templates 263 | 264 | /* 265 | * function_hooker_manager 266 | * Manages many function_hookers that points to the same address 267 | * The need for this function arises because otherwise we would only be able to allow one hook per address using function_hookers 268 | * This manager takes care of the amount of hooks placed in a particular address, calls the hooks and unhooks when necessary. 269 | */ 270 | template 271 | class function_hooker_manager : protected scoped_call 272 | { 273 | private: 274 | using func_type_raw = typename ToManage::func_type_raw; 275 | using func_type = typename ToManage::func_type; 276 | using functor_type = typename ToManage::functor_type; 277 | using assoc_type = std::list>; 278 | 279 | // Only construction is allowed... by myself ofcourse... 280 | function_hooker_manager() = default; 281 | function_hooker_manager(const function_hooker_manager&) = delete; 282 | function_hooker_manager(function_hooker_manager&&) = delete; 283 | 284 | // 285 | func_type_raw original; // Pointer to the original function we've replaced 286 | assoc_type assoc; // Association between owners of a hook and the hook (map) 287 | bool has_hooked = false; // Is the hook already in place? 288 | 289 | // Find assoc iterator for the content owned by 'owned' 290 | typename assoc_type::iterator find_assoc(const ToManage& owner) 291 | { 292 | for(auto it = assoc.begin(); it != assoc.end(); ++it) 293 | if(it->first == &owner) return it; 294 | return assoc.end(); 295 | } 296 | 297 | // Adds a new item to the association map (or override if already in the map) 298 | void add(const ToManage& hooker, functor_type functor) 299 | { 300 | auto it = find_assoc(hooker); 301 | if(it != assoc.end()) 302 | it->second = std::move(functor); 303 | else 304 | assoc.emplace_back(&hooker, std::move(functor)); 305 | } 306 | 307 | public: 308 | // Forwards the call to all the installed hooks 309 | static Ret call_hooks(Args&... args) 310 | { 311 | auto& manager = *instance(); 312 | 313 | if(manager.assoc.size() == 0) // This may be uncommon but may happen (?), no hook installed 314 | return manager.original(args...); 315 | 316 | // Functor for the original call 317 | func_type original = [&manager](Args... args) -> Ret { 318 | return manager.original(args...); 319 | }; 320 | 321 | if(manager.assoc.size() == 1) 322 | { 323 | // We have only one hook, just use it directly no need to go further in complexity 324 | auto& functor = manager.assoc.begin()->second; 325 | return functor(std::move(original), args...); 326 | } 327 | else 328 | { 329 | // Build a serie of functors which captures the previous functor sending it to the next functor, 330 | // that's what would happen if the hooks took place independent of the template staticness (AAAAAAA) 331 | func_type next = std::move(original); 332 | for(auto it = manager.assoc.begin(); it != manager.assoc.end(); ++it) 333 | { 334 | auto& functor = it->second; 335 | next = [functor, next](Args... args) -> Ret 336 | { 337 | return functor(next, args...); 338 | }; 339 | } 340 | return next(args...); 341 | } 342 | } 343 | 344 | public: 345 | 346 | // Installs a hook associated with the function_hooker 'hooker' which would call the specified 'functor' 347 | // We need an auxiliar function pointer 'ptr' (to abstract calling conventions) which should forward itself to ^call_hooks 348 | void make_call(const ToManage& hooker, functor_type functor, memory_pointer_raw ptr) 349 | { 350 | this->add(hooker, std::move(functor)); 351 | 352 | // Make sure we only hook this address for the manager once 353 | if(!this->has_hooked) 354 | { 355 | // (the following cast is needed for __thiscall functions) 356 | this->original = (func_type_raw) (void*) scoped_call::make_call(hooker.addr, ptr).get(); 357 | this->has_hooked = true; 358 | } 359 | } 360 | 361 | // Restores the state of the call we've replaced in the game code 362 | // All installed hooks gets uninstalled after this 363 | void restore() 364 | { 365 | if(this->has_hooked) 366 | { 367 | this->has_hooked = false; 368 | this->assoc.clear(); 369 | return scoped_call::restore(); 370 | } 371 | } 372 | 373 | // Replaces the hook associated with 'from' to be associated with 'to' 374 | // After this call the 'from' object has no association in this manager 375 | void replace(const ToManage& from, const ToManage& to) 376 | { 377 | auto it = find_assoc(from); 378 | if(it != assoc.end()) 379 | { 380 | auto functor = std::move(it->second); 381 | assoc.erase(it); 382 | this->add(to, std::move(functor)); 383 | } 384 | } 385 | 386 | // Removes the hook associated with the specified 'hooker' 387 | // If the number of hooks reaches zero after the remotion, a restore will take place 388 | void remove(const ToManage& hooker) 389 | { 390 | auto it = find_assoc(hooker); 391 | if(it != assoc.end()) 392 | { 393 | assoc.erase(it); 394 | if(assoc.size() == 0) this->restore(); 395 | } 396 | } 397 | 398 | // The instance of this specific manager 399 | // This work as a shared pointer to avoid the static destruction of the manager even when a static function_hooker 400 | // still wants to use it. 401 | static std::shared_ptr instance() 402 | { 403 | static auto fm_ptr = std::shared_ptr(new function_hooker_manager()); 404 | return fm_ptr; 405 | } 406 | 407 | }; 408 | 409 | 410 | /* 411 | * function_hooker_base 412 | * Base for any function_hooker, this class manages the relationship with the function hooker manager 413 | */ 414 | template 415 | class function_hooker_base : public scoped_base 416 | { 417 | public: 418 | static const uintptr_t addr = addr1; 419 | 420 | using func_type_raw = FuncType; 421 | using func_type = std::function; 422 | using functor_type = std::function; 423 | using manager_type = function_hooker_manager; 424 | 425 | public: 426 | // Constructors, move constructors, assigment operators........ 427 | function_hooker_base(const function_hooker_base&) = delete; 428 | function_hooker_base& operator=(const function_hooker_base& rhs) = delete; 429 | 430 | function_hooker_base() : manager(manager_type::instance()) 431 | {} 432 | 433 | // 434 | virtual ~function_hooker_base() 435 | { 436 | this->restore(); 437 | } 438 | 439 | // The move constructor should do a replace in the manager 440 | function_hooker_base(function_hooker_base&& rhs) 441 | : scoped_base(std::move(rhs)), has_call(rhs.has_call), 442 | manager(rhs.manager) // (don't move the manager!, every function_hooker should own one) 443 | { 444 | manager->replace(rhs, *this); 445 | } 446 | 447 | // The move assignment operator should also do a replace in the manager 448 | function_hooker_base& operator=(function_hooker_base&& rhs) 449 | { 450 | scoped_base::operator=(std::move(rhs)); 451 | manager->replace(rhs, *this); 452 | this->has_call = rhs.has_call; 453 | this->manager = rhs.manager; // (don't move the manager! every function_hooker should own one) 454 | return *this; 455 | } 456 | 457 | // Deriveds should implement a proper make_call (yeah it's virtual so derived-deriveds can do some fest) 458 | virtual void make_call(functor_type functor) = 0; 459 | 460 | // Restores the state of the call we've replaced in the game code 461 | virtual void restore() 462 | { 463 | this->has_call = false; 464 | manager->remove(*this); 465 | } 466 | 467 | // Checkers whether a hook is installed 468 | bool has_hooked() 469 | { 470 | return this->has_call; 471 | } 472 | 473 | private: 474 | bool has_call = false; // Has a hook installed? 475 | std::shared_ptr manager; // **EVERY** function_hooker should have a ownership over it's manager_type 476 | // this prevents the static destruction of the manager_type while it may be still needed. 477 | 478 | protected: // Forwarders to the function hooker manager 479 | 480 | void make_call(functor_type functor, memory_pointer_raw ptr) 481 | { 482 | this->has_call = true; 483 | manager->make_call(*this, std::move(functor), ptr); 484 | } 485 | 486 | static Ret call_hooks(Args&... a) 487 | { 488 | return manager_type::call_hooks(a...); 489 | } 490 | }; 491 | 492 | 493 | 494 | 495 | /* 496 | * function_hooker 497 | * For standard conventions (usually __cdecl) 498 | */ 499 | template 500 | struct function_hooker; 501 | 502 | template 503 | class function_hooker 504 | : public function_hooker_base 505 | { 506 | private: 507 | using base = function_hooker_base; 508 | 509 | // The hook caller 510 | static Ret call(Args... a) 511 | { 512 | return base::call_hooks(a...); 513 | } 514 | 515 | public: 516 | // Constructors, move constructors, assigment operators........ 517 | function_hooker() = default; 518 | function_hooker(const function_hooker&) = delete; 519 | function_hooker(function_hooker&& rhs) : base(std::move(rhs)) {} 520 | function_hooker& operator=(const function_hooker& rhs) = delete; 521 | function_hooker& operator=(function_hooker&& rhs) 522 | { base::operator=(std::move(rhs)); return *this; } 523 | 524 | // Makes the hook 525 | void make_call(typename base::functor_type functor) 526 | { 527 | return base::make_call(std::move(functor), raw_ptr(call)); 528 | } 529 | }; 530 | 531 | 532 | /* 533 | * function_hooker_stdcall 534 | * For stdcall conventions (__stdcall) 535 | */ 536 | template 537 | struct function_hooker_stdcall; 538 | 539 | template 540 | struct function_hooker_stdcall 541 | : public function_hooker_base 542 | { 543 | private: 544 | using base = function_hooker_base; 545 | 546 | // The hook caller 547 | static Ret __stdcall call(Args... a) 548 | { 549 | return base::call_hooks(a...); 550 | } 551 | 552 | public: 553 | // Constructors, move constructors, assigment operators........ 554 | function_hooker_stdcall() = default; 555 | function_hooker_stdcall(const function_hooker_stdcall&) = delete; 556 | function_hooker_stdcall(function_hooker_stdcall&& rhs) : base(std::move(rhs)) {} 557 | function_hooker_stdcall& operator=(const function_hooker_stdcall& rhs) = delete; 558 | function_hooker_stdcall& operator=(function_hooker_stdcall&& rhs) 559 | { base::operator=(std::move(rhs)); return *this; } 560 | 561 | // Makes the hook 562 | void make_call(typename base::functor_type functor) 563 | { 564 | return base::make_call(std::move(functor), raw_ptr(call)); 565 | } 566 | }; 567 | 568 | 569 | /* 570 | * function_hooker_fastcall 571 | * For fastcall conventions (__fastcall) 572 | */ 573 | template 574 | struct function_hooker_fastcall; 575 | 576 | template 577 | struct function_hooker_fastcall 578 | : public function_hooker_base 579 | { 580 | private: 581 | using base = function_hooker_base; 582 | 583 | // The hook caller 584 | static Ret __fastcall call(Args... a) 585 | { 586 | return base::call_hooks(a...); 587 | } 588 | 589 | public: 590 | // Constructors, move constructors, assigment operators........ 591 | function_hooker_fastcall() = default; 592 | function_hooker_fastcall(const function_hooker_fastcall&) = delete; 593 | function_hooker_fastcall(function_hooker_fastcall&& rhs) : base(std::move(rhs)) {} 594 | function_hooker_fastcall& operator=(const function_hooker_fastcall& rhs) = delete; 595 | function_hooker_fastcall& operator=(function_hooker_fastcall&& rhs) 596 | { base::operator=(std::move(rhs)); return *this; } 597 | 598 | // Makes the hook 599 | void make_call(typename base::functor_type functor) 600 | { 601 | return base::make_call(std::move(functor), raw_ptr(call)); 602 | } 603 | }; 604 | 605 | 606 | /* 607 | * function_hooker_thiscall 608 | * For thiscall conventions (__thiscall, class methods) 609 | */ 610 | template 611 | struct function_hooker_thiscall; 612 | 613 | template 614 | struct function_hooker_thiscall 615 | : public function_hooker_base 616 | { 617 | private: 618 | using base = function_hooker_base; 619 | 620 | // The hook caller 621 | static Ret __thiscall call(Args... a) 622 | { 623 | return base::call_hooks(a...); 624 | } 625 | 626 | public: 627 | // Constructors, move constructors, assigment operators........ 628 | function_hooker_thiscall() = default; 629 | function_hooker_thiscall(const function_hooker_thiscall&) = delete; 630 | function_hooker_thiscall(function_hooker_thiscall&& rhs) : base(std::move(rhs)) {} 631 | function_hooker_thiscall& operator=(const function_hooker_thiscall& rhs) = delete; 632 | function_hooker_thiscall& operator=(function_hooker_thiscall&& rhs) 633 | { base::operator=(std::move(rhs)); return *this; } 634 | 635 | // Makes the hook 636 | void make_call(typename base::functor_type functor) 637 | { 638 | return base::make_call(std::move(functor), raw_ptr(call)); 639 | } 640 | }; 641 | 642 | 643 | 644 | /******************* HELPERS ******************/ 645 | 646 | /* 647 | * Adds a hook to be alive for the entire program lifetime 648 | * That means the hook received will be alive until the program dies. 649 | * Note: Parameter must be a rvalue 650 | */ 651 | template inline 652 | T& add_static_hook(T&& hooker) 653 | { 654 | static std::list a; 655 | return *a.emplace(a.end(), std::move(hooker)); 656 | } 657 | 658 | /* 659 | * Makes a hook which is alive until it gets out of scope 660 | * 'T' must be any function_hooker object 661 | */ 662 | template inline 663 | T make_function_hook(F functor) 664 | { 665 | T a; 666 | a.make_call(std::move(functor)); 667 | return a; 668 | } 669 | 670 | /* 671 | * Makes a hook which is alive for the entire lifetime of this program 672 | * 'T' must be any function_hooker object 673 | */ 674 | template inline 675 | T& make_static_hook(F functor) 676 | { 677 | return add_static_hook(make_function_hook(std::move(functor))); 678 | } 679 | 680 | 681 | // TODO when we have access to C++14 add a make_function_hook, make_stdcall_function_hook, and so on 682 | // the problem behind implement it with C++11 is that lambdas cannot be generic and the first param of a hook is a functor pointing 683 | // to the previous call pointer 684 | 685 | #endif 686 | 687 | } 688 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/injector/injector.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Base Header 3 | * 4 | * Copyright (C) 2012-2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | #define INJECTOR_HAS_INJECTOR_HPP 28 | #include 29 | #include 30 | #include 31 | #include "gvm/gvm.hpp" 32 | /* 33 | The following macros (#define) are relevant on this header: 34 | 35 | INJECTOR_GVM_HAS_TRANSLATOR 36 | If defined, the user should provide their own address_manager::translator function. 37 | That function is responssible for translating a void pointer (that mayn't be an actual pointer) into an actual address. 38 | The meaning of that void pointer will be made by YOU when you send it to the functions that receive pointers on this library. 39 | The default translator does nothing but returns that void pointer as the address. 40 | 41 | INJECTOR_GVM_OWN_DETECT 42 | If defined, the user should provide it's own game detection function thought game_version_manager::Detect 43 | By default it provide an good detection for the Grand Theft Auto series. 44 | 45 | INJECTOR_GVM_PLUGIN_NAME 46 | If this is defined, it will be used as the plugin name used at error messages. 47 | By default it will use ""Unknown Plugin Name" 48 | 49 | INJECTOR_GVM_DUMMY 50 | If defined, the game_version_manager will be a dummy object 51 | By default it provides a nice gvm for Grand Theft Auto series 52 | 53 | INJECTOR_OWN_GVM 54 | If defined, the game_version_manager should be implemented by the user before including this library. 55 | By default it provides a nice gvm for Grand Theft Auto series 56 | */ 57 | #include "gvm/gvm.hpp" 58 | 59 | 60 | 61 | namespace injector 62 | { 63 | 64 | 65 | /* 66 | * auto_pointer 67 | * Casts itself to another pointer type in the lhs 68 | */ 69 | union auto_pointer 70 | { 71 | protected: 72 | friend union memory_pointer_tr; 73 | template friend union basic_memory_pointer; 74 | 75 | void* p; 76 | uintptr_t a; 77 | 78 | public: 79 | auto_pointer() : p(0) {} 80 | auto_pointer(const auto_pointer& x) : p(x.p) {} 81 | explicit auto_pointer(void* x) : p(x) {} 82 | explicit auto_pointer(uint32_t x) : a(x) {} 83 | 84 | bool is_null() const { return this->p != nullptr; } 85 | 86 | #if __cplusplus >= 201103L || _MSC_VER >= 1800 87 | explicit operator bool() const { return is_null(); } 88 | #endif 89 | 90 | auto_pointer get() const { return *this; } 91 | template T* get() const { return (T*) this->p; } 92 | template T* get_raw() const { return (T*) this->p; } 93 | 94 | template 95 | operator T*() const { return reinterpret_cast(p); } 96 | }; 97 | 98 | /* 99 | * basic_memory_pointer 100 | * A memory pointer class that is capable of many operations, including address translation 101 | * MemTranslator is the translator functor 102 | */ 103 | template 104 | union basic_memory_pointer 105 | { 106 | protected: 107 | void* p; 108 | uintptr_t a; 109 | 110 | // Translates address p to the running executable pointer 111 | static auto_pointer memory_translate(void* p) 112 | { 113 | return auto_pointer(MemTranslator()(p)); 114 | } 115 | 116 | public: 117 | basic_memory_pointer() : p(nullptr) {} 118 | basic_memory_pointer(std::nullptr_t) : p(nullptr) {} 119 | basic_memory_pointer(uintptr_t x) : a(x) {} 120 | basic_memory_pointer(const auto_pointer& x) : p(x.p) {} 121 | basic_memory_pointer(const basic_memory_pointer& rhs) : p(rhs.p) {} 122 | 123 | template 124 | basic_memory_pointer(T* x) : p((void*)x) {} 125 | 126 | 127 | 128 | 129 | // Gets the translated pointer (plus automatic casting to lhs) 130 | auto_pointer get() const { return memory_translate(p); } 131 | 132 | // Gets the translated pointer (casted to T*) 133 | template T* get() const { return get(); } 134 | 135 | // Gets the raw pointer, without translation (casted to T*) 136 | template T* get_raw() const { return auto_pointer(p); } 137 | 138 | // This type can get assigned from void* and uintptr_t 139 | basic_memory_pointer& operator=(void* x) { return p = x, *this; } 140 | basic_memory_pointer& operator=(uintptr_t x) { return a = x, *this; } 141 | 142 | /* Arithmetic */ 143 | basic_memory_pointer operator+(const basic_memory_pointer& rhs) const 144 | { return basic_memory_pointer(this->a + rhs.a); } 145 | 146 | basic_memory_pointer operator-(const basic_memory_pointer& rhs) const 147 | { return basic_memory_pointer(this->a - rhs.a); } 148 | 149 | basic_memory_pointer operator*(const basic_memory_pointer& rhs) const 150 | { return basic_memory_pointer(this->a * rhs.a); } 151 | 152 | basic_memory_pointer operator/(const basic_memory_pointer& rhs) const 153 | { return basic_memory_pointer(this->a / rhs.a); } 154 | 155 | 156 | /* Comparision */ 157 | bool operator==(const basic_memory_pointer& rhs) const 158 | { return this->a == rhs.a; } 159 | 160 | bool operator!=(const basic_memory_pointer& rhs) const 161 | { return this->a != rhs.a; } 162 | 163 | bool operator<(const basic_memory_pointer& rhs) const 164 | { return this->a < rhs.a; } 165 | 166 | bool operator<=(const basic_memory_pointer& rhs) const 167 | { return this->a <= rhs.a; } 168 | 169 | bool operator>(const basic_memory_pointer& rhs) const 170 | { return this->a > rhs.a; } 171 | 172 | bool operator>=(const basic_memory_pointer& rhs) const 173 | { return this->a >=rhs.a; } 174 | 175 | bool is_null() const { return this->p == nullptr; } 176 | uintptr_t as_int() const { return this->a; } // does not perform translation 177 | 178 | 179 | 180 | #if __cplusplus >= 201103L || _MSC_VER >= 1800 // MSVC 2013 181 | /* Conversion to other types */ 182 | explicit operator uintptr_t() const 183 | { return this->a; } // does not perform translation 184 | explicit operator bool() const 185 | { return this->p != nullptr; } 186 | #else 187 | //operator bool() -------------- Causes casting problems because of implicitness, use !is_null() 188 | //{ return this->p != nullptr; } 189 | #endif 190 | 191 | }; 192 | 193 | // Typedefs including memory translator for the above type 194 | typedef basic_memory_pointer memory_pointer; 195 | typedef basic_memory_pointer memory_pointer_raw; 196 | typedef basic_memory_pointer memory_pointer_aslr; 197 | 198 | 199 | 200 | /* 201 | * memory_pointer_tr 202 | * Stores a basic_memory_pointer as a raw pointer from translated pointer 203 | */ 204 | union memory_pointer_tr 205 | { 206 | protected: 207 | void* p; 208 | uintptr_t a; 209 | 210 | public: 211 | template 212 | memory_pointer_tr(const basic_memory_pointer& ptr) 213 | : p(ptr.get()) 214 | {} // Constructs from a basic_memory_pointer 215 | 216 | memory_pointer_tr(const auto_pointer& ptr) 217 | : p(ptr.p) 218 | {} // Constructs from a auto_pointer, probably comming from basic_memory_pointer::get 219 | 220 | memory_pointer_tr(const memory_pointer_tr& rhs) 221 | : p(rhs.p) 222 | {} // Constructs from my own type, copy constructor 223 | 224 | memory_pointer_tr(uintptr_t x) 225 | : p(memory_pointer(x).get()) 226 | {} // Constructs from a integer, translating the address 227 | 228 | memory_pointer_tr(void* x) 229 | : p(memory_pointer(x).get()) 230 | {} // Constructs from a void pointer, translating the address 231 | 232 | // Just to be method-compatible with basic_memory_pointer ... 233 | auto_pointer get() { return auto_pointer(p); } 234 | template T* get() { return get(); } 235 | template T* get_raw() { return get(); } 236 | 237 | memory_pointer_tr operator+(const uintptr_t& rhs) const 238 | { return memory_pointer_raw(this->a + rhs); } 239 | 240 | memory_pointer_tr operator-(const uintptr_t& rhs) const 241 | { return memory_pointer_raw(this->a - rhs); } 242 | 243 | memory_pointer_tr operator*(const uintptr_t& rhs) const 244 | { return memory_pointer_raw(this->a * rhs); } 245 | 246 | memory_pointer_tr operator/(const uintptr_t& rhs) const 247 | { return memory_pointer_raw(this->a / rhs); } 248 | 249 | bool is_null() const { return this->p == nullptr; } 250 | uintptr_t as_int() const { return this->a; } 251 | 252 | #if __cplusplus >= 201103L 253 | explicit operator uintptr_t() const 254 | { return this->a; } 255 | #else 256 | #endif 257 | 258 | }; 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | /* 267 | * ProtectMemory 268 | * Makes the address @addr have a protection of @protection 269 | */ 270 | inline bool ProtectMemory(memory_pointer_tr addr, size_t size, DWORD protection) 271 | { 272 | return VirtualProtect(addr.get(), size, protection, &protection) != 0; 273 | } 274 | 275 | /* 276 | * UnprotectMemory 277 | * Unprotect the memory at @addr with size @size so it have all accesses (execute, read and write) 278 | * Returns the old protection to out_oldprotect 279 | */ 280 | inline bool UnprotectMemory(memory_pointer_tr addr, size_t size, DWORD& out_oldprotect) 281 | { 282 | return VirtualProtect(addr.get(), size, PAGE_EXECUTE_READWRITE, &out_oldprotect) != 0; 283 | } 284 | 285 | /* 286 | * scoped_unprotect 287 | * RAII wrapper for UnprotectMemory 288 | * On construction unprotects the memory, on destruction reprotects the memory 289 | */ 290 | struct scoped_unprotect 291 | { 292 | memory_pointer_raw addr; 293 | size_t size; 294 | DWORD dwOldProtect; 295 | bool bUnprotected; 296 | 297 | scoped_unprotect(memory_pointer_tr addr, size_t size) 298 | { 299 | if(size == 0) bUnprotected = false; 300 | else bUnprotected = UnprotectMemory(this->addr = addr.get(), this->size = size, dwOldProtect); 301 | } 302 | 303 | ~scoped_unprotect() 304 | { 305 | if(bUnprotected) ProtectMemory(this->addr.get(), this->size, this->dwOldProtect); 306 | } 307 | }; 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | /* 317 | * WriteMemoryRaw 318 | * Writes into memory @addr the content of @value with a sizeof @size 319 | * Does memory unprotection if @vp is true 320 | */ 321 | inline void WriteMemoryRaw(memory_pointer_tr addr, void* value, size_t size, bool vp) 322 | { 323 | scoped_unprotect xprotect(addr, vp? size : 0); 324 | memcpy(addr.get(), value, size); 325 | } 326 | 327 | /* 328 | * ReadMemoryRaw 329 | * Reads the memory at @addr with a sizeof @size into address @ret 330 | * Does memory unprotection if @vp is true 331 | */ 332 | inline void ReadMemoryRaw(memory_pointer_tr addr, void* ret, size_t size, bool vp) 333 | { 334 | scoped_unprotect xprotect(addr, vp? size : 0); 335 | memcpy(ret, addr.get(), size); 336 | } 337 | 338 | /* 339 | * MemoryFill 340 | * Fills the memory at @addr with the byte @value doing it @size times 341 | * Does memory unprotection if @vp is true 342 | */ 343 | inline void MemoryFill(memory_pointer_tr addr, uint8_t value, size_t size, bool vp) 344 | { 345 | scoped_unprotect xprotect(addr, vp? size : 0); 346 | memset(addr.get(), value, size); 347 | } 348 | 349 | /* 350 | * WriteObject 351 | * Assigns the object @value into the same object type at @addr 352 | * Does memory unprotection if @vp is true 353 | */ 354 | template 355 | inline T& WriteObject(memory_pointer_tr addr, const T& value, bool vp = false) 356 | { 357 | scoped_unprotect xprotect(addr, vp? sizeof(value) : 0); 358 | return (*addr.get() = value); 359 | } 360 | 361 | /* 362 | * ReadObject 363 | * Assigns the object @value with the value of the same object type at @addr 364 | * Does memory unprotection if @vp is true 365 | */ 366 | template 367 | inline T& ReadObject(memory_pointer_tr addr, T& value, bool vp = false) 368 | { 369 | scoped_unprotect xprotect(addr, vp? sizeof(value) : 0); 370 | return (value = *addr.get()); 371 | } 372 | 373 | 374 | /* 375 | * WriteMemory 376 | * Writes the object of type T into the address @addr 377 | * Does memory unprotection if @vp is true 378 | */ 379 | template 380 | inline void WriteMemory(memory_pointer_tr addr, T value, bool vp = false) 381 | { 382 | WriteObject(addr, value, vp); 383 | } 384 | 385 | /* 386 | * ReadMemory 387 | * Reads the object type T at address @addr 388 | * Does memory unprotection if @vp is true 389 | */ 390 | template 391 | inline T ReadMemory(memory_pointer_tr addr, bool vp = false) 392 | { 393 | T value; 394 | return ReadObject(addr, value, vp); 395 | } 396 | 397 | /* 398 | * AdjustPointer 399 | * Searches in the range [@addr, @addr + @max_search] for a pointer in the range [@default_base, @default_end] and replaces 400 | * it with the proper offset in the pointer @replacement_base. 401 | * Does memory unprotection if @vp is true. 402 | */ 403 | inline memory_pointer_raw AdjustPointer(memory_pointer_tr addr, 404 | memory_pointer_raw replacement_base, memory_pointer_tr default_base, memory_pointer_tr default_end, 405 | size_t max_search = 8, bool vp = true) 406 | { 407 | scoped_unprotect xprotect(addr, vp? max_search + sizeof(void*) : 0); 408 | for(size_t i = 0; i < max_search; ++i) 409 | { 410 | memory_pointer_raw ptr = ReadMemory(addr + i); 411 | if(ptr >= default_base.get() && ptr <= default_end.get()) 412 | { 413 | auto result = replacement_base + (ptr - default_base.get()); 414 | WriteMemory(addr + i, result.get()); 415 | return result; 416 | } 417 | } 418 | return nullptr; 419 | } 420 | 421 | 422 | 423 | 424 | 425 | 426 | /* 427 | * GetAbsoluteOffset 428 | * Gets absolute address based on relative offset @rel_value from instruction that ends at @end_of_instruction 429 | */ 430 | inline memory_pointer_raw GetAbsoluteOffset(int rel_value, memory_pointer_tr end_of_instruction) 431 | { 432 | return end_of_instruction.get() + rel_value; 433 | } 434 | 435 | /* 436 | * GetRelativeOffset 437 | * Gets relative offset based on absolute address @abs_value for instruction that ends at @end_of_instruction 438 | */ 439 | inline int GetRelativeOffset(memory_pointer_tr abs_value, memory_pointer_tr end_of_instruction) 440 | { 441 | return uintptr_t(abs_value.get() - end_of_instruction.get()); 442 | } 443 | 444 | /* 445 | * ReadRelativeOffset 446 | * Reads relative offset from address @at 447 | */ 448 | inline memory_pointer_raw ReadRelativeOffset(memory_pointer_tr at, size_t sizeof_addr = 4, bool vp = true) 449 | { 450 | switch(sizeof_addr) 451 | { 452 | case 1: return (GetAbsoluteOffset(ReadMemory (at, vp), at+sizeof_addr)); 453 | case 2: return (GetAbsoluteOffset(ReadMemory(at, vp), at+sizeof_addr)); 454 | case 4: return (GetAbsoluteOffset(ReadMemory(at, vp), at+sizeof_addr)); 455 | } 456 | return nullptr; 457 | } 458 | 459 | /* 460 | * MakeRelativeOffset 461 | * Writes relative offset into @at based on absolute destination @dest 462 | */ 463 | inline void MakeRelativeOffset(memory_pointer_tr at, memory_pointer_tr dest, size_t sizeof_addr = 4, bool vp = true) 464 | { 465 | switch(sizeof_addr) 466 | { 467 | case 1: WriteMemory (at, static_cast (GetRelativeOffset(dest, at+sizeof_addr)), vp); 468 | case 2: WriteMemory(at, static_cast(GetRelativeOffset(dest, at+sizeof_addr)), vp); 469 | case 4: WriteMemory(at, static_cast(GetRelativeOffset(dest, at+sizeof_addr)), vp); 470 | } 471 | } 472 | 473 | /* 474 | * GetBranchDestination 475 | * Gets the destination of a branch instruction at address @at 476 | * *** Works only with JMP and CALL for now *** 477 | */ 478 | inline memory_pointer_raw GetBranchDestination(memory_pointer_tr at, bool vp = true) 479 | { 480 | switch(ReadMemory(at, vp)) 481 | { 482 | // We need to handle other instructions (and prefixes) later... 483 | case 0xE8: // call rel 484 | case 0xE9: // jmp rel 485 | return ReadRelativeOffset(at + 1, 4, vp); 486 | 487 | case 0xFF: 488 | switch(ReadMemory(at + 1, vp)) 489 | { 490 | case 0x15: // call dword ptr [addr] 491 | case 0x25: // jmp dword ptr [addr] 492 | return *(ReadMemory(at + 2, vp)); 493 | } 494 | break; 495 | } 496 | return nullptr; 497 | } 498 | 499 | /* 500 | * MakeJMP 501 | * Creates a JMP instruction at address @at that jumps into address @dest 502 | * If there was already a branch instruction there, returns the previosly destination of the branch 503 | */ 504 | inline memory_pointer_raw MakeJMP(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 505 | { 506 | auto p = GetBranchDestination(at, vp); 507 | WriteMemory(at, 0xE9, vp); 508 | MakeRelativeOffset(at+1, dest, 4, vp); 509 | return p; 510 | } 511 | 512 | /* 513 | * MakeCALL 514 | * Creates a CALL instruction at address @at that jumps into address @dest 515 | * If there was already a branch instruction there, returns the previosly destination of the branch 516 | */ 517 | inline memory_pointer_raw MakeCALL(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 518 | { 519 | auto p = GetBranchDestination(at, vp); 520 | WriteMemory(at, 0xE8, vp); 521 | MakeRelativeOffset(at+1, dest, 4, vp); 522 | return p; 523 | } 524 | 525 | /* 526 | * MakeJA 527 | * Creates a JA instruction at address @at that jumps if above into address @dest 528 | * If there was already a branch instruction there, returns the previosly destination of the branch 529 | */ 530 | inline void MakeJA(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 531 | { 532 | WriteMemory(at, 0x87F0, vp); 533 | MakeRelativeOffset(at+2, dest, 4, vp); 534 | } 535 | 536 | /* 537 | * MakeNOP 538 | * Creates a bunch of NOP instructions at address @at 539 | */ 540 | inline void MakeNOP(memory_pointer_tr at, size_t count = 1, bool vp = true) 541 | { 542 | MemoryFill(at, 0x90, count, vp); 543 | } 544 | 545 | /* 546 | * MakeRangedNOP 547 | * Creates a bunch of NOP instructions at address @at until address @until 548 | */ 549 | inline void MakeRangedNOP(memory_pointer_tr at, memory_pointer_tr until, bool vp = true) 550 | { 551 | return MakeNOP(at, size_t(until.get_raw() - at.get_raw()), vp); 552 | } 553 | 554 | 555 | /* 556 | * MakeRET 557 | * Creates a RET instruction at address @at popping @pop values from the stack 558 | * If @pop is equal to 0 it will use the 1 byte form of the instruction 559 | */ 560 | inline void MakeRET(memory_pointer_tr at, uint16_t pop = 0, bool vp = true) 561 | { 562 | WriteMemory(at, pop? 0xC2 : 0xC3, vp); 563 | if(pop) WriteMemory(at+1, pop, vp); 564 | } 565 | 566 | 567 | 568 | 569 | 570 | /* 571 | * lazy_pointer 572 | * Lazy pointer, where it's final value will get evaluated only once when finally needed. 573 | */ 574 | template 575 | struct lazy_pointer 576 | { 577 | public: 578 | // Returns the final raw pointer 579 | static auto_pointer get() 580 | { 581 | return xget().get(); 582 | } 583 | 584 | template 585 | static T* get() 586 | { 587 | return get().get(); 588 | } 589 | 590 | private: 591 | // Returns the final pointer 592 | static memory_pointer_raw xget() 593 | { 594 | static void* ptr = nullptr; 595 | if(!ptr) ptr = memory_pointer(addr).get(); 596 | return memory_pointer_raw(ptr); 597 | } 598 | }; 599 | 600 | /* 601 | * lazy_object 602 | * Lazy object, where it's final object will get evaluated only once when finally needed. 603 | */ 604 | template 605 | struct lazy_object 606 | { 607 | static T& get() 608 | { 609 | static T data; 610 | static bool has_data = false; 611 | if(!has_data) 612 | { 613 | ReadObject(addr, data, true); 614 | has_data = true; 615 | } 616 | return data; 617 | } 618 | }; 619 | 620 | 621 | /* 622 | Helpers 623 | */ 624 | 625 | template 626 | inline memory_pointer mem_ptr(T p) 627 | { 628 | return memory_pointer(p); 629 | } 630 | 631 | template 632 | inline memory_pointer_raw raw_ptr(T p) 633 | { 634 | return memory_pointer_raw(p); 635 | } 636 | 637 | template 638 | inline memory_pointer_raw raw_ptr(basic_memory_pointer p) 639 | { 640 | return raw_ptr(p.get()); 641 | } 642 | 643 | template 644 | inline memory_pointer_raw lazy_ptr() 645 | { 646 | return lazy_pointer::get(); 647 | } 648 | 649 | template 650 | inline memory_pointer_aslr aslr_ptr(T p) 651 | { 652 | return memory_pointer_aslr(p); 653 | } 654 | 655 | 656 | 657 | 658 | 659 | 660 | #ifndef INJECTOR_GVM_OWN_DETECT // Should we implement our detection method? 661 | 662 | // Detects game, region and version; returns false if could not detect it 663 | inline bool game_version_manager::Detect() 664 | { 665 | // Cleanup data 666 | this->Clear(); 667 | 668 | // Find NT header 669 | uintptr_t base = (uintptr_t) GetModuleHandleA(NULL); 670 | IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)(base); 671 | IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(base + dos->e_lfanew); 672 | 673 | // Look for game and version thought the entry-point 674 | // Thanks to Silent for many of the entry point offsets 675 | switch (base + nt->OptionalHeader.AddressOfEntryPoint + (0x400000 - base)) 676 | { 677 | case 0x5C1E70: // GTA III 1.0 678 | game = '3', major = 1, minor = 0, region = 0, steam = false; 679 | return true; 680 | 681 | case 0x5C2130: // GTA III 1.1 682 | game = '3', major = 1, minor = 1, region = 0, steam = false; 683 | return true; 684 | 685 | case 0x5C6FD0: // GTA III 1.1 (Cracked Steam Version) 686 | case 0x9912ED: // GTA III 1.1 (Encrypted Steam Version) 687 | game = '3', major = 1, minor = 1, region = 0, steam = true; 688 | return true; 689 | 690 | case 0x667BF0: // GTA VC 1.0 691 | game = 'V', major = 1, minor = 0, region = 0, steam = false; 692 | return true; 693 | 694 | case 0x667C40: // GTA VC 1.1 695 | game = 'V', major = 1, minor = 1, region = 0, steam = false; 696 | return true; 697 | 698 | case 0x666BA0: // GTA VC 1.1 (Cracked Steam Version) 699 | case 0xA402ED: // GTA VC 1.1 (Encrypted Steam Version) 700 | game = 'V', major = 1, minor = 1, region = 0, steam = true; 701 | return true; 702 | 703 | case 0x82457C: // GTA SA 1.0 US Cracked 704 | case 0x824570: // GTA SA 1.0 US Compact 705 | game = 'S', major = 1, minor = 0, region = 'U', steam = false; 706 | cracker = injector::ReadMemory(raw_ptr(0x406A20), true) == 0xE9? 'H' : 0; 707 | return true; 708 | 709 | case 0x8245BC: // GTA SA 1.0 EU Cracked (??????) 710 | case 0x8245B0: // GTA SA 1.0 EU Cracked 711 | game = 'S', major = 1, minor = 0, region = 'E', steam = false; 712 | cracker = injector::ReadMemory(raw_ptr(0x406A20), true) == 0xE9? 'H' : 0; // just to say 'securom' 713 | return true; 714 | 715 | case 0x8252FC: // GTA SA 1.1 US Cracked 716 | game = 'S', major = 1, minor = 1, region = 'U', steam = false; 717 | return true; 718 | 719 | case 0x82533C: // GTA SA 1.1 EU Cracked 720 | game = 'S', major = 1, minor = 1, region = 'E', steam = false; 721 | return true; 722 | 723 | case 0x85EC4A: // GTA SA 3.0 (Cracked Steam Version) 724 | case 0xD3C3DB: // GTA SA 3.0 (Encrypted Steam Version) 725 | game = 'S', major = 3, minor = 0, region = 0, steam = true; 726 | return true; 727 | 728 | case 0xC965AD: // GTA IV 1.0.0.4 US 729 | game = 'I', major = 1, minor = 0, majorRevision = 0, minorRevision = 4, region = 'U', steam = false; 730 | return true; 731 | 732 | case 0xD0D011: // GTA IV 1.0.0.7 US 733 | game = 'I', major = 1, minor = 0, majorRevision = 0, minorRevision = 7, region = 'U', steam = false; 734 | return true; 735 | 736 | case 0xD0AF06: // GTA EFLC 1.1.2.0 US 737 | game = 'E', major = 1, minor = 1, majorRevision = 2, minorRevision = 0, region = 'U', steam = false; 738 | return true; 739 | 740 | default: 741 | return false; 742 | } 743 | } 744 | 745 | #endif 746 | 747 | 748 | } // namespace 749 | 750 | -------------------------------------------------------------------------------- /XLivelessAddon/includes/injector/utility.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Utility / Helpers 3 | * 4 | * Copyright (C) 2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | 28 | namespace injector 29 | { 30 | template 31 | T return_value() 32 | { 33 | return value; 34 | } 35 | 36 | template 37 | void* force_ptr(const T& fun) 38 | { 39 | auto ptr = fun; 40 | return *(void**)&ptr; 41 | } 42 | 43 | 44 | // Helper structure to help calling back what was there before a hook 45 | // e.g. hb.fun = MakeCALL(0x0, raw_ptr(my_hook)); 46 | template 47 | struct hook_back 48 | { 49 | typedef FuncType func_type; 50 | 51 | func_type fun; 52 | 53 | hook_back() : fun(nullptr) 54 | {} 55 | }; 56 | }; 57 | --------------------------------------------------------------------------------