├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── CTemplate ├── CTemplate.vcxproj ├── CTemplate.vcxproj.filters ├── ExampleHooks.cpp ├── ExampleHooks.h ├── MapleAPI.cpp ├── MapleAPI.h ├── dllmain.cpp ├── framework.h ├── ijl15.cpp ├── ijl15.h ├── pch.cpp └── pch.h ├── Common ├── Common.cpp ├── Common.h ├── Common.vcxproj ├── Common.vcxproj.filters ├── FakeModule.cpp ├── FakeModule.h ├── TSecType.h ├── ZAllocAnonSelector.h ├── ZAllocBase.h ├── ZAllocEx.cpp ├── ZAllocEx.h ├── ZAllocStrSelector.h ├── ZArray.h ├── ZFatalSection.h ├── ZList.h ├── ZMap.h ├── ZRecyclable.h ├── ZRecyclableAvBuffer.h ├── ZRecyclableStatic.h ├── ZRef.h ├── ZRefCounted.h ├── ZRefCountedAccessor.h ├── ZRefCountedDummy.h ├── ZXString.h ├── ZtlSecure.h ├── detours.h ├── hooker.cpp ├── hooker.h ├── logger.cpp ├── logger.h ├── memedit.cpp ├── memedit.h ├── winhook_types.cpp ├── winhook_types.h ├── winhooks.cpp └── winhooks.h ├── GenericLauncher ├── GenericLauncher.vcxproj ├── GenericLauncher.vcxproj.filters └── main.cpp ├── Include ├── README.md └── detours.lib ├── MapleClientCollection.sln ├── README.md ├── TemplateTesting ├── TemplateTesting.cpp ├── TemplateTesting.vcxproj └── TemplateTesting.vcxproj.filters └── UnitTesting ├── TSecTypeUnitTesting.cpp ├── UnitTesting.vcxproj ├── UnitTesting.vcxproj.filters ├── ZArrayUnitTesting.cpp ├── ZListUnitTesting.cpp ├── ZRefUnitTesting.cpp ├── ZXStringUnitTesting.cpp ├── ZtlSecureUnitTesting.cpp ├── pch.cpp └── pch.h /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [MinimumDelta] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | [Oo]tput/ 30 | 31 | # Visual Studio 2015/2017 cache/options directory 32 | .vs/ 33 | # Uncomment if you have tasks that create the project's static files in wwwroot 34 | #wwwroot/ 35 | 36 | # Visual Studio 2017 auto generated files 37 | Generated\ Files/ 38 | 39 | # MSTest test Results 40 | [Tt]est[Rr]esult*/ 41 | [Bb]uild[Ll]og.* 42 | 43 | # NUNIT 44 | *.VisualState.xml 45 | TestResult.xml 46 | 47 | # Build Results of an ATL Project 48 | [Dd]ebugPS/ 49 | [Rr]eleasePS/ 50 | dlldata.c 51 | 52 | # Benchmark Results 53 | BenchmarkDotNet.Artifacts/ 54 | 55 | # .NET Core 56 | project.lock.json 57 | project.fragment.lock.json 58 | artifacts/ 59 | 60 | # StyleCop 61 | StyleCopReport.xml 62 | 63 | # Files built by Visual Studio 64 | *_i.c 65 | *_p.c 66 | *_h.h 67 | *.ilk 68 | *.meta 69 | *.obj 70 | *.iobj 71 | *.pch 72 | *.pdb 73 | *.ipdb 74 | *.pgc 75 | *.pgd 76 | *.rsp 77 | *.sbr 78 | *.tlb 79 | *.tli 80 | *.tlh 81 | *.tmp 82 | *.tmp_proj 83 | *_wpftmp.csproj 84 | *.log 85 | *.vspscc 86 | *.vssscc 87 | .builds 88 | *.pidb 89 | *.svclog 90 | *.scc 91 | 92 | # Chutzpah Test files 93 | _Chutzpah* 94 | 95 | # Visual C++ cache files 96 | ipch/ 97 | *.aps 98 | *.ncb 99 | *.opendb 100 | *.opensdf 101 | *.sdf 102 | *.cachefile 103 | *.VC.db 104 | *.VC.VC.opendb 105 | 106 | # Visual Studio profiler 107 | *.psess 108 | *.vsp 109 | *.vspx 110 | *.sap 111 | 112 | # Visual Studio Trace Files 113 | *.e2e 114 | 115 | # TFS 2012 Local Workspace 116 | $tf/ 117 | 118 | # Guidance Automation Toolkit 119 | *.gpState 120 | 121 | # ReSharper is a .NET coding add-in 122 | _ReSharper*/ 123 | *.[Rr]e[Ss]harper 124 | *.DotSettings.user 125 | 126 | # JustCode is a .NET coding add-in 127 | .JustCode 128 | 129 | # TeamCity is a build add-in 130 | _TeamCity* 131 | 132 | # DotCover is a Code Coverage Tool 133 | *.dotCover 134 | 135 | # AxoCover is a Code Coverage Tool 136 | .axoCover/* 137 | !.axoCover/settings.json 138 | 139 | # Visual Studio code coverage results 140 | *.coverage 141 | *.coveragexml 142 | 143 | # NCrunch 144 | _NCrunch_* 145 | .*crunch*.local.xml 146 | nCrunchTemp_* 147 | 148 | # MightyMoose 149 | *.mm.* 150 | AutoTest.Net/ 151 | 152 | # Web workbench (sass) 153 | .sass-cache/ 154 | 155 | # Installshield output folder 156 | [Ee]xpress/ 157 | 158 | # DocProject is a documentation generator add-in 159 | DocProject/buildhelp/ 160 | DocProject/Help/*.HxT 161 | DocProject/Help/*.HxC 162 | DocProject/Help/*.hhc 163 | DocProject/Help/*.hhk 164 | DocProject/Help/*.hhp 165 | DocProject/Help/Html2 166 | DocProject/Help/html 167 | 168 | # Click-Once directory 169 | publish/ 170 | 171 | # Publish Web Output 172 | *.[Pp]ublish.xml 173 | *.azurePubxml 174 | # Note: Comment the next line if you want to checkin your web deploy settings, 175 | # but database connection strings (with potential passwords) will be unencrypted 176 | *.pubxml 177 | *.publishproj 178 | 179 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 180 | # checkin your Azure Web App publish settings, but sensitive information contained 181 | # in these scripts will be unencrypted 182 | PublishScripts/ 183 | 184 | # NuGet Packages 185 | *.nupkg 186 | # The packages folder can be ignored because of Package Restore 187 | **/[Pp]ackages/* 188 | # except build/, which is used as an MSBuild target. 189 | !**/[Pp]ackages/build/ 190 | # Uncomment if necessary however generally it will be regenerated when needed 191 | #!**/[Pp]ackages/repositories.config 192 | # NuGet v3's project.json files produces more ignorable files 193 | *.nuget.props 194 | *.nuget.targets 195 | 196 | # Microsoft Azure Build Output 197 | csx/ 198 | *.build.csdef 199 | 200 | # Microsoft Azure Emulator 201 | ecf/ 202 | rcf/ 203 | 204 | # Windows Store app package directories and files 205 | AppPackages/ 206 | BundleArtifacts/ 207 | Package.StoreAssociation.xml 208 | _pkginfo.txt 209 | *.appx 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 | *.jfm 224 | *.pfx 225 | *.publishsettings 226 | orleans.codegen.cs 227 | 228 | # Including strong name files can present a security risk 229 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 230 | #*.snk 231 | 232 | # Since there are multiple workflows, uncomment next line to ignore bower_components 233 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 234 | #bower_components/ 235 | 236 | # RIA/Silverlight projects 237 | Generated_Code/ 238 | 239 | # Backup & report files from converting an old project file 240 | # to a newer Visual Studio version. Backup files are not needed, 241 | # because we have git ;-) 242 | _UpgradeReport_Files/ 243 | Backup*/ 244 | UpgradeLog*.XML 245 | UpgradeLog*.htm 246 | ServiceFabricBackup/ 247 | *.rptproj.bak 248 | 249 | # SQL Server files 250 | *.mdf 251 | *.ldf 252 | *.ndf 253 | 254 | # Business Intelligence projects 255 | *.rdl.data 256 | *.bim.layout 257 | *.bim_*.settings 258 | *.rptproj.rsuser 259 | *- Backup*.rdl 260 | 261 | # Microsoft Fakes 262 | FakesAssemblies/ 263 | 264 | # GhostDoc plugin setting file 265 | *.GhostDoc.xml 266 | 267 | # Node.js Tools for Visual Studio 268 | .ntvs_analysis.dat 269 | node_modules/ 270 | 271 | # Visual Studio 6 build log 272 | *.plg 273 | 274 | # Visual Studio 6 workspace options file 275 | *.opt 276 | 277 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 278 | *.vbw 279 | 280 | # Visual Studio LightSwitch build output 281 | **/*.HTMLClient/GeneratedArtifacts 282 | **/*.DesktopClient/GeneratedArtifacts 283 | **/*.DesktopClient/ModelManifest.xml 284 | **/*.Server/GeneratedArtifacts 285 | **/*.Server/ModelManifest.xml 286 | _Pvt_Extensions 287 | 288 | # Paket dependency manager 289 | .paket/paket.exe 290 | paket-files/ 291 | 292 | # FAKE - F# Make 293 | .fake/ 294 | 295 | # JetBrains Rider 296 | .idea/ 297 | *.sln.iml 298 | 299 | # CodeRush personal settings 300 | .cr/personal 301 | 302 | # Python Tools for Visual Studio (PTVS) 303 | __pycache__/ 304 | *.pyc 305 | 306 | # Cake - Uncomment if you are using it 307 | # tools/** 308 | # !tools/packages.config 309 | 310 | # Tabs Studio 311 | *.tss 312 | 313 | # Telerik's JustMock configuration file 314 | *.jmconfig 315 | 316 | # BizTalk build output 317 | *.btp.cs 318 | *.btm.cs 319 | *.odx.cs 320 | *.xsd.cs 321 | 322 | # OpenCover UI analysis results 323 | OpenCover/ 324 | 325 | # Azure Stream Analytics local run output 326 | ASALocalRun/ 327 | 328 | # MSBuild Binary and Structured Log 329 | *.binlog 330 | 331 | # NVidia Nsight GPU debugger configuration file 332 | *.nvuser 333 | 334 | # MFractors (Xamarin productivity tool) working folder 335 | .mfractor/ 336 | 337 | # Local History for Visual Studio 338 | .localhistory/ 339 | 340 | # BeatPulse healthcheck temp database 341 | healthchecksdb -------------------------------------------------------------------------------- /CTemplate/CTemplate.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 | 16.0 23 | Win32Proj 24 | {d2c48f68-c2b4-41c4-a581-550603163363} 25 | CTemplate 26 | 10.0 27 | CTemplate 28 | 29 | 30 | 31 | DynamicLibrary 32 | true 33 | v142 34 | MultiByte 35 | 36 | 37 | DynamicLibrary 38 | false 39 | v142 40 | true 41 | MultiByte 42 | 43 | 44 | DynamicLibrary 45 | true 46 | v142 47 | Unicode 48 | 49 | 50 | DynamicLibrary 51 | false 52 | v142 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | $(SolutionDir)Output\$(Configuration)\bin\ 77 | LEN 78 | $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(SolutionDir)Include; 79 | $(SolutionDir)Output\$(Configuration)\$(ProjectName)\ 80 | 81 | 82 | false 83 | $(SolutionDir)Output\$(Configuration)\bin\ 84 | $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(SolutionDir)Include; 85 | LEN 86 | $(SolutionDir)Output\$(Configuration)\$(ProjectName)\ 87 | 88 | 89 | true 90 | $(SolutionDir)Common;$(LibraryPath) 91 | 92 | 93 | false 94 | 95 | 96 | 97 | Level3 98 | true 99 | WIN32;_DEBUG;CTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 100 | true 101 | Create 102 | pch.h 103 | %(AdditionalIncludeDirectories);$(SolutionDir)Common 104 | $(IntDir)vc$(PlatformToolsetVersion).pdb 105 | 106 | 107 | Windows 108 | true 109 | false 110 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(SolutionDir)Output\$(Configuration)\bin\Common.lib 111 | $(OutDir)$(TargetName)$(TargetExt) 112 | 113 | 114 | 115 | 116 | Level3 117 | true 118 | true 119 | true 120 | WIN32;NDEBUG;CTemplate_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 121 | true 122 | Create 123 | pch.h 124 | %(AdditionalIncludeDirectories);$(SolutionDir)Common 125 | Speed 126 | $(IntDir)vc$(PlatformToolsetVersion).pdb 127 | 128 | 129 | Windows 130 | true 131 | true 132 | true 133 | false 134 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(SolutionDir)Output\$(Configuration)\bin\Common.lib 135 | $(OutDir)$(TargetName)$(TargetExt) 136 | 137 | 138 | 139 | 140 | Level3 141 | true 142 | _DEBUG;CTemplate_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 143 | true 144 | Use 145 | pch.h 146 | $(SolutionDir)Common;%(AdditionalIncludeDirectories) 147 | 148 | 149 | Windows 150 | true 151 | false 152 | 153 | 154 | 155 | 156 | Level3 157 | true 158 | true 159 | true 160 | NDEBUG;CTemplate_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 161 | true 162 | Use 163 | pch.h 164 | 165 | 166 | Windows 167 | true 168 | true 169 | true 170 | false 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | Create 187 | Create 188 | Create 189 | Create 190 | 191 | 192 | 193 | 194 | {3eb29228-1f83-4dd3-b3c5-0f284fd50f39} 195 | 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /CTemplate/CTemplate.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;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 | {ee34f9f8-3892-447c-8cee-869a00197983} 18 | 19 | 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Proxy 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | 38 | 39 | Source Files 40 | 41 | 42 | Source Files 43 | 44 | 45 | Proxy 46 | 47 | 48 | Source Files 49 | 50 | 51 | Source Files 52 | 53 | 54 | -------------------------------------------------------------------------------- /CTemplate/ExampleHooks.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "ExampleHooks.h" 3 | 4 | // define your hook functions 5 | 6 | namespace MapleHooks 7 | { 8 | /// 9 | /// Example regular cdecl function hook 10 | /// 11 | /// First arg 12 | /// second arg 13 | /// Nothing 14 | void __cdecl ExampleCDecl_Hook(void* pArg1, int nArg2) 15 | { 16 | Log("Detour triggered!"); 17 | 18 | // we only want to execute this function if nArg2 is more than 420, otherwise we return without doing anything 19 | if (nArg2 > 420) 20 | { 21 | _ExampleFunc_cdecl(pArg1, nArg2); 22 | } 23 | } 24 | 25 | /// 26 | /// Example member function call hook 27 | /// 28 | /// Member object4 29 | /// Dummy parameter because we are swapping calling conventions. 30 | /// Some value 31 | /// An integer 32 | int __fastcall ExampleFunc_thiscall2(void* pThis, void* edx, int nArg1) 33 | { 34 | Log("Detour func called, arg1: %d", nArg1); 35 | 36 | if (nArg1 == 420) 37 | { 38 | nArg1 = 69; // possible to change args or do whatever 39 | 40 | Log("Arg changed to 69"); 41 | } 42 | 43 | return _ExampleFunc_thiscall(pThis, NULL, nArg1); // second argument should be null because of the __thiscall -> __fastcall conversion 44 | } 45 | } -------------------------------------------------------------------------------- /CTemplate/ExampleHooks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // declare your hook functions 4 | 5 | namespace MapleHooks 6 | { 7 | void __cdecl ExampleCDecl_Hook(void* pArg1, int nArg2); 8 | int __fastcall ExampleFunc_thiscall2(void* pThis, void* edx, int nArg1); 9 | } -------------------------------------------------------------------------------- /CTemplate/MapleAPI.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | /* 4 | Link your externs in here. 5 | */ 6 | 7 | // example linking 8 | _ExampleFunc_cdecl_t _ExampleFunc_cdecl; 9 | _ExampleFunc_thiscall_t _ExampleFunc_thiscall; 10 | // end example linking -------------------------------------------------------------------------------- /CTemplate/MapleAPI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | Put all your MapleAPI typedefs in here and link them in MapleAPI.cpp 5 | If you don't link them in MapleAPI.cpp you will get import errors 6 | */ 7 | 8 | /// 9 | /// Example cdecl typedef. stdcall will use the same format. 10 | /// 11 | typedef void(__cdecl* _ExampleFunc_cdecl_t)(void* pArg1, int nArg2); 12 | extern _ExampleFunc_cdecl_t _ExampleFunc_cdecl; 13 | 14 | /// 15 | /// Example __thiscall typedef 16 | /// Because this calling convention is only used for member calls, we need to swap it with __fastcall to make it work with our hooks. 17 | /// I'll save you details, but in a nutshell every __fastcall needs at least two arguments. 18 | /// The first argument is pointer to the member class and the second argument is a null pointer to make the calling convention swap work. 19 | /// See more information here: https://guidedhacking.com/threads/thiscall-member-function-hooking.4036/ 20 | /// 21 | typedef int(__fastcall* _ExampleFunc_thiscall_t)(void* pThis, void* edx, int nArg1); 22 | extern _ExampleFunc_thiscall_t _ExampleFunc_thiscall; -------------------------------------------------------------------------------- /CTemplate/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | // BE AWARE ===v 4 | // in order to reference other projects you need to add: 5 | // $(SolutionDir)Common;%(AdditionalIncludeDirectories) 6 | // to project properties -> c/c++ -> additional include directories 7 | #include "ExampleHooks.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // BE AWARE ===v 14 | // in order to fix the detours.lib link error you need to replace 15 | // project properties -> vc++ directories -> library directories 16 | // with $(SolutionDir)Common;$(LibraryPath) 17 | 18 | /// ================ \\\ 19 | 20 | // executed after the client is unpacked 21 | VOID MainFunc() 22 | { 23 | Log(__FUNCTION__); 24 | 25 | return; 26 | 27 | // below hooks only serve as examples -- they will not do anything as-is 28 | 29 | INITMAPLEHOOK( 30 | _ExampleFunc_cdecl, // pointer to original function 31 | _ExampleFunc_cdecl_t, // function type 32 | MapleHooks::ExampleCDecl_Hook, // function to detour to 33 | 0x0 // maple address 34 | ); 35 | 36 | INITMAPLEHOOK( 37 | _ExampleFunc_thiscall, // pointer to original function 38 | _ExampleFunc_thiscall_t, // function type 39 | MapleHooks::ExampleFunc_thiscall2, // function to detour to 40 | 0x0 // maple address 41 | ); 42 | 43 | // edit memory 44 | 45 | WriteValue(0x0, 0xEB); // address to write to, value to write 46 | WriteValue(0x0, 0x42069); 47 | WriteValue(0x0, 420.69); 48 | 49 | PatchNop(0x0, 6); // address to write to, number of nops 50 | 51 | PatchRetZero(0x0); // function start address to return zero at 52 | } 53 | 54 | // prolly don't edit this region if youre a noob 55 | #pragma region EntryThread 56 | 57 | // main thread 58 | VOID MainProc() 59 | { 60 | Log(__FUNCTION__); 61 | 62 | Common::CreateInstance 63 | ( 64 | TRUE, // true if you want to hook windows libraries (besides mutex) set this to false if you already edited your IP into the client (eg v83 localhosts) 65 | MainFunc, // function to be executed after client is unpacked 66 | "127.0.0.1", // IP to connect to (your server IP) 67 | "127.0.0.1" // IP to redirect from (nexon IP) 68 | ); 69 | } 70 | 71 | // dll entry point 72 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 73 | { 74 | switch (ul_reason_for_call) 75 | { 76 | case DLL_PROCESS_ATTACH: 77 | { 78 | Log("DLL_PROCESS_ATTACH"); 79 | 80 | DisableThreadLibraryCalls(hModule); 81 | CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&MainProc, NULL, 0, 0); 82 | break; 83 | } 84 | case DLL_PROCESS_DETACH: 85 | { 86 | Log("DLL_PROCESS_DETACH"); 87 | Common::GetInstance()->~Common(); 88 | break; 89 | } 90 | } 91 | return TRUE; 92 | } 93 | 94 | #pragma endregion -------------------------------------------------------------------------------- /CTemplate/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 4 | // Windows Header Files 5 | #include 6 | -------------------------------------------------------------------------------- /CTemplate/ijl15.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include // for ijl compiler macro 3 | 4 | #if MAPLE_INJECT_USE_IJL 5 | 6 | #include "ijl15.h" 7 | 8 | #define LIB_NAME "ijl15.dll.bak" 9 | #define LIB_EXPORT extern "C" __declspec(dllexport) 10 | 11 | DWORD ijlErrorStr_Proc; 12 | DWORD ijlFree_Proc; 13 | DWORD ijlGetLibVersion_Proc; 14 | DWORD ijlInit_Proc; 15 | DWORD ijlRead_Proc; 16 | DWORD ijlWrite_Proc; 17 | 18 | BOOL InitializeIjl15() 19 | { 20 | DWORD dw; 21 | HANDLE hOrg = CreateFileA(LIB_NAME, (GENERIC_READ | GENERIC_WRITE), NULL, NULL, CREATE_ALWAYS, NULL, NULL); 22 | 23 | if (hOrg) 24 | { 25 | WriteFile(hOrg, g_Ijl15_Raw, sizeof(g_Ijl15_Raw), &dw, NULL); 26 | CloseHandle(hOrg); 27 | } 28 | else 29 | { 30 | OutputDebugStringA("ijl15: Unable to write " LIB_NAME " to disk"); 31 | } 32 | 33 | HMODULE SeData_Base = LoadLibraryA(LIB_NAME); 34 | 35 | if (SeData_Base) 36 | { 37 | ijlErrorStr_Proc = (DWORD)GetProcAddress(SeData_Base, "ijlErrorStr"); 38 | ijlFree_Proc = (DWORD)GetProcAddress(SeData_Base, "ijlFree"); 39 | ijlGetLibVersion_Proc = (DWORD)GetProcAddress(SeData_Base, "ijlGetLibVersion"); 40 | ijlInit_Proc = (DWORD)GetProcAddress(SeData_Base, "ijlInit"); 41 | ijlRead_Proc = (DWORD)GetProcAddress(SeData_Base, "ijlRead"); 42 | ijlWrite_Proc = (DWORD)GetProcAddress(SeData_Base, "ijlWrite"); 43 | 44 | return TRUE; 45 | } 46 | 47 | return FALSE; 48 | } 49 | 50 | BOOL g_InitIjl15 = InitializeIjl15(); //This is the static initializer !!! 51 | 52 | LIB_EXPORT void ijlGetLibVersion() 53 | { 54 | __asm jmp dword ptr[ijlGetLibVersion_Proc] // make sure you're compiling in x86 55 | } 56 | 57 | LIB_EXPORT void ijlInit() 58 | { 59 | __asm jmp dword ptr[ijlInit_Proc] 60 | } 61 | 62 | LIB_EXPORT void ijlFree() 63 | { 64 | __asm jmp dword ptr[ijlFree_Proc] 65 | } 66 | 67 | LIB_EXPORT void ijlRead() 68 | { 69 | __asm jmp dword ptr[ijlRead_Proc] 70 | } 71 | 72 | LIB_EXPORT void ijlWrite() 73 | { 74 | __asm jmp dword ptr[ijlWrite_Proc] 75 | } 76 | 77 | LIB_EXPORT void ijlErrorStr() 78 | { 79 | __asm jmp dword ptr[ijlErrorStr_Proc] 80 | } 81 | 82 | #endif -------------------------------------------------------------------------------- /CTemplate/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /CTemplate/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | #include "framework.h" 12 | #include "MapleAPI.h" 13 | #include 14 | 15 | #endif //PCH_H 16 | -------------------------------------------------------------------------------- /Common/Common.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | 3 | Common* Common::_s_pInstance; 4 | Common::Config* Common::_s_pConfig; 5 | 6 | Common::Common(BOOL bHookWinLibs, std::function pPostMutexFunc, const char* sIP, const char* sOriginalIP) 7 | { 8 | this->m_sRedirectIP = sIP; 9 | this->m_sOriginalIP = sOriginalIP; 10 | this->m_GameSock = INVALID_SOCKET; 11 | this->m_ProcTable = { 0 }; 12 | this->m_bThemidaUnpacked = FALSE; 13 | this->m_dwGetProcRetAddr = 0; 14 | 15 | if (!pPostMutexFunc) 16 | { 17 | #if _DEBUG 18 | Log("Invalid function pointer passed to Common constructor."); 19 | #endif 20 | return; 21 | } 22 | 23 | if (this->GetConfig()->InjectImmediately) // call post-unpack function right away 24 | { 25 | pPostMutexFunc(); 26 | } 27 | else // set pointer to function that is executed after client unpacks itself 28 | { 29 | this->m_PostMutexFunc = pPostMutexFunc; 30 | } 31 | 32 | #if _DEBUG 33 | Log("Common created => Hook winsock libs: %s || IP: %s || Original IP: %s", (bHookWinLibs ? "Yes" : "No"), sIP, sOriginalIP); 34 | #endif 35 | 36 | // required for proper injection 37 | INITWINHOOK("KERNEL32", "CreateMutexA", CreateMutexA_Original, CreateMutexA_t, WinHooks::CreateMutexA_Hook); 38 | 39 | if (Common::GetConfig()->MaplePatcherClass || Common::GetConfig()->MapleWindowClass || Common::GetConfig()->InjectImmediately) 40 | { 41 | INITWINHOOK("USER32", "CreateWindowExA", CreateWindowExA_Original, CreateWindowExA_t, WinHooks::CreateWindowExA_Hook); 42 | } 43 | 44 | if (Common::GetConfig()->LocaleSpoofValue) 45 | { 46 | INITWINHOOK("KERNEL32", "GetACP", GetACP_Original, GetACP_t, WinHooks::GetACP_Hook); 47 | } 48 | 49 | if (Common::GetConfig()->HookToggleInfo.OpenProcess_Logging) 50 | { 51 | INITWINHOOK("KERNEL32", "OpenProcess", OpenProcess_Original, OpenProcess_t, WinHooks::OpenProcess_Hook); 52 | } 53 | 54 | if (Common::GetConfig()->HookToggleInfo.CreateProcess_Logging) 55 | { 56 | INITWINHOOK("KERNEL32", "CreateProcessW", CreateProcessW_Original, CreateProcessW_t, WinHooks::CreateProcessW_Hook); 57 | INITWINHOOK("KERNEL32", "CreateProcessA", CreateProcessA_Original, CreateProcessA_t, WinHooks::CreateProcessA_Hook); 58 | } 59 | else if (Common::GetConfig()->MapleExitWindowWebUrl && *Common::GetConfig()->MapleExitWindowWebUrl) 60 | { 61 | INITWINHOOK("KERNEL32", "CreateProcessA", CreateProcessA_Original, CreateProcessA_t, WinHooks::CreateProcessA_Hook); 62 | } 63 | 64 | if (Common::GetConfig()->HookToggleInfo.OpenMutexA_Logging || Common::GetConfig()->HookToggleInfo.OpenMutexA_Spoof) 65 | { 66 | INITWINHOOK("KERNEL32", "OpenMutexA", OpenMutexA_Original, OpenMutexA_t, WinHooks::OpenMutexA_Hook); 67 | } 68 | 69 | if (Common::GetConfig()->HookToggleInfo.NtTerminateProc_Logging) 70 | { 71 | INITWINHOOK("NTDLL", "NtTerminateProcess", NtTerminateProcess_Original, NtTerminateProcess_t, WinHooks::NtTerminateProcess_Hook); 72 | } 73 | 74 | if (Common::GetConfig()->HookToggleInfo.RegCreateKeyA_Logging) 75 | { 76 | INITWINHOOK("KERNEL32", "RegCreateKeyExA", RegCreateKeyExA_Original, RegCreateKeyExA_t, WinHooks::RegCreateKeyExA_Hook); 77 | } 78 | 79 | if (Common::GetConfig()->HookToggleInfo.GetProcAddress_Logging) 80 | { 81 | INITWINHOOK("KERNEL32", "GetProcAddress", GetProcAddress_Original, GetProcAddress_t, WinHooks::GetProcAddress_Hook); 82 | } 83 | 84 | if (!bHookWinLibs) return; 85 | 86 | if (!sIP || !sOriginalIP) 87 | { 88 | #if _DEBUG 89 | Log("Null IP string passed to Common constructor."); 90 | #endif 91 | return; 92 | } 93 | 94 | INITWINHOOK("MSWSOCK", "WSPStartup", WSPStartup_Original, WSPStartup_t, WinHooks::WinSock::WSPStartup_Hook); 95 | } 96 | 97 | Common::~Common() 98 | { 99 | #if _DEBUG 100 | Log("Cleaning up common.."); 101 | #endif 102 | 103 | if (this->m_pFakeHsModule) 104 | { 105 | // TODO figure out some common library call to put this instead of in dll detach 106 | // CLogo constructor is pretty good but its not a library call so idk 107 | this->m_pFakeHsModule->DeleteModule(); 108 | } 109 | 110 | if (this->m_GameSock != INVALID_SOCKET) 111 | { 112 | #if _DEBUG 113 | Log("Closing socket.."); 114 | #endif 115 | 116 | this->m_ProcTable.lpWSPCloseSocket(this->m_GameSock, nullptr); 117 | this->m_GameSock = INVALID_SOCKET; 118 | } 119 | } 120 | 121 | void Common::OnThemidaUnpack() 122 | { 123 | if (Common::GetConfig()->InjectImmediately) return; 124 | 125 | if (this->m_bThemidaUnpacked) return; 126 | 127 | this->m_bThemidaUnpacked = TRUE; 128 | 129 | if (Common::GetConfig()->SleepAfterUnpackDuration) 130 | { 131 | Log("Themida unpacked => sleeping for %d milliseconds.", Common::GetConfig()->SleepAfterUnpackDuration); 132 | Sleep(Common::GetConfig()->SleepAfterUnpackDuration); 133 | } 134 | 135 | #if _DEBUG 136 | Log("Themida unpacked, editing memory.."); 137 | #endif 138 | 139 | this->m_PostMutexFunc(); 140 | } 141 | -------------------------------------------------------------------------------- /Common/Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Exclude rarely-used stuff from Windows headers 4 | // Important to define this before Windows.h is included in a project because of linker issues with the WinSock2 lib 5 | #define WIN32_LEAN_AND_MEAN 6 | 7 | #include 8 | #include 9 | #include "hooker.h" 10 | #include "logger.h" 11 | #include "winhooks.h" 12 | #include "winhook_types.h" 13 | #include "FakeModule.h" 14 | 15 | #define MAPLE_INJECT_USE_IJL TRUE 16 | 17 | /// 18 | /// 19 | /// 20 | class Common 21 | { 22 | private: 23 | struct Config 24 | { 25 | private: 26 | struct WinHooks 27 | { 28 | /* define toggles for logging and other behavior separately */ 29 | public: 30 | BOOL OpenMutexA_Spoof; 31 | 32 | BOOL WSPConnect_Logging; 33 | BOOL NtTerminateProc_Logging; 34 | BOOL OpenProcess_Logging; 35 | BOOL CreateProcess_Logging; 36 | BOOL OpenMutexA_Logging; 37 | BOOL RegCreateKeyA_Logging; 38 | BOOL GetProcAddress_Logging; 39 | 40 | WinHooks() 41 | { 42 | OpenMutexA_Spoof = TRUE; 43 | WSPConnect_Logging = TRUE; 44 | NtTerminateProc_Logging = TRUE; 45 | OpenProcess_Logging = FALSE; 46 | CreateProcess_Logging = TRUE; 47 | OpenMutexA_Logging = TRUE; 48 | RegCreateKeyA_Logging = FALSE; 49 | GetProcAddress_Logging = FALSE; 50 | } 51 | }; 52 | public: 53 | const char* DllName = "LEN.dll"; 54 | const char* MapleExeName = "MapleStory.exe"; 55 | const char* MapleStartupArgs = " GameLaunching 127.0.0.1 8484"; 56 | 57 | const char* MapleExitWindowWebUrl = "http"; 58 | const char* MapleWindowClass = "MapleStoryClass"; 59 | const char* MaplePatcherClass = "StartUpDlgClass"; 60 | const char* MapleMutex = "WvsClientMtx"; 61 | 62 | DWORD LocaleSpoofValue; 63 | DWORD SleepAfterUnpackDuration; 64 | 65 | BOOL ForceWindowedOnStart; 66 | BOOL InjectImmediately; 67 | BOOL AllowMulticlient; 68 | 69 | Common::Config::WinHooks HookToggleInfo; 70 | 71 | Config() 72 | { 73 | HookToggleInfo = WinHooks(); 74 | 75 | LocaleSpoofValue = 0; 76 | SleepAfterUnpackDuration = 0; 77 | ForceWindowedOnStart = TRUE; 78 | InjectImmediately = FALSE; 79 | AllowMulticlient = TRUE; 80 | } 81 | }; 82 | 83 | private: 84 | static Common* _s_pInstance; 85 | static Common::Config* _s_pConfig; 86 | 87 | public: // public because all the C-style hooks have to access these members 88 | const char* m_sRedirectIP; 89 | const char* m_sOriginalIP; 90 | 91 | /* TODO throw all the winsock stuff into its own class */ 92 | SOCKET m_GameSock; 93 | WSPPROC_TABLE m_ProcTable; 94 | DWORD m_dwGetProcRetAddr; 95 | BOOL m_bThemidaUnpacked; 96 | FakeModule* m_pFakeHsModule; 97 | 98 | /// 99 | /// Gets called when mutex hook is triggered. 100 | /// 101 | std::function m_PostMutexFunc; 102 | 103 | private: // forcing the class to only have one instance, created through CreateInstance 104 | Common(BOOL bHookWinLibs, std::function pPostMutexFunc, const char* sIP, const char* sOriginalIP); 105 | Common() = delete; 106 | Common(const Common&) = delete; 107 | Common& operator =(const Common&) = delete; 108 | 109 | public: 110 | ~Common(); 111 | 112 | /// 113 | /// Function called from library hooks. 114 | /// Most of the time this should be triggered by the Mutex hook, however, in the case that 115 | /// the Mutex hook does not get triggered then this will be executed by CreateWindowExA 116 | /// for redundancy. The contents of this function will only be executed once, even if both 117 | /// Mutex and CreateWindow hooks are called properly. 118 | /// 119 | void OnThemidaUnpack(); 120 | 121 | public: 122 | static void CreateInstance(BOOL bHookWinLibs, std::function pMutexFunc, const char* sIP, const char* sOriginalIP) 123 | { 124 | if (_s_pInstance) return; 125 | 126 | _s_pInstance = new Common(bHookWinLibs, pMutexFunc, sIP, sOriginalIP); 127 | } 128 | 129 | static Common* GetInstance() 130 | { 131 | return _s_pInstance; 132 | } 133 | 134 | static Config* GetConfig() 135 | { 136 | if (!_s_pConfig) _s_pConfig = new Config(); 137 | 138 | return _s_pConfig; 139 | } 140 | }; 141 | 142 | -------------------------------------------------------------------------------- /Common/Common.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 | 16.0 23 | Win32Proj 24 | {3eb29228-1f83-4dd3-b3c5-0f284fd50f39} 25 | Common 26 | 10.0 27 | 28 | 29 | 30 | StaticLibrary 31 | true 32 | v142 33 | MultiByte 34 | 35 | 36 | StaticLibrary 37 | false 38 | v142 39 | true 40 | MultiByte 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(SolutionDir)Include; 76 | $(SolutionDir)Output\$(Configuration)\bin\ 77 | $(SolutionDir)Output\$(Configuration)\$(ProjectName)\ 78 | false 79 | true 80 | 81 | 82 | false 83 | $(SolutionDir)Output\$(Configuration)\bin\ 84 | $(SolutionDir)Output\$(Configuration)\$(ProjectName)\ 85 | $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(SolutionDir)Include; 86 | false 87 | true 88 | 89 | 90 | true 91 | 92 | 93 | false 94 | 95 | 96 | 97 | Level3 98 | true 99 | WIN32;_DEBUG;COMMON_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 100 | true 101 | NotUsing 102 | pch.h 103 | Speed 104 | 105 | 106 | Windows 107 | true 108 | false 109 | 110 | 111 | 112 | 113 | Level3 114 | true 115 | true 116 | true 117 | WIN32;NDEBUG;COMMON_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 118 | true 119 | NotUsing 120 | pch.h 121 | Speed 122 | 123 | 124 | Windows 125 | true 126 | true 127 | true 128 | false 129 | 130 | 131 | 132 | 133 | Level3 134 | true 135 | _DEBUG;COMMON_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 136 | true 137 | Use 138 | pch.h 139 | 140 | 141 | Windows 142 | true 143 | false 144 | 145 | 146 | 147 | 148 | Level3 149 | true 150 | true 151 | true 152 | NDEBUG;COMMON_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 153 | true 154 | Use 155 | pch.h 156 | 157 | 158 | Windows 159 | true 160 | true 161 | true 162 | false 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /Common/Common.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;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 | {8694bb41-e655-4ee5-8d0d-df08d5e043f4} 18 | 19 | 20 | {99fc11fd-c7ce-4ed7-9985-42a80e654ac2} 21 | 22 | 23 | {1987d19a-3c1d-45ff-bb93-51bc09536df4} 24 | 25 | 26 | {0194d71c-6d23-4669-a225-f03b551901e0} 27 | 28 | 29 | {ae38652d-82f0-4033-9927-3e1e372f46c1} 30 | 31 | 32 | {3cb1c63c-3e12-4bf9-8b34-faa933169f29} 33 | 34 | 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files\MapleTypes 41 | 42 | 43 | Header Files\MapleTypes 44 | 45 | 46 | Header Files\MapleTypes 47 | 48 | 49 | Header Files\MapleTypes 50 | 51 | 52 | Header Files\MapleTypes 53 | 54 | 55 | Header Files\Tools 56 | 57 | 58 | Header Files\Tools 59 | 60 | 61 | Header Files 62 | 63 | 64 | Header Files\Tools 65 | 66 | 67 | FakeModule 68 | 69 | 70 | Header Files\MapleTypes\ZAllocEx 71 | 72 | 73 | Header Files\MapleTypes\ZAllocEx 74 | 75 | 76 | Header Files\MapleTypes\ZAllocEx 77 | 78 | 79 | Header Files\MapleTypes\ZAllocEx 80 | 81 | 82 | Header Files\Tools 83 | 84 | 85 | Header Files\Tools 86 | 87 | 88 | Header Files\MapleTypes 89 | 90 | 91 | Header Files\MapleTypes\ZRef 92 | 93 | 94 | Header Files\MapleTypes\ZRef 95 | 96 | 97 | Header Files\MapleTypes\ZRef 98 | 99 | 100 | Header Files\MapleTypes\ZRecycleable 101 | 102 | 103 | Header Files\MapleTypes\ZRecycleable 104 | 105 | 106 | Header Files\MapleTypes\ZRecycleable 107 | 108 | 109 | Header Files\MapleTypes\ZRef 110 | 111 | 112 | Header Files\MapleTypes 113 | 114 | 115 | 116 | 117 | FakeModule 118 | 119 | 120 | Source Files 121 | 122 | 123 | Source Files 124 | 125 | 126 | Source Files 127 | 128 | 129 | Source Files 130 | 131 | 132 | Source Files 133 | 134 | 135 | Source Files 136 | 137 | 138 | Source Files 139 | 140 | 141 | -------------------------------------------------------------------------------- /Common/FakeModule.cpp: -------------------------------------------------------------------------------- 1 | #include "FakeModule.h" 2 | 3 | BOOL FakeModule::CreateModule(const char* szModuleName) 4 | { 5 | this->szModuleName = szModuleName; 6 | 7 | HANDLE hWrite = CreateFile( 8 | szModuleName, 9 | (GENERIC_READ | GENERIC_WRITE), 10 | NULL, 11 | NULL, 12 | CREATE_ALWAYS, 13 | NULL, 14 | NULL 15 | ); 16 | 17 | if (hWrite != INVALID_HANDLE_VALUE) 18 | { 19 | if (!WriteFile(hWrite, this->s_abFakeModuleBinary, sizeof(this->s_abFakeModuleBinary), NULL, NULL)) 20 | { 21 | CloseHandle(hWrite); 22 | return FALSE; 23 | } 24 | 25 | CloseHandle(hWrite); 26 | } 27 | else 28 | { 29 | return FALSE; 30 | } 31 | 32 | this->hFakeModule = LoadLibrary(szModuleName); 33 | 34 | if (!this->hFakeModule) 35 | { 36 | return FALSE; 37 | } 38 | 39 | return TRUE; 40 | } 41 | 42 | BOOL FakeModule::DeleteModule() 43 | { 44 | if (!szModuleName || !hFakeModule) return FALSE; 45 | 46 | BOOL bRet = TRUE; 47 | 48 | bRet &= FreeLibrary(hFakeModule); 49 | bRet &= DeleteFile(TEXT(szModuleName)); 50 | 51 | return bRet; 52 | } 53 | -------------------------------------------------------------------------------- /Common/TSecType.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /* 5 | Original Credits: https://github.com/67-6f-64/Firefly/blob/master/Firefly%20Spy/TSecType.hpp 6 | Modifications Made By: 7 | - Rajan Grewal 8 | - Minimum Delta 9 | 10 | Additional Information From: https://en.cppreference.com/w/cpp/language/operators 11 | */ 12 | 13 | template 14 | class TSecData 15 | { 16 | public: 17 | T data; 18 | BYTE bKey; 19 | BYTE FakePtr1; 20 | BYTE FakePtr2; 21 | WORD wChecksum; 22 | }; 23 | 24 | template 25 | class TSecType 26 | { 27 | private: 28 | DWORD FakePtr1; 29 | DWORD FakePtr2; 30 | TSecData* m_secdata; 31 | 32 | public: 33 | TSecType() 34 | { 35 | this->m_secdata = new TSecData(); // uses proper ZAllocEx now (since global new operator overload) 36 | 37 | this->FakePtr1 = static_cast(rand()); 38 | this->FakePtr2 = static_cast(rand()); 39 | 40 | this->m_secdata->FakePtr1 = LOBYTE(this->FakePtr1); 41 | this->m_secdata->FakePtr2 = LOBYTE(this->FakePtr2); 42 | 43 | this->SetData(NULL); 44 | } 45 | 46 | TSecType(const T op) 47 | { 48 | this->m_secdata = new TSecData(); // uses proper ZAllocEx now (since global new operator overload) 49 | 50 | this->FakePtr1 = static_cast(rand()); 51 | this->FakePtr2 = static_cast(rand()); 52 | 53 | this->m_secdata->FakePtr1 = LOBYTE(this->FakePtr1); 54 | this->m_secdata->FakePtr2 = LOBYTE(this->FakePtr2); 55 | 56 | this->SetData(op); 57 | } 58 | 59 | ~TSecType() 60 | { 61 | if (this->m_secdata) 62 | { 63 | delete this->m_secdata; 64 | } 65 | } 66 | 67 | operator T() 68 | { 69 | return this->GetData(); 70 | } 71 | 72 | BOOL operator ==(TSecType* op) 73 | { 74 | return this->GetData() == op->GetData(); 75 | } 76 | 77 | TSecType* operator =(const T op) 78 | { 79 | this->SetData(op); 80 | return this; 81 | } 82 | 83 | TSecType* operator =(TSecType* op) 84 | { 85 | T data = op->GetData(); 86 | this->SetData(data); 87 | return this; 88 | } 89 | 90 | T operator /=(const T op) 91 | { 92 | T tmp = this->GetData() / op; 93 | this->SetData(tmp); 94 | return tmp; 95 | } 96 | 97 | T operator *=(const T op) 98 | { 99 | T tmp = this->GetData() * op; 100 | this->SetData(tmp); 101 | return tmp; 102 | } 103 | 104 | T operator +=(const T op) 105 | { 106 | T tmp = this->GetData() + op; 107 | this->SetData(tmp); 108 | return tmp; 109 | } 110 | 111 | T operator -=(const T op) 112 | { 113 | T tmp = this->GetData() - op; 114 | this->SetData(tmp); 115 | return tmp; 116 | } 117 | 118 | T GetData() 119 | { 120 | T decrypted_data = this->m_secdata->data; 121 | WORD wChecksum = 0; 122 | 123 | for (BYTE i = 0, key = this->m_secdata->bKey; i < (sizeof(T) + 1); i++) 124 | { 125 | if (i > 0) 126 | { 127 | key = reinterpret_cast(&this->m_secdata->data)[i - 1] + key + 42;; 128 | wChecksum = i > 1 ? ((8 * wChecksum) | (key + (wChecksum >> 13))) : ((key + 4) | 0xD328); 129 | } 130 | 131 | if (i < sizeof(T)) 132 | { 133 | if (!key) 134 | { 135 | key = 42; 136 | } 137 | 138 | reinterpret_cast(&decrypted_data)[i] = reinterpret_cast(&this->m_secdata->data)[i] ^ key; 139 | } 140 | 141 | } 142 | 143 | if (this->m_secdata->wChecksum != wChecksum || LOBYTE(this->FakePtr1) != this->m_secdata->FakePtr1 || LOBYTE(this->FakePtr2) != this->m_secdata->FakePtr2) 144 | { 145 | return NULL; //TODO: CxxThrow 146 | } 147 | 148 | return decrypted_data; 149 | } 150 | 151 | VOID SetData(T data) 152 | { 153 | this->m_secdata->bKey = LOBYTE(rand()); 154 | this->m_secdata->wChecksum = sizeof(T) > 1 ? static_cast(39525) : static_cast(-26011); 155 | 156 | for (BYTE i = 0, key = this->m_secdata->bKey; i < (sizeof(T) + 1); i++) 157 | { 158 | if (i > 0) 159 | { 160 | key = (key ^ reinterpret_cast(&data)[i - 1]) + key + 42; 161 | this->m_secdata->wChecksum = (8 * this->m_secdata->wChecksum) | (key + (this->m_secdata->wChecksum >> 13)); 162 | } 163 | 164 | if (i < sizeof(T)) 165 | { 166 | if (!key) 167 | { 168 | key = 42; 169 | } 170 | 171 | reinterpret_cast(&this->m_secdata->data)[i] = reinterpret_cast(&data)[i] ^ key; 172 | } 173 | 174 | } 175 | } 176 | }; 177 | 178 | class SECPOINT 179 | { 180 | public: 181 | TSecType y; 182 | TSecType x; 183 | 184 | SECPOINT() { } 185 | 186 | SECPOINT(long ptX, long ptY) 187 | { 188 | this->x = ptX; 189 | this->y = ptY; 190 | } 191 | 192 | SECPOINT(SECPOINT* ptSrc) 193 | { 194 | this->x.SetData(ptSrc->x.GetData()); 195 | this->y.SetData(ptSrc->y.GetData()); 196 | } 197 | 198 | SECPOINT(tagPOINT* ptSrc) 199 | { 200 | this->x.SetData(ptSrc->x); 201 | this->y.SetData(ptSrc->y); 202 | } 203 | 204 | ~SECPOINT() 205 | { 206 | this->x.~TSecType(); 207 | this->y.~TSecType(); 208 | } 209 | 210 | SECPOINT* operator =(tagPOINT* ptSrc) 211 | { 212 | this->x.SetData(ptSrc->x); 213 | this->y.SetData(ptSrc->y); 214 | return this; 215 | } 216 | 217 | SECPOINT* operator =(SECPOINT* ptSrc) 218 | { 219 | this->x.SetData(ptSrc->x.GetData()); 220 | this->y.SetData(ptSrc->y.GetData()); 221 | return this; 222 | } 223 | 224 | BOOL operator !=(tagPOINT* ptSrc) 225 | { 226 | return this->x.GetData() != ptSrc->x || this->y.GetData() != ptSrc->y; 227 | } 228 | 229 | BOOL operator ==(tagPOINT* ptSrc) 230 | { 231 | return this->x.GetData() == ptSrc->x && this->y.GetData() == ptSrc->y; 232 | } 233 | 234 | BOOL operator !=(SECPOINT* ptSrc) 235 | { 236 | return this->x.GetData() != ptSrc->x.GetData() || this->y.GetData() != ptSrc->y.GetData(); 237 | } 238 | 239 | BOOL operator ==(SECPOINT* ptSrc) 240 | { 241 | return this->x.GetData() == ptSrc->x.GetData() && this->y.GetData() == ptSrc->y.GetData(); 242 | } 243 | 244 | operator tagPOINT() 245 | { 246 | return { this->x.GetData(), this->y.GetData() }; 247 | } 248 | }; 249 | 250 | //assert_size(sizeof(TSecData), 0x0C) 251 | //assert_size(sizeof(TSecType), 0x0C) -------------------------------------------------------------------------------- /Common/ZAllocAnonSelector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ZAllocBase.h" 3 | 4 | struct ZAllocAnonSelector 5 | { 6 | protected: 7 | unsigned int GetBlockSize(ZAllocBase::BLOCK_SIZE nIndex, int* nAllocBlocks) 8 | { 9 | switch (nIndex) 10 | { 11 | case ZAllocBase::BLOCK_SIZE::BLOCK16: 12 | *nAllocBlocks = 64; 13 | return 16; 14 | case ZAllocBase::BLOCK_SIZE::BLOCK32: 15 | *nAllocBlocks = 32; 16 | return 32; 17 | case ZAllocBase::BLOCK_SIZE::BLOCK64: 18 | *nAllocBlocks = 16; 19 | return 64; 20 | case ZAllocBase::BLOCK_SIZE::BLOCK128: 21 | *nAllocBlocks = 8; 22 | return 128; 23 | default: 24 | *nAllocBlocks = 0; 25 | return 0; 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /Common/ZAllocBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class ZAllocBase 4 | { 5 | public: 6 | enum BLOCK_SIZE 7 | { 8 | BLOCK16 = 0, 9 | BLOCK32 = 1, 10 | BLOCK64 = 2, 11 | BLOCK128 = 3, 12 | }; 13 | 14 | static PVOID* AllocRawBlocks(UINT uBlockSize, UINT uNumberOfBlocks) 15 | { 16 | /* TODO make this more legible */ 17 | 18 | unsigned int uEnlargedBlockSize = uBlockSize + 4; 19 | unsigned int uTotalAllocationSize = (uNumberOfBlocks * uEnlargedBlockSize) + 8; 20 | 21 | HANDLE hHeap = GetProcessHeap(); 22 | PVOID* pAlloc = reinterpret_cast(HeapAlloc(hHeap, NULL, uTotalAllocationSize)); 23 | 24 | /* if we deallocate the entire block collection, this first address is where we start */ 25 | *(pAlloc) = reinterpret_cast((uNumberOfBlocks * uEnlargedBlockSize) + 4); 26 | 27 | /* block collection header */ 28 | *(pAlloc + 1) = 0; 29 | 30 | /* size of first block */ 31 | *(pAlloc + 2) = reinterpret_cast(uBlockSize); 32 | 33 | PVOID* pRet = pAlloc + 3; 34 | DWORD* pdwRet = (DWORD*)(pAlloc + 3); 35 | 36 | for (UINT i = 0; i < uNumberOfBlocks - 1; i++) 37 | { 38 | /* initialize each block with a pointer to the next block */ 39 | *pRet = reinterpret_cast(pRet) + uEnlargedBlockSize; 40 | 41 | /* increase pointer by block size (we divide because the compiler tries to multiply) */ 42 | pRet = reinterpret_cast(reinterpret_cast(pRet) + uEnlargedBlockSize); 43 | 44 | /* set the preceding address to equal the size of the block */ 45 | *(pRet - 1) = reinterpret_cast(uBlockSize); 46 | } 47 | 48 | /* nullptr indicates last block in the linked list */ 49 | *pRet = nullptr; 50 | 51 | /* return address of the first block in the linked list */ 52 | return pAlloc + 3; 53 | } 54 | }; -------------------------------------------------------------------------------- /Common/ZAllocEx.cpp: -------------------------------------------------------------------------------- 1 | #include "ZAllocEx.h" 2 | 3 | void* operator new(size_t uSize) 4 | { 5 | return ZAllocEx::GetInstance()->Alloc(uSize); 6 | } 7 | 8 | void* operator new[](size_t uSize) 9 | { 10 | return ZAllocEx::GetInstance()->Alloc(uSize); 11 | } 12 | 13 | void operator delete(void* p) 14 | { 15 | ZAllocEx::GetInstance()->Free((void**)p); 16 | } 17 | 18 | void operator delete[](void* p) 19 | { 20 | ZAllocEx::GetInstance()->Free((void**)p); 21 | } 22 | -------------------------------------------------------------------------------- /Common/ZAllocEx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "logger.h" 3 | #include "ZXString.h" 4 | #include "ZAllocAnonSelector.h" 5 | #include "ZAllocBase.h" 6 | #include "ZAllocStrSelector.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "ZFatalSection.h" 13 | 14 | // fix returnaddress func 15 | // https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress?view=msvc-160 16 | #pragma intrinsic(_ReturnAddress) 17 | 18 | template struct ZAllocEx : ZAllocBase, T { }; // dummy base 19 | 20 | /// 21 | /// Exact replica (to the best of my ability) of MapleStory's memory mangement template class. 22 | /// ZAllocEx uses a combination of memory pool and slab allocation for objects up to a certain size (based on pool type). Objects in 23 | /// excess of the pool max limit are allocated and deallocated freely, without the use of the memory caches. 24 | /// When memory is requested from ZAllocEx, it first attempts to provide already allocated memory that is not in use. If none is available, 25 | /// it allocates a chunk of memory that varies in size based on the requested amount and pool type, and then returns a fraction of 26 | /// the allocated memory to the calling function. 27 | /// When memory is freed through ZAllocEx, it does not get deallocated but instead gets placed on top of an available memory stack to be 28 | /// used by the next call to the allocator. 29 | /// ZAllocEx has 4 stacks of available memory of different sizes which differ based on the pool type. 30 | /// 31 | template <> 32 | struct ZAllocEx : ZAllocBase, ZAllocAnonSelector 33 | { 34 | private: 35 | BYTE gap0[1]; 36 | ZFatalSection m_lock; // we dont use this but we keep it for proper maple struct alignment 37 | LPVOID m_apBuff[4]; 38 | LPVOID m_apBlockHead[4]; 39 | 40 | std::mutex* GetMutex() 41 | { 42 | static std::mutex mtx; 43 | 44 | return &mtx; 45 | } 46 | 47 | ZAllocEx() 48 | { 49 | gap0[0] = 0; 50 | 51 | for (int i = 0; i < 4; i++) 52 | { 53 | m_apBuff[i] = nullptr; 54 | m_apBlockHead[i] = nullptr; 55 | } 56 | } 57 | 58 | /* ZAlloc instantiation has to used malloc because it cant initialize itself */ 59 | void* operator new(unsigned int uSize) 60 | { 61 | return malloc(uSize); 62 | } 63 | 64 | void operator delete(void* p) 65 | { 66 | free(p); 67 | } 68 | 69 | public: 70 | static ZAllocEx* GetInstance() 71 | { 72 | static ZAllocEx _s_ZAllocEx = ZAllocEx(); 73 | return &_s_ZAllocEx; 74 | } 75 | 76 | PVOID Alloc(size_t uSize) 77 | { 78 | ZAllocBase::BLOCK_SIZE nBlockSizeIndex; 79 | 80 | if (uSize <= 16) 81 | { 82 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK16; 83 | } 84 | else if (uSize <= 32) 85 | { 86 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK32; 87 | } 88 | else if (uSize <= 64) 89 | { 90 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK64; 91 | } 92 | else if (uSize <= 128) 93 | { 94 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK128; 95 | } 96 | else 97 | { 98 | /* get handle to process heap */ 99 | HANDLE hHeap = GetProcessHeap(); 100 | 101 | /* allocate an extra pointer to store the allocation size */ 102 | PVOID* lpAlloc = reinterpret_cast(HeapAlloc(hHeap, NULL, uSize + sizeof(PVOID))); 103 | 104 | /* set allocation size at head of allocation */ 105 | *lpAlloc = reinterpret_cast(uSize); 106 | 107 | return lpAlloc + 1; // return next 32-bit address after the size imprint 108 | } 109 | 110 | INT nAllocBlocks; 111 | INT nBlockSize = this->GetBlockSize(nBlockSizeIndex, &nAllocBlocks); 112 | 113 | GetMutex()->lock(); 114 | 115 | /* check if theres an available block of memory at the current buffer position */ 116 | /* if not, we allocate another kb of memory blocks */ 117 | if (!this->m_apBuff[nBlockSizeIndex]) 118 | { 119 | 120 | /* returns pointer to the first memory block in the linked list allocation */ 121 | PVOID* pBlockAllocHead = this->AllocRawBlocks(nBlockSize, nAllocBlocks); 122 | 123 | /* the preceding address holds the block size, the address before that holds a pointer to the next block in the linked list series */ 124 | *(pBlockAllocHead - 2) = this->m_apBlockHead[nBlockSizeIndex]; 125 | 126 | /* throw the newly allocated memory block list onto the top of the stack */ 127 | this->m_apBlockHead[nBlockSizeIndex] = pBlockAllocHead; 128 | this->m_apBuff[nBlockSizeIndex] = pBlockAllocHead; 129 | } 130 | 131 | /* grab the top pointer off the stack */ 132 | PVOID* lpAllocRet = reinterpret_cast(this->m_apBuff[nBlockSizeIndex]); 133 | 134 | #if _DEBUG 135 | if ((DWORD) * (lpAllocRet - 1) > 0x400) // 1kb 136 | { 137 | auto p = lpAllocRet; 138 | 139 | Log("Memory Dump:"); 140 | Log("Address[-2]: %08X Value: %08X", p - 2, *(p - 2)); 141 | Log("Address[-1]: %08X Value: %08X", p - 1, *(p - 1)); 142 | Log("Address[0]: %08X Value: %08X", p, *(p)); 143 | Log("Address[1]: %08X Value: %08X", p + 1, *(p + 1)); 144 | Log("Address[2]: %08X Value: %08X", p + 2, *(p + 2)); 145 | } 146 | #endif 147 | 148 | /* set the top of the stack to equal the previous pointer */ 149 | this->m_apBuff[nBlockSizeIndex] = *lpAllocRet; 150 | 151 | GetMutex()->unlock(); 152 | 153 | /* return memory */ 154 | return lpAllocRet; 155 | } 156 | 157 | void Free(void** p) 158 | { 159 | ZAllocBase::BLOCK_SIZE nBlockSizeIndex; 160 | 161 | if (!p) return; 162 | 163 | DWORD uSize = *(DWORD*)(p - 1); // pointer before the mem address holds allocation size 164 | 165 | if (uSize & 0x80000000) 166 | { 167 | uSize = ~uSize; 168 | } 169 | 170 | if (uSize == 16) 171 | { 172 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK16; 173 | } 174 | else if (uSize == 32) 175 | { 176 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK32; 177 | } 178 | else if (uSize == 64) 179 | { 180 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK64; 181 | } 182 | else if (uSize == 128) 183 | { 184 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK128; 185 | } 186 | else 187 | { 188 | HANDLE hHeap = GetProcessHeap(); // get handle to process heap 189 | 190 | LPVOID pMem = p - 1; 191 | 192 | BOOL bRet = HeapFree(hHeap, NULL, pMem); // release memory starting at memory size pointer 193 | #if _DEBUG 194 | if (!bRet) 195 | { 196 | Log("Error caught when freeing heap memory. Error code: %d", GetLastError()); 197 | 198 | Log("Memory Dump:"); 199 | Log("Address[-2]: %08X Value: %08X", p - 2, *(p - 2)); 200 | Log("Address[-1]: %08X Value: %08X", p - 1, *(p - 1)); 201 | Log("Address[0]: %08X Value: %08X", p, *(p)); 202 | Log("Address[1]: %08X Value: %08X", p + 1, *(p + 1)); 203 | Log("Address[2]: %08X Value: %08X", p + 2, *(p + 2)); 204 | } 205 | #endif 206 | return; 207 | } 208 | 209 | GetMutex()->lock(); 210 | 211 | /* assign the top block pointer to the head of the freed memory*/ 212 | *p = this->m_apBuff[nBlockSizeIndex]; 213 | 214 | /* put the freed memory on top of the available memory stack */ 215 | this->m_apBuff[nBlockSizeIndex] = p; 216 | 217 | GetMutex()->unlock(); 218 | } 219 | }; 220 | 221 | /// 222 | /// Exact replica (to the best of my ability) of MapleStory's memory mangement template class. 223 | /// ZAllocEx uses a combination of memory pool and slab allocation for objects up to a certain size (based on pool type). Objects in 224 | /// excess of the pool max limit are allocated and deallocated freely, without the use of the memory caches. 225 | /// When memory is requested from ZAllocEx, it first attempts to provide already allocated memory that is not in use. If none is available, 226 | /// it allocates a chunk of memory that varies in size based on the requested amount and pool type, and then returns a fraction of 227 | /// the allocated memory to the calling function. 228 | /// When memory is freed through ZAllocEx, it does not get deallocated but instead gets placed on top of an available memory stack to be 229 | /// used by the next call to the allocator. 230 | /// ZAllocEx has 4 stacks of available memory of different sizes which differ based on the pool type. 231 | /// 232 | template 233 | struct ZAllocEx> : ZAllocBase, ZAllocStrSelector 234 | { 235 | private: 236 | BYTE gap0[1]; 237 | ZFatalSection m_lock; // we dont use this but we keep it for proper maple struct alignment 238 | LPVOID m_apBuff[4]; 239 | LPVOID m_apBlockHead[4]; 240 | 241 | std::mutex* GetMutex() 242 | { 243 | static std::mutex mtx; 244 | return &mtx; 245 | } 246 | 247 | ZAllocEx() 248 | { 249 | gap0[0] = 0; 250 | 251 | for (int i = 0; i < 4; i++) 252 | { 253 | m_apBuff[i] = nullptr; 254 | m_apBlockHead[i] = nullptr; 255 | } 256 | } 257 | 258 | /* ZAlloc instantiation has to used malloc because it cant initialize itself */ 259 | void* operator new(unsigned int uSize) 260 | { 261 | return malloc(uSize); 262 | } 263 | 264 | void operator delete(void* p) 265 | { 266 | free(p); 267 | } 268 | 269 | public: 270 | static ZAllocEx>* GetInstance() 271 | { 272 | static ZAllocEx> _s_ZAllocEx = ZAllocEx(); 273 | return &_s_ZAllocEx; 274 | } 275 | 276 | PVOID Alloc(size_t uSize) 277 | { 278 | ZAllocBase::BLOCK_SIZE nBlockSizeIndex; 279 | 280 | if (uSize <= (sizeof(T) * 16) + sizeof(ZXString::_ZXStringData) + sizeof(T)) 281 | { 282 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK16; 283 | } 284 | else if (uSize <= (sizeof(T) * 32) + sizeof(ZXString::_ZXStringData) + sizeof(T)) 285 | { 286 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK32; 287 | } 288 | else if (uSize <= (sizeof(T) * 64) + sizeof(ZXString::_ZXStringData) + sizeof(T)) 289 | { 290 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK64; 291 | } 292 | else if (uSize <= (sizeof(T) * 128) + sizeof(ZXString::_ZXStringData) + sizeof(T)) 293 | { 294 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK128; 295 | } 296 | else 297 | { 298 | /* get handle to process heap */ 299 | HANDLE hHeap = GetProcessHeap(); 300 | 301 | /* allocate an extra pointer to store the allocation size */ 302 | PVOID* lpAlloc = reinterpret_cast(HeapAlloc(hHeap, NULL, uSize + sizeof(PVOID))); 303 | 304 | /* set allocation size at head of allocation */ 305 | *lpAlloc = reinterpret_cast(uSize); 306 | 307 | return lpAlloc + 1; // return next 32-bit address after the size imprint 308 | } 309 | 310 | INT nAllocBlocks; 311 | INT nBlockSize = this->GetBlockSize(nBlockSizeIndex, &nAllocBlocks); 312 | 313 | GetMutex()->lock(); 314 | 315 | /* check if theres an available block of memory at the current buffer position */ 316 | /* if not, we allocate another kb of memory blocks */ 317 | if (!this->m_apBuff[nBlockSizeIndex]) 318 | { 319 | /* returns pointer to the first memory block in the linked list allocation */ 320 | PVOID* pBlockAllocHead = this->AllocRawBlocks(nBlockSize, nAllocBlocks); 321 | 322 | /* the preceding address holds the block size, the address before that holds a pointer to the next block in the linked list series */ 323 | *(pBlockAllocHead - 2) = this->m_apBlockHead[nBlockSizeIndex]; 324 | 325 | /* throw the newly allocated memory block list onto the top of the stack */ 326 | this->m_apBlockHead[nBlockSizeIndex] = pBlockAllocHead; 327 | this->m_apBuff[nBlockSizeIndex] = pBlockAllocHead; 328 | } 329 | 330 | /* grab the top pointer off the stack */ 331 | PVOID* lpAllocRet = reinterpret_cast(this->m_apBuff[nBlockSizeIndex]); 332 | 333 | #if _DEBUG 334 | if ((DWORD) * (lpAllocRet - 1) > 0x400) // 1kb 335 | { 336 | auto p = lpAllocRet; 337 | 338 | Log("Memory Dump:"); 339 | Log("Address[-2]: %08X Value: %08X", p - 2, *(p - 2)); 340 | Log("Address[-1]: %08X Value: %08X", p - 1, *(p - 1)); 341 | Log("Address[0]: %08X Value: %08X", p, *(p)); 342 | Log("Address[1]: %08X Value: %08X", p + 1, *(p + 1)); 343 | Log("Address[2]: %08X Value: %08X", p + 2, *(p + 2)); 344 | } 345 | #endif 346 | 347 | /* set the top of the stack to equal the previous pointer */ 348 | this->m_apBuff[nBlockSizeIndex] = *lpAllocRet; 349 | 350 | GetMutex()->unlock(); 351 | 352 | /* return memory */ 353 | return lpAllocRet; 354 | } 355 | 356 | void Free(void** p) 357 | { 358 | ZAllocBase::BLOCK_SIZE nBlockSizeIndex; 359 | 360 | if (!p) return; 361 | 362 | DWORD uSize = *(DWORD*)(p - 1); // pointer before the mem address holds allocation size 363 | 364 | if (uSize & 0x80000000) 365 | { 366 | uSize = ~uSize; 367 | } 368 | 369 | if (uSize == (sizeof(T) * 16) + sizeof(ZXString::_ZXStringData) + sizeof(T)) 370 | { 371 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK16; 372 | } 373 | else if (uSize == (sizeof(T) * 32) + sizeof(ZXString::_ZXStringData) + sizeof(T)) 374 | { 375 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK32; 376 | } 377 | else if (uSize == (sizeof(T) * 64) + sizeof(ZXString::_ZXStringData) + sizeof(T)) 378 | { 379 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK64; 380 | } 381 | else if (uSize == (sizeof(T) * 128) + sizeof(ZXString::_ZXStringData) + sizeof(T)) 382 | { 383 | nBlockSizeIndex = ZAllocBase::BLOCK_SIZE::BLOCK128; 384 | } 385 | else 386 | { 387 | HANDLE hHeap = GetProcessHeap(); // get handle to process heap 388 | 389 | BOOL bRet = HeapFree(hHeap, NULL, p - 1); // release memory starting at memory size pointer 390 | #if _DEBUG 391 | if (!bRet) 392 | { 393 | Log("Error caught when freeing heap memory. Error code: %d", GetLastError()); 394 | 395 | Log("Memory Dump:"); 396 | Log("Address[-2]: %08X Value: %08X", p - 2, *(p - 2)); 397 | Log("Address[-1]: %08X Value: %08X", p - 1, *(p - 1)); 398 | Log("Address[0]: %08X Value: %08X", p, *(p)); 399 | Log("Address[1]: %08X Value: %08X", p + 1, *(p + 1)); 400 | Log("Address[2]: %08X Value: %08X", p + 2, *(p + 2)); 401 | } 402 | #endif 403 | return; 404 | } 405 | 406 | GetMutex()->lock(); 407 | 408 | /* assign the top block pointer to the head of the freed memory*/ 409 | *p = this->m_apBuff[nBlockSizeIndex]; 410 | 411 | /* put the freed memory on top of the available memory stack */ 412 | this->m_apBuff[nBlockSizeIndex] = p; 413 | 414 | GetMutex()->unlock(); 415 | } 416 | }; 417 | 418 | /* Global memory management overloading */ 419 | 420 | void* operator new(size_t uSize); 421 | void* operator new[](size_t uSize); 422 | void operator delete(void* p); 423 | void operator delete[](void* p); 424 | 425 | assert_size(sizeof(ZAllocEx), 0x2C) 426 | assert_size(sizeof(ZAllocEx>), 0x2C) 427 | assert_size(sizeof(ZAllocEx>), 0x2C) -------------------------------------------------------------------------------- /Common/ZAllocStrSelector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ZAllocBase.h" 3 | #include "ZXString.h" 4 | 5 | /* forward declare zxstring for compiler happiness */ 6 | template class ZXString; 7 | 8 | template 9 | struct ZAllocStrSelector 10 | { 11 | protected: 12 | unsigned int GetBlockSize(ZAllocBase::BLOCK_SIZE nIndex, int* nAllocBlocks) 13 | { 14 | switch (nIndex) 15 | { 16 | case ZAllocBase::BLOCK_SIZE::BLOCK16: 17 | *nAllocBlocks = 64; 18 | return (sizeof(T) * 16) + sizeof(ZXString::_ZXStringData) + sizeof(T); 19 | case ZAllocBase::BLOCK_SIZE::BLOCK32: 20 | *nAllocBlocks = 32; 21 | return (sizeof(T) * 32) + sizeof(ZXString::_ZXStringData) + sizeof(T); 22 | case ZAllocBase::BLOCK_SIZE::BLOCK64: 23 | *nAllocBlocks = 16; 24 | return (sizeof(T) * 64) + sizeof(ZXString::_ZXStringData) + sizeof(T); 25 | case ZAllocBase::BLOCK_SIZE::BLOCK128: 26 | *nAllocBlocks = 8; 27 | return (sizeof(T) * 128) + sizeof(ZXString::_ZXStringData) + sizeof(T); 28 | default: 29 | *nAllocBlocks = 0; 30 | return 0; 31 | } 32 | } 33 | }; -------------------------------------------------------------------------------- /Common/ZArray.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "logger.h" 3 | #include "ZAllocEx.h" 4 | 5 | template 6 | class ZArray 7 | { 8 | private: 9 | T* a; 10 | 11 | public: 12 | /// 13 | /// Default constructor 14 | /// 15 | ZArray() 16 | { 17 | this->a = nullptr; 18 | } 19 | 20 | /// 21 | /// Allocating constructor 22 | /// 23 | /// Number of spaces to allocate for in the array 24 | ZArray(size_t uSize) 25 | { 26 | this->a = nullptr; 27 | this->Alloc(uSize); 28 | } 29 | 30 | ZArray(ZArray* r) 31 | { 32 | this->a = nullptr; 33 | this = r; // TODO make sure this is using the overloaded operator 34 | } 35 | 36 | ~ZArray() 37 | { 38 | this->RemoveAll(); 39 | } 40 | 41 | ZArray* operator=(ZArray* r) 42 | { 43 | if (this == r) return this; 44 | 45 | this->RemoveAll(); 46 | 47 | size_t uCount = r->GetCount(); 48 | 49 | if (uCount) 50 | { 51 | /* Allocate new memory for array copy */ 52 | PVOID pAlloc = ZAllocEx::Alloc(sizeof(T) * uCount + sizeof(T)); 53 | 54 | /* Assign array count to head address */ 55 | *(DWORD*)pAlloc = uCount; 56 | 57 | pAlloc += 1; // first address holds count, second address is pointer to start of ZArray 58 | 59 | T* pHead = &r->a[uCount]; 60 | 61 | if (r->a < pHead) 62 | { 63 | for (int i = 0; i < uCount; i++) 64 | { 65 | this->a[i] = T(r->a[i]); // copy constructors are required for classes to be used here 66 | } 67 | } 68 | } 69 | 70 | return this; 71 | } 72 | 73 | T& operator[](size_t i) 74 | { 75 | return this->a[i]; 76 | } 77 | 78 | T& GetAt(size_t i) 79 | { 80 | return this->a[i]; 81 | } 82 | 83 | BOOL IsEmpty() 84 | { 85 | return this->GetCount() == 0; 86 | } 87 | 88 | T* Insert(T* e, int nIdx = -1) 89 | { 90 | T* result = this->InsertBefore(nIdx); 91 | *result = *e; // operator= overloading is required to make it function as the PDB does 92 | return result; 93 | } 94 | 95 | T* InsertBefore(int nIdx = -1) 96 | { 97 | BOOL bAllocateMoreMemory; 98 | size_t uSizeToAllocate; 99 | size_t uAllocationBytes; 100 | size_t uCount = this->GetCount(); 101 | 102 | if (nIdx == -1) nIdx = uCount; 103 | 104 | /* Determine if more space is required to fit another T object into the array */ 105 | if (this->a) 106 | { 107 | /* Grab size of allocation block -- remember, ZAllocEx encodes this at the head of each block */ 108 | uAllocationBytes = reinterpret_cast(this->a)[-2]; 109 | 110 | if (uAllocationBytes > INT_MAX) // this means its negative since the datatype is unsigned 111 | { 112 | uAllocationBytes = ~uAllocationBytes; 113 | } 114 | /* If there is enough space is the allocation block for another object, do not allocate more memory */ 115 | bAllocateMoreMemory = (uAllocationBytes - sizeof(PVOID)) / sizeof(T) <= uCount; 116 | } 117 | else 118 | { 119 | bAllocateMoreMemory = TRUE; 120 | } 121 | 122 | if (bAllocateMoreMemory) 123 | { 124 | if (uCount) 125 | { 126 | /* Always double the existing array size so we don't have to allocate mem as often */ 127 | uSizeToAllocate = 2 * uCount; 128 | } 129 | else 130 | { 131 | uSizeToAllocate = 1; 132 | } 133 | 134 | this->Reserve(uSizeToAllocate); 135 | } 136 | 137 | /* Increase array count by one */ 138 | size_t* pCount = &reinterpret_cast(this->a)[-1]; 139 | *pCount += 1; 140 | 141 | T* pDest = &this->a[nIdx + 1]; 142 | T* pSrc = &this->a[nIdx]; 143 | size_t uSize = sizeof(T) * (uCount - nIdx); 144 | 145 | /* Shift memory to make space for the new object */ 146 | memmove(pDest, pSrc, uSize); 147 | 148 | /* Initialize new memory space with T constructor */ 149 | this->a[nIdx] = T(); 150 | 151 | /* Return pointer to new object */ 152 | return &this->a[nIdx]; 153 | } 154 | 155 | void MakeSpace(size_t uNewSize) 156 | { 157 | size_t uCurSize = this->GetCount(); 158 | 159 | if (uCurSize == 0) uCurSize = 1; 160 | 161 | if (uNewSize > uCurSize) 162 | { 163 | while (uCurSize < uNewSize) 164 | { 165 | uCurSize *= 2; 166 | } 167 | this->Realloc(uCurSize, FALSE); 168 | } 169 | } 170 | 171 | void RemoveAt(size_t nIdx) 172 | { 173 | this->RemoveAt(&this->a[nIdx]); 174 | } 175 | 176 | void RemoveAt(T* pos) // TODO test this 177 | { 178 | pos->~T(); 179 | 180 | T* pItemToRemove = pos; 181 | T* pNextItem = pos + 1; 182 | 183 | size_t nItemIdx = this->IndexOf(pNextItem); 184 | size_t nItemsToMove = this->GetCount() - nItemIdx; 185 | size_t nBytesToMove = nItemsToMove * sizeof(T); 186 | 187 | memmove(pItemToRemove, pNextItem, nBytesToMove); 188 | 189 | size_t* pCount = &reinterpret_cast(this->a)[-1]; 190 | *pCount -= 1; 191 | } 192 | 193 | /// 194 | /// Gets size of array. 195 | /// 196 | size_t GetCount() 197 | { 198 | if (this->a) 199 | { 200 | size_t nCount = reinterpret_cast(this->a)[-1]; 201 | return nCount; 202 | } 203 | else 204 | { 205 | return 0; 206 | } 207 | } 208 | 209 | /// 210 | /// Gets the index of the pointer passed into the function. 211 | /// Passing a pointer that does not exist in the array will result in undefined behavior. 212 | /// 213 | UINT IndexOf(T* pos) 214 | { 215 | return pos - this->a; // compiler automatically does the math here so all we need to write is the subtraction 216 | } 217 | 218 | /// 219 | /// Gets the next pointer in the array sequence, or nullptr if there is no next item. 220 | /// The return value is the original pointer passed into the pos parameter, and the pos 221 | /// parameter will contain the pointer to the next value. 222 | /// 223 | T* GetNext(T** pos) 224 | { 225 | T* result = *pos; 226 | 227 | *pos = *pos > this->a ? &result[-1] : nullptr; 228 | 229 | return result; 230 | } 231 | 232 | /// 233 | /// Gets the previous pointer in the array sequence, or nullptr if there is no previous item. 234 | /// The return value is the original pointer passed into the pos parameter, and the pos 235 | /// parameter will contain the pointer to the previous value. 236 | /// 237 | T* GetPrev(T** pos) 238 | { 239 | T* result = *pos; 240 | 241 | /* Highest index is array size - 1 */ 242 | size_t nIndex = reinterpret_cast(this->a)[-1]; 243 | nIndex -= 1; 244 | 245 | if (*pos < &this->a[nIndex]) 246 | { 247 | *pos = result + 1; 248 | } 249 | else 250 | { 251 | *pos = nullptr; 252 | } 253 | 254 | return result; 255 | } 256 | 257 | /// 258 | /// Fetches a pointer to the value on the top of the array stack. 259 | /// 260 | T* GetHeadPosition() 261 | { 262 | if (this->a) 263 | { 264 | size_t nIdx = reinterpret_cast(this->a)[-1] - 1; 265 | return &this->a[nIdx]; 266 | } 267 | else 268 | { 269 | return nullptr; 270 | } 271 | } 272 | 273 | T* GetTailPosition() 274 | { 275 | return this->a; 276 | } 277 | 278 | /// 279 | /// Removes all items from the array and calls their destructors. 280 | /// 281 | void RemoveAll() 282 | { 283 | if (this->a) 284 | { 285 | /* Get pointer to allocation base (array base - 4 bytes) */ 286 | DWORD* pAllocationBasePointer = &reinterpret_cast(this->a)[-1]; 287 | size_t nMaxIndex = *pAllocationBasePointer - 1; 288 | 289 | /* Call destructor */ 290 | T* start = this->a; 291 | T* end = reinterpret_cast(&this->a[nMaxIndex]); 292 | this->Destroy(start, end); 293 | 294 | /* Free array allocation */ 295 | delete pAllocationBasePointer; 296 | this->a = nullptr; 297 | } 298 | } 299 | 300 | private: 301 | static void Construct(T* start, T* end) 302 | { 303 | for (T* i = start; i < end; i++) 304 | { 305 | i = T(); 306 | } 307 | } 308 | 309 | static void Destroy(T* start, T* end) 310 | { 311 | for (T* i = start; i < end; i++) 312 | { 313 | i->~T(); 314 | } 315 | } 316 | 317 | void Alloc(size_t uSize) 318 | { 319 | this->RemoveAll(); 320 | 321 | if (!uSize) return; 322 | 323 | /* Allocate Desired Array Size + 4 bytes */ 324 | /* We casting to a dword so we can write and adjust the pointer easier */ 325 | DWORD* pAlloc = (DWORD*)ZAllocEx::GetInstance()->Alloc(sizeof(T) * uSize + sizeof(PVOID)); 326 | 327 | /* Assign number of array items to array head */ 328 | *pAlloc = uSize; 329 | 330 | /* Assign start of real allocated block to array pointer */ 331 | /* We take index 1 because index zero is the array item count */ 332 | pAlloc += 1; 333 | this->a = reinterpret_cast(pAlloc); 334 | } 335 | 336 | void Realloc(size_t u, int nMode) 337 | { 338 | size_t uCurArraySize = this->GetCount(); 339 | size_t uAllocationSize; 340 | 341 | if (u > uCurArraySize) 342 | { 343 | if (this->a) 344 | { 345 | /* Grab size of allocation block -- remember, ZAllocEx encodes this at the head of each block */ 346 | uAllocationSize = reinterpret_cast(this->a)[-2]; 347 | 348 | if (uAllocationSize > INT_MAX) // this means its negative since the datatype is unsigned 349 | { 350 | uAllocationSize = ~uAllocationSize; 351 | } 352 | 353 | uAllocationSize -= sizeof(DWORD); 354 | } 355 | else 356 | { 357 | uAllocationSize = 0; 358 | } 359 | 360 | if (u > uAllocationSize) 361 | { 362 | /* Allocate enough space for desired size and the extra storage slot for the size of the array */ 363 | PVOID pNewAlloc = ZAllocEx::GetInstance()->Alloc(u + sizeof(DWORD)); 364 | 365 | /* set new allocation pointer to the array start location (+1) */ 366 | pNewAlloc = (PVOID)(reinterpret_cast(pNewAlloc) + 1); 367 | 368 | if (this->a) 369 | { 370 | /* Copy old array to new array */ 371 | if ((nMode & 1) == FALSE) 372 | { 373 | memcpy(pNewAlloc, this->a, uCurArraySize); 374 | } 375 | 376 | /* Free old memory allocation */ 377 | void** pCurrentAllocationBase = &reinterpret_cast(this->a)[-1]; 378 | delete pCurrentAllocationBase; 379 | //ZAllocEx::GetInstance()->Free(pCurrentAllocationBase); 380 | } 381 | this->a = reinterpret_cast(pNewAlloc); 382 | } 383 | } 384 | 385 | if (this->a) 386 | { 387 | size_t* pCount = &reinterpret_cast(this->a)[-1]; 388 | *pCount = u; 389 | } 390 | } 391 | 392 | void Reserve(size_t uItems) 393 | { 394 | size_t uCurArraySize; 395 | size_t uAllocationSize; 396 | size_t uMaxCountInAllocBlock; 397 | 398 | if (this->a) 399 | { 400 | /* Grab size of allocation block -- remember, ZAllocEx encodes this at the head of each block */ 401 | uAllocationSize = reinterpret_cast(this->a)[-2]; 402 | 403 | if (uAllocationSize > INT_MAX) // this means its negative since the datatype is unsigned 404 | { 405 | uAllocationSize = ~uAllocationSize; 406 | } 407 | 408 | /* Determine the real number of array item slots based on the allocation block header */ 409 | uMaxCountInAllocBlock = (uAllocationSize - sizeof(PVOID)) / sizeof(T); 410 | } 411 | else 412 | { 413 | uMaxCountInAllocBlock = 0; 414 | } 415 | 416 | if (uMaxCountInAllocBlock == uItems) return; 417 | 418 | uCurArraySize = this->GetCount(); 419 | 420 | /* Allocate new block */ 421 | DWORD* pNewAllocationBase = (DWORD*)ZAllocEx::GetInstance()->Alloc(sizeof(T) * uItems + sizeof(PVOID)); 422 | 423 | /* Encode new array size at allocation base */ 424 | *pNewAllocationBase = uCurArraySize; 425 | 426 | /* Increment allocation base */ 427 | pNewAllocationBase += 1; 428 | 429 | if (this->a) 430 | { 431 | /* Copy existing memory into the new allocation */ 432 | memcpy(pNewAllocationBase, this->a, sizeof(T) * uCurArraySize); 433 | 434 | /* Free old memory allocation */ 435 | void** pCurrentAllocationBase = &reinterpret_cast(this->a)[-1]; 436 | delete pCurrentAllocationBase; 437 | //ZAllocEx::GetInstance()->Free(pCurrentAllocationBase); 438 | } 439 | 440 | this->a = reinterpret_cast(pNewAllocationBase); 441 | 442 | /* Reassign value at array size pointer to match new size */ 443 | /*DWORD* pdwArraySize = &reinterpret_cast(this->a)[-1]; 444 | *pdwArraySize = uCurArraySize;*/ 445 | } 446 | }; 447 | 448 | assert_size(sizeof(ZArray), 0x04) -------------------------------------------------------------------------------- /Common/ZFatalSection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "logger.h" 3 | 4 | struct ZFatalSectionData 5 | { 6 | void* _m_pTIB; 7 | int _m_nRef; 8 | }; 9 | 10 | struct ZFatalSection : ZFatalSectionData 11 | { 12 | /* TODO emulate this class */ 13 | }; 14 | 15 | assert_size(sizeof(ZFatalSection), 0x8) -------------------------------------------------------------------------------- /Common/ZList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ZRefCountedAccessor.h" 3 | #include "ZRefCountedDummy.h" 4 | 5 | /// 6 | /// Emulation of MapleStory's implementation of a doubly linked list template. 7 | /// 8 | /// 9 | template 10 | class ZList : ZRefCountedAccessor, ZRefCountedAccessor> 11 | { 12 | #define ZLIST_INVALID_INDEX -1 13 | 14 | private: 15 | char gap4[1]; 16 | size_t m_uCount; 17 | T* m_pHead; 18 | T* m_pTail; 19 | public: 20 | 21 | /***=========== CONSTRUCTORS ===========***/ 22 | 23 | ZList() 24 | { 25 | this->gap4[0] = 0; 26 | this->m_uCount = 0; 27 | this->m_pHead = nullptr; 28 | this->m_pTail = nullptr; 29 | } 30 | 31 | ZList(ZList* l) 32 | { 33 | this->gap4[0] = 0; 34 | this->m_uCount = 0; 35 | this->m_pHead = nullptr; 36 | this->m_pTail = nullptr; 37 | 38 | this->RemoveAll(); 39 | this->AddTail(l); 40 | } 41 | 42 | virtual ~ZList() 43 | { 44 | this->RemoveAll(); 45 | } 46 | 47 | /***=========== OPERATOR OVERLOAD ===========***/ 48 | 49 | ZList* operator=(ZList* l) 50 | { 51 | this->RemoveAll(); 52 | this->AddTail(l); 53 | 54 | return this; 55 | } 56 | 57 | /***=========== MEMBER VARIABLE ACCESSORS ===========***/ 58 | 59 | T* GetHeadPosition() 60 | { 61 | return this->m_pHead; 62 | } 63 | 64 | T* GetTailPosition() 65 | { 66 | return this->m_pTail; 67 | } 68 | 69 | size_t GetCount() 70 | { 71 | return this->m_uCount; 72 | } 73 | 74 | /***=========== ADD HEAD ===========***/ 75 | 76 | /* TODO fix: doesnt work hehe */ 77 | 78 | //T* AddHead() 79 | //{ 80 | // T* pAlloc = this->New(nullptr, this->m_pHead); 81 | 82 | // if (this->m_pTail) 83 | // { 84 | // ZRefCountedDummy* pNode = pAlloc ? this->CastNode(pAlloc) : nullptr; 85 | // ZRefCountedDummy* pHeadNode = this->CastNode(this->m_pHead); 86 | 87 | // pHeadNode->m_pPrev = pNode; 88 | // this->m_pHead = pAlloc; 89 | // } 90 | // else 91 | // { 92 | // this->m_pHead = pAlloc; 93 | // this->m_pTail = pAlloc; 94 | // } 95 | 96 | // return pAlloc; 97 | //} 98 | 99 | //T* AddHead(T* d) 100 | //{ 101 | // T* pNewHead = this->AddHead(); 102 | // *pNewHead = *d; 103 | // return pNewHead; 104 | //} 105 | 106 | //T* AddHead(ZList* l) 107 | //{ 108 | // return nullptr; // TODO 109 | //} 110 | 111 | /***=========== ADD TAIL ===========***/ 112 | 113 | T* AddTail() 114 | { 115 | T* pAlloc = this->New(this->m_pTail, nullptr); 116 | 117 | if (this->m_pTail) 118 | { 119 | ZRefCountedDummy* pNode = pAlloc ? this->CastNode(pAlloc) : nullptr; 120 | ZRefCountedDummy* pTailNode = this->CastNode(this->m_pTail); 121 | 122 | pTailNode->m_pNext = pNode; 123 | this->m_pTail = pAlloc; 124 | } 125 | else 126 | { 127 | this->m_pHead = pAlloc; 128 | this->m_pTail = pAlloc; 129 | } 130 | 131 | return pAlloc; 132 | } 133 | 134 | T* AddTail(T* d) 135 | { 136 | T* pNewTail = this->AddTail(); 137 | *pNewTail = *d; 138 | return pNewTail; 139 | } 140 | 141 | void AddTail(ZList* l) // TODO test this, currently untested and prolly not working 142 | { 143 | T* pHead = l->m_pHead; 144 | 145 | while (pHead) 146 | { 147 | T* pNext = pHead; 148 | 149 | ZRefCountedDummy* pNode = this->CastNode(pHead); 150 | ZRefCountedDummy* pNodePrev = reinterpret_cast*>(pNode->m_pPrev); 151 | 152 | pHead = pNodePrev ? &pNodePrev->t : reinterpret_cast(nullptr); 153 | 154 | T* pNew = this->AddTail(); 155 | *pNew = *pNext; 156 | } 157 | } 158 | 159 | /***=========== NODE REMOVAL ===========***/ 160 | 161 | void RemoveAll() 162 | { 163 | T* pPosition = this->GetHeadPosition(); 164 | 165 | while (pPosition) 166 | { 167 | T* pItem = this->GetNext(&pPosition); 168 | 169 | delete this->CastNode(pItem); // IMPORTANT: must delete the node, not the wrapped object (T) 170 | } 171 | 172 | this->m_pTail = nullptr; 173 | this->m_pHead = nullptr; 174 | this->m_uCount = 0; 175 | } 176 | 177 | void RemoveAt(T* pos) 178 | { 179 | ZRefCountedDummy* pNodeDelete = pos ? this->CastNode(pos) : nullptr; 180 | 181 | if (pNodeDelete && pNodeDelete->m_pPrev) 182 | { 183 | ZRefCountedDummy* pPrevNode = reinterpret_cast*>(pNodeDelete->m_pPrev); 184 | 185 | if (pNodeDelete->m_pNext) 186 | { 187 | ZRefCountedDummy* pNextNode = reinterpret_cast*>(pNodeDelete->m_pNext); 188 | 189 | pPrevNode->m_pNext = pNodeDelete->m_pNext; 190 | pNextNode->m_pPrev = pNodeDelete->m_pPrev; 191 | } 192 | else // there is no node after the deleted node, meaning the deleted node is the tail node 193 | { 194 | // the node prior to the deleted node is the new tail 195 | pPrevNode->m_pNext = nullptr; 196 | this->m_pTail = &pPrevNode->t; 197 | } 198 | } 199 | else if (pNodeDelete && pNodeDelete->m_pNext) 200 | { 201 | ZRefCountedDummy* pNextNode = reinterpret_cast*>(pNodeDelete->m_pNext); 202 | 203 | pNextNode->m_pPrev = nullptr; 204 | this->m_pHead = &pNextNode->t; 205 | } 206 | else // no next and no prev node 207 | { 208 | this->m_pTail = nullptr; 209 | this->m_pHead = nullptr; 210 | } 211 | 212 | this->m_uCount -= 1; 213 | 214 | delete pNodeDelete; // IMPORTANT: must delete the node, not the wrapped object (T) 215 | } 216 | 217 | /***=========== NODE SEARCH ===========***/ 218 | 219 | T* FindIndex(const size_t uIndex) // TODO fix this 220 | { 221 | T* pRet; 222 | 223 | if (uIndex >= this->m_uCount) return nullptr; 224 | 225 | if (uIndex <= this->m_uCount / 2) 226 | { 227 | pRet = this->m_pHead; 228 | 229 | for (int i = 0; i < uIndex; i++) 230 | { 231 | if (!pRet) break; 232 | 233 | ZRefCounted* pNode = this->CastNode(pRet)->m_pNext; 234 | 235 | if (pNode) pRet = &reinterpret_cast*>(pNode)->t; 236 | } 237 | } 238 | else 239 | { 240 | pRet = this->m_pTail; 241 | 242 | for (int i = this->m_uCount - 1; i > uIndex; i--) 243 | { 244 | if (!pRet) break; 245 | 246 | ZRefCounted* pNode = this->CastNode(pRet)->m_pPrev; 247 | 248 | if (pNode) pRet = &reinterpret_cast*>(pNode)->t; 249 | } 250 | } 251 | 252 | return pRet; 253 | } 254 | 255 | int IndexOf(const T* pos) 256 | { 257 | T* pHead = this->m_pHead; 258 | int nIdx = 0; 259 | 260 | if (!pHead) return ZLIST_INVALID_INDEX; 261 | 262 | while (pHead != pos) 263 | { 264 | nIdx += 1; 265 | 266 | ZRefCountedDummy* pNode = this->CastNode(pHead); 267 | 268 | if (!pNode->m_pNext) return ZLIST_INVALID_INDEX; 269 | 270 | pHead = &reinterpret_cast*>(pNode->m_pNext)->t; 271 | 272 | if (!pHead) return ZLIST_INVALID_INDEX; 273 | } 274 | 275 | if (!pHead) return ZLIST_INVALID_INDEX; 276 | 277 | return nIdx; 278 | } 279 | 280 | /// 281 | /// Tries to find a node in the list with the same value as the given node d. 282 | /// If posAfter is defined, the function will only search for items after the given posAfter item. 283 | /// If posAfter is defined but is not a list node, undefined behavior will occur. 284 | /// 285 | T* Find(T* d, T* posAfter) 286 | { 287 | T* pRet; 288 | if (posAfter) 289 | { 290 | ZRefCountedDummy* pNode = this->CastNode(posAfter); 291 | 292 | if (!pNode->m_pNext) return nullptr; 293 | 294 | pRet = &reinterpret_cast*>(pNode->m_pNext)->t; 295 | } 296 | else 297 | { 298 | pRet = this->m_pHead; 299 | } 300 | 301 | if (!pRet) return nullptr; 302 | 303 | while (*pRet != *d) 304 | { 305 | ZRefCountedDummy* pNode = this->CastNode(pRet); 306 | 307 | if (pNode->m_pNext) 308 | { 309 | pRet = &reinterpret_cast*>(pNode->m_pNext)->t; 310 | 311 | if (pRet) continue; 312 | } 313 | 314 | return nullptr; 315 | } 316 | 317 | return pRet; 318 | } 319 | 320 | /***=========== INSERTION ===========***/ 321 | 322 | T* Insert(T* d) 323 | { 324 | //T* result = this->AddTail(this); 325 | return nullptr; // TODO 326 | } 327 | 328 | T** InsertBefore(T* pos) 329 | { 330 | return nullptr; // TODO 331 | } 332 | 333 | /***=========== TRAVERSAL ===========***/ 334 | 335 | T* GetNext(T** pos) 336 | { 337 | if (!pos) return nullptr; 338 | 339 | T* pRet = *pos; 340 | 341 | if (!pRet) 342 | { 343 | *pos = nullptr; 344 | return nullptr; 345 | } 346 | 347 | ZRefCountedDummy* pNode = this->CastNode(pRet); 348 | 349 | *pos = pNode->m_pNext ? reinterpret_cast(&reinterpret_cast*>(pNode->m_pNext)->t) : nullptr; 350 | 351 | return pRet; 352 | } 353 | 354 | T* GetPrev(T** pos) 355 | { 356 | if (!pos) return nullptr; 357 | 358 | T* pRet = *pos; 359 | 360 | if (!pRet) 361 | { 362 | *pos = nullptr; 363 | return nullptr; 364 | } 365 | 366 | ZRefCountedDummy* pNode = this->CastNode(pRet); 367 | 368 | *pos = pNode->m_pPrev ? &reinterpret_cast*>(pNode->m_pPrev)->t : nullptr; 369 | 370 | return pRet; 371 | } 372 | 373 | private: 374 | 375 | /// 376 | /// If t is not a ZList member, then this will produce undefined results 377 | /// 378 | /// 379 | /// 380 | ZRefCountedDummy* CastNode(T* t) 381 | { 382 | return reinterpret_cast*>(reinterpret_cast(t) - 16); 383 | } 384 | 385 | T* New(T* pPrev, T* pNext) 386 | { 387 | ZRefCountedDummy* pAlloc = new ZRefCountedDummy(); // IDA: ZRefCounted_Alloc>(); 388 | 389 | pAlloc->m_pPrev = pPrev ? this->CastNode(pPrev) : nullptr; 390 | pAlloc->m_pNext = pNext ? this->CastNode(pNext) : nullptr; 391 | pAlloc->m_nRef = 0; 392 | 393 | this->m_uCount += 1; 394 | 395 | return &pAlloc->t; 396 | } 397 | }; 398 | 399 | assert_size(sizeof(ZList), 0x14); -------------------------------------------------------------------------------- /Common/ZMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ZRecyclable.h" 3 | 4 | template 5 | class ZMap 6 | { 7 | public: 8 | struct _PAIR : ZRecyclable::_PAIR, int, ZMap::_PAIR> 9 | { 10 | ZMap::_PAIR* pNext; 11 | T key; 12 | U value; 13 | 14 | _PAIR(T key, _PAIR* pNext) 15 | { 16 | this->pNext = pNext; 17 | this->key = key; 18 | this->value = U(); 19 | } 20 | }; 21 | 22 | private: 23 | _PAIR** m_apTable; 24 | size_t m_uTableSize; 25 | size_t m_uCount; 26 | size_t m_uAutoGrowEvery128; 27 | size_t m_uAutoGrowLimit; 28 | 29 | private: 30 | ZMap() 31 | { 32 | this->m_apTable = nullptr; 33 | this->m_uTableSize = 0; 34 | this->m_uCount = 0; 35 | this->m_uAutoGrowEvery128 = 0; 36 | this->m_uAutoGrowLimit = 0; 37 | } 38 | 39 | ZMap(size_t uHashTableSize, size_t uAutoGrowEvery128) 40 | { 41 | this->m_apTable = nullptr; 42 | this->m_uTableSize = uHashTableSize; 43 | this->m_uCount = 0; 44 | 45 | this->CalcAutoGrow(uAutoGrowEvery128); 46 | } 47 | 48 | virtual ~ZMap() 49 | { 50 | this->RemoveAll(); 51 | } 52 | 53 | _PAIR* GetHeadPosition() 54 | { 55 | _PAIR** apTable = this->m_apTable; 56 | 57 | if (!apTable) return nullptr; 58 | 59 | _PAIR** pTableEnd = &apTable[this->m_uTableSize]; 60 | 61 | if (apTable >= pTableEnd) return nullptr; 62 | 63 | while (!*apTable) 64 | { 65 | apTable += 1; 66 | if (apTable >= pTableEnd) return nullptr; 67 | } 68 | return *apTable; 69 | } 70 | 71 | _PAIR* GetPos() 72 | { 73 | 74 | } 75 | 76 | U* GetAt(const T* key) 77 | { 78 | ZMap::_PAIR** v3; // esi 79 | ZMap::_PAIR* v5; // esi 80 | 81 | v3 = this->m_apTable; 82 | 83 | if (!v3) return 0; 84 | 85 | v5 = v3[_rotr(*key, 5) % this->m_uTableSize]; 86 | 87 | if (!v5) return 0; 88 | 89 | while (v5->key != *key) 90 | { 91 | v5 = v5->pNext; 92 | 93 | if (!v5) return 0; 94 | } 95 | 96 | // we are gonna skip the copying for now 97 | //if (value) ZRef::operator=(value, &v5->value); 98 | 99 | return &v5->value; 100 | } 101 | 102 | _PAIR* GetNext() 103 | { 104 | 105 | } 106 | 107 | _PAIR* Insert() 108 | { 109 | 110 | } 111 | 112 | void RemoveAll() 113 | { 114 | 115 | } 116 | 117 | BOOL RemoveKey() 118 | { 119 | 120 | } 121 | 122 | private: 123 | void ResizeHashTable(size_t uHashTableSize, size_t uAutoGrowEvery128) 124 | { 125 | 126 | } 127 | 128 | void CalcAutoGrow(size_t uAutoGrowEvery128) 129 | { 130 | if (uAutoGrowEvery128) 131 | { 132 | this->m_uAutoGrowEvery128 = uAutoGrowEvery128; 133 | } 134 | if (this->m_uAutoGrowEvery128 == -1) 135 | { 136 | this->m_uAutoGrowLimit = -1; 137 | } 138 | else 139 | { 140 | this->m_uAutoGrowLimit = this->m_uAutoGrowEvery128 * this->m_uTableSize >> 7; 141 | } 142 | } 143 | }; 144 | 145 | assert_size(sizeof(ZMap::_PAIR), 0x10) 146 | assert_size(sizeof(ZMap), 0x18) -------------------------------------------------------------------------------- /Common/ZRecyclable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "logger.h" 3 | #include "ZRecyclableAvBuffer.h" 4 | 5 | template 6 | class ZRecyclable : protected ZAllocBase 7 | { 8 | protected: 9 | virtual ~ZRecyclable() = default; 10 | 11 | /* uses ZRecycleAvBuffer for memory management */ 12 | 13 | void* operator new(unsigned int uSize) 14 | { 15 | return ZRecyclableAvBuffer::GetInstance()->raw_new(); 16 | } 17 | 18 | void* operator new[](unsigned int uSize) 19 | { 20 | return ZRecyclableAvBuffer::GetInstance()->raw_new(); 21 | } 22 | 23 | void operator delete(void* p) 24 | { 25 | ZRecyclableAvBuffer::GetInstance()->raw_delete(p); 26 | } 27 | 28 | void operator delete[](void* p) 29 | { 30 | ZRecyclableAvBuffer::GetInstance()->raw_delete(p); 31 | } 32 | }; 33 | 34 | assert_size(sizeof(ZRecyclable), 0x4); -------------------------------------------------------------------------------- /Common/ZRecyclableAvBuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ZRecyclableStatic.h" 3 | #include "ZFatalSection.h" 4 | #include "ZAllocEx.h" 5 | #include 6 | 7 | #define ZRECYCLABLE_RAW_BLOCK_SIZE 0x10 8 | 9 | template 10 | class ZRecyclableAvBuffer : ZRecyclableStatic::CallBack 11 | { 12 | private: 13 | ZFakeStatAvBuff* m_pStat; 14 | ZFatalSection m_l; // TODO maybe emulate this one day 15 | void* m_pAv; 16 | 17 | ZRecyclableAvBuffer() 18 | { 19 | this->m_pAv = nullptr; 20 | this->m_pStat = nullptr; 21 | ZeroMemory(&this->m_l, sizeof(ZFatalSection)); 22 | } 23 | 24 | static std::mutex* GetMutex() // instead of ZFatalSection 25 | { 26 | static std::mutex mtx; 27 | return &mtx; 28 | } 29 | 30 | public: 31 | static ZRecyclableAvBuffer* GetInstance() 32 | { 33 | static ZRecyclableAvBuffer s_pInstance = ZRecyclableAvBuffer(); 34 | 35 | return &s_pInstance; 36 | } 37 | 38 | void** raw_new() 39 | { 40 | void** pAlloc; 41 | 42 | this->GetMutex()->lock(); 43 | 44 | if (!this->m_pAv) 45 | { 46 | this->m_pAv = ZAllocBase::AllocRawBlocks(sizeof(T), ZRECYCLABLE_RAW_BLOCK_SIZE); 47 | } 48 | 49 | pAlloc = (void**)this->m_pAv; 50 | this->m_pAv = *pAlloc; 51 | 52 | this->GetMutex()->unlock(); 53 | 54 | return pAlloc; 55 | } 56 | 57 | void raw_delete(void* p) 58 | { 59 | this->GetMutex()->lock(); 60 | 61 | *(void**)p = this->m_pAv; 62 | this->m_pAv = p; 63 | 64 | this->GetMutex()->unlock(); 65 | } 66 | }; 67 | 68 | assert_size(sizeof(ZRecyclableAvBuffer), 0x18) -------------------------------------------------------------------------------- /Common/ZRecyclableStatic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "logger.h" 3 | 4 | class ZFakeStatAvBuff { }; 5 | 6 | class ZRecyclableStatic 7 | { 8 | public: 9 | struct CallBack 10 | { 11 | public: 12 | ZRecyclableStatic::CallBack* m_pNext; 13 | 14 | protected: 15 | CallBack() 16 | { 17 | m_pNext = nullptr; 18 | } 19 | 20 | virtual ~CallBack() = default; 21 | }; 22 | 23 | protected: 24 | ZRecyclableStatic::CallBack* m_pHead; 25 | }; 26 | 27 | assert_size(sizeof(ZFakeStatAvBuff), 0x1) 28 | assert_size(sizeof(ZRecyclableStatic::CallBack), 0x8) 29 | assert_size(sizeof(ZRecyclableStatic), 0x4) -------------------------------------------------------------------------------- /Common/ZRef.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "logger.h" 3 | #include "ZRefCounted.h" 4 | #include "ZRefCountedDummy.h" 5 | #include "ZRefCountedAccessor.h" 6 | #include 7 | 8 | // ZRef is a smart pointer wrapper class that MapleStory uses to manage memory. 9 | // If the object passed to the ZRef template is a ZRefCounted object, it will treat it normally, 10 | // otherwise it will add ZRefCountedDummy as an additional wrapper level to simulate a 11 | // ZRefCounted derived class. 12 | 13 | template 14 | class ZRef : protected ZRefCountedAccessor, protected ZRefCountedAccessor> 15 | { 16 | private: 17 | BYTE gap0[1]; 18 | 19 | public: 20 | T* p; // TODO uhh maybe reconsider exposing this as public 21 | 22 | ZRef() 23 | { 24 | this->gap0[0] = NULL; 25 | this->p = nullptr; 26 | } 27 | 28 | ZRef(ZRefCounted* pT, BOOL bAddRef = TRUE) 29 | { 30 | this->gap0[0] = NULL; 31 | 32 | if (!pT) 33 | { 34 | this->p = nullptr; 35 | } 36 | else 37 | { 38 | this->p = reinterpret_cast(pT); 39 | 40 | if (bAddRef) 41 | { 42 | InterlockedIncrement(&pT->m_nRef); // (this->p - 12) 43 | } 44 | } 45 | } 46 | 47 | ZRef(ZRef* r) 48 | { 49 | ZRefCounted* pBase; 50 | this->gap0[0] = NULL; 51 | 52 | this->p = r->p; 53 | 54 | if (r->p) 55 | { 56 | pBase = r->GetBase(); 57 | 58 | InterlockedIncrement(&pBase->m_nRef); // (this->p - 12) 59 | } 60 | } 61 | 62 | ~ZRef() 63 | { 64 | this->ReleaseRaw(); 65 | } 66 | 67 | /// 68 | /// Allocate resources for encapsulated pointer and initialize type. 69 | /// 70 | void Alloc() 71 | { 72 | this->ReleaseRaw(); 73 | 74 | /* is_base_of was released in c++11, so maple did this some other way */ 75 | if (std::is_base_of()) 76 | { 77 | ZRefCounted* pAlloc = reinterpret_cast(new T()); 78 | 79 | pAlloc->m_nRef = 1; 80 | this->p = reinterpret_cast(pAlloc); 81 | } 82 | else 83 | { 84 | ZRefCountedDummy* pAlloc = new ZRefCountedDummy();// ZRefCounted_Alloc>(); 85 | 86 | pAlloc->m_nRef = 1; 87 | this->p = &pAlloc->t; 88 | } 89 | } 90 | 91 | /// 92 | /// Set this ZRef pointer equal to the given pointer. Only works for ZRefCounted types. 93 | /// 94 | ZRef* operator=(ZRefCounted* pT) 95 | { 96 | ZRef r; 97 | if (pT) 98 | { 99 | InterlockedIncrement(&pT->m_nRef); 100 | } 101 | 102 | T* old = this->p; 103 | this->p = reinterpret_cast(pT); 104 | r.p = old; // resources are automatically freed by compiler-generated destructor 105 | 106 | return this; 107 | } 108 | 109 | /// 110 | /// Set this ZRef equal to the given ZRef 111 | /// 112 | ZRef* operator=(ZRef* r) 113 | { 114 | ZRefCounted* pBase; 115 | 116 | if (r->p) 117 | { 118 | pBase = r->GetBase(); 119 | InterlockedIncrement(&pBase->m_nRef); 120 | } 121 | 122 | this->ReleaseRaw(); 123 | 124 | this->p = r->p; 125 | 126 | return this; 127 | } 128 | 129 | /// 130 | /// Release pointer resources. 131 | /// 132 | ZRef* operator=(int zero) 133 | { 134 | this->ReleaseRaw(); 135 | return this; 136 | } 137 | 138 | /// 139 | /// Fetch pointer to encapsulated object. 140 | /// 141 | operator T* () 142 | { 143 | return this->p; 144 | } 145 | 146 | /// 147 | /// Fetch pointer to encapsulated object. 148 | /// 149 | T* operator->() 150 | { 151 | return this->p; 152 | } 153 | 154 | /// 155 | /// Determine if encapsulated pointer is null. 156 | /// 157 | BOOL operator!() 158 | { 159 | return this->p == nullptr; 160 | } 161 | 162 | private: 163 | /// 164 | /// Decrement pointer reference count and release resources if references are zero. 165 | /// 166 | void ReleaseRaw() 167 | { 168 | if (!this->p) return; 169 | 170 | ZRefCounted* pBase = this->GetBase(); 171 | 172 | if (InterlockedDecrement(&pBase->m_nRef) <= 0) 173 | { 174 | InterlockedIncrement(&pBase->m_nRef); 175 | 176 | delete pBase; // if (v3) (**v3)(v3, 1); 177 | } 178 | 179 | this->p = nullptr; 180 | } 181 | 182 | /// 183 | /// Returns the associated ZRefCounted object pointer. 184 | /// 185 | ZRefCounted* GetBase() 186 | { 187 | ZRefCounted* pBase; 188 | 189 | /* is_base_of was released in c++11, so maple did this some other way */ 190 | if (std::is_base_of() || typeid(ZRefCounted) == typeid(T)) 191 | { 192 | pBase = reinterpret_cast(this->p); 193 | } 194 | else 195 | { 196 | pBase = reinterpret_cast(((char*)this->p) - (sizeof(ZRefCountedDummy) - sizeof(T))); 197 | 198 | static_assert(sizeof(ZRefCountedDummy) - sizeof(T) == 16, "Size is not expected value"); 199 | } 200 | 201 | return pBase; 202 | } 203 | }; 204 | 205 | assert_size(sizeof(ZRef), 0x08); -------------------------------------------------------------------------------- /Common/ZRefCounted.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "logger.h" 3 | 4 | class ZRefCounted 5 | { 6 | public: 7 | ZRefCounted() 8 | { 9 | this->m_nRef = 0; 10 | this->m_pPrev = nullptr; 11 | this->m_pNext = nullptr; 12 | } 13 | 14 | virtual ~ZRefCounted() = default; 15 | 16 | union 17 | { 18 | volatile long m_nRef; 19 | ZRefCounted* m_pNext; 20 | }; 21 | 22 | ZRefCounted* m_pPrev; 23 | }; 24 | 25 | /* not really sure why nexon has this class but this is all it does o__o */ 26 | template 27 | T* ZRefCounted_Alloc() 28 | { 29 | T* pAlloc = new T(); 30 | 31 | return pAlloc; 32 | } 33 | 34 | assert_size(sizeof(ZRefCounted), 0x0C); -------------------------------------------------------------------------------- /Common/ZRefCountedAccessor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "logger.h" 3 | 4 | struct ZRefCountedAccessorBase 5 | { 6 | }; 7 | 8 | 9 | template 10 | class ZRefCountedAccessor : ZRefCountedAccessorBase 11 | { 12 | 13 | }; 14 | 15 | assert_size(sizeof(ZRefCountedAccessor), 0x01); 16 | assert_size(sizeof(ZRefCountedAccessorBase), 0x01); -------------------------------------------------------------------------------- /Common/ZRefCountedDummy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ZRefCounted.h" 3 | #include "ZRecyclable.h" 4 | #include "ZRecyclableAvBuffer.h" 5 | 6 | template 7 | class ZRefCountedDummy : public ZRefCounted, public ZRecyclable, 16, T> 8 | { 9 | public: 10 | T t; 11 | 12 | void* operator new(unsigned int uSize) 13 | { 14 | return ZRecyclableAvBuffer>::GetInstance()->raw_new(); 15 | } 16 | 17 | void* operator new[](unsigned int uSize) 18 | { 19 | return ZRecyclableAvBuffer>::GetInstance()->raw_new(); 20 | } 21 | void operator delete[](void* p) 22 | { 23 | ZRecyclableAvBuffer>::GetInstance()->raw_delete(p); 24 | } 25 | 26 | void operator delete(void* p) 27 | { 28 | ZRecyclableAvBuffer>::GetInstance()->raw_delete(p); 29 | } 30 | }; 31 | 32 | assert_size(sizeof(ZRefCountedDummy), 0x14); -------------------------------------------------------------------------------- /Common/ZtlSecure.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | /* 6 | Credits: https://github.com/67-6f-64/Firefly/blob/master/Firefly%20Spy/ZtlSecure.hpp 7 | 8 | Modifications By: 9 | - Rajan Grewal 10 | - Minimum Delta 11 | */ 12 | 13 | #define ZTLSECURE_CHECKSUM 0xBAADF00D 14 | #define ZTLSECURE_ROTATION 5 15 | 16 | template // uses fastcall because it passes args through registers instead of on stack -- faster execution 17 | unsigned int __fastcall ZtlSecureTear(T* at, T t) 18 | { 19 | /* storage type is 1 byte large if size < 4, otherwise its 4 bytes large */ 20 | typedef std::conditional<(sizeof(T) < 4), unsigned char, unsigned int>::type PType; 21 | 22 | unsigned int checksum = ZTLSECURE_CHECKSUM; 23 | 24 | PType* key = reinterpret_cast(&at[0]); 25 | PType* encrypted_data = reinterpret_cast(&at[1]); 26 | 27 | PType* p = reinterpret_cast(&t); 28 | 29 | for (int i = 0; i < sizeof(T) / sizeof(PType); i++) 30 | { 31 | int rotations = sizeof(T) < sizeof(unsigned int) ? NULL : ZTLSECURE_ROTATION; 32 | 33 | /* TODO use CRand32 like MapleStory does */ 34 | key[i] = sizeof(T) < sizeof(unsigned int) ? LOBYTE(rand()) : rand(); 35 | encrypted_data[i] = _rotr(p[i] ^ key[i], rotations); 36 | 37 | checksum = encrypted_data[i] + _rotr(key[i] ^ checksum, ZTLSECURE_ROTATION); 38 | } 39 | 40 | return checksum; 41 | } 42 | 43 | template 44 | T __fastcall ZtlSecureFuse(T* at, unsigned int uCS) 45 | { 46 | /* storage type is 1 byte large if size < 4, otherwise its 4 bytes large */ 47 | typedef std::conditional<(sizeof(T) < 4), unsigned char, unsigned int>::type PType; 48 | 49 | unsigned int checksum = ZTLSECURE_CHECKSUM; 50 | 51 | PType* key = reinterpret_cast(&at[0]); 52 | PType* encrypted_data = reinterpret_cast(&at[1]); 53 | 54 | PType value[sizeof(T) / sizeof(PType)] = { 0 }; 55 | 56 | for (int i = 0; i < sizeof(T) / sizeof(PType); i++) 57 | { 58 | int rotations = sizeof(T) < sizeof(unsigned int) ? NULL : ZTLSECURE_ROTATION; 59 | 60 | value[i] = key[i] ^ _rotl(encrypted_data[i], rotations); 61 | checksum = encrypted_data[i] + _rotr(key[i] ^ checksum, ZTLSECURE_ROTATION); 62 | } 63 | 64 | if (checksum != uCS) 65 | { 66 | // TODO exception handling 67 | /*ZException zException = { 5 }; 68 | CxxThrowException(&zException, (void*)ThrowInfo::ZException);*/ 69 | } 70 | 71 | return *reinterpret_cast(&value[0]); 72 | } 73 | 74 | class SECRECT 75 | { 76 | private: 77 | int _ZtlSecureTear_left[2]; 78 | unsigned int _ZtlSecureTear_left_CS; 79 | int _ZtlSecureTear_top[2]; 80 | unsigned int _ZtlSecureTear_top_CS; 81 | int _ZtlSecureTear_right[2]; 82 | unsigned int _ZtlSecureTear_right_CS; 83 | int _ZtlSecureTear_bottom[2]; 84 | unsigned int _ZtlSecureTear_bottom_CS; 85 | 86 | public: 87 | SECRECT() 88 | { 89 | SetRect(0, 0, 0, 0); 90 | } 91 | 92 | SECRECT(int l, int t, int r, int b) 93 | { 94 | SetRect(l, t, r, b); 95 | } 96 | 97 | void SetRect(int l, int t, int r, int b) 98 | { 99 | this->_ZtlSecureTear_left_CS = ZtlSecureTear(this->_ZtlSecureTear_left, l); 100 | this->_ZtlSecureTear_top_CS = ZtlSecureTear(this->_ZtlSecureTear_top, t); 101 | this->_ZtlSecureTear_right_CS = ZtlSecureTear(this->_ZtlSecureTear_right, r); 102 | this->_ZtlSecureTear_bottom_CS = ZtlSecureTear(this->_ZtlSecureTear_bottom, b); 103 | } 104 | 105 | void SetRectEmpty() 106 | { 107 | this->_ZtlSecureTear_left_CS = ZtlSecureTear(this->_ZtlSecureTear_left, NULL); 108 | this->_ZtlSecureTear_top_CS = ZtlSecureTear(this->_ZtlSecureTear_top, NULL); 109 | this->_ZtlSecureTear_right_CS = ZtlSecureTear(this->_ZtlSecureTear_right, NULL); 110 | this->_ZtlSecureTear_bottom_CS = ZtlSecureTear(this->_ZtlSecureTear_bottom, NULL); 111 | } 112 | 113 | BOOL IsRectEmpty() 114 | { 115 | if (GetLeft() < GetRight() && GetTop() < GetBottom()) 116 | { 117 | return FALSE; 118 | } 119 | 120 | return TRUE; 121 | } 122 | 123 | int GetRight() // original name: ZtlSecureGet_right 124 | { 125 | return ZtlSecureFuse(this->_ZtlSecureTear_right, this->_ZtlSecureTear_right_CS); 126 | } 127 | 128 | int GetLeft() // original name: ZtlSecureGet_left 129 | { 130 | return ZtlSecureFuse(this->_ZtlSecureTear_left, this->_ZtlSecureTear_left_CS); 131 | } 132 | 133 | int GetTop() 134 | { 135 | return ZtlSecureFuse(this->_ZtlSecureTear_top, this->_ZtlSecureTear_top_CS); 136 | } 137 | 138 | int GetBottom() 139 | { 140 | return ZtlSecureFuse(this->_ZtlSecureTear_bottom, this->_ZtlSecureTear_bottom_CS); 141 | } 142 | 143 | void PutRight(int r) 144 | { 145 | this->_ZtlSecureTear_right_CS = ZtlSecureTear(this->_ZtlSecureTear_right, r); 146 | } 147 | 148 | void PutLeft(int l) 149 | { 150 | this->_ZtlSecureTear_left_CS = ZtlSecureTear(this->_ZtlSecureTear_left, l); 151 | } 152 | 153 | void PutTop(int t) 154 | { 155 | this->_ZtlSecureTear_top_CS = ZtlSecureTear(this->_ZtlSecureTear_top, t); 156 | } 157 | 158 | void PutBottom(int b) 159 | { 160 | this->_ZtlSecureTear_bottom_CS = ZtlSecureTear(this->_ZtlSecureTear_bottom, b); 161 | } 162 | }; -------------------------------------------------------------------------------- /Common/hooker.cpp: -------------------------------------------------------------------------------- 1 | #include "hooker.h" 2 | 3 | BOOL SetHook(bool bInstall, void** ppvTarget, void* pvDetour) 4 | { 5 | if (DetourTransactionBegin() != NO_ERROR) 6 | { 7 | return FALSE; 8 | } 9 | 10 | HANDLE pCurThread = GetCurrentThread(); 11 | 12 | if (DetourUpdateThread(pCurThread) == NO_ERROR) 13 | { 14 | auto pDetourFunc = bInstall ? DetourAttach : DetourDetach; 15 | 16 | if (pDetourFunc(ppvTarget, pvDetour) == NO_ERROR) 17 | { 18 | if (DetourTransactionCommit() == NO_ERROR) 19 | { 20 | return TRUE; 21 | } 22 | } 23 | } 24 | 25 | DetourTransactionAbort(); 26 | return FALSE; 27 | } 28 | 29 | DWORD GetFuncAddress(const char* lpModule, const char* lpFunc) 30 | { 31 | HMODULE hMod = LoadLibraryA(lpModule); 32 | 33 | return !hMod ? 0 : (DWORD)GetProcAddress(hMod, lpFunc); 34 | } 35 | 36 | // Credits: https://guidedhacking.com/threads/hook-vtable.13096/post-76763 37 | PVOID HookVTableFunction(void* pVTable, void* fnHookFunc, int nOffset) { 38 | intptr_t ptrVtable = *((intptr_t*)pVTable); // Pointer to our chosen vtable 39 | intptr_t ptrFunction = ptrVtable + sizeof(intptr_t) * nOffset; // The offset to the function (remember it's a zero indexed array with a size of four bytes) 40 | intptr_t ptrOriginal = *((intptr_t*)ptrFunction); // Save original address 41 | 42 | // Edit the memory protection so we can modify it 43 | MEMORY_BASIC_INFORMATION mbi; 44 | VirtualQuery((LPCVOID)ptrFunction, &mbi, sizeof(mbi)); 45 | VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect); 46 | 47 | // Overwrite the old function with our new one 48 | *((intptr_t*)ptrFunction) = (intptr_t)fnHookFunc; 49 | 50 | // Restore the protection 51 | VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &mbi.Protect); 52 | 53 | // Return the original function address incase we want to call it 54 | return (void*)ptrOriginal; 55 | } 56 | -------------------------------------------------------------------------------- /Common/hooker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "detours.h" 4 | 5 | #pragma comment(lib, "detours.lib") 6 | 7 | #pragma region Macros 8 | 9 | /// Sets hook and outputs result to debug window (pass/fail) 10 | #define INITWINHOOK(sModName, sFuncName, pOrigFunc, Func_t, pNewFunc) \ 11 | pOrigFunc = (Func_t)GetFuncAddress(sModName, sFuncName); \ 12 | if (SetHook(TRUE, reinterpret_cast(&pOrigFunc), pNewFunc)) \ 13 | { Log("Hooked %s", sFuncName); } \ 14 | else \ 15 | { Log("Failed to hook %s", sFuncName); } // end macro 16 | 17 | #define INITMAPLEHOOK(pOrigFunc, Func_t, pNewFunc, dwAddress) \ 18 | pOrigFunc = reinterpret_cast(dwAddress); \ 19 | if (!SetHook(TRUE, reinterpret_cast(&pOrigFunc), pNewFunc)) \ 20 | { Log("Failed to hook maple func at address %d", dwAddress); } // end macro 21 | 22 | #define HOOKDEF(retType, callConv, defName, typeName, funcName, ...) \ 23 | typedef retType (callConv* typeName)(__VA_ARGS__); \ 24 | typeName defName = nullptr; \ 25 | retType callConv funcName(__VA_ARGS__); 26 | 27 | #pragma endregion 28 | 29 | extern BOOL SetHook(bool bInstall, void** ppvTarget, void* pvDetour); 30 | extern DWORD GetFuncAddress(const char* lpModule, const char* lpFunc); 31 | extern PVOID HookVTableFunction(void* pVTable, void* fnHookFunc, int nOffset); -------------------------------------------------------------------------------- /Common/logger.cpp: -------------------------------------------------------------------------------- 1 | #include "logger.h" 2 | 3 | void Log(const char* format, ...) 4 | { 5 | char buf[1024] = { 0 }; 6 | 7 | va_list args; 8 | va_start(args, format); 9 | vsprintf_s(buf, format, args); 10 | 11 | OutputDebugString(buf); 12 | 13 | va_end(args); 14 | } -------------------------------------------------------------------------------- /Common/logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #define assert_size(x, y) static_assert(x == y, "Static size assert failed."); 6 | 7 | //void Log(const char* format, ...); 8 | //void PrintStackTrace(unsigned long dwSize); 9 | 10 | /// 11 | /// Prints to windows debug output. 12 | /// 13 | extern void Log(const char* format, ...); -------------------------------------------------------------------------------- /Common/memedit.cpp: -------------------------------------------------------------------------------- 1 | #include "memedit.h" 2 | 3 | VOID PatchRetZero(DWORD dwAddress) 4 | { 5 | *(BYTE*)(dwAddress + 0) = x86XOR; 6 | *(BYTE*)(dwAddress + 1) = x86EAXEAX; 7 | *(BYTE*)(dwAddress + 2) = x86RET; 8 | } 9 | 10 | VOID PatchJmp(DWORD dwAddress, PVOID pDestination) 11 | { 12 | *(BYTE*)(dwAddress + 0) = x86JMP; 13 | *(DWORD*)(dwAddress + 1) = relative_address(dwAddress, pDestination); 14 | } 15 | 16 | VOID PatchCall(DWORD dwAddress, PVOID pDestination) 17 | { 18 | *(BYTE*)(dwAddress + 0) = x86CALL; 19 | *(DWORD*)(dwAddress + 1) = relative_address(dwAddress, pDestination); 20 | } 21 | 22 | VOID PatchNop(DWORD dwAddress, UINT nCount) 23 | { 24 | for (UINT i = 0; i < nCount; i++) 25 | *(BYTE*)(dwAddress + i) = x86NOP; 26 | } 27 | 28 | VOID WriteBytes(DWORD dwAddress, const char* pData, UINT nCount) 29 | { 30 | memcpy((PVOID)dwAddress, pData, nCount); 31 | } -------------------------------------------------------------------------------- /Common/memedit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | // thanks raj for some of these 6 | 7 | #define relative_address(frm, to) (int)(((int)to - (int)frm) - 5) 8 | 9 | #define x86CMPEAX 0x3D 10 | #define x86XOR 0x33 11 | #define x86EAXEAX 0xC0 12 | #define x86RET 0xC3 13 | #define x86JMP 0xE9 14 | #define x86CALL 0xE8 15 | #define x86NOP 0x90 16 | 17 | extern VOID PatchRetZero(DWORD dwAddress); 18 | extern VOID PatchJmp(DWORD dwAddress, PVOID pDestination); 19 | extern VOID PatchCall(DWORD dwAddress, PVOID pDestination); 20 | extern VOID PatchNop(DWORD dwAddress, UINT nCount); 21 | extern VOID WriteBytes(DWORD dwAddress, const char* pData, UINT nCount); 22 | 23 | template 24 | VOID WriteValue(DWORD dwAddress, TType pValue) 25 | { 26 | *((TType*)dwAddress) = pValue; 27 | } 28 | 29 | template 30 | TType ReadValue(DWORD dwAddr) 31 | { 32 | return *((TType*)dwAddr); 33 | } -------------------------------------------------------------------------------- /Common/winhook_types.cpp: -------------------------------------------------------------------------------- 1 | #include "winhook_types.h" 2 | 3 | CreateFileA_t CreateFileA_Original; 4 | WinExec_t WinExec_Original; 5 | GetProcAddress_t GetProcAddress_Original; 6 | CreateMutexA_t CreateMutexA_Original; 7 | OpenMutexA_t OpenMutexA_Original; 8 | WSPStartup_t WSPStartup_Original; 9 | RegisterClassExA_t RegisterClassExA_Original; 10 | CreateProcessW_t CreateProcessW_Original; 11 | CreateProcessA_t CreateProcessA_Original; 12 | OpenProcess_t OpenProcess_Original; 13 | CreateThread_t CreateThread_Original; 14 | GetACP_t GetACP_Original; 15 | CreateWindowExA_t CreateWindowExA_Original; 16 | NtTerminateProcess_t NtTerminateProcess_Original; 17 | RegCreateKeyExA_t RegCreateKeyExA_Original; -------------------------------------------------------------------------------- /Common/winhook_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /// 8 | /// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea 9 | /// 10 | typedef HANDLE(WINAPI* CreateFileA_t)( 11 | LPCSTR lpFileName, 12 | DWORD dwDesiredAccess, 13 | DWORD dwShareMode, 14 | LPSECURITY_ATTRIBUTES lpSecurityAttributes, 15 | DWORD dwCreationDisposition, 16 | DWORD dwFlagsAndAttributes, 17 | HANDLE hTemplateFile 18 | ); 19 | extern CreateFileA_t CreateFileA_Original; 20 | 21 | /// 22 | /// https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-winexec 23 | /// 24 | typedef UINT(WINAPI* WinExec_t)(LPCSTR lpCmdLine, UINT uCmdShow); 25 | extern WinExec_t WinExec_Original; 26 | 27 | /// 28 | /// https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress 29 | /// 30 | typedef FARPROC(WINAPI* GetProcAddress_t)(HMODULE hModule, LPCSTR lpProcName); 31 | extern GetProcAddress_t GetProcAddress_Original; 32 | 33 | /// 34 | /// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexa 35 | /// 36 | typedef HANDLE(WINAPI* CreateMutexA_t)(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCSTR lpName); 37 | extern CreateMutexA_t CreateMutexA_Original; 38 | 39 | /// 40 | /// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-openmutexw 41 | /// Please note: there is no microsoft doc for OpenMutexA, but OpenMutexW is the same except for the type of string passed (LPCSTR vs LPCWSTR) 42 | /// 43 | typedef HANDLE(WINAPI* OpenMutexA_t)(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName); 44 | extern OpenMutexA_t OpenMutexA_Original; 45 | 46 | /// 47 | /// https://docs.microsoft.com/en-us/windows/win32/api/ws2spi/nf-ws2spi-wspstartup 48 | /// 49 | typedef int(WSPAPI* WSPStartup_t)(WORD wVersionRequested, LPWSPDATA lpWSPData, LPWSAPROTOCOL_INFOW lpProtocolInfo, WSPUPCALLTABLE UpcallTable, LPWSPPROC_TABLE lpProcTable); 50 | extern WSPStartup_t WSPStartup_Original; 51 | /// 52 | /// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclassexa 53 | /// 54 | typedef ATOM(WINAPI* RegisterClassExA_t)(const WNDCLASSEXA* lpWc); 55 | extern RegisterClassExA_t RegisterClassExA_Original; 56 | 57 | /// 58 | /// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw 59 | /// 60 | typedef BOOL(WINAPI* CreateProcessW_t)( 61 | LPCWSTR lpApplicationName, 62 | LPWSTR lpCommandLine, 63 | LPSECURITY_ATTRIBUTES lpProcessAttributes, 64 | LPSECURITY_ATTRIBUTES lpThreadAttributes, 65 | BOOL bInheritHandles, 66 | DWORD dwCreationFlags, 67 | LPVOID lpEnvironment, 68 | LPCWSTR lpCurrentDirectory, 69 | LPSTARTUPINFOW lpStartupInfo, 70 | LPPROCESS_INFORMATION lpProcessInformation 71 | ); 72 | extern CreateProcessW_t CreateProcessW_Original; 73 | 74 | /// 75 | /// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa 76 | /// 77 | typedef BOOL(WINAPI* CreateProcessA_t)( 78 | LPCSTR lpApplicationName, 79 | LPSTR lpCommandLine, 80 | LPSECURITY_ATTRIBUTES lpProcessAttributes, 81 | LPSECURITY_ATTRIBUTES lpThreadAttributes, 82 | BOOL bInheritHandles, 83 | DWORD dwCreationFlags, 84 | LPVOID lpEnvironment, 85 | LPCSTR lpCurrentDirectory, 86 | LPSTARTUPINFOA lpStartupInfo, 87 | LPPROCESS_INFORMATION lpProcessInformation 88 | ); 89 | extern CreateProcessA_t CreateProcessA_Original; 90 | 91 | /// 92 | /// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess 93 | /// 94 | typedef HANDLE(WINAPI* OpenProcess_t)(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId); 95 | extern OpenProcess_t OpenProcess_Original; 96 | 97 | /// 98 | /// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread 99 | /// 100 | typedef HANDLE(WINAPI* CreateThread_t)( 101 | LPSECURITY_ATTRIBUTES lpThreadAttributes, 102 | SIZE_T dwStackSize, 103 | LPTHREAD_START_ROUTINE lpStartAddress, 104 | __drv_aliasesMem LPVOID lpParameter, 105 | DWORD dwCreationFlags, 106 | LPDWORD lpThreadId 107 | ); 108 | extern CreateThread_t CreateThread_Original; 109 | 110 | /// 111 | /// https://docs.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getacp 112 | /// 113 | typedef UINT(WINAPI* GetACP_t)(); 114 | extern GetACP_t GetACP_Original; 115 | 116 | /// 117 | /// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexa 118 | /// 119 | typedef HWND(WINAPI* CreateWindowExA_t)( 120 | DWORD dwExStyle, 121 | LPCSTR lpClassName, 122 | LPCSTR lpWindowName, 123 | DWORD dwStyle, 124 | int X, 125 | int Y, 126 | int nWidth, 127 | int nHeight, 128 | HWND hWndParent, 129 | HMENU hMenu, 130 | HINSTANCE hInstance, 131 | LPVOID lpParam 132 | ); 133 | extern CreateWindowExA_t CreateWindowExA_Original; 134 | 135 | /// 136 | /// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FProcess%2FNtTerminateProcess.html 137 | /// 138 | typedef LONG(NTAPI* NtTerminateProcess_t)(HANDLE hProcHandle, LONG ntExitStatus); 139 | extern NtTerminateProcess_t NtTerminateProcess_Original; 140 | 141 | /// 142 | /// https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regcreatekeyexa 143 | /// 144 | /// 145 | typedef LSTATUS(WINAPI* RegCreateKeyExA_t)( 146 | HKEY hKey, 147 | LPCSTR lpSubKey, 148 | DWORD Reserved, 149 | LPSTR lpClass, 150 | DWORD dwOptions, 151 | REGSAM samDesired, 152 | const LPSECURITY_ATTRIBUTES lpSecurityAttributes, 153 | PHKEY phkResult, 154 | LPDWORD lpdwDisposition 155 | ); 156 | extern RegCreateKeyExA_t RegCreateKeyExA_Original; -------------------------------------------------------------------------------- /Common/winhooks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace WinHooks 7 | { 8 | /// 9 | /// Used to map out imports used by MapleStory. 10 | /// The log output can be used to reconstruct the _ZAPIProcAddress struct 11 | /// ZAPI struct is the dword before the while loop when searching for aob: 68 FE 00 00 00 ?? 8D 12 | /// 13 | FARPROC WINAPI GetProcAddress_Hook(HMODULE hModule, LPCSTR lpProcName); 14 | 15 | /// 16 | /// CreateMutexA is the first Windows library call after the executable unpacks itself. 17 | /// We hook this function to do all our memory edits and hooks when it's called. 18 | /// 19 | HANDLE WINAPI CreateMutexA_Hook( 20 | LPSECURITY_ATTRIBUTES lpMutexAttributes, 21 | BOOL bInitialOwner, 22 | LPCSTR lpName 23 | ); 24 | 25 | /// 26 | /// In some versions, Maple calls this library function to check if the anticheat has started. 27 | /// We can spoof this and return a fake handle for it to close. 28 | /// 29 | HANDLE WINAPI OpenMutexA_Hook( 30 | DWORD dwDesiredAccess, 31 | BOOL bInitialOwner, 32 | LPCSTR lpName 33 | ); 34 | 35 | /// 36 | /// Used to track what maple is trying to start (mainly for anticheat modules). 37 | /// 38 | BOOL WINAPI CreateProcessW_Hook( 39 | LPCWSTR lpApplicationName, 40 | LPWSTR lpCommandLine, 41 | LPSECURITY_ATTRIBUTES lpProcessAttributes, 42 | LPSECURITY_ATTRIBUTES lpThreadAttributes, 43 | BOOL bInheritHandles, 44 | DWORD dwCreationFlags, 45 | LPVOID lpEnvironment, 46 | LPCWSTR lpCurrentDirectory, 47 | LPSTARTUPINFOW lpStartupInfo, 48 | LPPROCESS_INFORMATION lpProcessInformation 49 | ); 50 | 51 | /// 52 | /// Used same as above and also to kill/redirect some web requests. 53 | /// 54 | BOOL WINAPI CreateProcessA_Hook( 55 | LPCSTR lpApplicationName, 56 | LPSTR lpCommandLine, 57 | LPSECURITY_ATTRIBUTES lpProcessAttributes, 58 | LPSECURITY_ATTRIBUTES lpThreadAttributes, 59 | BOOL bInheritHandles, 60 | DWORD dwCreationFlags, 61 | LPVOID lpEnvironment, 62 | LPCSTR lpCurrentDirectory, 63 | LPSTARTUPINFOA lpStartupInfo, 64 | LPPROCESS_INFORMATION lpProcessInformation 65 | ); 66 | 67 | /// 68 | /// Same as CreateProcessW 69 | /// 70 | HANDLE WINAPI CreateThread_Hook( 71 | LPSECURITY_ATTRIBUTES lpThreadAttributes, 72 | SIZE_T dwStackSize, 73 | LPTHREAD_START_ROUTINE lpStartAddress, 74 | __drv_aliasesMem LPVOID lpParameter, 75 | DWORD dwCreationFlags, 76 | LPDWORD lpThreadId 77 | ); 78 | 79 | /// 80 | /// Used to track what processes Maple opens. 81 | /// 82 | HANDLE WINAPI OpenProcess_Hook( 83 | DWORD dwDesiredAccess, 84 | BOOL bInheritHandle, 85 | DWORD dwProcessId 86 | ); 87 | 88 | /// 89 | /// This library call is used by nexon to determine the locale of the connecting clients PC. We spoof it. 90 | /// 91 | /// 92 | UINT WINAPI GetACP_Hook(); // AOB: FF 15 ?? ?? ?? ?? 3D ?? ?? ?? 00 00 74 <- library call inside winmain func 93 | 94 | /// 95 | /// Blocks the startup patcher "Play!" window and forces the login screen to be minimized 96 | /// 97 | HWND WINAPI CreateWindowExA_Hook( 98 | DWORD dwExStyle, 99 | LPCSTR lpClassName, 100 | LPCSTR lpWindowName, 101 | DWORD dwStyle, 102 | int X, 103 | int Y, 104 | int nWidth, 105 | int nHeight, 106 | HWND hWndParent, 107 | HMENU hMenu, 108 | HINSTANCE hInstance, 109 | LPVOID lpParam 110 | ); 111 | 112 | /// 113 | /// We use this function to track what memory addresses are killing the process. 114 | /// There are more ways that Maple kills itself, but this is one of them. 115 | /// 116 | LONG NTAPI NtTerminateProcess_Hook( 117 | HANDLE hProcHandle, 118 | LONG ntExitStatus 119 | ); 120 | 121 | /// 122 | /// Maplestory saves registry information (config stuff) for a number of things. This can be used to track that. 123 | /// 124 | LSTATUS WINAPI RegCreateKeyExA_Hook( 125 | HKEY hKey, 126 | LPCSTR lpSubKey, 127 | DWORD Reserved, 128 | LPSTR lpClass, 129 | DWORD dwOptions, 130 | REGSAM samDesired, 131 | const LPSECURITY_ATTRIBUTES lpSecurityAttributes, 132 | PHKEY phkResult, 133 | LPDWORD lpdwDisposition 134 | ); 135 | 136 | namespace WinSock 137 | { 138 | /// 139 | /// 140 | /// 141 | INT WSPAPI WSPConnect_Hook( 142 | SOCKET s, 143 | const struct sockaddr* name, 144 | int namelen, 145 | LPWSABUF lpCallerData, 146 | LPWSABUF lpCalleeData, 147 | LPQOS lpSQOS, 148 | LPQOS lpGQOS, 149 | LPINT lpErrno 150 | ); 151 | 152 | /// 153 | /// 154 | /// 155 | INT WSPAPI WSPGetPeerName_Hook( 156 | SOCKET s, 157 | struct sockaddr* name, 158 | LPINT namelen, 159 | LPINT lpErrno 160 | ); 161 | 162 | /// 163 | /// 164 | /// 165 | INT WSPAPI WSPCloseSocket_Hook( 166 | SOCKET s, 167 | LPINT lpErrno 168 | ); 169 | 170 | /// 171 | /// 172 | /// 173 | INT WSPAPI WSPStartup_Hook( 174 | WORD wVersionRequested, 175 | LPWSPDATA lpWSPData, 176 | LPWSAPROTOCOL_INFOW lpProtocolInfo, 177 | WSPUPCALLTABLE UpcallTable, 178 | LPWSPPROC_TABLE lpProcTable 179 | ); 180 | } 181 | } -------------------------------------------------------------------------------- /GenericLauncher/GenericLauncher.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 | 16.0 23 | Win32Proj 24 | {6e0b10d1-1a13-4c13-9f00-1525a7e53019} 25 | GenericLauncher 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | MultiByte 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | MultiByte 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(SolutionDir)Output\$(Configuration)\bin\ 76 | $(SolutionDir)Output\$(Configuration)\$(ProjectName)\ 77 | $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(SolutionDir)Include; 78 | 79 | 80 | false 81 | $(SolutionDir)Output\$(Configuration)\bin\ 82 | $(SolutionDir)Output\$(Configuration)\$(ProjectName)\ 83 | $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(SolutionDir)Include; 84 | 85 | 86 | true 87 | 88 | 89 | false 90 | 91 | 92 | 93 | Level3 94 | true 95 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | true 97 | $(SolutionDir)Common;%(AdditionalIncludeDirectories) 98 | 99 | 100 | Windows 101 | true 102 | RequireAdministrator 103 | /ENTRY:"mainCRTStartup" %(AdditionalOptions) 104 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;$(SolutionDir)Output\$(Configuration)\bin\Common.lib;%(AdditionalDependencies) 105 | 106 | 107 | 108 | 109 | Level3 110 | true 111 | true 112 | true 113 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 114 | true 115 | $(SolutionDir)Common;%(AdditionalIncludeDirectories) 116 | Speed 117 | 118 | 119 | Console 120 | true 121 | true 122 | true 123 | RequireAdministrator 124 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;$(SolutionDir)Output\$(Configuration)\bin\Common.lib;%(AdditionalDependencies) 125 | 126 | 127 | 128 | 129 | Level3 130 | true 131 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | 134 | 135 | Console 136 | true 137 | 138 | 139 | 140 | 141 | Level3 142 | true 143 | true 144 | true 145 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 146 | true 147 | 148 | 149 | Console 150 | true 151 | true 152 | true 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /GenericLauncher/GenericLauncher.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;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 | -------------------------------------------------------------------------------- /GenericLauncher/main.cpp: -------------------------------------------------------------------------------- 1 | #include // has to be before Windows.h because of linker issues with winsock lib 2 | #include 3 | #include 4 | #include 5 | 6 | void ErrorBox(const char* format, ...) 7 | { 8 | char szMessage[1024]; 9 | 10 | va_list args; 11 | va_start(args, format); 12 | 13 | vsnprintf(szMessage, 1023, format, args); 14 | MessageBoxA(NULL, szMessage, "Launch Error", MB_ICONERROR); 15 | 16 | va_end(args); 17 | } 18 | 19 | #define ErrorBoxWithCode(msg) ErrorBox(msg" Error code: %d", GetLastError()); 20 | 21 | int main() 22 | { 23 | OutputDebugStringA("Launcher entrypoint."); 24 | 25 | // additional information 26 | STARTUPINFO siMaple; 27 | PROCESS_INFORMATION piMaple; 28 | 29 | // Initialize the startup structures 30 | ZeroMemory(&siMaple, sizeof(siMaple)); 31 | ZeroMemory(&piMaple, sizeof(piMaple)); 32 | 33 | siMaple.cb = sizeof(siMaple); 34 | 35 | // Start the child process. 36 | BOOL bCreateProc = CreateProcess( 37 | Common::GetConfig()->MapleExeName, // name of application 38 | const_cast(Common::GetConfig()->MapleStartupArgs), // Command line args 39 | NULL, // Process handle not inheritable 40 | NULL, // Thread handle not inheritable 41 | FALSE, // Set handle inheritance to FALSE 42 | CREATE_SUSPENDED, // Creation flags 43 | NULL, // Use parent's environment block 44 | NULL, // Use parent's starting directory 45 | &siMaple, // Pointer to STARTUPINFO structure 46 | &piMaple // Pointer to PROCESS_INFORMATION structure 47 | ); 48 | 49 | if (bCreateProc) 50 | { 51 | #if !MAPLE_INJECT_USE_IJL 52 | size_t nLoadDllStrLen; 53 | 54 | if (Common::GetConfig()->DllName) 55 | { 56 | nLoadDllStrLen = strlen(Common::GetConfig()->DllName); 57 | } 58 | else 59 | { 60 | ErrorBoxWithCode("DLL name is null."); 61 | return FALSE; 62 | } 63 | 64 | HMODULE hKernel = GetModuleHandleA("Kernel32.dll"); 65 | 66 | if (!hKernel) 67 | { 68 | ErrorBoxWithCode("Unable to get Kernel32 handle."); 69 | } 70 | 71 | LPVOID lpLoadLibAddr = (LPVOID)GetProcAddress(hKernel, "LoadLibraryA"); 72 | 73 | if (!lpLoadLibAddr) 74 | { 75 | ErrorBoxWithCode("Unable to get address for library call."); 76 | } 77 | 78 | LPVOID lpRemoteStr = VirtualAllocEx(piMaple.hProcess, NULL, nLoadDllStrLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 79 | 80 | if (!lpRemoteStr) 81 | { 82 | ErrorBoxWithCode("Unable to allocate memory."); 83 | } 84 | 85 | BOOL bWriteProc = WriteProcessMemory(piMaple.hProcess, lpRemoteStr, Common::GetConfig()->DllName, nLoadDllStrLen, NULL); 86 | 87 | if (!bWriteProc) 88 | { 89 | ErrorBoxWithCode("Unable to attach to MapleStory process."); 90 | } 91 | 92 | HANDLE hRemThread = CreateRemoteThread(piMaple.hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)lpLoadLibAddr, lpRemoteStr, 0, NULL); 93 | 94 | if (!hRemThread) 95 | { 96 | ErrorBoxWithCode("Unable to create remote thread."); 97 | } 98 | #endif 99 | 100 | DWORD dwRet = ResumeThread(piMaple.hThread); 101 | 102 | if (dwRet == -1) 103 | { 104 | ErrorBoxWithCode("Unable to resume thread."); 105 | } 106 | 107 | #if !MAPLE_INJECT_USE_IJL 108 | // close handle for injected dll 109 | CloseHandle(hRemThread); 110 | #endif 111 | 112 | // Close process and thread handles. 113 | CloseHandle(piMaple.hProcess); 114 | CloseHandle(piMaple.hThread); 115 | 116 | OutputDebugStringA("End launcher"); 117 | 118 | return TRUE; 119 | } 120 | else 121 | { 122 | ErrorBoxWithCode("Make sure you're running as Administrator and that the launcher is in the same folder as MapleStory.exe."); 123 | return FALSE; 124 | } 125 | 126 | return TRUE; 127 | } -------------------------------------------------------------------------------- /Include/README.md: -------------------------------------------------------------------------------- 1 | ###Include Dir 2 | Contains .lib files to include in any project. Detours library goes here, and any potentional virtualization libraries that you may add. -------------------------------------------------------------------------------- /Include/detours.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MapleStory-Archive/MapleClientEditTemplate/6aec480e45c8389e55a22d3b767f785415c61a64/Include/detours.lib -------------------------------------------------------------------------------- /MapleClientCollection.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30320.27 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GenericLauncher", "GenericLauncher\GenericLauncher.vcxproj", "{6E0B10D1-1A13-4C13-9F00-1525A7E53019}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {3EB29228-1F83-4DD3-B3C5-0F284FD50F39} = {3EB29228-1F83-4DD3-B3C5-0F284FD50F39} 9 | EndProjectSection 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "Common\Common.vcxproj", "{3EB29228-1F83-4DD3-B3C5-0F284FD50F39}" 12 | EndProject 13 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CTemplate", "CTemplate\CTemplate.vcxproj", "{D2C48F68-C2B4-41C4-A581-550603163363}" 14 | EndProject 15 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TemplateTesting", "TemplateTesting\TemplateTesting.vcxproj", "{4C5F5040-839B-43E2-AABD-0DFD96069A0B}" 16 | ProjectSection(ProjectDependencies) = postProject 17 | {3EB29228-1F83-4DD3-B3C5-0F284FD50F39} = {3EB29228-1F83-4DD3-B3C5-0F284FD50F39} 18 | EndProjectSection 19 | EndProject 20 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTesting", "UnitTesting\UnitTesting.vcxproj", "{AFB54969-A72A-41C5-B5F5-142A318614A7}" 21 | ProjectSection(ProjectDependencies) = postProject 22 | {3EB29228-1F83-4DD3-B3C5-0F284FD50F39} = {3EB29228-1F83-4DD3-B3C5-0F284FD50F39} 23 | EndProjectSection 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|x86 = Debug|x86 28 | Release|x86 = Release|x86 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {6E0B10D1-1A13-4C13-9F00-1525A7E53019}.Debug|x86.ActiveCfg = Debug|Win32 32 | {6E0B10D1-1A13-4C13-9F00-1525A7E53019}.Debug|x86.Build.0 = Debug|Win32 33 | {6E0B10D1-1A13-4C13-9F00-1525A7E53019}.Release|x86.ActiveCfg = Release|Win32 34 | {6E0B10D1-1A13-4C13-9F00-1525A7E53019}.Release|x86.Build.0 = Release|Win32 35 | {3EB29228-1F83-4DD3-B3C5-0F284FD50F39}.Debug|x86.ActiveCfg = Debug|Win32 36 | {3EB29228-1F83-4DD3-B3C5-0F284FD50F39}.Debug|x86.Build.0 = Debug|Win32 37 | {3EB29228-1F83-4DD3-B3C5-0F284FD50F39}.Release|x86.ActiveCfg = Release|Win32 38 | {3EB29228-1F83-4DD3-B3C5-0F284FD50F39}.Release|x86.Build.0 = Release|Win32 39 | {D2C48F68-C2B4-41C4-A581-550603163363}.Debug|x86.ActiveCfg = Debug|Win32 40 | {D2C48F68-C2B4-41C4-A581-550603163363}.Debug|x86.Build.0 = Debug|Win32 41 | {D2C48F68-C2B4-41C4-A581-550603163363}.Release|x86.ActiveCfg = Release|Win32 42 | {D2C48F68-C2B4-41C4-A581-550603163363}.Release|x86.Build.0 = Release|Win32 43 | {4C5F5040-839B-43E2-AABD-0DFD96069A0B}.Debug|x86.ActiveCfg = Debug|Win32 44 | {4C5F5040-839B-43E2-AABD-0DFD96069A0B}.Debug|x86.Build.0 = Debug|Win32 45 | {4C5F5040-839B-43E2-AABD-0DFD96069A0B}.Release|x86.ActiveCfg = Release|Win32 46 | {4C5F5040-839B-43E2-AABD-0DFD96069A0B}.Release|x86.Build.0 = Release|Win32 47 | {AFB54969-A72A-41C5-B5F5-142A318614A7}.Debug|x86.ActiveCfg = Debug|Win32 48 | {AFB54969-A72A-41C5-B5F5-142A318614A7}.Debug|x86.Build.0 = Debug|Win32 49 | {AFB54969-A72A-41C5-B5F5-142A318614A7}.Release|x86.ActiveCfg = Release|Win32 50 | {AFB54969-A72A-41C5-B5F5-142A318614A7}.Release|x86.Build.0 = Release|Win32 51 | EndGlobalSection 52 | GlobalSection(SolutionProperties) = preSolution 53 | HideSolutionNode = FALSE 54 | EndGlobalSection 55 | GlobalSection(ExtensibilityGlobals) = postSolution 56 | SolutionGuid = {321069B6-E641-4B0F-9523-415E55A7A91F} 57 | EndGlobalSection 58 | EndGlobal 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MapleClientCollection 2 | 3 | A (increasingly generic) framework that is intended to be used when crafting new MapleStory client redirectors/edits. 4 | This should work pretty much out of the box and all that is required is potential anticheat and crc bypasses. These are version dependant. 5 | This solution contains all the Windows library hooks that are required for a MapleStory localhost enabler and is configured to be as noob friendly as possible. 6 | The Windows hooks are abstracted away (as best as I could) and can be toggled on/off depending on need. For example, if using this with an unvirtualized localhost (eg v83), the WinSock hooks can be disabled with a simple switch and memory editing can be done immediately on injection rather than waiting for the client to be unpacked (since it's already unpacked no need to wait). 7 | 8 | ### DebugView 9 | In order to see the debug log output you can use this program: https://docs.microsoft.com/en-us/sysinternals/downloads/debugview 10 | 11 | ### Tutorial 12 | I recorded a tutorial on how to use this package which can be watched here: https://www.youtube.com/watch?v=Yr5IOhc5sPk 13 | ALSO: Join the discord for support! https://discord.gg/tmyac7tU 14 | 15 | ### Current Status 16 | This project is in active development by myself (Minimum Delta). Issue/feature requests are appreciated. 17 | The project is in alpha stage, so do not expect it to work perfectly (even though it should work pretty well). 18 | 19 | ### Credits 20 | Darter (aka Moozi) - Very helpful mentor 21 | Ez - Another mentor and friend 22 | SunCat - Great insight from this friendly fellow 23 | The Muffin Man - Always staying positive 24 | DAVHEED - u know 25 | 26 | ### Setup 27 | BE ADVISED: 28 | The below is no longer required, but I'm keeping it here in-case something breaks in the future. The solution should work out-of-the-box with no configuration required. 29 | _I'm going to expand this at some point, but there's a few things that need to be configured in order for the project to work:_ 30 | * _Put detours.lib into your Debug folder (folder generated on compilation in main folder)_ 31 | * _Paste: $(SolutionDir)Common;%(AdditionalIncludeDirectories) in your (CTemplate) project properties -> c/c++ -> additional include directories. This will let the project see the other files for proper compilation. It will not compile unless this is done properly._ 32 | * _Sometimes VS defaults to x64 which is incorrect and will not work. Make sure you're compiling in Debug x86 -> if you try to compile in x64 it'll give detour linker errors._ 33 | 34 | ### Config settings 35 | All generic config settings are in the Common/Common.h file. More information on the specifics of this will come at a later point (documentation is always last, eh). However, I've commented reasonbly well so it should be pretty apparent what each setting does. 36 | 37 | ### Compiling for proxy vs regular injection 38 | Ijl15 proxy injection (auto injection on Maple start rather than using a launcher) can be turned on/off by defining MAPLE_INJECT_USE_IJL in Common.h. 39 | You'll need to rename the compiled DLL to ijl15.dll instead of LEN.dll in order for it to be auto injected. 40 | 41 | ### GenericLauncher 42 | Regular injection can be done with the GenericLauncher project exe. This program will boot up MapleStory and inject LEN.dll. It's very simple, but works very well. 43 | 44 | ### Common Project 45 | The Common project contains all the Windows library hooks that are not version specific. 46 | These can be used by any version and are already configured to be hooked on injection. They can be toggled on/off in the Common.h file. 47 | There are also a number of Maple templates in this project -- feel free to contribute yours! 48 | 49 | ### When does hooking and memory editing happen? 50 | Windows functions are hooked immediately after injection which happens directly after MapleStory is launched (for ijl proxy and regular GenericLauncher injection). 51 | Unlike MapleStory function hooks, external API hooks (winhooks) do not need to wait for the client to be unpacked. Assuming that the instaject config option is disabled, when the WinAPI CreateMutexA hook is triggered by MapleStory then MainFunc in dllmain is triggered and any hooks or editing called from that function will be executed. If the instaject option is enabled, this happens on injection. 52 | It is recommended to have all Maple hooking and memory editing inside or called from the CreateMutexA function. This is the default behavior. 53 | 54 | ### MapleAPI Hooking Examples 55 | I have added some simple memory editing and hooking examples to the repo. They are also explained in the above linked YouTube series. 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /TemplateTesting/TemplateTesting.cpp: -------------------------------------------------------------------------------- 1 | // TemplateTesting.cpp : This file contains the 'main' function. Program execution begins and ends there. 2 | // 3 | 4 | #include 5 | #include "ZXString.h" 6 | #include "ZRefCounted.h" 7 | #include "ZRefCountedDummy.h" 8 | #include "ZRef.h" 9 | #include "ZtlSecure.h" 10 | #include 11 | #include "ZArray.h" 12 | #include "ZList.h" 13 | 14 | struct test 15 | { 16 | int val; 17 | ~test() 18 | { 19 | std::cout << "destroying"; 20 | } 21 | }; 22 | 23 | int main() 24 | { 25 | auto list = ZList(); 26 | 27 | for (int i = 0; i < 4; i++) 28 | { 29 | auto n = new test(); 30 | n->val = i; 31 | list.AddTail(n); 32 | } 33 | 34 | int x = 1; 35 | } 36 | 37 | // Run program: Ctrl + F5 or Debug > Start Without Debugging menu 38 | // Debug program: F5 or Debug > Start Debugging menu 39 | 40 | // Tips for Getting Started: 41 | // 1. Use the Solution Explorer window to add/manage files 42 | // 2. Use the Team Explorer window to connect to source control 43 | // 3. Use the Output window to see build output and other messages 44 | // 4. Use the Error List window to view errors 45 | // 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project 46 | // 6. In the future, to open this project again, go to File > Open > Project and select the .sln file 47 | -------------------------------------------------------------------------------- /TemplateTesting/TemplateTesting.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 | 16.0 23 | Win32Proj 24 | {4c5f5040-839b-43e2-aabd-0dfd96069a0b} 25 | TemplateTesting 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(SolutionDir)Include; 76 | $(SolutionDir)Output\$(Configuration)\bin\ 77 | $(SolutionDir)Output\$(Configuration)\$(ProjectName)\ 78 | 79 | 80 | false 81 | $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(SolutionDir)Include; 82 | $(SolutionDir)Output\$(Configuration)\bin\ 83 | $(SolutionDir)Output\$(Configuration)\$(ProjectName)\ 84 | 85 | 86 | true 87 | 88 | 89 | false 90 | 91 | 92 | 93 | Level3 94 | true 95 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | true 97 | $(SolutionDir)Common;%(AdditionalIncludeDirectories) 98 | 99 | 100 | Console 101 | true 102 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(SolutionDir)Output\$(Configuration)\bin\Common.lib 103 | 104 | 105 | 106 | 107 | Level3 108 | true 109 | true 110 | true 111 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 112 | true 113 | $(SolutionDir)Common;%(AdditionalIncludeDirectories) 114 | 115 | 116 | Console 117 | true 118 | true 119 | true 120 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(SolutionDir)Output\$(Configuration)\bin\Common.lib 121 | 122 | 123 | 124 | 125 | Level3 126 | true 127 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 128 | true 129 | 130 | 131 | Console 132 | true 133 | 134 | 135 | 136 | 137 | Level3 138 | true 139 | true 140 | true 141 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 142 | true 143 | 144 | 145 | Console 146 | true 147 | true 148 | true 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /TemplateTesting/TemplateTesting.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;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 | -------------------------------------------------------------------------------- /UnitTesting/TSecTypeUnitTesting.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "CppUnitTest.h" 3 | #include "TSecType.h" 4 | 5 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 6 | 7 | namespace CommonUnitTesting 8 | { 9 | TEST_CLASS(TSecTypeUnitTesting) 10 | { 11 | public: 12 | TEST_METHOD(ConstructorTesting) 13 | { 14 | TSecType ts_c = TSecType(); 15 | TSecType ts_s = TSecType(); 16 | TSecType ts_l = TSecType(); 17 | TSecType ts_d = TSecType(); 18 | 19 | Assert::IsTrue(ts_c.GetData() == 0, TEXT("Char constructor failed to initialize data to zero.")); 20 | Assert::IsTrue(ts_s.GetData() == 0, TEXT("Short constructor failed to initialize data to zero.")); 21 | Assert::IsTrue(ts_l.GetData() == 0, TEXT("Long constructor failed to initialize data to zero.")); 22 | Assert::IsTrue(ts_d.GetData() == 0.0, TEXT("Double constructor failed to initialize data to zero.")); 23 | 24 | ts_c = 1; 25 | ts_s = 1; 26 | ts_l = 1; 27 | ts_d = 1.0; 28 | 29 | Assert::IsTrue(ts_c.GetData() == 1, TEXT("Char SetData() failed to set data to 1.")); 30 | Assert::IsTrue(ts_s.GetData() == 1, TEXT("Short SetData() failed to set data to 1.")); 31 | Assert::IsTrue(ts_l.GetData() == 1, TEXT("Long SetData() failed to set data to 1.")); 32 | Assert::IsTrue(ts_d.GetData() == 1.0, TEXT("Double SetData() failed to set data to 1.")); 33 | } 34 | 35 | TEST_METHOD(OperatorOverloadTesting) 36 | { 37 | TSecType ts_l = TSecType(); 38 | ts_l = 1; 39 | 40 | TSecType ts_l1 = ts_l; 41 | 42 | Assert::IsTrue(ts_l1.GetData() == ts_l.GetData(), TEXT("Operator = overload failed.")); 43 | 44 | Assert::IsTrue(ts_l == ts_l1, TEXT("Operator == overload failed.")); 45 | Assert::IsFalse(ts_l != ts_l1, TEXT("Operator != overload failed.")); 46 | 47 | ts_l *= 2; 48 | 49 | Assert::IsTrue(ts_l == 2, TEXT("Operator *= overload failed.")); 50 | 51 | ts_l += ts_l; 52 | 53 | Assert::IsTrue(ts_l == 4, TEXT("Operator += overload failed.")); 54 | 55 | ts_l += 4; 56 | 57 | Assert::IsTrue(ts_l == 8, TEXT("Operator += overload failed.")); 58 | 59 | int x = ts_l + 4 + ts_l; 60 | 61 | Assert::IsTrue(x == 20, TEXT("Operator + overload failed.")); 62 | 63 | int y = ts_l - 4 - ts_l; 64 | 65 | Assert::IsTrue(y == -4, TEXT("Operator - overload failed.")); 66 | 67 | ts_l -= 4; 68 | 69 | Assert::IsTrue(ts_l == 4, TEXT("Operator -= overload failed.")); 70 | } 71 | }; 72 | 73 | TEST_CLASS(SECPOINTUnitTesting) 74 | { 75 | public: 76 | TEST_METHOD(ConstructorTesting) 77 | { 78 | SECPOINT sp = SECPOINT(); 79 | 80 | Assert::IsTrue(sp.x.GetData() == 0, TEXT("X does not equal 0")); 81 | Assert::IsTrue(sp.y.GetData() == 0, TEXT("X does not equal 0")); 82 | 83 | SECPOINT sp2 = SECPOINT(15, 20); 84 | 85 | Assert::IsTrue(sp2.x.GetData() == 15, TEXT("X does not equal 15")); 86 | Assert::IsTrue(sp2.y.GetData() == 20, TEXT("Y does not equal 20")); 87 | 88 | SECPOINT sp3 = &sp2; 89 | 90 | Assert::IsTrue(sp2.x.GetData() == sp3.x.GetData(), TEXT("SECPOINT2.X does not equal SECPOINT3.X")); 91 | Assert::IsTrue(sp2.y.GetData() == sp3.y.GetData(), TEXT("SECPOINT2.Y does not equal SECPOINT3.Y")); 92 | 93 | tagPOINT tp = tagPOINT(); 94 | tp.x = 4; 95 | tp.y = 3; 96 | 97 | SECPOINT sp4 = &tp; 98 | 99 | Assert::IsTrue(tp.x == sp4.x.GetData(), TEXT("tagPOINT->SECPOINT constructor failed (X value incorrect)")); 100 | Assert::IsTrue(tp.y == sp4.y.GetData(), TEXT("tagPOINT->SECPOINT constructor failed (Y value incorrect)")); 101 | 102 | SECPOINT sp5; 103 | 104 | sp5 = &tp; 105 | 106 | Assert::IsTrue(tp.x == sp5.x.GetData(), TEXT("SECPOINT = tagPOINT operator overload failed (X value incorrect)")); 107 | Assert::IsTrue(tp.y == sp5.y.GetData(), TEXT("SECPOINT = tagPOINT operator overload failed (Y value incorrect)")); 108 | 109 | Assert::IsTrue(sp5 == &sp4, TEXT("Operator == overload failed.")); 110 | Assert::IsFalse(sp5 != &sp4, TEXT("Operator != overload failed.")); 111 | } 112 | 113 | TEST_METHOD(TagPointTesting) 114 | { 115 | SECPOINT sp; 116 | tagPOINT tp = tagPOINT(); 117 | 118 | tp.x = 1; 119 | tp.y = 2; 120 | 121 | Assert::IsTrue(tp.x == 1 && tp.y == 2); 122 | 123 | sp = &tp; 124 | Assert::IsTrue(sp.x == tp.x, TEXT("TagPoint and SecPoint X-Value missmatch")); 125 | Assert::IsTrue(sp.y == tp.y, TEXT("TagPoint and SecPoint Y-Value missmatch")); 126 | 127 | SECPOINT sp1 = SECPOINT(15, 20); 128 | tagPOINT tp1 = tagPOINT(sp); 129 | 130 | Assert::IsTrue(sp == &tp, TEXT("SECPOINT does not equal tagPOINT")); 131 | Assert::IsFalse(sp != &tp, TEXT("SECPOINT equals tagPOINT")); 132 | 133 | tagPOINT tp2 = sp; 134 | 135 | Assert::IsTrue(sp == &tp2, TEXT("tagPOINT operator overload failed.")); 136 | } 137 | }; 138 | } 139 | -------------------------------------------------------------------------------- /UnitTesting/UnitTesting.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 | 16.0 23 | {AFB54969-A72A-41C5-B5F5-142A318614A7} 24 | Win32Proj 25 | UnitTesting 26 | 10.0 27 | NativeUnitTestProject 28 | UnitTesting 29 | 30 | 31 | 32 | DynamicLibrary 33 | true 34 | v142 35 | Unicode 36 | false 37 | 38 | 39 | DynamicLibrary 40 | false 41 | v142 42 | true 43 | Unicode 44 | false 45 | 46 | 47 | DynamicLibrary 48 | true 49 | v142 50 | Unicode 51 | false 52 | 53 | 54 | DynamicLibrary 55 | false 56 | v142 57 | true 58 | Unicode 59 | false 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | true 81 | $(SolutionDir)Output\$(Configuration)\bin\ 82 | $(SolutionDir)Output\$(Configuration)\$(ProjectName)\ 83 | $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(SolutionDir)Include; 84 | 85 | 86 | true 87 | 88 | 89 | false 90 | $(SolutionDir)Output\$(Configuration)\bin\ 91 | $(SolutionDir)Output\$(Configuration)\$(ProjectName)\ 92 | $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(SolutionDir)Include; 93 | 94 | 95 | false 96 | 97 | 98 | 99 | Use 100 | Level3 101 | true 102 | $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories);$(SolutionDir)Common 103 | WIN32;_DEBUG;%(PreprocessorDefinitions) 104 | true 105 | pch.h 106 | 107 | 108 | Windows 109 | $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) 110 | $(SolutionDir)Output\$(Configuration)\bin\Common.lib;%(AdditionalDependencies) 111 | 112 | 113 | 114 | 115 | Use 116 | Level3 117 | true 118 | $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) 119 | _DEBUG;%(PreprocessorDefinitions) 120 | true 121 | pch.h 122 | 123 | 124 | Windows 125 | $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) 126 | 127 | 128 | 129 | 130 | Use 131 | Level3 132 | true 133 | true 134 | true 135 | $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories);$(SolutionDir)Common 136 | WIN32;NDEBUG;%(PreprocessorDefinitions) 137 | true 138 | pch.h 139 | 140 | 141 | Windows 142 | true 143 | true 144 | $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) 145 | $(SolutionDir)Output\$(Configuration)\bin\Common.lib;%(AdditionalDependencies) 146 | 147 | 148 | 149 | 150 | Use 151 | Level3 152 | true 153 | true 154 | true 155 | $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) 156 | NDEBUG;%(PreprocessorDefinitions) 157 | true 158 | pch.h 159 | 160 | 161 | Windows 162 | true 163 | true 164 | $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) 165 | 166 | 167 | 168 | 169 | Create 170 | Create 171 | Create 172 | Create 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /UnitTesting/UnitTesting.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;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 | {7495a7fe-59b9-48bb-99a1-53d3daa6ab3b} 18 | 19 | 20 | {90c3a3b7-71e6-4ff4-ad44-437f18d7094b} 21 | 22 | 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files\Common 29 | 30 | 31 | Source Files\Common 32 | 33 | 34 | Source Files\Common 35 | 36 | 37 | Source Files\Common 38 | 39 | 40 | Source Files\Common 41 | 42 | 43 | Source Files\Common 44 | 45 | 46 | 47 | 48 | Header Files 49 | 50 | 51 | -------------------------------------------------------------------------------- /UnitTesting/ZArrayUnitTesting.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "CppUnitTest.h" 3 | #include "ZArray.h" 4 | #include 5 | 6 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 7 | 8 | namespace CommonUnitTesting 9 | { 10 | TEST_CLASS(ZArrayUnitTesting) 11 | { 12 | struct SizeTest10 13 | { 14 | public: 15 | char aPad[10]; 16 | }; 17 | struct SizeTest20 18 | { 19 | public: 20 | char aPad[20]; 21 | }; 22 | struct SizeTest40 23 | { 24 | public: 25 | char aPad[40]; 26 | }; 27 | struct SizeTest80 28 | { 29 | public: 30 | char aPad[80]; 31 | }; 32 | struct SizeTest160 33 | { 34 | public: 35 | char aPad[160]; 36 | }; 37 | struct SizeTest320 38 | { 39 | public: 40 | char aPad[320]; 41 | }; 42 | 43 | struct ZArrayDummyTest 44 | { 45 | public: 46 | int test_number; 47 | 48 | ZArrayDummyTest() 49 | { 50 | this->test_number = 42; 51 | } 52 | 53 | ZArrayDummyTest(ZArrayDummyTest* pToCopy) 54 | { 55 | this->test_number = pToCopy->test_number; 56 | } 57 | }; 58 | 59 | public: 60 | TEST_METHOD(ConstructorTesting) 61 | { 62 | ZArray arr = ZArray(10); 63 | 64 | Assert::IsTrue(arr.GetCount() == 10); 65 | } 66 | 67 | TEST_METHOD(GetHeadTesting) 68 | { 69 | ZArray arr = ZArray(2); 70 | 71 | arr[0] = 10; 72 | arr[1] = 20; 73 | 74 | auto pHead = arr.GetHeadPosition(); 75 | Assert::AreEqual(20, *pHead); 76 | 77 | arr.RemoveAt(pHead); 78 | 79 | pHead = arr.GetHeadPosition(); 80 | Assert::AreEqual(10, *pHead); 81 | } 82 | 83 | TEST_METHOD(SubscriptOperatorTesting) 84 | { 85 | ZArray arr = ZArray(10); 86 | 87 | for (int i = 0; i < arr.GetCount(); i++) 88 | { 89 | arr[i] = i * 5; 90 | } 91 | 92 | for (int i = 0; i < arr.GetCount(); i++) 93 | { 94 | Assert::AreEqual(i * 5, arr[i]); 95 | } 96 | } 97 | 98 | TEST_METHOD(NextPrevTesting) 99 | { 100 | ZArray arr = ZArray(10); 101 | 102 | for (int i = 0; i < arr.GetCount(); i++) 103 | { 104 | arr[i] = i * 10; 105 | Assert::AreEqual(i * 10, arr[i]); 106 | } 107 | 108 | PINT pHead = arr.GetHeadPosition(); 109 | 110 | Assert::AreEqual(90, *pHead); 111 | 112 | for (int i = arr.GetCount() - 1; i > 0; i--) 113 | { 114 | Assert::AreEqual(i * 10, *pHead); 115 | Assert::AreEqual(i, (int)arr.IndexOf(pHead)); 116 | 117 | arr.GetNext(&pHead); 118 | } 119 | 120 | for (int i = 0; i < arr.GetCount(); i++) 121 | { 122 | Assert::AreEqual(i * 10, *pHead); 123 | arr.GetPrev(&pHead); 124 | } 125 | } 126 | 127 | // observing the duration of this function vs the one below will showcase just how much 128 | // faster it is to use maples block/pool allocation method instead of malloc/free 129 | /*TEST_METHOD(MassMallocTesting) 130 | { 131 | for (int i = 0; i < 250; i++) 132 | { 133 | for (int j = 0; j < 50000; j++) 134 | { 135 | PVOID pAlloc = malloc(100); 136 | free(pAlloc); 137 | } 138 | } 139 | }*/ 140 | 141 | TEST_METHOD(InsertRemoveTesting) 142 | { 143 | ZArray arr = ZArray(); 144 | 145 | for (int i = 0; i < 10; i++) 146 | { 147 | auto item = arr.InsertBefore(); 148 | Assert::AreEqual((size_t)i + 1, arr.GetCount()); 149 | Assert::AreEqual(42, item->test_number); 150 | } 151 | } 152 | 153 | TEST_METHOD(EqualsOperatorTesting) 154 | { 155 | ZArray arr1 = ZArray(); 156 | ZArray arr2 = ZArray(); 157 | 158 | for (int i = 0; i < 100; i++) 159 | { 160 | auto x = arr1.InsertBefore(); 161 | x->test_number += i; 162 | } 163 | 164 | arr2 = arr1; 165 | 166 | for (int i = 0; i < 100; i++) 167 | { 168 | Assert::AreEqual(42 + i, arr1[i].test_number); 169 | Assert::AreEqual(arr1[i].test_number, arr2[i].test_number); 170 | } 171 | } 172 | 173 | TEST_METHOD(ReallocTesting) 174 | { 175 | size_t initial_size = sizeof(ZArrayDummyTest) * 10; 176 | ZArray arr = ZArray(initial_size); 177 | 178 | size_t uCount = arr.GetCount(); 179 | 180 | arr.MakeSpace(initial_size); 181 | Assert::AreEqual(uCount, arr.GetCount()); 182 | arr.MakeSpace(initial_size * 2); 183 | Assert::AreEqual(uCount * 2, arr.GetCount()); 184 | 185 | arr.MakeSpace(initial_size * 3); 186 | Assert::AreEqual(uCount * 4, arr.GetCount()); // *4 cuz size is always doubled 187 | } 188 | }; 189 | } 190 | -------------------------------------------------------------------------------- /UnitTesting/ZListUnitTesting.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "CppUnitTest.h" 3 | #include "ZList.h" 4 | 5 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 6 | 7 | namespace CommonUnitTesting 8 | { 9 | TEST_CLASS(ZListUnitTesting) 10 | { 11 | public: 12 | TEST_METHOD(TraversalTesting) 13 | { 14 | ZList list = ZList(); 15 | 16 | int a[5]; 17 | 18 | /* Populate array */ 19 | for (int i = 0; i < 5; i++) 20 | { 21 | a[i] = 10 + (10 * i); 22 | } 23 | 24 | /* Populate list and verify tail and head positions, and the list count are updated correctly */ 25 | for (int i = 0; i < 5; i++) 26 | { 27 | Assert::AreEqual((size_t)i, list.GetCount()); 28 | list.AddTail(&a[i]); 29 | Assert::AreEqual((size_t)i + 1, list.GetCount()); 30 | 31 | Assert::AreEqual(10, *list.GetHeadPosition()); // head should never change 32 | Assert::AreEqual(10 + (i * 10), *list.GetTailPosition()); // tail should change after each insertion 33 | } 34 | 35 | int iterations = 0; 36 | 37 | /* Verify head->next iteration works properly */ 38 | int* head = list.GetHeadPosition(); 39 | while (head) 40 | { 41 | iterations += 1; 42 | int* value = list.GetNext(&head); 43 | 44 | Assert::AreEqual(iterations * 10, *value); 45 | } 46 | 47 | /* Verify we looped the correct number of times */ 48 | Assert::AreEqual(5, iterations); 49 | 50 | /* Verify tail->prev iteration works properly */ 51 | iterations = 0; 52 | int* tail = list.GetTailPosition(); 53 | while (tail) 54 | { 55 | iterations += 1; 56 | int* value = list.GetPrev(&tail); 57 | 58 | Assert::AreEqual(60 - (iterations * 10), *value); 59 | } 60 | 61 | /* Verify we looped the correct number of times */ 62 | Assert::AreEqual(5, iterations); 63 | 64 | /* Verify no items are linked before head position */ 65 | head = list.GetHeadPosition(); 66 | list.GetPrev(&head); 67 | 68 | Assert::AreEqual((int*)nullptr, head); 69 | 70 | /* Verify no items are linked after tail position */ 71 | tail = list.GetTailPosition(); 72 | list.GetNext(&tail); 73 | 74 | Assert::AreEqual((int*)nullptr, tail); 75 | } 76 | 77 | TEST_METHOD(RemovalTesting) 78 | { 79 | ZList list = ZList(); 80 | 81 | int a[5]; 82 | 83 | /* Populate array */ 84 | for (int i = 0; i < 5; i++) 85 | { 86 | a[i] = 10 + (10 * i); 87 | } 88 | 89 | /* Populate list with array item pointers */ 90 | for (int i = 0; i < 5; i++) 91 | { 92 | list.AddTail(&a[i]); 93 | } 94 | 95 | /* Remove all items using head pointer */ 96 | for (int i = 0; i < 5; i++) 97 | { 98 | PINT pHead = list.GetHeadPosition(); 99 | 100 | /* Verify initial pointer is correct */ 101 | Assert::AreEqual(10 + (10 * i), *pHead); 102 | 103 | list.RemoveAt(pHead); 104 | 105 | /* Verify new head has the correct value */ 106 | if (i == 4) 107 | { 108 | /* Last item removed, list is now empty */ 109 | Assert::AreEqual((int*)nullptr, list.GetHeadPosition()); 110 | Assert::AreEqual((int*)nullptr, list.GetTailPosition()); 111 | } 112 | else 113 | { 114 | Assert::AreEqual(20 + (10 * i), *list.GetHeadPosition()); 115 | } 116 | 117 | /* Verify remaining size of array is correct */ 118 | Assert::AreEqual((size_t)(4 - i), list.GetCount()); 119 | } 120 | 121 | /* Re-populate list with array item pointers */ 122 | for (int i = 0; i < 5; i++) 123 | { 124 | list.AddTail(&a[i]); 125 | } 126 | 127 | /* Remove all items using tail pointer */ 128 | for (int i = 0; i < 5; i++) 129 | { 130 | PINT pTail = list.GetTailPosition(); 131 | 132 | /* Verify initial pointer is correct */ 133 | Assert::AreEqual(50 - (10 * i), *pTail); 134 | 135 | list.RemoveAt(pTail); 136 | 137 | /* Verify new head has the correct value */ 138 | if (i == 4) 139 | { 140 | /* Last item removed, list is now empty */ 141 | Assert::AreEqual((int*)nullptr, list.GetHeadPosition()); 142 | Assert::AreEqual((int*)nullptr, list.GetTailPosition()); 143 | } 144 | else 145 | { 146 | Assert::AreEqual(40 - (10 * i), *list.GetTailPosition()); 147 | } 148 | 149 | /* Verify remaining size of array is correct */ 150 | Assert::AreEqual((size_t)(4 - i), list.GetCount()); 151 | } 152 | 153 | /* Re-populate list with array item pointers */ 154 | for (int i = 0; i < 5; i++) 155 | { 156 | list.AddTail(&a[i]); 157 | } 158 | 159 | /* Remove item from middle of list */ 160 | PINT pHead = list.GetHeadPosition(); 161 | list.GetNext(&pHead); // scroll forward 162 | list.GetNext(&pHead); // scroll forward 163 | 164 | list.RemoveAt(pHead); // remove pHead 165 | 166 | Assert::AreEqual((size_t)4, list.GetCount()); 167 | 168 | pHead = list.GetHeadPosition(); 169 | 170 | /* Verify node with value 30 has been removed */ 171 | Assert::AreEqual(10, *list.GetNext(&pHead)); 172 | Assert::AreEqual(20, *list.GetNext(&pHead)); 173 | Assert::AreEqual(40, *list.GetNext(&pHead)); 174 | Assert::AreEqual(50, *list.GetNext(&pHead)); 175 | Assert::AreEqual((int*)nullptr, list.GetNext(&pHead)); 176 | 177 | /* Re-populate list with array item pointers */ 178 | for (int i = 0; i < 5; i++) 179 | { 180 | list.AddTail(&a[i]); 181 | } 182 | 183 | list.RemoveAll(); // TODO determine how to test this 184 | } 185 | 186 | TEST_METHOD(FindIndexTesting) // TODO finish this 187 | { 188 | ZList list = ZList(); 189 | 190 | int a[5]; 191 | 192 | /* Populate array */ 193 | for (int i = 0; i < 5; i++) 194 | { 195 | a[i] = 10 + (10 * i); 196 | } 197 | 198 | /* Populate list with array item pointers */ 199 | for (int i = 0; i < 5; i++) 200 | { 201 | list.AddTail(&a[i]); 202 | } 203 | 204 | Assert::AreEqual(10, *list.FindIndex(0)); 205 | Assert::AreEqual(30, *list.FindIndex(2)); 206 | Assert::AreEqual(40, *list.FindIndex(3)); 207 | Assert::AreEqual(50, *list.FindIndex(4)); 208 | Assert::AreEqual((int*)nullptr, list.FindIndex(5)); 209 | } 210 | 211 | TEST_METHOD(IndexOfTesting) 212 | { 213 | ZList list = ZList(); 214 | 215 | int a[5]; 216 | 217 | /* Populate array */ 218 | for (int i = 0; i < 5; i++) 219 | { 220 | a[i] = 10 + (10 * i); 221 | } 222 | 223 | /* Populate list with array item pointers */ 224 | for (int i = 0; i < 5; i++) 225 | { 226 | list.AddTail(&a[i]); 227 | } 228 | 229 | /* Verify head and tail indices are correct */ 230 | Assert::AreEqual(0, list.IndexOf(list.GetHeadPosition())); 231 | Assert::AreEqual(4, list.IndexOf(list.GetTailPosition())); 232 | 233 | PINT pHead = list.GetHeadPosition(); 234 | 235 | /* Verify indices are returned correctly */ 236 | for (int i = 0; i < 5; i++) 237 | { 238 | Assert::AreEqual(i, list.IndexOf(pHead)); 239 | list.GetNext(&pHead); 240 | } 241 | 242 | /* Verify passing invalid pointer returns correct error number */ 243 | Assert::AreEqual(ZLIST_INVALID_INDEX, list.IndexOf(pHead)); 244 | } 245 | 246 | // TODO expand this to include finding objects rather than just integer pointers 247 | TEST_METHOD(FindTesting) 248 | { 249 | ZList list = ZList(); 250 | 251 | int a[5]; 252 | 253 | /* Populate array */ 254 | for (int i = 0; i < 5; i++) 255 | { 256 | a[i] = 10 + (10 * i); 257 | } 258 | 259 | /* Populate list with array item pointers */ 260 | for (int i = 0; i < 5; i++) 261 | { 262 | list.AddTail(&a[i]); 263 | } 264 | 265 | int nNumToFind = 20; 266 | int nNumToNotFind = 60; 267 | 268 | /* 20 exists in the list, so a pointer to that value should be returned */ 269 | Assert::AreEqual(nNumToFind, *list.Find(&nNumToFind, nullptr)); 270 | 271 | /* 60 does not exist in the list, so a nullptr should be returned */ 272 | Assert::AreEqual((int*)nullptr, list.Find(&nNumToNotFind, nullptr)); 273 | 274 | nNumToNotFind = 10; 275 | 276 | /* Since the head node is 10 and we are telling it to search after the head node, it should return nullptr */ 277 | Assert::AreEqual((int*)nullptr, list.Find(&nNumToNotFind, list.GetHeadPosition())); 278 | 279 | /* Since 20 is after the head node, that value should be returned */ 280 | Assert::AreEqual(20, *list.Find(&nNumToFind, list.GetHeadPosition())); 281 | } 282 | }; 283 | } 284 | -------------------------------------------------------------------------------- /UnitTesting/ZRefUnitTesting.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "CppUnitTest.h" 3 | #include "ZRef.h" 4 | 5 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 6 | 7 | namespace CommonUnitTesting 8 | { 9 | TEST_CLASS(ZRefUnitTesting) 10 | { 11 | public: 12 | 13 | struct ZRefCountedTest : ZRefCounted 14 | { 15 | const int test_member_value = 6; 16 | int test_member; 17 | 18 | ZRefCountedTest() 19 | { 20 | test_member = test_member_value; 21 | } 22 | }; 23 | 24 | struct ZRefUnCountedTest 25 | { 26 | const int test_member_value = 6; 27 | int test_member; 28 | 29 | ZRefUnCountedTest() 30 | { 31 | test_member = test_member_value; 32 | } 33 | }; 34 | 35 | TEST_METHOD(NonCountedReferenceTesting) 36 | { 37 | ZRef pZRefTestBase = ZRef(); 38 | 39 | pZRefTestBase.Alloc(); 40 | 41 | ZRefCounted* pBase = reinterpret_cast(((char*)pZRefTestBase.p) - (sizeof(ZRefCountedDummy) - sizeof(ZRefUnCountedTest))); 42 | 43 | /* Verify reference count is correct */ 44 | Assert::AreEqual(1, (int)pBase->m_nRef); 45 | 46 | ZRef pZRefTestInc = ZRef(&pZRefTestBase); 47 | 48 | /* Verify reference count is correct */ 49 | Assert::AreEqual(2, (int)pBase->m_nRef); 50 | 51 | pZRefTestInc.~ZRef(); 52 | 53 | /* Verify reference count is correct */ 54 | Assert::AreEqual(1, (int)pBase->m_nRef); 55 | } 56 | 57 | TEST_METHOD(CountedReferenceTesting) 58 | { 59 | ZRef pZRefTestBase = ZRef(); 60 | 61 | pZRefTestBase.Alloc(); 62 | 63 | /* Verify reference count is correct */ 64 | Assert::AreEqual(1, (int)pZRefTestBase->m_nRef); 65 | 66 | ZRef pZRefTestInc = ZRef(&pZRefTestBase); 67 | 68 | /* Verify reference count is correct */ 69 | Assert::AreEqual(2, (int)pZRefTestInc->m_nRef); 70 | Assert::AreEqual(2, (int)pZRefTestBase->m_nRef); 71 | 72 | /* Kill the second reference */ 73 | pZRefTestInc.~ZRef(); 74 | 75 | /* Verify reference count is correct */ 76 | Assert::AreEqual(1, (int)pZRefTestBase->m_nRef); 77 | } 78 | 79 | TEST_METHOD(EncapsulatedConstructorTesting) 80 | { 81 | ZRef pZRefTest = ZRef(); 82 | ZRef pZRefTest2 = ZRef(); 83 | 84 | pZRefTest.Alloc(); 85 | pZRefTest2.Alloc(); 86 | 87 | Assert::AreEqual(pZRefTest->test_member_value, pZRefTest->test_member); 88 | Assert::AreEqual(pZRefTest2->test_member_value, pZRefTest2->test_member); 89 | } 90 | }; 91 | } 92 | -------------------------------------------------------------------------------- /UnitTesting/ZXStringUnitTesting.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "CppUnitTest.h" 3 | #include 4 | 5 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 6 | 7 | namespace CommonUnitTesting 8 | { 9 | TEST_CLASS(ZXStringUnitTesting) 10 | { 11 | public: 12 | TEST_METHOD(ConstructorTesting) 13 | { 14 | ZXString str1 = ZXString("str1"); 15 | ZXString str2 = ZXString(str1); 16 | 17 | Assert::IsTrue(!strcmp(str1, "str1"), TEXT("String assignment constructor failed (char)")); 18 | Assert::IsTrue(!strcmp(str2, "str1"), TEXT("Copy constructor failed (char)")); 19 | 20 | ZXString str3 = ZXString(L"str3"); 21 | ZXString str4 = ZXString(str3); 22 | 23 | Assert::IsTrue(!wcscmp(str3, L"str3"), TEXT("String assignment constructor failed (wchar_t)")); 24 | Assert::IsTrue(!wcscmp(str3, L"str3"), TEXT("Copy constructor failed (wchar_t)")); 25 | } 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /UnitTesting/ZtlSecureUnitTesting.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "CppUnitTest.h" 3 | #include "ZtlSecure.h" 4 | 5 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 6 | 7 | namespace CommonUnitTesting 8 | { 9 | TEST_CLASS(ZtlSecureUnitTesting) 10 | { 11 | template 12 | void ZtlSecureTest(T expected_val) 13 | { 14 | T arr[2]; 15 | 16 | T result = ZtlSecureFuse(arr, ZtlSecureTear(arr, expected_val)); 17 | 18 | Assert::AreEqual(expected_val, result); 19 | } 20 | public: 21 | TEST_METHOD(FuseTearChar) 22 | { 23 | ZtlSecureTest(-45); 24 | } 25 | TEST_METHOD(FuseTearShort) 26 | { 27 | ZtlSecureTest(-45); 28 | } 29 | TEST_METHOD(FuseTearInt) 30 | { 31 | ZtlSecureTest(-45); 32 | } 33 | TEST_METHOD(FuseTearLongLong) 34 | { 35 | ZtlSecureTest(-45); 36 | } 37 | TEST_METHOD(FuseTearFloat) 38 | { 39 | ZtlSecureTest(-45); 40 | } 41 | TEST_METHOD(FuseTearDouble) 42 | { 43 | ZtlSecureTest(-45); 44 | } 45 | TEST_METHOD(MacroValidation) 46 | { 47 | Assert::AreEqual((unsigned int)0xBAADF00D, ZTLSECURE_CHECKSUM); 48 | Assert::AreEqual(5, ZTLSECURE_ROTATION); 49 | } 50 | 51 | // =============== SECRECT TESTING 52 | }; 53 | 54 | TEST_CLASS(SECRECTUnitTesting) 55 | { 56 | TEST_METHOD(SetRectTest) 57 | { 58 | SECRECT s = SECRECT(10, 15, 20, 30); 59 | 60 | Assert::AreEqual(s.GetLeft(), 10); 61 | Assert::AreEqual(s.GetTop(), 15); 62 | Assert::AreEqual(s.GetRight(), 20); 63 | Assert::AreEqual(s.GetBottom(), 30); 64 | } 65 | TEST_METHOD(SetRectEmptyTest) 66 | { 67 | SECRECT s = SECRECT(10, 15, 20, 30); 68 | 69 | Assert::AreEqual(s.GetLeft(), 10); 70 | Assert::AreEqual(s.GetTop(), 15); 71 | Assert::AreEqual(s.GetRight(), 20); 72 | Assert::AreEqual(s.GetBottom(), 30); 73 | 74 | s.SetRectEmpty(); 75 | 76 | Assert::AreEqual(s.GetLeft(), 0); 77 | Assert::AreEqual(s.GetTop(), 0); 78 | Assert::AreEqual(s.GetRight(), 0); 79 | Assert::AreEqual(s.GetBottom(), 0); 80 | } 81 | TEST_METHOD(PutGetRectTest) 82 | { 83 | SECRECT s = SECRECT(); 84 | 85 | s.PutRight(10); 86 | s.PutLeft(15); 87 | s.PutTop(20); 88 | s.PutBottom(25); 89 | 90 | Assert::AreEqual(s.GetRight(), 10); 91 | Assert::AreEqual(s.GetLeft(), 15); 92 | Assert::AreEqual(s.GetTop(), 20); 93 | Assert::AreEqual(s.GetBottom(), 25); 94 | } 95 | TEST_METHOD(IsRectEmptyTest) 96 | { 97 | SECRECT s = SECRECT(10, 15, 7, 4); 98 | 99 | Assert::AreEqual(TRUE, s.IsRectEmpty()); 100 | 101 | s.SetRect(7, 4, 10, 15); 102 | 103 | Assert::AreEqual(FALSE, s.IsRectEmpty()); 104 | } 105 | }; 106 | } 107 | -------------------------------------------------------------------------------- /UnitTesting/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /UnitTesting/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | 12 | #endif //PCH_H 13 | --------------------------------------------------------------------------------