├── .gitattributes
├── .gitignore
├── .gitmodules
├── InjectedDLL.sln
├── build
├── InjectedDLL
│ ├── InjectedDLL.vcxproj
│ └── InjectedDLL.vcxproj.filters
└── PolyHook
│ ├── PolyHook.vcxproj
│ └── PolyHook.vcxproj.filters
└── src
├── APB
├── APB.h
├── CompressionHook.cpp
├── CryptHook.cpp
├── NetworkHook.cpp
└── SRPHook.cpp
├── AntiDebug.cpp
├── AntiDebug.h
├── MemorySignature.h
├── ModuleScan.cpp
├── ModuleScan.h
├── Util.cpp
├── Util.h
└── main.cpp
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | # http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
3 | * text=auto
4 |
5 | # Custom for Visual Studio
6 | *.sln text eol=crlf
7 | *.csproj text eol=crlf
8 | *.vbproj text eol=crlf
9 | *.fsproj text eol=crlf
10 | *.dbproj text eol=crlf
11 |
12 | *.vcxproj text eol=crlf
13 | *.vcxitems text eol=crlf
14 | *.props text eol=crlf
15 | *.filters text eol=crlf
16 |
17 | #sources
18 | *.c text
19 | *.cc text
20 | *.cxx text
21 | *.cpp text
22 | *.c++ text
23 | *.hpp text
24 | *.h text
25 | *.h++ text
26 | *.hh text
27 |
28 | # Compiled Object files
29 | *.slo binary
30 | *.lo binary
31 | *.o binary
32 | *.obj binary
33 |
34 | # Precompiled Headers
35 | *.gch binary
36 | *.pch binary
37 |
38 | # Compiled Dynamic libraries
39 | *.so binary
40 | *.dylib binary
41 | *.dll binary
42 |
43 | # Compiled Static libraries
44 | *.lai binary
45 | *.la binary
46 | *.a binary
47 | *.lib binary
48 |
49 | # Executables
50 | *.exe binary
51 | *.out binary
52 | *.app binary
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | *.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.pfx
193 | *.publishsettings
194 | node_modules/
195 | orleans.codegen.cs
196 |
197 | # Since there are multiple workflows, uncomment next line to ignore bower_components
198 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
199 | #bower_components/
200 |
201 | # RIA/Silverlight projects
202 | Generated_Code/
203 |
204 | # Backup & report files from converting an old project file
205 | # to a newer Visual Studio version. Backup files are not needed,
206 | # because we have git ;-)
207 | _UpgradeReport_Files/
208 | Backup*/
209 | UpgradeLog*.XML
210 | UpgradeLog*.htm
211 |
212 | # SQL Server files
213 | *.mdf
214 | *.ldf
215 |
216 | # Business Intelligence projects
217 | *.rdl.data
218 | *.bim.layout
219 | *.bim_*.settings
220 |
221 | # Microsoft Fakes
222 | FakesAssemblies/
223 |
224 | # GhostDoc plugin setting file
225 | *.GhostDoc.xml
226 |
227 | # Node.js Tools for Visual Studio
228 | .ntvs_analysis.dat
229 |
230 | # Visual Studio 6 build log
231 | *.plg
232 |
233 | # Visual Studio 6 workspace options file
234 | *.opt
235 |
236 | # Visual Studio LightSwitch build output
237 | **/*.HTMLClient/GeneratedArtifacts
238 | **/*.DesktopClient/GeneratedArtifacts
239 | **/*.DesktopClient/ModelManifest.xml
240 | **/*.Server/GeneratedArtifacts
241 | **/*.Server/ModelManifest.xml
242 | _Pvt_Extensions
243 |
244 | # Paket dependency manager
245 | .paket/paket.exe
246 | paket-files/
247 |
248 | # FAKE - F# Make
249 | .fake/
250 |
251 | # JetBrains Rider
252 | .idea/
253 | *.sln.iml
254 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "thirdparty/PolyHook"]
2 | path = thirdparty/PolyHook
3 | url = https://github.com/stevemk14ebr/PolyHook.git
4 | [submodule "thirdparty/capstone"]
5 | path = thirdparty/capstone
6 | url = https://github.com/stevemk14ebr/capstone.git
7 | [submodule "thirdparty/spdlog"]
8 | path = thirdparty/spdlog
9 | url = https://github.com/gabime/spdlog.git
10 |
--------------------------------------------------------------------------------
/InjectedDLL.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.24720.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PolyHook", "build\PolyHook\PolyHook.vcxproj", "{9F326103-B058-4919-94C8-2936B7CFC4E8}"
7 | EndProject
8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "capstone_static", "thirdparty\capstone\msvc\capstone_static\capstone_static.vcxproj", "{5B01D900-2359-44CA-9914-6B0C6AFB7BE7}"
9 | EndProject
10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InjectedDLL", "build\InjectedDLL\InjectedDLL.vcxproj", "{8F13B8DF-4D93-4D25-AFF2-6BDD2EC75953}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|x64 = Debug|x64
15 | Debug|x86 = Debug|x86
16 | Release|x64 = Release|x64
17 | Release|x86 = Release|x86
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {9F326103-B058-4919-94C8-2936B7CFC4E8}.Debug|x64.ActiveCfg = Debug|x64
21 | {9F326103-B058-4919-94C8-2936B7CFC4E8}.Debug|x64.Build.0 = Debug|x64
22 | {9F326103-B058-4919-94C8-2936B7CFC4E8}.Debug|x86.ActiveCfg = Debug|Win32
23 | {9F326103-B058-4919-94C8-2936B7CFC4E8}.Debug|x86.Build.0 = Debug|Win32
24 | {9F326103-B058-4919-94C8-2936B7CFC4E8}.Release|x64.ActiveCfg = Release|x64
25 | {9F326103-B058-4919-94C8-2936B7CFC4E8}.Release|x64.Build.0 = Release|x64
26 | {9F326103-B058-4919-94C8-2936B7CFC4E8}.Release|x86.ActiveCfg = Release|Win32
27 | {9F326103-B058-4919-94C8-2936B7CFC4E8}.Release|x86.Build.0 = Release|Win32
28 | {5B01D900-2359-44CA-9914-6B0C6AFB7BE7}.Debug|x64.ActiveCfg = Debug|x64
29 | {5B01D900-2359-44CA-9914-6B0C6AFB7BE7}.Debug|x64.Build.0 = Debug|x64
30 | {5B01D900-2359-44CA-9914-6B0C6AFB7BE7}.Debug|x86.ActiveCfg = Debug|Win32
31 | {5B01D900-2359-44CA-9914-6B0C6AFB7BE7}.Debug|x86.Build.0 = Debug|Win32
32 | {5B01D900-2359-44CA-9914-6B0C6AFB7BE7}.Release|x64.ActiveCfg = Release|x64
33 | {5B01D900-2359-44CA-9914-6B0C6AFB7BE7}.Release|x64.Build.0 = Release|x64
34 | {5B01D900-2359-44CA-9914-6B0C6AFB7BE7}.Release|x86.ActiveCfg = Release|Win32
35 | {5B01D900-2359-44CA-9914-6B0C6AFB7BE7}.Release|x86.Build.0 = Release|Win32
36 | {8F13B8DF-4D93-4D25-AFF2-6BDD2EC75953}.Debug|x64.ActiveCfg = Debug|x64
37 | {8F13B8DF-4D93-4D25-AFF2-6BDD2EC75953}.Debug|x64.Build.0 = Debug|x64
38 | {8F13B8DF-4D93-4D25-AFF2-6BDD2EC75953}.Debug|x86.ActiveCfg = Debug|Win32
39 | {8F13B8DF-4D93-4D25-AFF2-6BDD2EC75953}.Debug|x86.Build.0 = Debug|Win32
40 | {8F13B8DF-4D93-4D25-AFF2-6BDD2EC75953}.Release|x64.ActiveCfg = Release|x64
41 | {8F13B8DF-4D93-4D25-AFF2-6BDD2EC75953}.Release|x64.Build.0 = Release|x64
42 | {8F13B8DF-4D93-4D25-AFF2-6BDD2EC75953}.Release|x86.ActiveCfg = Release|Win32
43 | {8F13B8DF-4D93-4D25-AFF2-6BDD2EC75953}.Release|x86.Build.0 = Release|Win32
44 | EndGlobalSection
45 | GlobalSection(SolutionProperties) = preSolution
46 | HideSolutionNode = FALSE
47 | EndGlobalSection
48 | EndGlobal
49 |
--------------------------------------------------------------------------------
/build/InjectedDLL/InjectedDLL.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 | {8F13B8DF-4D93-4D25-AFF2-6BDD2EC75953}
23 | Win32Proj
24 | InjectedDLL
25 | 8.1
26 |
27 |
28 |
29 | DynamicLibrary
30 | true
31 | v140
32 | Unicode
33 |
34 |
35 | DynamicLibrary
36 | false
37 | v140
38 | true
39 | Unicode
40 |
41 |
42 | DynamicLibrary
43 | true
44 | v140
45 | Unicode
46 |
47 |
48 | DynamicLibrary
49 | false
50 | v140
51 | true
52 | Unicode
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | true
74 | ..\..\thirdparty\spdlog\include;..\..\thirdparty\PolyHook;$(IncludePath)
75 |
76 |
77 | true
78 | ..\..\thirdparty\spdlog\include;..\..\thirdparty\PolyHook;$(IncludePath)
79 |
80 |
81 | false
82 | ..\..\thirdparty\spdlog\include;..\..\thirdparty\PolyHook;$(IncludePath)
83 |
84 |
85 | false
86 | ..\..\thirdparty\spdlog\include;..\..\thirdparty\PolyHook;$(IncludePath)
87 |
88 |
89 |
90 |
91 |
92 | Level3
93 | Disabled
94 | WIN32;_DEBUG;_WINDOWS;_USRDLL;INJECTEDDLL_EXPORTS;%(PreprocessorDefinitions)
95 | true
96 | MultiThreadedDebug
97 | 4091;%(DisableSpecificWarnings)
98 |
99 |
100 | Windows
101 | true
102 |
103 |
104 |
105 |
106 |
107 |
108 | Level3
109 | Disabled
110 | _DEBUG;_WINDOWS;_USRDLL;INJECTEDDLL_EXPORTS;%(PreprocessorDefinitions)
111 | true
112 | MultiThreadedDebug
113 | 4091;%(DisableSpecificWarnings)
114 |
115 |
116 | Windows
117 | true
118 |
119 |
120 |
121 |
122 | Level3
123 |
124 |
125 | MaxSpeed
126 | true
127 | true
128 | WIN32;NDEBUG;_WINDOWS;_USRDLL;INJECTEDDLL_EXPORTS;%(PreprocessorDefinitions)
129 | true
130 | MultiThreaded
131 | 4091;%(DisableSpecificWarnings)
132 |
133 |
134 | Windows
135 | true
136 | true
137 | true
138 |
139 |
140 |
141 |
142 | Level3
143 |
144 |
145 | MaxSpeed
146 | true
147 | true
148 | NDEBUG;_WINDOWS;_USRDLL;INJECTEDDLL_EXPORTS;%(PreprocessorDefinitions)
149 | true
150 | MultiThreaded
151 | 4091;%(DisableSpecificWarnings)
152 |
153 |
154 | Windows
155 | true
156 | true
157 | true
158 |
159 |
160 |
161 |
162 | {9f326103-b058-4919-94c8-2936b7cfc4e8}
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
--------------------------------------------------------------------------------
/build/InjectedDLL/InjectedDLL.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 | {01504071-1a0c-42c2-9df9-5d392ad16246}
18 |
19 |
20 | {d93dba35-5ff0-4a81-bb4c-998d1832262f}
21 |
22 |
23 |
24 |
25 | Source Files
26 |
27 |
28 | Source Files
29 |
30 |
31 | Source Files
32 |
33 |
34 | Source Files
35 |
36 |
37 | Source Files\APB
38 |
39 |
40 | Source Files\APB
41 |
42 |
43 | Source Files\APB
44 |
45 |
46 | Source Files\APB
47 |
48 |
49 |
50 |
51 | Header Files
52 |
53 |
54 | Header Files
55 |
56 |
57 | Header Files
58 |
59 |
60 | Header Files
61 |
62 |
63 | Header Files\APB
64 |
65 |
66 |
--------------------------------------------------------------------------------
/build/PolyHook/PolyHook.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 |
23 | {5b01d900-2359-44ca-9914-6b0c6afb7be7}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | {9F326103-B058-4919-94C8-2936B7CFC4E8}
34 | Win32Proj
35 | PolyHook
36 | 8.1
37 |
38 |
39 |
40 | StaticLibrary
41 | true
42 | v140
43 | Unicode
44 |
45 |
46 | StaticLibrary
47 | false
48 | v140
49 | true
50 | Unicode
51 |
52 |
53 | StaticLibrary
54 | true
55 | v140
56 | Unicode
57 |
58 |
59 | StaticLibrary
60 | false
61 | v140
62 | true
63 | Unicode
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | Level3
89 | Disabled
90 | WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)
91 | true
92 | MultiThreadedDebug
93 |
94 |
95 | Windows
96 |
97 |
98 |
99 |
100 |
101 |
102 | Level3
103 | Disabled
104 | _DEBUG;_LIB;%(PreprocessorDefinitions)
105 | true
106 | MultiThreadedDebug
107 |
108 |
109 | Windows
110 |
111 |
112 |
113 |
114 | Level3
115 |
116 |
117 | MaxSpeed
118 | true
119 | true
120 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)
121 | true
122 | MultiThreaded
123 |
124 |
125 | Windows
126 | true
127 | true
128 |
129 |
130 |
131 |
132 | Level3
133 |
134 |
135 | MaxSpeed
136 | true
137 | true
138 | NDEBUG;_LIB;%(PreprocessorDefinitions)
139 | true
140 | MultiThreaded
141 |
142 |
143 | Windows
144 | true
145 | true
146 |
147 |
148 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/build/PolyHook/PolyHook.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 |
23 |
24 | Header Files
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/APB/APB.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace CryptHook
4 | {
5 | void Initialise();
6 | void Shutdown();
7 | }
8 |
9 | namespace NetworkHook
10 | {
11 | void Initialise();
12 | void Shutdown();
13 | }
14 |
15 | namespace SRPHook
16 | {
17 | void Initialise();
18 | void Shutdown();
19 | }
20 |
21 | namespace CompressionHook
22 | {
23 | void Initialise();
24 | void Shutdown();
25 | }
26 |
--------------------------------------------------------------------------------
/src/APB/CompressionHook.cpp:
--------------------------------------------------------------------------------
1 | #include "APB.h"
2 | #include
3 | #include
4 | #include "../Util.h"
5 | #include
6 | #include "../ModuleScan.h"
7 |
8 | namespace CompressionHook
9 | {
10 | PLH::Detour detUncompressMemory;
11 | PLH::Detour detCompressMemory;
12 |
13 | std::shared_ptr logger;
14 |
15 | int __cdecl hkUncompressMemory(int Flags, void* UncompressedBuffer, int UncompressedSize, const void* CompressedBuffer, int CompressedSize)
16 | {
17 | if (Flags != 0x01)
18 | {
19 | return detUncompressMemory.GetOriginal()(Flags, UncompressedBuffer, UncompressedSize, CompressedBuffer, CompressedSize);
20 | }
21 |
22 | logger->info("appUncompressMemory:");
23 | logger->info(" Flags = 0x{:X}", Flags);
24 | logger->info(" UncompressedSize = {}", UncompressedSize);
25 | logger->info(" CompressedBuffer = {}", Util::DataToHex((const char*)CompressedBuffer, CompressedSize));
26 | logger->info(" CompressedSize = {}", CompressedSize);
27 |
28 | int result = detUncompressMemory.GetOriginal()(Flags, UncompressedBuffer, UncompressedSize, CompressedBuffer, CompressedSize);
29 |
30 | if (result)
31 | {
32 | logger->info(" UncompressedBuffer = {}", Util::DataToHex((const char*)UncompressedBuffer, UncompressedSize));
33 | }
34 | else
35 | {
36 | logger->info(" Failed to uncompress data");
37 | }
38 |
39 | return result;
40 | }
41 |
42 | int __cdecl hkCompressMemory(int Flags, void* CompressedBuffer, int* CompressedSize, const void* UncompressedBuffer, int UncompressedSize)
43 | {
44 | if (Flags != 0x01)
45 | {
46 | return detCompressMemory.GetOriginal()(Flags, CompressedBuffer, CompressedSize, UncompressedBuffer, UncompressedSize);
47 | }
48 |
49 | logger->info("appCompressMemory:");
50 | logger->info(" Flags = 0x{:X}", Flags);
51 | logger->info(" UncompressedSize = {}", UncompressedSize);
52 | logger->info(" UncompressedBuffer = {}", Util::DataToHex((const char*)UncompressedBuffer, UncompressedSize));
53 |
54 | int result = detCompressMemory.GetOriginal()(Flags, CompressedBuffer, CompressedSize, UncompressedBuffer, UncompressedSize);
55 |
56 | if (result)
57 | {
58 | logger->info(" CompressedSize = {}", *CompressedSize);
59 | logger->info(" CompressedBuffer = {}", Util::DataToHex((const char*)CompressedBuffer, *CompressedSize));
60 | }
61 | else
62 | {
63 | logger->info(" Failed to compress data");
64 | }
65 |
66 | return result;
67 | }
68 |
69 | void Initialise()
70 | {
71 | logger = spdlog::get("logger");
72 | logger->info("Hooking compression functions");
73 |
74 | ModuleScan APBScan("APB.exe");
75 |
76 | Util::HookSignatureFunction(detUncompressMemory, APBScan, "\x55\x8B\xEC\x8B\x45\x08\x83\xE0\x0F", "xxxxxxxxx", &hkUncompressMemory);
77 | Util::HookSignatureFunction(detCompressMemory, APBScan, "\x55\x8B\xEC\x83\xEC\x10\xE8\x00\x00\x00\x00\x89\x45\xF8", "xxxxxxx????xxx", &hkCompressMemory);
78 |
79 | logger->info("Compression functions hooked");
80 | }
81 |
82 | void Shutdown()
83 | {
84 | logger->info("Unhooking compression functions");
85 |
86 | detUncompressMemory.UnHook();
87 | detCompressMemory.UnHook();
88 |
89 | logger->info("Compression functions unhooked");
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/APB/CryptHook.cpp:
--------------------------------------------------------------------------------
1 | #include "APB.h"
2 | #include
3 | #include
4 | #include "../Util.h"
5 | #include
6 | #include
7 |
8 | namespace CryptHook
9 | {
10 | PLH::Detour detCryptGenKey;
11 | PLH::Detour detCryptImportKey;
12 | PLH::Detour detCryptEncrypt;
13 | PLH::Detour detCryptDecrypt;
14 | PLH::Detour detRC4SetKey;
15 | PLH::Detour detSHA1Update;
16 | PLH::Detour detXXTEADecrypt;
17 |
18 | std::shared_ptr logger;
19 | std::vector lastSHA1Data;
20 |
21 | BOOL WINAPI hkCryptGenKey(HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYPTKEY* phKey)
22 | {
23 | // Ensure we can export the private key
24 | dwFlags |= CRYPT_ARCHIVABLE | CRYPT_EXPORTABLE;
25 | BOOL result = detCryptGenKey.GetOriginal()(hProv, Algid, dwFlags, phKey);
26 | if (Algid != AT_KEYEXCHANGE || !result)
27 | {
28 | return result;
29 | }
30 |
31 | logger->info("CryptGenKey:");
32 | logger->info(" handle = 0x{:X}", *phKey);
33 |
34 | // Export private key
35 | BYTE keyData[2048];
36 | DWORD keyLength = 2048;
37 | if (!CryptExportKey(*phKey, 0, PRIVATEKEYBLOB, 0, keyData, &keyLength))
38 | {
39 | logger->error(" CryptExportKey for PRIVATEKEYBLOB failed (Error = {})", GetLastError());
40 | }
41 | else
42 | {
43 | logger->info(" privKeyLength = {}", keyLength);
44 | logger->info(" privKey = {}", Util::DataToHex((const char*)keyData, keyLength));
45 | }
46 |
47 | // Export public key
48 | keyLength = 2048;
49 | if (!CryptExportKey(*phKey, 0, PUBLICKEYBLOB, 0, keyData, &keyLength))
50 | {
51 | logger->error(" CryptExportKey for PUBLICKEYBLOB failed (Error = {})", GetLastError());
52 | }
53 | else
54 | {
55 | logger->info(" pubKeyLength = {}", keyLength);
56 | logger->info(" pubKey = {}", Util::DataToHex((const char*)keyData, keyLength));
57 | }
58 |
59 | return result;
60 | }
61 |
62 | BOOL WINAPI hkCryptImportKey(HCRYPTPROV hProv, BYTE* pbData, DWORD dwDataLen, HCRYPTKEY hPubKey, DWORD dwFlags, HCRYPTKEY* phKey)
63 | {
64 | logger->info("CryptImportKey:");
65 | logger->info(" flags = 0x{:X}", dwFlags);
66 | logger->info(" keyLength = {}", dwDataLen);
67 | logger->info(" key = {}", Util::DataToHex((const char*)pbData, dwDataLen));
68 |
69 | BOOL result = detCryptImportKey.GetOriginal()(hProv, pbData, dwDataLen, hPubKey, dwFlags, phKey);
70 | logger->info(" handle = 0x{:X}", *phKey);
71 | return result;
72 | }
73 |
74 | BOOL WINAPI hkCryptEncrypt(HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags, BYTE* pbData, DWORD* pdwDataLen, DWORD dwBufLen)
75 | {
76 | if (pbData == nullptr)
77 | {
78 | return detCryptEncrypt.GetOriginal()(hKey, hHash, Final, dwFlags, pbData, pdwDataLen, dwBufLen);
79 | }
80 |
81 | logger->info("CryptEncrypt:");
82 | logger->info(" hKey = 0x{:X}", hKey);
83 | logger->info(" hHash = 0x{:X}", hHash);
84 | logger->info(" Final = {}", Final);
85 | logger->info(" dwFlags = 0x{:X}", dwFlags);
86 | logger->info(" plainDataLen = {}", *pdwDataLen);
87 | logger->info(" plainData = {}", Util::DataToHex((const char*)pbData, *pdwDataLen));
88 |
89 | BOOL result = detCryptEncrypt.GetOriginal()(hKey, hHash, Final, dwFlags, pbData, pdwDataLen, dwBufLen);
90 |
91 | if (result)
92 | {
93 | logger->info(" encryptedDataLen = {}", *pdwDataLen);
94 | logger->info(" encryptedData = {}", Util::DataToHex((const char*)pbData, *pdwDataLen));
95 | }
96 | else
97 | {
98 | logger->info(" Failed to encrypt data (Error = {})", GetLastError());
99 | }
100 |
101 | return result;
102 | }
103 |
104 | BOOL WINAPI hkCryptDecrypt(HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags, BYTE* pbData, DWORD* pdwDataLen)
105 | {
106 | if (pbData == nullptr)
107 | {
108 | return detCryptDecrypt.GetOriginal()(hKey, hHash, Final, dwFlags, pbData, pdwDataLen);
109 | }
110 |
111 | logger->info("CryptDecrypt:");
112 | logger->info(" hKey = 0x{:X}", hKey);
113 | logger->info(" hHash = 0x{:X}", hHash);
114 | logger->info(" Final = {}", Final);
115 | logger->info(" dwFlags = 0x{:X}", dwFlags);
116 | logger->info(" encryptedDataLen = {}", *pdwDataLen);
117 | logger->info(" encryptedData = {}", Util::DataToHex((const char*)pbData, *pdwDataLen));
118 |
119 | BOOL result = detCryptDecrypt.GetOriginal()(hKey, hHash, Final, dwFlags, pbData, pdwDataLen);
120 |
121 | if (result)
122 | {
123 | logger->info(" plainDataLen = {}", *pdwDataLen);
124 | logger->info(" plainData = {}", Util::DataToHex((const char*)pbData, *pdwDataLen));
125 | }
126 | else
127 | {
128 | logger->info(" Failed to decrpyt data (Error = {})", GetLastError());
129 | }
130 |
131 | return result;
132 | }
133 |
134 | void hkRC4SetKey(void* key, int len, const unsigned char* data)
135 | {
136 | logger->info("RC4_set_key:");
137 | logger->info(" keyPtr = {}", key);
138 | logger->info(" hashedKeyData = {}", Util::DataToHex((const char*)data, len));
139 | logger->info(" rawKeyData = {}", Util::DataToHex((const char*)lastSHA1Data.data(), lastSHA1Data.size()));
140 |
141 | return detRC4SetKey.GetOriginal()(key, len, data);
142 | }
143 |
144 | int hkSHA1Update(void* c, const unsigned char* data, unsigned long len)
145 | {
146 | // SHA1_Update is always called before the SetEncryptionKeys function
147 | // so we store the data that it hashed.
148 | lastSHA1Data.clear();
149 | lastSHA1Data.insert(lastSHA1Data.begin(), data, data + len);
150 | return detSHA1Update.GetOriginal()(c, data, len);
151 | }
152 |
153 | int hkXXTEADecrypt(uint32_t* data, int n, uint32_t* key, int rounds)
154 | {
155 | logger->info("XXTEA_Decrypt:");
156 | logger->info(" key = {}", Util::DataToHex((const char*)key, 16));
157 | logger->info(" rounds = {}", rounds);
158 | logger->info(" encrypted = {}", Util::DataToHex((const char*)data, n*4));
159 |
160 | int result = detXXTEADecrypt.GetOriginal()(data, n, key, rounds);
161 |
162 | logger->info(" decrypted = {}", Util::DataToHex((const char*)data, n * 4));
163 |
164 | return result;
165 | }
166 |
167 | void Initialise()
168 | {
169 | logger = spdlog::get("logger");
170 | logger->info("Hooking Crypt functions");
171 |
172 | ModuleScan APBScan("APB.exe");
173 |
174 | Util::HookLibraryFunction(detCryptGenKey, "Advapi32.dll", "CryptGenKey", &hkCryptGenKey);
175 | Util::HookLibraryFunction(detCryptImportKey, "Advapi32.dll", "CryptImportKey", &hkCryptImportKey);
176 | Util::HookLibraryFunction(detCryptEncrypt, "Advapi32.dll", "CryptEncrypt", &hkCryptEncrypt);
177 | Util::HookLibraryFunction(detCryptDecrypt, "Advapi32.dll", "CryptDecrypt", &hkCryptDecrypt);
178 | Util::HookLibraryFunction(detRC4SetKey, "LIBEAY32.dll", "RC4_set_key", &hkRC4SetKey);
179 | Util::HookLibraryFunction(detSHA1Update, "LIBEAY32.dll", "SHA1_Update", &hkSHA1Update);
180 | Util::HookSignatureFunction(detXXTEADecrypt, APBScan, "\x55\x8B\xEC\x83\xEC\x08\x8B\x55\x14\x56", "xxxxxxxxxx", &hkXXTEADecrypt);
181 |
182 | logger->info("Crypt functions hooked");
183 | }
184 |
185 | void Shutdown()
186 | {
187 | logger->info("Unhooking Crypt functions");
188 |
189 | detCryptGenKey.UnHook();
190 | detCryptImportKey.UnHook();
191 | detCryptEncrypt.UnHook();
192 | detCryptDecrypt.UnHook();
193 | detRC4SetKey.UnHook();
194 | detSHA1Update.UnHook();
195 | detXXTEADecrypt.UnHook();
196 |
197 | logger->info("Crypt functions unhooked");
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/src/APB/NetworkHook.cpp:
--------------------------------------------------------------------------------
1 | #include "APB.h"
2 | #include
3 | #include
4 | #include "../Util.h"
5 | #include
6 | #include "../ModuleScan.h"
7 | #include
8 |
9 | namespace NetworkHook
10 | {
11 | PLH::Detour detSend;
12 | PLH::Detour detSendWrapper;
13 | PLH::Detour detRecv;
14 | PLH::Detour detLobbyPacketHandler;
15 | PLH::Detour detWorldPacketHandler;
16 | PLH::Detour detSendTo;
17 | PLH::Detour detRecvFrom;
18 | PLH::Detour detReceivedRawPacket;
19 | PLH::Detour detLowLevelSend;
20 | PLH::Detour detSendAck;
21 | PLH::Detour detFlushNet;
22 | PLH::Detour detReceivedNak;
23 | PLH::Detour detTcpTickDispatch;
24 | PLH::Detour detTickFlush;
25 | PLH::Detour detSendBunch;
26 |
27 | std::shared_ptr logger;
28 |
29 | int WINAPI hkSend(SOCKET s, const char* buf, int len, int flags)
30 | {
31 | logger->info("send:");
32 | logger->info(" socket = {}", (void*)s);
33 | logger->info(" len = {}", len);
34 | logger->info(" buf = {}", Util::DataToHex(buf, len));
35 | return detSend.GetOriginal()(s, buf, len, flags);
36 | }
37 |
38 | typedef bool(__thiscall* tSendWrapper)(void* thisPtr, const char* buf);
39 | bool __fastcall hkSendWrapper(void* thisPtr, void* edx, const char* buf)
40 | {
41 | int size = *(int*)buf;
42 | logger->info("SendWrapper:");
43 | logger->info(" size = {}", size);
44 | logger->info(" buf = {}", Util::DataToHex(buf, size));
45 |
46 | return detSendWrapper.GetOriginal()(thisPtr, buf);
47 | }
48 |
49 | int WINAPI hkRecv(SOCKET s, char* buf, int len, int flags)
50 | {
51 | int result = detRecv.GetOriginal()(s, buf, len, flags);
52 | if (result <= 0)
53 | {
54 | return result;
55 | }
56 |
57 | logger->info("recv:");
58 | logger->info(" socket = {}", (void*)s);
59 | logger->info(" len = {}", result);
60 | logger->info(" buf = {}", Util::DataToHex(buf, result));
61 |
62 | return result;
63 | }
64 |
65 | typedef int(__thiscall* tPacketHandler)(void* thisPtr, const char* buf);
66 | int __fastcall hkLobbyPacketHandler(void* thisPtr, void* edx, const char* buf)
67 | {
68 | int size = *(int*)buf;
69 | logger->info("LobbyPacketHandler:");
70 | logger->info(" size = {}", size);
71 | logger->info(" buf = {}", Util::DataToHex(buf, size));
72 |
73 | return detLobbyPacketHandler.GetOriginal()(thisPtr, buf);
74 | }
75 |
76 | int __fastcall hkWorldPacketHandler(void* thisPtr, void* edx, const char* buf)
77 | {
78 | int size = *(int*)buf;
79 | logger->info("WorldPacketHandler:");
80 | logger->info(" size = {}", size);
81 | logger->info(" buf = {}", Util::DataToHex(buf, size));
82 |
83 | return detWorldPacketHandler.GetOriginal()(thisPtr, buf);
84 | }
85 |
86 | int WINAPI hkSendTo(SOCKET s, const char* buf, int len, int flags, const struct sockaddr_in* to, int tolen)
87 | {
88 | logger->info("sendto:");
89 | logger->info(" socket = {}", (void*)s);
90 | logger->info(" len = {}", len);
91 | logger->info(" buf = {}", Util::DataToHex(buf, len));
92 | logger->info(" to = {}", to->sin_addr.s_addr);
93 | return detSendTo.GetOriginal()(s, buf, len, flags, to, tolen);
94 | }
95 |
96 | int WINAPI hkRecvFrom(SOCKET s, char* buf, int len, int flags, struct sockaddr* from, int* fromLen)
97 | {
98 | int result = detRecvFrom.GetOriginal()(s, buf, len, flags, from, fromLen);
99 | if (result <= 0)
100 | {
101 | return result;
102 | }
103 |
104 | logger->info("recvfrom:");
105 | logger->info(" socket = {}", (void*)s);
106 | logger->info(" len = {}", result);
107 | logger->info(" buf = {}", Util::DataToHex(buf, result));
108 |
109 | return result;
110 | }
111 |
112 | typedef int(__thiscall* tReceivedRawPacket)(void* thisPtr, const char* InData, int Count);
113 | int __fastcall hkReceivedRawPacket(void* thisPtr, void* edx, const char* InData, int Count)
114 | {
115 | logger->info("ReceivedRawPacket:");
116 | logger->info(" Count = {}", Count);
117 | logger->info(" InData = {}", Util::DataToHex(InData, Count));
118 |
119 | return detReceivedRawPacket.GetOriginal()(thisPtr, InData, Count);
120 | }
121 |
122 | typedef int(__thiscall* tLowLevelSend)(void* thisPtr, const char* Data, int Count);
123 | int __fastcall hkLowLevelSend(void* thisPtr, void* edx, const char* Data, int Count)
124 | {
125 | logger->info("LowLevelSend:");
126 | logger->info(" Count = {}", Count);
127 | logger->info(" InData = {}", Util::DataToHex(Data, Count));
128 |
129 | return detLowLevelSend.GetOriginal()(thisPtr, Data, Count);
130 | }
131 |
132 | typedef int(__thiscall* tSendAck)(void* thisPtr, int AckPacketId, int FirstTime);
133 | int __fastcall hkSendAck(void* thisPtr, void* edx, int AckPacketId, int FirstTime)
134 | {
135 | logger->info("SendAck:");
136 | logger->info(" AckPacketId = {}", AckPacketId);
137 | logger->info(" FirstTime = {}", FirstTime);
138 |
139 | return detSendAck.GetOriginal()(thisPtr, AckPacketId, FirstTime);
140 | }
141 |
142 | typedef int(__thiscall* tFlushNet)(void* thisPtr, int IgnoreSimulation);
143 | int __fastcall hkFlushNet(void* thisPtr, void* edx, int IgnoreSimulation)
144 | {
145 | logger->info("FlushNet:");
146 | logger->info(" IgnoreSimulation = {}", IgnoreSimulation);
147 |
148 | return detFlushNet.GetOriginal()(thisPtr, IgnoreSimulation);
149 | }
150 |
151 | typedef int(__thiscall* tReceivedNak)(void* thisPtr, int NakPacketId);
152 | int __fastcall hkReceivedNak(void* thisPtr, void* edx, int NakPacketId)
153 | {
154 | logger->info("ReceivedNak:");
155 | logger->info(" NakPacketId = {}", NakPacketId);
156 |
157 | return detReceivedNak.GetOriginal()(thisPtr, NakPacketId);
158 | }
159 |
160 | typedef int(__thiscall* tTcpTickDispatch)(void* thisPtr, float DeltaTime);
161 | int __fastcall hkTcpTickDispatch(void* thisPtr, void* edx, float DeltaTime)
162 | {
163 | logger->info("Start UTcpNetDriver::TickDispatch:");
164 | logger->info(" DeltaTime = {}", DeltaTime);
165 |
166 | int retVal = detTcpTickDispatch.GetOriginal()(thisPtr, DeltaTime);
167 |
168 | logger->info("Finish UTcpNetDriver::TickDispatch");
169 |
170 | return retVal;
171 | }
172 |
173 | typedef int(__thiscall* tTickFlush)(void* thisPtr);
174 | int __fastcall hkTickFlush(void* thisPtr, void* edx)
175 | {
176 | logger->info("Start UNetDriver::TickFlush:");
177 |
178 | int retVal = detTickFlush.GetOriginal()(thisPtr);
179 |
180 | logger->info("Finish UNetDriver::TickFlush");
181 |
182 | return retVal;
183 | }
184 |
185 |
186 | template class TArray
187 | {
188 | public:
189 | T* AllocatorInstance;
190 | INT ArrayNum;
191 | INT ArrayMax;
192 | };
193 |
194 | class FBitWriter
195 | {
196 | public:
197 | unsigned char _data[0x24];
198 | TArray Buffer;
199 | INT Num;
200 | INT Max;
201 | };
202 |
203 | class FOutBunch : public FBitWriter
204 | {
205 | public:
206 | void* Next;
207 | void* Channel;
208 | double Time;
209 | unsigned int ReceivedAck;
210 | int ChIndex;
211 | int ChType;
212 | int ChSequence;
213 | int PacketId;
214 | unsigned char bOpen;
215 | unsigned char bClose;
216 | unsigned char bReliable;
217 | };
218 |
219 | class UChannel
220 | {
221 | public:
222 | void** vtable;
223 | };
224 |
225 | class FString : public TArray
226 | {
227 |
228 | };
229 |
230 | typedef int(__thiscall* tSendBunch)(UChannel* thisPtr, FOutBunch* Bunch, int Merge);
231 | typedef FString(__thiscall* tDescribe)(UChannel* thisPtr);
232 |
233 | int __fastcall hkSendBunch(UChannel* thisPtr, void* edx, FOutBunch* Bunch, int Merge)
234 | {
235 | logger->info("UChannel::SendBunch");
236 |
237 |
238 | logger->info(" ChIndex = {}, ChType = {}, bOpen = {}, bClose = {}, bReliable = {}", Bunch->ChIndex, Bunch->ChType, Bunch->bOpen, Bunch->bClose, Bunch->bReliable);
239 | logger->info(" NumBits = {}", Bunch->Num);
240 | logger->info(" Data = {}", Util::DataToHex((const char*)Bunch->Buffer.AllocatorInstance, (Bunch->Num + 8 - 1) / 8));
241 |
242 | return detSendBunch.GetOriginal()(thisPtr, Bunch, Merge);
243 | }
244 |
245 | void Initialise()
246 | {
247 | // Ensure FOutBunch matches the structure in the game
248 | static_assert(offsetof(FBitWriter, Num) == 0x30, "FBitWriter.Num offset is invalid");
249 | static_assert(offsetof(FOutBunch, Next) == 0x38, "FOutBunch.Next offset is invalid");
250 | static_assert(offsetof(FOutBunch, Channel) == 0x3C, "FOutBunch.Channel offset is invalid");
251 | static_assert(offsetof(FOutBunch, Time) == 0x40, "FOutBunch.Time offset is invalid");
252 | static_assert(offsetof(FOutBunch, ChIndex) == 0x4C, "FOutBunch.ChIndex offset is invalid");
253 | static_assert(offsetof(FOutBunch, bReliable) == 0x5E, "FOutBunch.bReliable offset is invalid");
254 |
255 | logger = spdlog::get("logger");
256 | logger->info("Hooking Network functions");
257 |
258 | ModuleScan APBScan("APB.exe");
259 |
260 | Util::HookLibraryFunction(detSend, "wsock32.dll", "send", &hkSend); logger->info("Hooked send");
261 | Util::HookSignatureFunction(detSendWrapper, APBScan, "\x55\x8B\xEC\x56\x8B\xF1\x83\x7E\x04\x03", "xxxxxxxxxx", &hkSendWrapper); logger->info("Hooked SendWrapper");
262 | Util::HookLibraryFunction(detRecv, "wsock32.dll", "recv", &hkRecv); logger->info("Hooked recv");
263 | Util::HookSignatureFunction(detLobbyPacketHandler, APBScan, "\x55\x8B\xEC\x56\x8B\x75\x08\x8B\x46\x04\x05", "xxxxxxxxxxx", &hkLobbyPacketHandler); logger->info("Hooked LobbyPacketHandler");
264 | Util::HookSignatureFunction(detWorldPacketHandler, APBScan, "\x55\x8B\xEC\x8B\x45\x08\x56\x8B\x50\x04\x8D\xB2", "xxxxxxxxxxxx", &hkWorldPacketHandler); logger->info("Hooked WorldPacketHandler");
265 | Util::HookLibraryFunction(detSendTo, "wsock32.dll", "sendto", &hkSendTo); logger->info("Hooked sendto");
266 | Util::HookLibraryFunction(detRecvFrom, "wsock32.dll", "recvfrom", &hkRecvFrom); logger->info("Hooked recvfrom");
267 | Util::HookSignatureFunction(detReceivedRawPacket, APBScan, "\x55\x8B\xEC\x83\xEC\x38\x56\x8B\x75\x0C", "xxxxxxxxxx", &hkReceivedRawPacket); logger->info("Hooked ReceivedRawPacket");
268 | Util::HookSignatureFunction(detLowLevelSend, APBScan, "\x55\x8B\xEC\x83\xEC\x1C\x56\x8B\xF1\x8B\x8E\x00\x00\x00\x00\x85\xC9\x0F\x84\x00\x00\x00\x00\x8B\x01\xFF\x50\x04", "xxxxxxxxxxx????xxxx????xxxxx", &hkLowLevelSend); logger->info("Hooked LowLevelSend");
269 | Util::HookSignatureFunction(detSendAck, APBScan, "\x55\x8B\xEC\x83\xEC\x14\x57\x6A\x01\x8B\xF9", "xxxxxxxxxxx", &hkSendAck); logger->info("Hooked SendAck");
270 | Util::HookSignatureFunction(detFlushNet, APBScan, "\x55\x8B\xEC\x83\xEC\x10\x8B\x45\xF0", "xxxxxxxxx", &hkFlushNet); logger->info("Hooked FlushNet");
271 | Util::HookSignatureFunction(detReceivedNak, APBScan, "\x55\x8B\xEC\x51\x8B\xC1\x56\x8B\xB0\x00\x00\x00\x00\x4E\x89\x45\xFC\x78\x35", "xxxxxxxxx????xxxxxx", &hkReceivedNak); logger->info("Hooked ReceivedNak");
272 | Util::HookSignatureFunction(detTcpTickDispatch, APBScan, "\x55\x8B\xEC\x81\xEC\x00\x00\x00\x00\x53\x56\x8B\xD9\x57\x89\x5D\xE4\xE8\x00\x00\x00\x00\xF3\x0F\x10\x83", "xxxxx????xxxxxxxxx????xxxx", &hkTcpTickDispatch); logger->info("Hooked TcpTickDispatch");
273 | Util::HookSignatureFunction(detTickFlush, APBScan, "\x56\x8B\xF1\x57\xF3\x0F\x10\x86\x00\x00\x00\x00\xF2\x0F\x10\x4E", "xxxxxxxx????xxxx", &hkTickFlush); logger->info("Hooked TickFlush");
274 | Util::HookSignatureFunction(detSendBunch, APBScan, "\x55\x8B\xEC\x53\x8B\x5D\x08\x56\x57\x8B\xF9\x83\x7F\x50\xFF", "xxxxxxxxxxxxxxx", &hkSendBunch); logger->info("Hooked SendBunch");
275 |
276 | logger->info("Network functions hooked");
277 | }
278 |
279 | void Shutdown()
280 | {
281 | logger->info("Unhooking Network functions");
282 |
283 | detSend.UnHook();
284 | detSendWrapper.UnHook();
285 | detRecv.UnHook();
286 | detLobbyPacketHandler.UnHook();
287 | detWorldPacketHandler.UnHook();
288 | detSendTo.UnHook();
289 | detReceivedRawPacket.UnHook();
290 | detLowLevelSend.UnHook();
291 | detSendAck.UnHook();
292 | detFlushNet.UnHook();
293 | detReceivedNak.UnHook();
294 | detTcpTickDispatch.UnHook();
295 | detTickFlush.UnHook();
296 | detSendBunch.UnHook();
297 |
298 | logger->info("Network functions unhooked");
299 | }
300 | }
301 |
--------------------------------------------------------------------------------
/src/APB/SRPHook.cpp:
--------------------------------------------------------------------------------
1 | #include "APB.h"
2 | #include
3 | #include
4 | #include "../Util.h"
5 |
6 | namespace SRPHook
7 | {
8 | PLH::Detour detSetUsername;
9 | PLH::Detour detSetParams;
10 | PLH::Detour detSetAuthPassword;
11 | PLH::Detour detGenPub;
12 | PLH::Detour detComputeKey;
13 | PLH::Detour detRespond;
14 |
15 | std::shared_ptr logger;
16 |
17 | typedef struct cstr_st {
18 | char* data;
19 | int length;
20 | int cap;
21 | int ref;
22 | void* allocator;
23 | } cstr;
24 |
25 | typedef void* BigInteger;
26 |
27 | typedef struct srp_st {
28 | int magic;
29 | int flags;
30 | cstr* username;
31 | BigInteger modulus;
32 | BigInteger generator;
33 | cstr* salt;
34 | BigInteger verifier;
35 | BigInteger password;
36 | BigInteger pubkey;
37 | BigInteger secret;
38 | BigInteger u;
39 | BigInteger key;
40 | cstr* ex_data;
41 | void* meth;
42 | void* meth_data;
43 | void* bctx;
44 | void* accel;
45 | void* param_cb;
46 | void* slu;
47 | } SRP;
48 |
49 | typedef int SRP_RESULT;
50 |
51 | typedef int(*tBigIntegerToBytes)(BigInteger src, unsigned char* dest, int destlen);
52 | tBigIntegerToBytes pBigIntegerToBytes;
53 |
54 | SRP_RESULT hkSRPSetUsername(SRP* srp, const char* username)
55 | {
56 | logger->info("SRP_set_username: username = {}", username);
57 | return detSetUsername.GetOriginal()(srp, username);
58 | }
59 |
60 | SRP_RESULT hkSRPSetParams(SRP* srp,
61 | const unsigned char* modulus, int modlen,
62 | const unsigned char* generator, int genlen,
63 | const unsigned char* salt, int saltlen)
64 | {
65 | logger->info("SRP_set_params:");
66 | logger->info(" modulus = {}", Util::DataToHex((const char*)modulus, modlen));
67 | logger->info(" generator = {}", Util::DataToHex((const char*)generator, genlen));
68 | logger->info(" salt = {}", Util::DataToHex((const char*)salt, saltlen));
69 |
70 | return detSetParams.GetOriginal()(srp, modulus, modlen, generator, genlen, salt, saltlen);
71 | }
72 |
73 | SRP_RESULT hkSetAuthPassword(SRP* srp, const char* password)
74 | {
75 | logger->info("SRP_set_auth_password: password = {}", password);
76 | return detSetAuthPassword.GetOriginal()(srp, password);
77 | }
78 |
79 | SRP_RESULT hkGenPub(SRP* srp, cstr** result)
80 | {
81 | SRP_RESULT retVal = detGenPub.GetOriginal()(srp, result);
82 |
83 | logger->info("SRP_gen_pub:");
84 | logger->info(" pubkey = {}", Util::DataToHex((*result)->data, (*result)->length));
85 |
86 | // Grab the generated private key too
87 | unsigned char privKey[512];
88 | int privLength = pBigIntegerToBytes(srp->secret, privKey, sizeof(privKey));
89 | if (privLength != 0)
90 | {
91 | logger->info(" privkey = {}", Util::DataToHex((const char*)privKey, privLength));
92 | }
93 | else
94 | {
95 | logger->error(" Failed to retrieve private key");
96 | }
97 |
98 | return retVal;
99 | }
100 |
101 | SRP_RESULT hkComputeKey(SRP* srp, cstr** result, const unsigned char* pubkey, int pubkeylen)
102 | {
103 | SRP_RESULT retVal = detComputeKey.GetOriginal()(srp, result, pubkey, pubkeylen);
104 | logger->info("SRP_compute_key:");
105 | logger->info(" result = {}", Util::DataToHex((*result)->data, (*result)->length));
106 | logger->info(" pubkey = {}", Util::DataToHex((const char*)pubkey, pubkeylen));
107 | return retVal;
108 | }
109 |
110 | SRP_RESULT hkRespond(SRP* srp, cstr** proof)
111 | {
112 | SRP_RESULT retVal = detRespond.GetOriginal()(srp, proof);
113 | logger->info("SRP_respond: proof = {}", Util::DataToHex((*proof)->data, (*proof)->length));
114 | return retVal;
115 | }
116 |
117 | void Initialise()
118 | {
119 | logger = spdlog::get("logger");
120 | logger->info("Hooking SRP functions");
121 |
122 | Util::HookLibraryFunction(detSetUsername, "srp32.dll", "SRP_set_username", &hkSRPSetUsername);
123 | Util::HookLibraryFunction(detSetParams, "srp32.dll", "SRP_set_params", &hkSRPSetParams);
124 | Util::HookLibraryFunction(detSetAuthPassword, "srp32.dll", "SRP_set_auth_password", &hkSetAuthPassword);
125 | Util::HookLibraryFunction(detGenPub, "srp32.dll", "SRP_gen_pub", &hkGenPub);
126 | Util::HookLibraryFunction(detComputeKey, "srp32.dll", "SRP_compute_key", &hkComputeKey);
127 | Util::HookLibraryFunction(detRespond, "srp32.dll", "SRP_respond", &hkRespond);
128 |
129 | pBigIntegerToBytes = (tBigIntegerToBytes)Util::ResolveLibraryFunction("srp32.dll", "BigIntegerToBytes");
130 |
131 | logger->info("SRP functions hooked");
132 | }
133 |
134 | void Shutdown()
135 | {
136 | logger->info("Unhooking SRP functions");
137 |
138 | detSetUsername.UnHook();
139 | detSetParams.UnHook();
140 | detSetAuthPassword.UnHook();
141 | detGenPub.UnHook();
142 | detComputeKey.UnHook();
143 | detRespond.UnHook();
144 |
145 | logger->info("SRP functions unhooked");
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/AntiDebug.cpp:
--------------------------------------------------------------------------------
1 | #include "AntiDebug.h"
2 | #include "Util.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
9 | #define STATUS_PORT_NOT_SET ((NTSTATUS)0xC0000353L)
10 |
11 | namespace AntiDebug
12 | {
13 | PLH::Detour detNtSIT;
14 | PLH::Detour detNtQIP;
15 |
16 | std::shared_ptr logger;
17 |
18 | NTSTATUS NTAPI hkNtSetInformationThread(
19 | __in HANDLE ThreadHandle,
20 | __in THREAD_INFORMATION_CLASS ThreadInformationClass,
21 | __in PVOID ThreadInformation,
22 | __in ULONG ThreadInformationLength)
23 | {
24 | // TODO: Check handle is valid and is for a valid thread in this process
25 | // TODO: Maybe need to hook NtQueryInformationThread in case the process actually checks that ThreadHideFromDebugger is set
26 |
27 | if (ThreadInformationClass == 17 && ThreadInformation == nullptr && ThreadInformationLength == 0) // ThreadHideFromDebugger
28 | {
29 | logger->info("NtSetInformationThread called with ThreadHideFromDebugger (Thread ID = %d)\n", GetCurrentThreadId());
30 | return STATUS_SUCCESS;
31 | }
32 |
33 | return detNtSIT.GetOriginal()(ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength);
34 | }
35 |
36 | NTSTATUS WINAPI hkNtQueryInformationProcess(
37 | __in HANDLE ProcessHandle,
38 | __in PROCESSINFOCLASS ProcessInformationClass,
39 | __out PVOID ProcessInformation,
40 | __in ULONG ProcessInformationLength,
41 | __out_opt PULONG ReturnLength)
42 | {
43 | NTSTATUS result = detNtQIP.GetOriginal()(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength);
44 |
45 | if (!NT_SUCCESS(result) || ProcessInformation == nullptr || ProcessInformationLength == 0)
46 | {
47 | return result;
48 | }
49 |
50 | if (ProcessInformationClass == 30) // ProcessDebugObjectHandle
51 | {
52 | *((HANDLE*)ProcessInformation) = 0;
53 | return STATUS_PORT_NOT_SET;
54 | }
55 | else if (ProcessInformationClass == 31) // ProcessDebugFlags
56 | {
57 | *((ULONG*)ProcessInformation) = 1;
58 | }
59 | else if (ProcessInformationClass == ProcessDebugPort)
60 | {
61 | *((HANDLE*)ProcessInformation) = 0;
62 | }
63 |
64 | return result;
65 | }
66 |
67 | void DisableAntiDebug()
68 | {
69 | logger = spdlog::get("logger");
70 |
71 | Util::HookLibraryFunction(detNtSIT, "ntdll.dll", "NtSetInformationThread", &hkNtSetInformationThread);
72 | Util::HookLibraryFunction(detNtQIP, "ntdll.dll", "NtQueryInformationProcess", &hkNtQueryInformationProcess);
73 |
74 | logger->info("Anti-debug APIs hooked successfully");
75 | }
76 |
77 | void Cleanup()
78 | {
79 | logger->info("Unhooking Anti-debug APIs");
80 |
81 | detNtSIT.UnHook();
82 | detNtQIP.UnHook();
83 |
84 | logger->info("Anti-debug APIs unhooked successfully");
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/AntiDebug.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace AntiDebug
4 | {
5 | void DisableAntiDebug();
6 | void Cleanup();
7 | }
8 |
--------------------------------------------------------------------------------
/src/MemorySignature.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | struct MemorySignature
4 | {
5 | const char* Sig;
6 | const char* Mask;
7 | int Length;
8 | };
9 |
--------------------------------------------------------------------------------
/src/ModuleScan.cpp:
--------------------------------------------------------------------------------
1 | #include "ModuleScan.h"
2 | #include "spdlog/spdlog.h"
3 | #include "Util.h"
4 |
5 | // Based off CSigScan from https://wiki.alliedmods.net/Signature_Scanning
6 |
7 | ModuleScan::ModuleScan(const std::string& moduleName)
8 | {
9 | m_moduleHandle = GetModuleHandle(Util::Widen(moduleName).c_str());
10 | if (m_moduleHandle == nullptr)
11 | {
12 | throw SigScanException(fmt::sprintf("GetModuleHandle returned NULL (Win32 Error = %d)", GetLastError()));
13 | }
14 |
15 | MEMORY_BASIC_INFORMATION mem;
16 |
17 | if (!VirtualQuery(m_moduleHandle, &mem, sizeof(mem)))
18 | {
19 | throw SigScanException(fmt::sprintf("VirtualQuery returned NULL (Win32 Error = %d)", GetLastError()));
20 | }
21 |
22 | m_moduleBase = (char*)mem.AllocationBase;
23 | if (m_moduleBase == nullptr)
24 | {
25 | throw SigScanException("mem.AllocationBase was NULL");
26 | }
27 |
28 | IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)mem.AllocationBase;
29 | IMAGE_NT_HEADERS* pe = (IMAGE_NT_HEADERS*)((unsigned long)dos + (unsigned long)dos->e_lfanew);
30 |
31 | if (pe->Signature != IMAGE_NT_SIGNATURE)
32 | {
33 | throw SigScanException("PE signature is not a valid NT signature");
34 | }
35 |
36 | m_moduleLen = pe->OptionalHeader.SizeOfImage;
37 | }
38 |
39 | void* ModuleScan::Scan(const MemorySignature& sigStruct)
40 | {
41 | return Scan(sigStruct.Sig, sigStruct.Mask, sigStruct.Length);
42 | }
43 |
44 | void* ModuleScan::Scan(const char* sig, const char* mask)
45 | {
46 | int sigLength = strlen(mask);
47 | return Scan(sig, mask, sigLength);
48 | }
49 |
50 | void* ModuleScan::Scan(const char* sig, const char* mask, int sigLength)
51 | {
52 | char* pData = m_moduleBase;
53 | char* pEnd = m_moduleBase + m_moduleLen;
54 |
55 | while (pData < pEnd)
56 | {
57 | int i;
58 | for (i = 0; i < sigLength; i++)
59 | {
60 | if (mask[i] != '?' && sig[i] != pData[i])
61 | break;
62 | }
63 |
64 | // The for loop finished on its own accord
65 | if (i == sigLength)
66 | {
67 | return (void*)pData;
68 | }
69 |
70 | pData++;
71 | }
72 |
73 | throw SigScanException(fmt::sprintf("Signature could not be resolved - %s", Util::DataToHex(sig, sigLength).c_str()));
74 | }
75 |
--------------------------------------------------------------------------------
/src/ModuleScan.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define WIN32_LEAN_AND_MEAN
4 | #include
5 |
6 | #include
7 |
8 | #include "MemorySignature.h"
9 |
10 | class SigScanException : public std::runtime_error
11 | {
12 | public:
13 | SigScanException(const std::string& errorStr)
14 | : std::runtime_error(errorStr) {}
15 | };
16 |
17 | class ModuleScan
18 | {
19 | private:
20 | char* m_moduleBase;
21 | size_t m_moduleLen;
22 | HMODULE m_moduleHandle;
23 |
24 | public:
25 | ModuleScan(const std::string& moduleName);
26 |
27 | void* Scan(const MemorySignature& sigStruct);
28 | void* Scan(const char* sig, const char* mask, int sigLength);
29 | void* Scan(const char* sig, const char* mask);
30 | };
31 |
--------------------------------------------------------------------------------
/src/Util.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "Util.h"
4 | #include
5 | #include
6 |
7 | namespace Util
8 | {
9 | // Taken from https://stackoverflow.com/a/18374698
10 | std::wstring Widen(const std::string& input)
11 | {
12 | using convert_typeX = std::codecvt_utf8;
13 | std::wstring_convert converterX;
14 | return converterX.from_bytes(input);
15 | }
16 |
17 | // Taken from https://stackoverflow.com/a/18374698
18 | std::string Narrow(const std::wstring& input)
19 | {
20 | using convert_typeX = std::codecvt_utf8;
21 | std::wstring_convert converterX;
22 | return converterX.to_bytes(input);
23 | }
24 |
25 | // This will convert some data like "Hello World" to "48 65 6C 6C 6F 20 57 6F 72 6C 64"
26 | // Taken mostly from https://stackoverflow.com/a/3382894
27 | std::string DataToHex(const char* input, size_t len)
28 | {
29 | static const char* const lut = "0123456789ABCDEF";
30 |
31 | std::string output;
32 | output.reserve(2 * len);
33 | for (size_t i = 0; i < len; i++)
34 | {
35 | const unsigned char c = input[i];
36 | output.push_back(lut[c >> 4]);
37 | output.push_back(lut[c & 15]);
38 | }
39 |
40 | return output;
41 | }
42 |
43 | void HookLibraryFunction(PLH::Detour& detour, const std::string& module, const std::string& funcName, void* hookFunc)
44 | {
45 | void* funcAddr = ResolveLibraryFunction(module, funcName);
46 |
47 | detour.SetupHook((BYTE*)funcAddr, (BYTE*)hookFunc);
48 |
49 | if (!detour.Hook())
50 | {
51 | PLH::RuntimeError err = detour.GetLastError();
52 | throw std::runtime_error(fmt::sprintf("Hook failed for %s: %s", funcName, err.GetString()));
53 | }
54 | }
55 |
56 | void* HookSignatureFunction(PLH::Detour& detour, ModuleScan& scanner, const char* sig, const char* mask, void* hookFunc)
57 | {
58 | int sigLen = strlen(mask);
59 | void* sigAddr = scanner.Scan(sig, mask, sigLen);
60 |
61 | detour.SetupHook((BYTE*)sigAddr, (BYTE*)hookFunc);
62 |
63 | if (!detour.Hook())
64 | {
65 | PLH::RuntimeError err = detour.GetLastError();
66 | throw std::runtime_error(fmt::sprintf("Hook failed for signature %s: %s", Util::DataToHex(sig, sigLen), err.GetString()));
67 | }
68 |
69 | return sigAddr;
70 | }
71 |
72 | void* ResolveLibraryFunction(const std::string& module, const std::string& funcName)
73 | {
74 | HMODULE hModule = GetModuleHandle(Util::Widen(module).c_str());
75 | if (!hModule)
76 | {
77 | throw std::runtime_error(fmt::sprintf("GetModuleHandle failed for %s (Error = 0x%X)", module, GetLastError()));
78 | }
79 |
80 | FARPROC funcAddr = GetProcAddress(hModule, funcName.c_str());
81 | if (!funcAddr)
82 | {
83 | throw std::runtime_error(fmt::sprintf("GetProcAddress failed for %s (Error = 0x%X)", funcName, GetLastError()));
84 | }
85 |
86 | return funcAddr;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/Util.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include "ModuleScan.h"
6 |
7 | namespace Util
8 | {
9 | std::wstring Widen(const std::string& input);
10 | std::string Narrow(const std::wstring& input);
11 | std::string DataToHex(const char* input, size_t len);
12 | void HookLibraryFunction(PLH::Detour& detour, const std::string& module, const std::string& funcName, void* hookFunc);
13 | void* HookSignatureFunction(PLH::Detour& detour, ModuleScan& scanner, const char* sig, const char* mask, void* hookFunc);
14 | void* ResolveLibraryFunction(const std::string& module, const std::string& funcName);
15 | }
16 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "AntiDebug.h"
5 | #include "APB/APB.h"
6 |
7 | void Initialise()
8 | {
9 | AntiDebug::DisableAntiDebug();
10 | SRPHook::Initialise();
11 | CryptHook::Initialise();
12 | NetworkHook::Initialise();
13 | CompressionHook::Initialise();
14 | }
15 |
16 | void Shutdown()
17 | {
18 | AntiDebug::Cleanup();
19 | SRPHook::Shutdown();
20 | CryptHook::Shutdown();
21 | NetworkHook::Shutdown();
22 | CompressionHook::Shutdown();
23 | }
24 |
25 | void PreInitialise()
26 | {
27 | // Create console window and redirect stdout
28 | AllocConsole();
29 | FILE* stream;
30 | freopen_s(&stream, "CONOUT$", "w", stdout);
31 |
32 | // Create sinks to file and console
33 | std::vector sinks;
34 | sinks.push_back(std::make_shared());
35 |
36 | // The file sink could fail so capture the error if so
37 | std::unique_ptr fileError;
38 | try
39 | {
40 | sinks.push_back(std::make_shared("DebugLog.log", true));
41 | }
42 | catch (spdlog::spdlog_ex& ex)
43 | {
44 | fileError = std::make_unique(ex.what());
45 | }
46 |
47 | // Create logger from sink
48 | auto logger = std::make_shared("logger", begin(sinks), end(sinks));
49 | logger->set_pattern("[%T] [%l] [thread %t] %v");
50 | spdlog::register_logger(logger);
51 |
52 | if (fileError)
53 | {
54 | logger->warn("Failed to initialise file sink, log file will be unavailable ({})", *fileError);
55 | }
56 | }
57 |
58 | DWORD WINAPI OnAttach(LPVOID lpThreadParameter)
59 | {
60 | PreInitialise();
61 |
62 | auto logger = spdlog::get("logger");
63 |
64 | try
65 | {
66 | Initialise();
67 | logger->info("Initialisation complete");
68 | }
69 | catch (std::exception& ex)
70 | {
71 | logger->error("Failed to initialise DLL ({})", ex.what());
72 | }
73 |
74 | // Wait for key press to unload DLL
75 | while (true)
76 | {
77 | if (GetAsyncKeyState(VK_F9))
78 | {
79 | logger->info("Unloading DLL");
80 | break;
81 | }
82 | Sleep(100);
83 | }
84 |
85 | Shutdown();
86 | FreeLibraryAndExitThread((HMODULE)lpThreadParameter, 0);
87 |
88 | return 0;
89 | }
90 |
91 | BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
92 | {
93 | switch (dwReason)
94 | {
95 | case DLL_PROCESS_ATTACH:
96 | DisableThreadLibraryCalls(hModule);
97 | CreateThread(NULL, 0, OnAttach, hModule, 0, NULL);
98 | break;
99 | case DLL_PROCESS_DETACH:
100 | break;
101 | }
102 |
103 | return TRUE;
104 | }
105 |
--------------------------------------------------------------------------------