├── .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