├── .gitattributes ├── .gitignore ├── Common ├── directory.cpp ├── directory.h ├── encoding.cpp ├── encoding.h ├── file.cpp ├── file.h ├── log.cpp ├── log.h ├── path.cpp ├── path.h ├── pe.cpp ├── pe.h ├── stringhelper.cpp ├── stringhelper.h ├── util.cpp └── util.h ├── CxdecExtractor ├── Application.cpp ├── Application.h ├── CxdecExtractor.vcxproj ├── CxdecExtractor.vcxproj.filters ├── ExtendUtils.h ├── ExtractCore.cpp ├── ExtractCore.h ├── dllmain.cpp └── export.def ├── CxdecExtractorLoader ├── AmetoYuki_1.ico ├── CxdecExtractorLoader.cpp ├── CxdecExtractorLoader.rc ├── CxdecExtractorLoader.vcxproj ├── CxdecExtractorLoader.vcxproj.filters └── resource.h ├── CxdecExtractorUI ├── CxdecExtractorUI.rc ├── CxdecExtractorUI.vcxproj ├── CxdecExtractorUI.vcxproj.filters ├── dllmain.cpp └── resource.h ├── CxdecStringDumper ├── Application.cpp ├── Application.h ├── CxdecStringDumper.vcxproj ├── CxdecStringDumper.vcxproj.filters ├── ExtendUtils.h ├── HashCore.cpp ├── HashCore.h └── dllmain.cpp ├── Detours ├── creatwth.cpp ├── detours.cpp ├── detours.h ├── disasm.cpp ├── image.cpp ├── modules.cpp └── uimports.cpp ├── KrkrPlugin ├── tp_stub.cpp └── tp_stub.h ├── KrkrZCxdecV2.sln ├── LICENSE └── README.md /.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 | -------------------------------------------------------------------------------- /.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 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /Common/directory.cpp: -------------------------------------------------------------------------------- 1 |  2 | #include 3 | #include "directory.h" 4 | #include "path.h" 5 | 6 | namespace Directory 7 | { 8 | bool Exists(const std::string& dirPath) 9 | { 10 | DWORD fileAttr = GetFileAttributesA(dirPath.c_str()); 11 | if (fileAttr == INVALID_FILE_ATTRIBUTES || (fileAttr & FILE_ATTRIBUTE_DIRECTORY) == 0) 12 | { 13 | return false; 14 | } 15 | return true; 16 | } 17 | 18 | bool Exists(const std::wstring& dirPath) 19 | { 20 | DWORD fileAttr = GetFileAttributesW(dirPath.c_str()); 21 | if (fileAttr == INVALID_FILE_ATTRIBUTES || (fileAttr & FILE_ATTRIBUTE_DIRECTORY) == 0) 22 | { 23 | return false; 24 | } 25 | return true; 26 | } 27 | 28 | void Create(const std::string& dirPath) 29 | { 30 | if (!Directory::Exists(dirPath)) 31 | { 32 | if (!CreateDirectoryA(dirPath.c_str(), NULL)) 33 | { 34 | Directory::Create(Path::GetDirectoryName(dirPath)); 35 | CreateDirectoryA(dirPath.c_str(), NULL); 36 | } 37 | } 38 | } 39 | 40 | 41 | void Create(const std::wstring& dirPath) 42 | { 43 | if (!Directory::Exists(dirPath)) 44 | { 45 | if (!CreateDirectoryW(dirPath.c_str(), NULL)) 46 | { 47 | Directory::Create(Path::GetDirectoryName(dirPath)); 48 | CreateDirectoryW(dirPath.c_str(), NULL); 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Common/directory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | namespace Directory 5 | { 6 | bool Exists(const std::string& dirPath); 7 | bool Exists(const std::wstring& dirPath); 8 | void Create(const std::string& dirPath); 9 | void Create(const std::wstring& dirPath); 10 | } 11 | -------------------------------------------------------------------------------- /Common/encoding.cpp: -------------------------------------------------------------------------------- 1 | // encoding.cpp 2 | 3 | #include 4 | #include "encoding.h" 5 | 6 | #undef max 7 | 8 | namespace Encoding 9 | { 10 | std::wstring AnsiToUnicode(const std::string& source, int codePage) 11 | { 12 | if (source.length() == 0) 13 | { 14 | return std::wstring(); 15 | } 16 | 17 | if (source.length() > (size_t)std::numeric_limits::max()) 18 | { 19 | return std::wstring(); 20 | } 21 | 22 | int length = MultiByteToWideChar(codePage, 0, source.c_str(), (int)source.length(), NULL, 0); 23 | 24 | if (length <= 0) 25 | { 26 | return std::wstring(); 27 | } 28 | 29 | std::wstring output(length, L'\0'); 30 | 31 | if (MultiByteToWideChar(codePage, 0, source.c_str(), (int)source.length(), (LPWSTR)output.data(), (int)output.length() + 1) == 0) 32 | { 33 | return std::wstring(); 34 | } 35 | 36 | return output; 37 | } 38 | 39 | std::string UnicodeToAnsi(const std::wstring& source, int codePage) 40 | { 41 | if (source.length() == 0) 42 | { 43 | return std::string(); 44 | } 45 | 46 | if (source.length() > (size_t)std::numeric_limits::max()) 47 | { 48 | return std::string(); 49 | } 50 | 51 | int length = WideCharToMultiByte(codePage, 0, source.c_str(), (int)source.length(), NULL, 0, NULL, NULL); 52 | 53 | if (length <= 0) 54 | { 55 | return std::string(); 56 | } 57 | 58 | std::string output(length, '\0'); 59 | 60 | if (WideCharToMultiByte(codePage, 0, source.c_str(), (int)source.length(), (LPSTR)output.data(), (int)output.length() + 1, NULL, NULL) == 0) 61 | { 62 | return std::string(); 63 | } 64 | 65 | return output; 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /Common/encoding.h: -------------------------------------------------------------------------------- 1 | // encoding.h 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | namespace Encoding 8 | { 9 | enum CodePage 10 | { 11 | ACP = 0, 12 | UTF_8 = 65001, 13 | SHIFT_JIS = 932, 14 | GBK = 936, 15 | }; 16 | 17 | std::wstring AnsiToUnicode(const std::string& source, int codePage); 18 | std::string UnicodeToAnsi(const std::wstring& source, int codePage); 19 | } 20 | -------------------------------------------------------------------------------- /Common/file.cpp: -------------------------------------------------------------------------------- 1 | // file.cpp 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "file.h" 8 | 9 | namespace File 10 | { 11 | std::string ReadAllText(const std::string& path) 12 | { 13 | FILE* fp; 14 | long long size; 15 | size_t length; 16 | unsigned char buf[3]; 17 | bool utf8bom; 18 | std::string output; 19 | 20 | if (fopen_s(&fp, path.c_str(), "rb") != 0) 21 | { 22 | goto error; 23 | } 24 | 25 | if (_fseeki64(fp, 0, SEEK_END) != 0) 26 | { 27 | goto error; 28 | } 29 | 30 | size = _ftelli64(fp); 31 | 32 | if (size <= 0) 33 | { 34 | goto error; 35 | } 36 | 37 | if (static_cast(size) > std::numeric_limits::max()) 38 | { 39 | goto error; 40 | } 41 | 42 | if (_fseeki64(fp, 0, SEEK_SET) != 0) 43 | { 44 | goto error; 45 | } 46 | 47 | length = static_cast(size); 48 | 49 | // Check UTF-8 BOM 50 | 51 | utf8bom = false; 52 | 53 | if (fread(buf, 3, 1, fp) == 1) 54 | { 55 | if (buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) 56 | { 57 | utf8bom = true; 58 | } 59 | } 60 | 61 | if (utf8bom) 62 | { 63 | length -= 3; 64 | } 65 | else 66 | { 67 | if (_fseeki64(fp, 0, SEEK_SET) != 0) 68 | { 69 | goto error; 70 | } 71 | } 72 | 73 | if (length == 0) 74 | { 75 | goto error; 76 | } 77 | 78 | output.resize(length); 79 | 80 | if (fread(output.data(), length, 1, fp) != 1) 81 | { 82 | goto error; 83 | } 84 | 85 | fclose(fp); 86 | 87 | return output; 88 | 89 | error: 90 | if (fp) 91 | { 92 | fclose(fp); 93 | } 94 | 95 | return std::string(); 96 | } 97 | 98 | std::string ReadAllText(const std::wstring& path) 99 | { 100 | FILE* fp; 101 | long long size; 102 | size_t length; 103 | unsigned char buf[3]; 104 | bool utf8bom; 105 | std::string output; 106 | 107 | if (_wfopen_s(&fp, path.c_str(), L"rb") != 0) 108 | { 109 | goto error; 110 | } 111 | 112 | if (fp == nullptr) 113 | { 114 | goto error; 115 | } 116 | 117 | if (_fseeki64(fp, 0, SEEK_END) != 0) 118 | { 119 | goto error; 120 | } 121 | 122 | size = _ftelli64(fp); 123 | 124 | if (size <= 0) 125 | { 126 | goto error; 127 | } 128 | 129 | if (static_cast(size) > std::numeric_limits::max()) 130 | { 131 | goto error; 132 | } 133 | 134 | if (_fseeki64(fp, 0, SEEK_SET) != 0) 135 | { 136 | goto error; 137 | } 138 | 139 | length = static_cast(size); 140 | 141 | // Check UTF-8 BOM 142 | 143 | utf8bom = false; 144 | 145 | if (fread(buf, 3, 1, fp) == 1) 146 | { 147 | if (buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) 148 | { 149 | utf8bom = true; 150 | } 151 | } 152 | 153 | if (utf8bom) 154 | { 155 | length -= 3; 156 | } 157 | else 158 | { 159 | if (_fseeki64(fp, 0, SEEK_SET) != 0) 160 | { 161 | goto error; 162 | } 163 | } 164 | 165 | if (length == 0) 166 | { 167 | goto error; 168 | } 169 | 170 | output.resize(length); 171 | 172 | if (fread(output.data(), length, 1, fp) != 1) 173 | { 174 | goto error; 175 | } 176 | 177 | fclose(fp); 178 | 179 | return output; 180 | 181 | error: 182 | if (fp) 183 | { 184 | fclose(fp); 185 | } 186 | 187 | return std::string(); 188 | } 189 | 190 | bool WriteAllBytes(const std::string& path, const void* buffer, size_t size) 191 | { 192 | FILE* fp; 193 | 194 | if (fopen_s(&fp, path.c_str(), "wb") != 0) 195 | { 196 | goto error; 197 | } 198 | 199 | if (buffer == nullptr) 200 | { 201 | goto error; 202 | } 203 | 204 | if (size == 0) 205 | { 206 | goto error; 207 | } 208 | 209 | if (fwrite(buffer, size, 1, fp) != 1) 210 | { 211 | goto error; 212 | } 213 | 214 | fflush(fp); 215 | 216 | fclose(fp); 217 | 218 | return true; 219 | 220 | error: 221 | if (fp) 222 | { 223 | fclose(fp); 224 | } 225 | 226 | return false; 227 | } 228 | 229 | bool WriteAllBytes(const std::wstring& path, const void* buffer, size_t size) 230 | { 231 | FILE* fp; 232 | 233 | if (_wfopen_s(&fp, path.c_str(), L"wb") != 0) 234 | { 235 | goto error; 236 | } 237 | 238 | if (fp == nullptr) 239 | { 240 | goto error; 241 | } 242 | 243 | if (buffer == nullptr) 244 | { 245 | goto error; 246 | } 247 | 248 | if (size == 0) 249 | { 250 | goto error; 251 | } 252 | 253 | if (fwrite(buffer, size, 1, fp) != 1) 254 | { 255 | goto error; 256 | } 257 | 258 | fflush(fp); 259 | 260 | fclose(fp); 261 | 262 | return true; 263 | 264 | error: 265 | if (fp) 266 | { 267 | fclose(fp); 268 | } 269 | 270 | return false; 271 | } 272 | 273 | void Delete(const std::string& path) 274 | { 275 | remove(path.c_str()); 276 | } 277 | 278 | void Delete(const std::wstring& path) 279 | { 280 | _wremove(path.c_str()); 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /Common/file.h: -------------------------------------------------------------------------------- 1 | // file.h 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | namespace File 8 | { 9 | std::string ReadAllText(const std::string& path); 10 | std::string ReadAllText(const std::wstring& path); 11 | bool WriteAllBytes(const std::string& path, const void* buffer, size_t size); 12 | bool WriteAllBytes(const std::wstring& path, const void* buffer, size_t size); 13 | void Delete(const std::string& path); 14 | void Delete(const std::wstring& path); 15 | } 16 | -------------------------------------------------------------------------------- /Common/log.cpp: -------------------------------------------------------------------------------- 1 | // log.cpp 2 | 3 | #include 4 | #include 5 | #include "log.h" 6 | #include "stringhelper.h" 7 | #include "encoding.h" 8 | 9 | 10 | namespace Log 11 | { 12 | Logger::Logger() : m_pOutput{} 13 | { 14 | } 15 | 16 | Logger::Logger(const wchar_t* lpFileName) 17 | : m_pOutput{} 18 | { 19 | Open(lpFileName); 20 | } 21 | 22 | Logger::~Logger() 23 | { 24 | Close(); 25 | } 26 | 27 | void Logger::Open(const wchar_t* lpFileName) 28 | { 29 | m_pOutput = _wfsopen(lpFileName, L"ab", _SH_DENYWR); 30 | } 31 | 32 | void Logger::Close() 33 | { 34 | Flush(); 35 | 36 | if (m_pOutput) 37 | { 38 | fclose(m_pOutput); 39 | m_pOutput = nullptr; 40 | } 41 | } 42 | 43 | void Logger::Flush() 44 | { 45 | if (m_pOutput) 46 | { 47 | fflush(m_pOutput); 48 | } 49 | } 50 | 51 | static std::string GetTimeString() 52 | { 53 | time_t tv; 54 | struct tm tm; 55 | char buf[32]; 56 | 57 | time(&tv); 58 | localtime_s(&tm, &tv); 59 | strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm); 60 | 61 | return std::string(buf); 62 | } 63 | 64 | void Logger::WriteAnsi(int iCodePage, const char* lpFormat, ...) 65 | { 66 | if (!m_pOutput) 67 | { 68 | return; 69 | } 70 | 71 | va_list ap; 72 | 73 | va_start(ap, lpFormat); 74 | auto content = StringHelper::VFormat(lpFormat, ap); 75 | va_end(ap); 76 | 77 | auto unicode = Encoding::AnsiToUnicode(content, iCodePage); 78 | auto output = Encoding::UnicodeToAnsi(unicode, Encoding::CodePage::UTF_8); 79 | 80 | fwrite(output.data(), output.length(), 1, m_pOutput); 81 | fflush(m_pOutput); 82 | } 83 | 84 | void Logger::WriteLineAnsi(int iCodePage, const char* lpFormat, ...) 85 | { 86 | if (!m_pOutput) 87 | { 88 | return; 89 | } 90 | 91 | va_list ap; 92 | 93 | va_start(ap, lpFormat); 94 | auto content = StringHelper::VFormat(lpFormat, ap); 95 | va_end(ap); 96 | 97 | auto unicode = Encoding::AnsiToUnicode(content, iCodePage); 98 | auto utf = Encoding::UnicodeToAnsi(unicode, Encoding::CodePage::UTF_8); 99 | auto timestamp = GetTimeString(); 100 | 101 | auto output = timestamp + " | " + utf + "\r\n"; 102 | 103 | fwrite(output.data(), output.length(), 1, m_pOutput); 104 | fflush(m_pOutput); 105 | } 106 | 107 | void Logger::Write(const wchar_t* lpFormat, ...) 108 | { 109 | if (!m_pOutput) 110 | { 111 | return; 112 | } 113 | 114 | va_list ap; 115 | 116 | va_start(ap, lpFormat); 117 | auto content = StringHelper::VFormat(lpFormat, ap); 118 | va_end(ap); 119 | 120 | auto output = Encoding::UnicodeToAnsi(content, Encoding::CodePage::UTF_8); 121 | 122 | fwrite(output.data(), output.length(), 1, m_pOutput); 123 | fflush(m_pOutput); 124 | } 125 | 126 | void Logger::WriteLine(const wchar_t* lpFormat, ...) 127 | { 128 | if (!m_pOutput) 129 | { 130 | return; 131 | } 132 | 133 | va_list ap; 134 | 135 | va_start(ap, lpFormat); 136 | auto content = StringHelper::VFormat(lpFormat, ap); 137 | va_end(ap); 138 | 139 | auto utf = Encoding::UnicodeToAnsi(content, Encoding::CodePage::UTF_8); 140 | auto timestamp = GetTimeString(); 141 | 142 | auto output = timestamp + " | " + utf + "\r\n"; 143 | 144 | fwrite(output.data(), output.length(), 1, m_pOutput); 145 | fflush(m_pOutput); 146 | } 147 | 148 | 149 | void Logger::WriteUnicode(const wchar_t* lpFormat, ...) 150 | { 151 | if (!m_pOutput) 152 | { 153 | return; 154 | } 155 | 156 | va_list ap; 157 | 158 | va_start(ap, lpFormat); 159 | auto content = StringHelper::VFormat(lpFormat, ap); 160 | va_end(ap); 161 | 162 | fwrite(content.data(), content.length() * 2, 1, m_pOutput); 163 | fflush(m_pOutput); 164 | } 165 | 166 | 167 | void Logger::WriteData(void* data, unsigned int size) 168 | { 169 | if (!m_pOutput) 170 | { 171 | return; 172 | } 173 | 174 | fwrite(data, size, 1, m_pOutput); 175 | fflush(m_pOutput); 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /Common/log.h: -------------------------------------------------------------------------------- 1 | // log.h 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | namespace Log 8 | { 9 | class Logger 10 | { 11 | public: 12 | Logger(); 13 | Logger(const wchar_t* lpFileName); 14 | ~Logger(); 15 | 16 | Logger(const Logger&) = delete; 17 | Logger& operator=(const Logger&) = delete; 18 | 19 | void Open(const wchar_t* lpFileName); 20 | void Close(); 21 | void Flush(); 22 | 23 | void WriteAnsi(int iCodePage, const char* lpFormat, ...); 24 | void WriteLineAnsi(int iCodePage, const char* lpFormat, ...); 25 | void Write(const wchar_t* lpFormat, ...); 26 | void WriteLine(const wchar_t* lpFormat, ...); 27 | void WriteUnicode(const wchar_t* lpFormat, ...); 28 | void WriteData(void* data, unsigned int size); 29 | 30 | 31 | private: 32 | FILE* m_pOutput; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /Common/path.cpp: -------------------------------------------------------------------------------- 1 | // path.cpp 2 | 3 | #include 4 | #include 5 | #include "path.h" 6 | 7 | namespace Path 8 | { 9 | std::string GetFileName(const std::string& path) 10 | { 11 | size_t pos; 12 | 13 | pos = path.find_last_of('\\'); 14 | 15 | if (pos != std::string::npos) 16 | { 17 | return path.substr(pos + 1); 18 | } 19 | 20 | pos = path.find_last_of('/'); 21 | 22 | if (pos != std::string::npos) 23 | { 24 | return path.substr(pos + 1); 25 | } 26 | 27 | return path; 28 | } 29 | 30 | std::wstring GetFileName(const std::wstring& path) 31 | { 32 | size_t pos; 33 | 34 | pos = path.find_last_of(L'\\'); 35 | 36 | if (pos != std::wstring::npos) 37 | { 38 | return path.substr(pos + 1); 39 | } 40 | 41 | pos = path.find_last_of(L'/'); 42 | 43 | if (pos != std::wstring::npos) 44 | { 45 | return path.substr(pos + 1); 46 | } 47 | 48 | return path; 49 | } 50 | 51 | std::string GetFileNameWithoutExtension(const std::string& path) 52 | { 53 | std::string name = GetFileName(path); 54 | 55 | size_t pos = name.find_last_of('.'); 56 | 57 | if (pos != std::string::npos && pos > 0) 58 | { 59 | return name.substr(0, pos); 60 | } 61 | 62 | return name; 63 | } 64 | 65 | std::wstring GetFileNameWithoutExtension(const std::wstring& path) 66 | { 67 | std::wstring name = GetFileName(path); 68 | 69 | size_t pos = name.find_last_of(L'.'); 70 | 71 | if (pos != std::wstring::npos && pos > 0) 72 | { 73 | return name.substr(0, pos); 74 | } 75 | 76 | return name; 77 | } 78 | 79 | std::string GetDirectoryName(const std::string& path) 80 | { 81 | size_t pos; 82 | 83 | pos = path.find_last_of('\\'); 84 | 85 | if (pos != std::string::npos && pos > 0) 86 | { 87 | return path.substr(0, pos); 88 | } 89 | 90 | pos = path.find_last_of('/'); 91 | 92 | if (pos != std::string::npos && pos > 0) 93 | { 94 | return path.substr(0, pos); 95 | } 96 | 97 | return std::string(); 98 | } 99 | 100 | std::wstring GetDirectoryName(const std::wstring& path) 101 | { 102 | size_t pos; 103 | 104 | pos = path.find_last_of(L'\\'); 105 | 106 | if (pos != std::wstring::npos && pos > 0) 107 | { 108 | return path.substr(0, pos); 109 | } 110 | 111 | pos = path.find_last_of(L'/'); 112 | 113 | if (pos != std::wstring::npos && pos > 0) 114 | { 115 | return path.substr(0, pos); 116 | } 117 | 118 | return std::wstring(); 119 | } 120 | 121 | std::string GetExtension(const std::string& path) 122 | { 123 | int length = static_cast(path.length()); 124 | 125 | for (int i = length - 1; i >= 0; i--) 126 | { 127 | char ch = path[i]; 128 | 129 | if (ch == '.') 130 | { 131 | if (i != length - 1) 132 | { 133 | return path.substr(i, length - i); 134 | } 135 | else 136 | { 137 | return std::string(); 138 | } 139 | } 140 | 141 | if (ch == '\\' || ch == '/') 142 | { 143 | break; 144 | } 145 | } 146 | 147 | return std::string(); 148 | } 149 | 150 | std::wstring GetExtension(const std::wstring& path) 151 | { 152 | int length = static_cast(path.length()); 153 | 154 | for (int i = length - 1; i >= 0; i--) 155 | { 156 | wchar_t ch = path[i]; 157 | 158 | if (ch == '.') 159 | { 160 | if (i != length - 1) 161 | { 162 | return path.substr(i, length - i); 163 | } 164 | else 165 | { 166 | return std::wstring(); 167 | } 168 | } 169 | 170 | if (ch == L'\\' || ch == L'/') 171 | { 172 | break; 173 | } 174 | } 175 | 176 | return std::wstring(); 177 | } 178 | 179 | std::string ChangeExtension(const std::string& path, const std::string& ext) 180 | { 181 | int length = static_cast(path.length()); 182 | 183 | if (length == 0) 184 | { 185 | return std::string(); 186 | } 187 | 188 | int subLength = static_cast(path.length()); 189 | 190 | for (int i = length - 1; i >= 0; i--) 191 | { 192 | char ch = path[i]; 193 | 194 | if (ch == '.') 195 | { 196 | subLength = i; 197 | break; 198 | } 199 | 200 | if (ch == '\\' || ch == '/') 201 | { 202 | break; 203 | } 204 | } 205 | 206 | std::string subPath = path.substr(0, subLength); 207 | 208 | if (ext.length() == 0) 209 | { 210 | return subPath; 211 | } 212 | 213 | if (ext.front() != '.') 214 | { 215 | return subPath + "." + ext; 216 | } 217 | else 218 | { 219 | return subPath + ext; 220 | } 221 | } 222 | 223 | std::wstring ChangeExtension(const std::wstring& path, const std::wstring& ext) 224 | { 225 | int length = static_cast(path.length()); 226 | 227 | if (length == 0) 228 | { 229 | return std::wstring(); 230 | } 231 | 232 | int subLength = static_cast(path.length()); 233 | 234 | for (int i = length - 1; i >= 0; i--) 235 | { 236 | wchar_t ch = path[i]; 237 | 238 | if (ch == L'.') 239 | { 240 | subLength = i; 241 | break; 242 | } 243 | 244 | if (ch == L'\\' || ch == L'/') 245 | { 246 | break; 247 | } 248 | } 249 | 250 | std::wstring subPath = path.substr(0, subLength); 251 | 252 | if (ext.length() == 0) 253 | { 254 | return subPath; 255 | } 256 | 257 | if (ext.front() != L'.') 258 | { 259 | return subPath + L'.' + ext; 260 | } 261 | else 262 | { 263 | return subPath + ext; 264 | } 265 | } 266 | 267 | std::string GetFullPath(const std::string& path) 268 | { 269 | DWORD dwBufferSize = MAX_PATH; 270 | 271 | std::string output; 272 | 273 | while (dwBufferSize < USHRT_MAX) 274 | { 275 | output.resize(dwBufferSize); 276 | 277 | DWORD nSize = GetFullPathNameA(path.c_str(), dwBufferSize, const_cast(output.data()), NULL); 278 | 279 | if (nSize == 0) 280 | { 281 | return std::string(); 282 | } 283 | 284 | if (nSize < dwBufferSize) 285 | { 286 | return output.substr(0, nSize); 287 | } 288 | else 289 | { 290 | dwBufferSize *= 2; 291 | } 292 | } 293 | 294 | return std::string(); 295 | } 296 | 297 | std::wstring GetFullPath(const std::wstring& path) 298 | { 299 | DWORD dwBufferSize = MAX_PATH; 300 | 301 | std::wstring output; 302 | 303 | while (dwBufferSize < USHRT_MAX) 304 | { 305 | output.resize(dwBufferSize); 306 | 307 | DWORD nSize = GetFullPathNameW(path.c_str(), dwBufferSize, const_cast(output.data()), NULL); 308 | 309 | if (nSize == 0) 310 | { 311 | return std::wstring(); 312 | } 313 | 314 | if (nSize < dwBufferSize) 315 | { 316 | return output.substr(0, nSize); 317 | } 318 | else 319 | { 320 | dwBufferSize *= 2; 321 | } 322 | } 323 | 324 | return std::wstring(); 325 | } 326 | 327 | 328 | std::string Combine(const std::string& dir, const std::string& fileName) 329 | { 330 | if (dir.empty()) 331 | { 332 | return fileName; 333 | } 334 | 335 | const char& lastChar = dir.back(); 336 | if (lastChar == '\\' || lastChar == '/') 337 | { 338 | return dir + fileName; 339 | } 340 | else 341 | { 342 | return dir + '\\' + fileName; 343 | } 344 | } 345 | 346 | std::wstring Combine(const std::wstring& dir, const std::wstring& fileName) 347 | { 348 | if (dir.empty()) 349 | { 350 | return fileName; 351 | } 352 | 353 | const wchar_t& lastChar = dir.back(); 354 | if (lastChar == L'\\' || lastChar == L'/') 355 | { 356 | return dir + fileName; 357 | } 358 | else 359 | { 360 | return dir + L'\\' + fileName; 361 | } 362 | } 363 | 364 | bool Exists(const std::string& filePath) 365 | { 366 | DWORD fileAttr = GetFileAttributesA(filePath.c_str()); 367 | if (fileAttr == INVALID_FILE_ATTRIBUTES || (fileAttr & FILE_ATTRIBUTE_DIRECTORY)) 368 | { 369 | return false; 370 | } 371 | return true; 372 | } 373 | 374 | bool Exists(const std::wstring& filePath) 375 | { 376 | DWORD fileAttr = GetFileAttributesW(filePath.c_str()); 377 | if (fileAttr == INVALID_FILE_ATTRIBUTES || (fileAttr & FILE_ATTRIBUTE_DIRECTORY)) 378 | { 379 | return false; 380 | } 381 | return true; 382 | } 383 | } -------------------------------------------------------------------------------- /Common/path.h: -------------------------------------------------------------------------------- 1 | // path.h 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | namespace Path 8 | { 9 | std::string GetFileName(const std::string& path); 10 | std::wstring GetFileName(const std::wstring& path); 11 | std::string GetFileNameWithoutExtension(const std::string& path); 12 | std::wstring GetFileNameWithoutExtension(const std::wstring& path); 13 | std::string GetDirectoryName(const std::string& path); 14 | std::wstring GetDirectoryName(const std::wstring& path); 15 | std::string GetExtension(const std::string& path); 16 | std::wstring GetExtension(const std::wstring& path); 17 | std::string ChangeExtension(const std::string& path, const std::string& ext); 18 | std::wstring ChangeExtension(const std::wstring& path, const std::wstring& ext); 19 | std::string GetFullPath(const std::string& path); 20 | std::wstring GetFullPath(const std::wstring& path); 21 | std::string Combine(const std::string& dir, const std::string& fileName); 22 | std::wstring Combine(const std::wstring& dir, const std::wstring& fileName); 23 | bool Exists(const std::string& filePath); 24 | bool Exists(const std::wstring& filePath); 25 | } 26 | -------------------------------------------------------------------------------- /Common/pe.cpp: -------------------------------------------------------------------------------- 1 | // pe.cpp 2 | 3 | #include "pe.h" 4 | 5 | namespace PE 6 | { 7 | PVOID GetModuleBase(HMODULE hModule) 8 | { 9 | MEMORY_BASIC_INFORMATION mem; 10 | 11 | if (!VirtualQuery(hModule, &mem, sizeof(mem))) 12 | return 0; 13 | 14 | return mem.AllocationBase; 15 | } 16 | 17 | DWORD GetModuleSize(HMODULE hModule) 18 | { 19 | return ((PIMAGE_NT_HEADERS)((ULONG_PTR)hModule + ((PIMAGE_DOS_HEADER)hModule)->e_lfanew))->OptionalHeader.SizeOfImage; 20 | } 21 | 22 | PIMAGE_SECTION_HEADER GetSectionHeader(HMODULE hModule, PCSTR lpName) 23 | { 24 | PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; 25 | 26 | if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) 27 | return NULL; 28 | 29 | PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + pDosHeader->e_lfanew); 30 | 31 | if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) 32 | return NULL; 33 | 34 | if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) 35 | return NULL; 36 | 37 | PIMAGE_SECTION_HEADER pSectionHeaders = (PIMAGE_SECTION_HEADER)((PBYTE)pNtHeader + sizeof(pNtHeader->Signature) + sizeof(pNtHeader->FileHeader) + pNtHeader->FileHeader.SizeOfOptionalHeader); 38 | 39 | for (DWORD n = 0; n < pNtHeader->FileHeader.NumberOfSections; n++) 40 | { 41 | if (strcmp((PCSTR)pSectionHeaders[n].Name, lpName) == 0) 42 | { 43 | if (pSectionHeaders[n].VirtualAddress == 0 || pSectionHeaders[n].SizeOfRawData == 0) 44 | return NULL; 45 | 46 | return &pSectionHeaders[n]; 47 | } 48 | } 49 | 50 | return NULL; 51 | } 52 | 53 | static inline PBYTE RvaAdjust(PIMAGE_DOS_HEADER pDosHeader, DWORD raddr) 54 | { 55 | if (raddr != NULL) 56 | { 57 | return ((PBYTE)pDosHeader) + raddr; 58 | } 59 | 60 | return NULL; 61 | } 62 | 63 | PVOID GetImportAddress(HMODULE hModule, LPCSTR lpModuleName, LPCSTR lpProcName) 64 | { 65 | PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; 66 | 67 | if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) 68 | return NULL; 69 | 70 | PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + pDosHeader->e_lfanew); 71 | 72 | if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) 73 | return NULL; 74 | 75 | if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) 76 | return NULL; 77 | 78 | PIMAGE_IMPORT_DESCRIPTOR iidp = (PIMAGE_IMPORT_DESCRIPTOR)RvaAdjust(pDosHeader, pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); 79 | 80 | if (iidp == NULL) 81 | return NULL; 82 | 83 | for (; iidp->OriginalFirstThunk != 0; iidp++) 84 | { 85 | LPCSTR lpszModule = (LPCSTR)RvaAdjust(pDosHeader, iidp->Name); 86 | 87 | if (lpszModule == NULL) 88 | return NULL; 89 | 90 | if (_stricmp(lpszModule, lpModuleName) != 0) 91 | continue; 92 | 93 | PIMAGE_THUNK_DATA pThunks = (PIMAGE_THUNK_DATA)RvaAdjust(pDosHeader, iidp->OriginalFirstThunk); 94 | 95 | PVOID* pAddrs = (PVOID*)RvaAdjust(pDosHeader, iidp->FirstThunk); 96 | 97 | if (pThunks == NULL) 98 | continue; 99 | 100 | for (DWORD i = 0; pThunks[i].u1.Ordinal; i++) 101 | { 102 | if (IMAGE_SNAP_BY_ORDINAL(pThunks[i].u1.Ordinal)) 103 | continue; 104 | 105 | LPCSTR lpszProc = (PCSTR)RvaAdjust(pDosHeader, (DWORD)pThunks[i].u1.AddressOfData + 2); 106 | 107 | if (lpszProc == NULL) 108 | continue; 109 | 110 | if (strcmp(lpszProc, lpProcName) == 0) 111 | return &pAddrs[i]; 112 | } 113 | } 114 | 115 | return NULL; 116 | } 117 | 118 | PVOID SearchPattern(PVOID lpStartSearch, DWORD dwSearchLen, const char* lpPattern, DWORD dwPatternLen) 119 | { 120 | ULONG_PTR dwStartAddr = (ULONG_PTR)lpStartSearch; 121 | ULONG_PTR dwEndAddr = dwStartAddr + dwSearchLen - dwPatternLen; 122 | 123 | while (dwStartAddr < dwEndAddr) 124 | { 125 | bool found = true; 126 | 127 | for (DWORD i = 0; i < dwPatternLen; i++) 128 | { 129 | char code = *(char*)(dwStartAddr + i); 130 | 131 | if (lpPattern[i] != 0x2A && lpPattern[i] != code) 132 | { 133 | found = false; 134 | break; 135 | } 136 | } 137 | 138 | if (found) 139 | return (PVOID)dwStartAddr; 140 | 141 | dwStartAddr++; 142 | } 143 | 144 | return 0; 145 | } 146 | 147 | BOOL WriteMemory(PVOID lpAddress, PVOID lpBuffer, DWORD nSize) 148 | { 149 | DWORD dwProtect; 150 | 151 | if (VirtualProtect(lpAddress, nSize, PAGE_EXECUTE_READWRITE, &dwProtect)) 152 | { 153 | memcpy(lpAddress, lpBuffer, nSize); 154 | VirtualProtect(lpAddress, nSize, dwProtect, &dwProtect); 155 | return TRUE; 156 | } 157 | 158 | return FALSE; 159 | } 160 | 161 | BOOL IATHook(HMODULE hModule, LPCSTR lpModuleName, LPCSTR lpProcName, PVOID lpNewProc, PVOID* lpOriginalProc) 162 | { 163 | PVOID lpAddress = GetImportAddress(hModule, lpModuleName, lpProcName); 164 | 165 | if (lpAddress == NULL) 166 | { 167 | return FALSE; 168 | } 169 | 170 | if (lpOriginalProc) 171 | { 172 | *lpOriginalProc = *(PVOID*)lpAddress; 173 | } 174 | 175 | return WriteValue(lpAddress, lpNewProc); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Common/pe.h: -------------------------------------------------------------------------------- 1 | // pe.h 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace PE 9 | { 10 | // Get the base address of the specified module. 11 | PVOID GetModuleBase(HMODULE hModule); 12 | 13 | // Get the size of the specified module. 14 | DWORD GetModuleSize(HMODULE hModule); 15 | 16 | // Get the section with the specified name. 17 | PIMAGE_SECTION_HEADER GetSectionHeader(HMODULE hModule, PCSTR lpName); 18 | 19 | // Get the address of the imported function in the import table. 20 | PVOID GetImportAddress(HMODULE hModule, LPCSTR lpModuleName, LPCSTR lpProcName); 21 | 22 | // Searche memory for the specified pattern. 23 | PVOID SearchPattern(PVOID lpStartSearch, DWORD dwSearchLen, const char* lpPattern, DWORD dwPatternLen); 24 | 25 | // Write data to the specified address. 26 | BOOL WriteMemory(PVOID lpAddress, PVOID lpBuffer, DWORD nSize); 27 | 28 | // Writes a scalar value to the specified address. 29 | // If you pass a pointer, the value of that pointer is written. 30 | template, bool> = true> 31 | BOOL WriteValue(PVOID lpAddress, T tValue) 32 | { 33 | return WriteMemory(lpAddress, &tValue, sizeof(T)); 34 | } 35 | 36 | // Replace imported function in the import table. 37 | BOOL IATHook(HMODULE hModule, LPCSTR lpModuleName, LPCSTR lpProcName, PVOID lpNewProc, PVOID* lpOriginalProc); 38 | } 39 | -------------------------------------------------------------------------------- /Common/stringhelper.cpp: -------------------------------------------------------------------------------- 1 | // stringhelper.cpp 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "stringhelper.h" 8 | 9 | namespace StringHelper 10 | { 11 | bool StartsWith(const char* source, const char* sub) 12 | { 13 | std::string_view vsource(source); 14 | std::string_view vsub(sub); 15 | 16 | if (vsource.length() == 0 || vsub.length() == 0 || vsource.length() < vsub.length()) 17 | { 18 | return false; 19 | } 20 | 21 | return vsource.compare(0, vsub.length(), sub) == 0; 22 | } 23 | 24 | bool StartsWith(const wchar_t* source, const wchar_t* sub) 25 | { 26 | std::wstring_view vsource(source); 27 | std::wstring_view vsub(sub); 28 | 29 | if (vsource.length() == 0 || vsub.length() == 0 || vsource.length() < vsub.length()) 30 | { 31 | return false; 32 | } 33 | 34 | return vsource.compare(0, vsub.length(), sub) == 0; 35 | } 36 | 37 | bool StartsWith(const std::string& source, const std::string& sub) 38 | { 39 | if (source.length() == 0 || sub.length() == 0 || source.length() < sub.length()) 40 | { 41 | return false; 42 | } 43 | 44 | return source.compare(0, sub.length(), sub) == 0; 45 | } 46 | 47 | bool StartsWith(const std::wstring& source, const std::wstring& sub) 48 | { 49 | if (source.length() == 0 || sub.length() == 0 || source.length() < sub.length()) 50 | { 51 | return false; 52 | } 53 | 54 | return source.compare(0, sub.length(), sub) == 0; 55 | } 56 | 57 | bool EndsWith(const char* source, const char* sub) 58 | { 59 | std::string_view vsource(source); 60 | std::string_view vsub(sub); 61 | 62 | if (vsource.length() == 0 || vsub.length() == 0 || vsource.length() < vsub.length()) 63 | { 64 | return false; 65 | } 66 | 67 | return vsource.compare(vsource.length() - vsub.length(), vsub.length(), sub) == 0; 68 | } 69 | 70 | bool EndsWith(const wchar_t* source, const wchar_t* sub) 71 | { 72 | std::wstring_view vsource(source); 73 | std::wstring_view vsub(sub); 74 | 75 | if (vsource.length() == 0 || vsub.length() == 0 || vsource.length() < vsub.length()) 76 | { 77 | return false; 78 | } 79 | 80 | return vsource.compare(vsource.length() - vsub.length(), vsub.length(), sub) == 0; 81 | } 82 | 83 | bool EndsWith(const std::string& source, const std::string& sub) 84 | { 85 | if (source.length() == 0 || sub.length() == 0 || source.length() < sub.length()) 86 | { 87 | return false; 88 | } 89 | 90 | return source.compare(source.length() - sub.length(), sub.length(), sub) == 0; 91 | } 92 | 93 | bool EndsWith(const std::wstring& source, const std::wstring& sub) 94 | { 95 | if (source.length() == 0 || sub.length() == 0 || source.length() < sub.length()) 96 | { 97 | return false; 98 | } 99 | 100 | return source.compare(source.length() - sub.length(), sub.length(), sub) == 0; 101 | } 102 | 103 | std::string ToLower(const std::string& source) 104 | { 105 | std::string output = source; 106 | 107 | std::transform(output.begin(), output.end(), output.begin(), [](auto c) { return (std::string::value_type)std::tolower(c); }); 108 | 109 | return output; 110 | } 111 | 112 | std::wstring ToLower(const std::wstring& source) 113 | { 114 | std::wstring output = source; 115 | 116 | std::transform(output.begin(), output.end(), output.begin(), [](auto c) { return (std::wstring::value_type)std::tolower(c); }); 117 | 118 | return output; 119 | } 120 | 121 | std::string ToUpper(const std::string& source) 122 | { 123 | std::string output = source; 124 | 125 | std::transform(output.begin(), output.end(), output.begin(), [](auto c) { return (std::string::value_type)std::toupper(c); }); 126 | 127 | return output; 128 | } 129 | 130 | std::wstring ToUpper(const std::wstring& source) 131 | { 132 | std::wstring output = source; 133 | 134 | std::transform(output.begin(), output.end(), output.begin(), [](auto c) { return (std::wstring::value_type)std::toupper(c); }); 135 | 136 | return output; 137 | } 138 | 139 | std::string Format(const char* format, ...) 140 | { 141 | char buf[1024]; 142 | int count; 143 | va_list ap; 144 | 145 | // Try to print to a small buffer first. 146 | // We don't need to allocate a large buffer if it's enough to hold all the characters. 147 | va_start(ap, format); 148 | count = vsnprintf(buf, sizeof(buf), format, ap); 149 | va_end(ap); 150 | 151 | if (count <= 0) 152 | { 153 | // Something error happened, We return an empty string. 154 | return std::string(); 155 | } 156 | 157 | if (count < sizeof(buf)) 158 | { 159 | // All characters have been written to the small buffer. 160 | return std::string(buf, count); 161 | } 162 | 163 | // Allocate a buffer large enough to hold all characters. 164 | std::string output(count, '\0'); 165 | 166 | // Try to print 167 | va_start(ap, format); 168 | count = vsnprintf(const_cast(output.data()), output.size() + 1, format, ap); 169 | va_end(ap); 170 | 171 | if (count <= 0) 172 | { 173 | // Something error happened, We return an empty string. 174 | return std::string(); 175 | } 176 | 177 | return output; 178 | } 179 | 180 | std::string VFormat(const char* format, va_list ap) 181 | { 182 | char buf[1024]; 183 | int count; 184 | 185 | // Try to print to a small buffer first. 186 | // We don't need to allocate a large buffer if it's enough to hold all the characters. 187 | count = vsnprintf(buf, sizeof(buf), format, ap); 188 | 189 | if (count <= 0) 190 | { 191 | // Something error happened, We return an empty string. 192 | return std::string(); 193 | } 194 | 195 | if (count < sizeof(buf)) 196 | { 197 | // All characters have been written to the small buffer. 198 | return std::string(buf, count); 199 | } 200 | 201 | // Allocate a buffer large enough to hold all characters. 202 | std::string output(count, '\0'); 203 | 204 | // Try to print 205 | count = vsnprintf(const_cast(output.data()), output.size() + 1, format, ap); 206 | 207 | if (count <= 0) 208 | { 209 | // Something error happened, We return an empty string. 210 | return std::string(); 211 | } 212 | 213 | return output; 214 | } 215 | 216 | std::wstring Format(const wchar_t* format, ...) 217 | { 218 | wchar_t buf[1024]; 219 | int count; 220 | va_list ap; 221 | 222 | // Try to print to a small buffer first. 223 | // We don't need to allocate a large buffer if it's enough to hold all the characters. 224 | va_start(ap, format); 225 | count = _vsnwprintf_s(buf, _countof(buf), format, ap); 226 | va_end(ap); 227 | 228 | if (count <= 0) 229 | { 230 | // Something error happened, We return an empty string. 231 | return std::wstring(); 232 | } 233 | 234 | if (count < sizeof(buf)) 235 | { 236 | // All characters have been written to the small buffer. 237 | return std::wstring(buf, count); 238 | } 239 | 240 | // Allocate a buffer large enough to hold all characters. 241 | std::wstring output(count, '\0'); 242 | 243 | // Try to print 244 | va_start(ap, format); 245 | count = _vsnwprintf_s(const_cast(output.data()), output.size() + 1, output.size() + 1, format, ap); 246 | va_end(ap); 247 | 248 | if (count <= 0) 249 | { 250 | // Something error happened, We return an empty string. 251 | return std::wstring(); 252 | } 253 | 254 | return output; 255 | } 256 | 257 | std::wstring VFormat(const wchar_t* format, va_list ap) 258 | { 259 | wchar_t buf[1024]; 260 | int count; 261 | 262 | // Try to print to a small buffer first. 263 | // We don't need to allocate a large buffer if it's enough to hold all the characters. 264 | count = _vsnwprintf_s(buf, _countof(buf), format, ap); 265 | 266 | if (count <= 0) 267 | { 268 | // Something error happened, We return an empty string. 269 | return std::wstring(); 270 | } 271 | 272 | if (count < sizeof(buf)) 273 | { 274 | // All characters have been written to the small buffer. 275 | return std::wstring(buf, count); 276 | } 277 | 278 | // Allocate a buffer large enough to hold all characters. 279 | std::wstring output(count, '\0'); 280 | 281 | // Try to print 282 | count = _vsnwprintf_s(const_cast(output.data()), output.size() + 1, output.size() + 1, format, ap); 283 | 284 | if (count <= 0) 285 | { 286 | // Something error happened, We return an empty string. 287 | return std::wstring(); 288 | } 289 | 290 | return output; 291 | } 292 | 293 | std::wstring StringHelper::BytesToHexStringW(const unsigned __int8* data, unsigned __int32 length) 294 | { 295 | constexpr const wchar_t hexStringW[32] = L"0123456789ABCDEF"; 296 | 297 | std::wstring s; 298 | for (unsigned __int32 index = 0; index < length; index++) 299 | { 300 | s += hexStringW[(data[index] & 0xF0) >> 4]; 301 | s += hexStringW[(data[index] & 0x0F) >> 0]; 302 | } 303 | return s; 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /Common/stringhelper.h: -------------------------------------------------------------------------------- 1 | // stringhelper.h 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace StringHelper 9 | { 10 | bool StartsWith(const char* source, const char* sub); 11 | bool StartsWith(const wchar_t* source, const wchar_t* sub); 12 | bool StartsWith(const std::string& source, const std::string& sub); 13 | bool StartsWith(const std::wstring& source, const std::wstring& sub); 14 | bool EndsWith(const char* source, const char* sub); 15 | bool EndsWith(const wchar_t* source, const wchar_t* sub); 16 | bool EndsWith(const std::string& source, const std::string& sub); 17 | bool EndsWith(const std::wstring& source, const std::wstring& sub); 18 | 19 | std::string ToLower(const std::string& source); 20 | std::wstring ToLower(const std::wstring& source); 21 | std::string ToUpper(const std::string& source); 22 | std::wstring ToUpper(const std::wstring& source); 23 | 24 | std::string Format(const char* format, ...); 25 | std::string VFormat(const char* format, va_list ap); 26 | std::wstring Format(const wchar_t* format, ...); 27 | std::wstring VFormat(const wchar_t* format, va_list ap); 28 | 29 | std::wstring BytesToHexStringW(const unsigned __int8* data, unsigned __int32 length); 30 | } 31 | -------------------------------------------------------------------------------- /Common/util.cpp: -------------------------------------------------------------------------------- 1 | // util.cpp 2 | 3 | #include 4 | #include 5 | #include "stringhelper.h" 6 | 7 | namespace Util 8 | { 9 | std::string GetModulePathA(HMODULE hModule) 10 | { 11 | DWORD dwBufferSize = MAX_PATH; 12 | 13 | std::string output; 14 | 15 | // Maximum file path limitation 16 | // @see https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation 17 | while (dwBufferSize < USHRT_MAX) 18 | { 19 | output.resize(dwBufferSize); 20 | 21 | // Try to get the file name. 22 | DWORD nSize = GetModuleFileNameA(hModule, const_cast(output.data()), dwBufferSize); 23 | DWORD dwErrorCode = GetLastError(); 24 | 25 | if (dwErrorCode != ERROR_SUCCESS && dwErrorCode != ERROR_INSUFFICIENT_BUFFER) 26 | { 27 | // Something unexpected happened. 28 | return std::string(); 29 | } 30 | 31 | if (dwErrorCode == ERROR_SUCCESS && nSize < dwBufferSize) 32 | { 33 | // All characters have been written into the buffer. 34 | return output.substr(0, nSize); 35 | } 36 | 37 | if (dwErrorCode == ERROR_INSUFFICIENT_BUFFER || nSize == dwBufferSize) 38 | { 39 | // Expand the buffer. 40 | dwBufferSize *= 2; 41 | } 42 | } 43 | 44 | return std::string(); 45 | } 46 | 47 | std::wstring GetModulePathW(HMODULE hModule) 48 | { 49 | DWORD dwBufferSize = MAX_PATH; 50 | 51 | std::wstring output; 52 | 53 | // Maximum file path limitation 54 | // @see https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation 55 | while (dwBufferSize < USHRT_MAX) 56 | { 57 | output.resize(dwBufferSize); 58 | 59 | // Try to get the file name. 60 | DWORD nSize = GetModuleFileNameW(hModule, const_cast(output.data()), dwBufferSize); 61 | DWORD dwErrorCode = GetLastError(); 62 | 63 | if (dwErrorCode != ERROR_SUCCESS && dwErrorCode != ERROR_INSUFFICIENT_BUFFER) 64 | { 65 | // Something unexpected happened. 66 | return std::wstring(); 67 | } 68 | 69 | if (dwErrorCode == ERROR_SUCCESS && nSize < dwBufferSize) 70 | { 71 | // All characters have been written into the buffer. 72 | return output.substr(0, nSize); 73 | } 74 | 75 | if (dwErrorCode == ERROR_INSUFFICIENT_BUFFER || nSize == dwBufferSize) 76 | { 77 | // Expand the buffer. 78 | dwBufferSize *= 2; 79 | } 80 | } 81 | 82 | return std::wstring(); 83 | } 84 | 85 | std::string GetAppPathA() 86 | { 87 | return GetModulePathA(GetModuleHandleW(NULL)); 88 | } 89 | 90 | std::wstring GetAppPathW() 91 | { 92 | return GetModulePathW(GetModuleHandleW(NULL)); 93 | } 94 | 95 | std::string GetAppDirectoryA() 96 | { 97 | std::string path = GetAppPathA(); 98 | 99 | size_t pos = path.find_last_of('\\'); 100 | 101 | if (pos != std::string::npos && pos > 0) 102 | { 103 | return path.substr(0, pos); 104 | } 105 | 106 | return path; 107 | } 108 | 109 | std::wstring GetAppDirectoryW() 110 | { 111 | std::wstring path = GetAppPathW(); 112 | 113 | size_t pos = path.find_last_of('\\'); 114 | 115 | if (pos != std::wstring::npos && pos > 0) 116 | { 117 | return path.substr(0, pos); 118 | } 119 | 120 | return path; 121 | } 122 | 123 | std::string GetLastErrorMessageA() 124 | { 125 | DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM; 126 | DWORD dwErrorCode = GetLastError(); 127 | DWORD dwLanguageId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); 128 | LPSTR pBuffer = NULL; 129 | 130 | if (FormatMessageA(dwFlags, NULL, dwErrorCode, dwLanguageId, (LPSTR)&pBuffer, 0, NULL) == 0) 131 | { 132 | return std::string(); 133 | } 134 | 135 | std::string message(pBuffer); 136 | 137 | LocalFree(pBuffer); 138 | 139 | return message; 140 | } 141 | 142 | std::wstring GetLastErrorMessageW() 143 | { 144 | DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM; 145 | DWORD dwErrorCode = GetLastError(); 146 | DWORD dwLanguageId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); 147 | PWSTR pBuffer = NULL; 148 | 149 | if (FormatMessageW(dwFlags, NULL, dwErrorCode, dwLanguageId, (PWSTR)&pBuffer, 0, NULL) == 0) 150 | { 151 | return std::wstring(); 152 | } 153 | 154 | std::wstring message(pBuffer); 155 | 156 | LocalFree(pBuffer); 157 | 158 | return message; 159 | } 160 | 161 | __declspec(noreturn) void ThrowError(const char* format, ...) 162 | { 163 | va_list ap; 164 | 165 | va_start(ap, format); 166 | auto message = StringHelper::VFormat(format, ap); 167 | va_end(ap); 168 | 169 | MessageBoxA(NULL, message.c_str(), "Fatal Error", MB_ICONERROR | MB_OK); 170 | ExitProcess(1); 171 | } 172 | 173 | __declspec(noreturn) void ThrowError(const wchar_t* format, ...) 174 | { 175 | va_list ap; 176 | 177 | va_start(ap, format); 178 | auto message = StringHelper::VFormat(format, ap); 179 | va_end(ap); 180 | 181 | MessageBoxW(NULL, message.c_str(), L"Fatal Error", MB_ICONERROR | MB_OK); 182 | ExitProcess(1); 183 | } 184 | 185 | void WriteDebugMessage(const char* format, ...) 186 | { 187 | va_list ap; 188 | 189 | va_start(ap, format); 190 | auto message = StringHelper::VFormat(format, ap); 191 | va_end(ap); 192 | 193 | OutputDebugStringA(message.c_str()); 194 | } 195 | 196 | void WriteDebugMessage(const wchar_t* format, ...) 197 | { 198 | va_list ap; 199 | 200 | va_start(ap, format); 201 | auto message = StringHelper::VFormat(format, ap); 202 | va_end(ap); 203 | 204 | OutputDebugStringW(message.c_str()); 205 | } 206 | 207 | std::string OpenFolderDialog(const std::string& title) 208 | { 209 | char buf[MAX_PATH]{}; 210 | BROWSEINFOA bi{}; 211 | 212 | bi.hwndOwner = GetActiveWindow(); 213 | bi.pidlRoot = NULL; 214 | bi.pszDisplayName = buf; 215 | bi.lpszTitle = title.c_str(); 216 | bi.ulFlags = BIF_NEWDIALOGSTYLE; 217 | bi.lpfn = NULL; 218 | bi.lParam = NULL; 219 | bi.iImage = 0; 220 | 221 | LPITEMIDLIST idl = SHBrowseForFolderA(&bi); 222 | 223 | if (idl == NULL) 224 | { 225 | return std::string(); 226 | } 227 | 228 | if (SHGetPathFromIDListA(idl, buf) == FALSE) 229 | { 230 | return std::string(); 231 | } 232 | 233 | return std::string(buf); 234 | } 235 | 236 | std::wstring OpenFolderDialog(const std::wstring& title) 237 | { 238 | WCHAR buf[MAX_PATH]{}; 239 | BROWSEINFOW bi{}; 240 | 241 | bi.hwndOwner = GetActiveWindow(); 242 | bi.pidlRoot = NULL; 243 | bi.pszDisplayName = buf; 244 | bi.lpszTitle = title.c_str(); 245 | bi.ulFlags = BIF_NEWDIALOGSTYLE; 246 | bi.lpfn = NULL; 247 | bi.lParam = NULL; 248 | bi.iImage = 0; 249 | 250 | LPITEMIDLIST idl = SHBrowseForFolderW(&bi); 251 | 252 | if (idl == NULL) 253 | { 254 | return std::wstring(); 255 | } 256 | 257 | if (SHGetPathFromIDListW(idl, buf) == FALSE) 258 | { 259 | return std::wstring(); 260 | } 261 | 262 | return std::wstring(buf); 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /Common/util.h: -------------------------------------------------------------------------------- 1 | // util.h 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace Util 9 | { 10 | // Get the full path of the specified module. 11 | std::string GetModulePathA(HMODULE hModule); 12 | 13 | // Get the full path of the specified module. 14 | std::wstring GetModulePathW(HMODULE hModule); 15 | 16 | // Get the full path to the executable. 17 | std::string GetAppPathA(); 18 | 19 | // Get the full path to the executable. 20 | std::wstring GetAppPathW(); 21 | 22 | // Get the directory path to the executable. 23 | std::string GetAppDirectoryA(); 24 | 25 | // Get the directory path to the executable. 26 | std::wstring GetAppDirectoryW(); 27 | 28 | // Get message from Win32 last error code. 29 | std::string GetLastErrorMessageA(); 30 | 31 | // Get message from Win32 last error code. 32 | std::wstring GetLastErrorMessageW(); 33 | 34 | // Display an error message then close the application. 35 | __declspec(noreturn) void ThrowError(const char* format, ...); 36 | 37 | // Display an error message then close the application. 38 | __declspec(noreturn) void ThrowError(const wchar_t* format, ...); 39 | 40 | // Sends a string to the debugger for display. 41 | void WriteDebugMessage(const char* format, ...); 42 | 43 | // Sends a string to the debugger for display. 44 | void WriteDebugMessage(const wchar_t* format, ...); 45 | 46 | // Display a folder select dialog. 47 | std::string OpenFolderDialog(const std::string& title); 48 | 49 | // Display a folder select dialog. 50 | std::wstring OpenFolderDialog(const std::wstring& title); 51 | } 52 | -------------------------------------------------------------------------------- /CxdecExtractor/Application.cpp: -------------------------------------------------------------------------------- 1 | #include "Application.h" 2 | #include "path.h" 3 | #include "util.h" 4 | #include "ExtendUtils.h" 5 | 6 | namespace Engine 7 | { 8 | /// 9 | /// 单实例 10 | /// 11 | static Application* g_Instance = nullptr; 12 | 13 | //Hook插件功能 14 | tTVPV2LinkProc g_V2Link = nullptr; 15 | HRESULT __stdcall HookV2Link(iTVPFunctionExporter* exporter) 16 | { 17 | HRESULT result = g_V2Link(exporter); 18 | HookUtils::InlineHook::UnHook(g_V2Link, HookV2Link); 19 | g_V2Link = nullptr; 20 | 21 | //初始化插件 22 | Application::GetInstance()->InitializeTVPEngine(exporter); 23 | 24 | return result; 25 | } 26 | 27 | //Hook插件加载 28 | auto g_GetProcAddressFunction = ::GetProcAddress; 29 | FARPROC WINAPI HookGetProcAddress(HMODULE hModule, LPCSTR lpProcName) 30 | { 31 | FARPROC result = g_GetProcAddressFunction(hModule, lpProcName); 32 | if (result) 33 | { 34 | // 忽略序号导出 35 | if (HIWORD(lpProcName) != 0) 36 | { 37 | if (strcmp(lpProcName, "V2Link") == 0) 38 | { 39 | //Nt头偏移 40 | PIMAGE_NT_HEADERS ntHeader = PIMAGE_NT_HEADERS((ULONG_PTR)hModule + ((PIMAGE_DOS_HEADER)hModule)->e_lfanew); 41 | //可选头大小 42 | DWORD optionalHeaderSize = ntHeader->FileHeader.SizeOfOptionalHeader; 43 | //第一个节表(代码段) 44 | PIMAGE_SECTION_HEADER codeSectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)ntHeader + sizeof(ntHeader->Signature) + sizeof(IMAGE_FILE_HEADER) + optionalHeaderSize); 45 | 46 | DWORD codeStartRva = codeSectionHeader->VirtualAddress; //代码段起始RVA 47 | DWORD codeSize = codeSectionHeader->SizeOfRawData; //代码段大小 48 | 49 | ULONG_PTR codeStartVa = (ULONG_PTR)hModule + codeStartRva; //代码段起始VA 50 | 51 | //初始化 52 | Application* app = Application::GetInstance(); 53 | if (!app->IsTVPEngineInitialize()) 54 | { 55 | g_V2Link = (tTVPV2LinkProc)result; 56 | HookUtils::InlineHook::Hook(g_V2Link, HookV2Link); 57 | } 58 | 59 | //解包接口 60 | ExtractCore* extractor = app->GetExtractor(); 61 | if (!extractor->IsInitialized()) 62 | { 63 | extractor->Initialize((PVOID)codeStartVa, codeSize); 64 | } 65 | 66 | //初始化完毕 解除Hook 67 | if (extractor->IsInitialized()) 68 | { 69 | HookUtils::InlineHook::UnHook(g_GetProcAddressFunction, HookGetProcAddress); 70 | } 71 | } 72 | } 73 | } 74 | return result; 75 | } 76 | 77 | 78 | //**********Application***********// 79 | Application::Application() 80 | { 81 | this->mCurrentDirectoryPath = Path::GetDirectoryName(Util::GetModulePathW(::GetModuleHandleW(NULL))); 82 | this->mTVPExporterInitialized = false; 83 | this->mExtractor = new ExtractCore(); 84 | 85 | //设置解包输出路径 86 | this->mExtractor->SetOutputDirectory(this->mCurrentDirectoryPath); 87 | } 88 | 89 | Application::~Application() 90 | { 91 | if (this->mExtractor) 92 | { 93 | delete this->mExtractor; 94 | this->mExtractor = nullptr; 95 | } 96 | } 97 | 98 | void Application::InitializeModule(HMODULE hModule) 99 | { 100 | this->mModuleDirectoryPath = Path::GetDirectoryName(Util::GetModulePathW(hModule)); 101 | 102 | //设置解包Log输出路径 103 | this->mExtractor->SetLoggerDirectory(this->mModuleDirectoryPath); 104 | } 105 | 106 | void Application::InitializeTVPEngine(iTVPFunctionExporter* exporter) 107 | { 108 | this->mTVPExporterInitialized = TVPInitImportStub(exporter); 109 | TVPSetCommandLine(L"-debugwin", L"yes"); 110 | } 111 | 112 | bool Application::IsTVPEngineInitialize() 113 | { 114 | return this->mTVPExporterInitialized; 115 | } 116 | 117 | ExtractCore* Application::GetExtractor() 118 | { 119 | return this->mExtractor; 120 | } 121 | 122 | //**********====Static====**********// 123 | 124 | Application* Application::GetInstance() 125 | { 126 | return g_Instance; 127 | } 128 | 129 | void Application::Initialize(HMODULE hModule) 130 | { 131 | g_Instance = new Application(); 132 | g_Instance->InitializeModule(hModule); 133 | 134 | //Hook 135 | HookUtils::InlineHook::Hook(g_GetProcAddressFunction, HookGetProcAddress); 136 | } 137 | 138 | void Application::Release() 139 | { 140 | if (g_Instance) 141 | { 142 | delete g_Instance; 143 | g_Instance = nullptr; 144 | } 145 | } 146 | 147 | //================================// 148 | } 149 | 150 | -------------------------------------------------------------------------------- /CxdecExtractor/Application.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "ExtractCore.h" 5 | namespace Engine 6 | { 7 | using tTVPV2LinkProc = HRESULT(__stdcall*)(iTVPFunctionExporter*); 8 | using tTVPV2UnlinkProc = HRESULT(__stdcall*)(); 9 | 10 | class Application 11 | { 12 | private: 13 | Application(); 14 | Application(const Application&) = delete; 15 | Application(Application&&) = delete; 16 | Application& operator=(const Application&) = delete; 17 | Application& operator=(Application&&) = delete; 18 | ~Application(); 19 | 20 | private: 21 | 22 | std::wstring mModuleDirectoryPath; //dll目录 23 | std::wstring mCurrentDirectoryPath; //游戏当前目录 24 | ExtractCore* mExtractor; //解包器 25 | bool mTVPExporterInitialized; //插件初始化成功标志 26 | 27 | public: 28 | 29 | /// 30 | /// 设置模块信息 31 | /// 32 | /// 模块信息 33 | void InitializeModule(HMODULE hModule); 34 | 35 | /// 36 | /// 初始化插件 37 | /// 38 | /// 插件导出函数 39 | void InitializeTVPEngine(iTVPFunctionExporter* exporter); 40 | 41 | /// 42 | /// 获取插件是否初始化完毕 43 | /// 44 | /// True已初始化 False未初始化 45 | bool IsTVPEngineInitialize(); 46 | 47 | /// 48 | /// 获取解包器 49 | /// 50 | /// 51 | ExtractCore* GetExtractor(); 52 | 53 | /// 54 | /// 获取对象实例 55 | /// 56 | static Application* GetInstance(); 57 | /// 58 | /// 初始化 59 | /// 60 | /// 模块信息 61 | static void Initialize(HMODULE hModule); 62 | /// 63 | /// 释放 64 | /// 65 | static void Release(); 66 | }; 67 | } 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /CxdecExtractor/CxdecExtractor.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 16.0 15 | Win32Proj 16 | {c43a55e8-8d0c-479e-bd44-a648d550e594} 17 | CxdecExtractor 18 | 10.0 19 | CxdecExtractor 20 | 21 | 22 | 23 | DynamicLibrary 24 | true 25 | v143 26 | Unicode 27 | 28 | 29 | DynamicLibrary 30 | false 31 | v143 32 | true 33 | Unicode 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | false 50 | false 51 | false 52 | false 53 | 54 | 55 | false 56 | false 57 | false 58 | false 59 | false 60 | 61 | 62 | 63 | Level3 64 | false 65 | _WIN32_WINNT=0x601;WIN32;_DEBUG;CxdecExtractor_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 66 | true 67 | NotUsing 68 | pch.h 69 | false 70 | stdcpp20 71 | Sync 72 | AdvancedVectorExtensions2 73 | MultiThreadedDebug 74 | $(SolutionDir)Common;$(SolutionDir)KrkrPlugin;$(SolutionDir)Detours;%(AdditionalIncludeDirectories) 75 | false 76 | false 77 | false 78 | false 79 | false 80 | false 81 | 82 | false 83 | false 84 | false 85 | false 86 | false 87 | false 88 | 89 | 90 | Windows 91 | true 92 | false 93 | false 94 | false 95 | false 96 | false 97 | false 98 | export.def 99 | false 100 | true 101 | false 102 | false 103 | 104 | 105 | false 106 | 107 | 108 | 109 | 110 | Level3 111 | true 112 | true 113 | false 114 | _WIN32_WINNT=0x601;WIN32;NDEBUG;CxdecExtractor_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 115 | true 116 | NotUsing 117 | pch.h 118 | false 119 | stdcpp20 120 | Sync 121 | AdvancedVectorExtensions2 122 | $(SolutionDir)Common;$(SolutionDir)KrkrPlugin;$(SolutionDir)Detours;%(AdditionalIncludeDirectories) 123 | MultiThreaded 124 | false 125 | false 126 | false 127 | false 128 | false 129 | false 130 | false 131 | false 132 | false 133 | false 134 | false 135 | false 136 | Disabled 137 | false 138 | MaxSpeed 139 | false 140 | 141 | 142 | Windows 143 | true 144 | true 145 | false 146 | false 147 | false 148 | false 149 | false 150 | false 151 | false 152 | 153 | 154 | export.def 155 | false 156 | false 157 | false 158 | true 159 | 160 | 161 | false 162 | 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 | -------------------------------------------------------------------------------- /CxdecExtractor/CxdecExtractor.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {9d43feff-c600-4b74-b6f0-eb414ba3849f} 10 | 11 | 12 | {958ae095-bb06-4568-ac2d-3f8d594c9460} 13 | 14 | 15 | {a9097175-9ff1-4420-9881-f5e971100d6b} 16 | 17 | 18 | 19 | 20 | 源文件 21 | 22 | 23 | Commom 24 | 25 | 26 | Commom 27 | 28 | 29 | Commom 30 | 31 | 32 | Commom 33 | 34 | 35 | Commom 36 | 37 | 38 | Commom 39 | 40 | 41 | Commom 42 | 43 | 44 | KrkrPlugin 45 | 46 | 47 | Detours 48 | 49 | 50 | Detours 51 | 52 | 53 | Detours 54 | 55 | 56 | Detours 57 | 58 | 59 | Detours 60 | 61 | 62 | 源文件 63 | 64 | 65 | 源文件 66 | 67 | 68 | Commom 69 | 70 | 71 | 72 | 73 | Commom 74 | 75 | 76 | Commom 77 | 78 | 79 | Commom 80 | 81 | 82 | Commom 83 | 84 | 85 | Commom 86 | 87 | 88 | Commom 89 | 90 | 91 | Commom 92 | 93 | 94 | KrkrPlugin 95 | 96 | 97 | Detours 98 | 99 | 100 | 源文件 101 | 102 | 103 | 源文件 104 | 105 | 106 | 源文件 107 | 108 | 109 | Commom 110 | 111 | 112 | 113 | 114 | 源文件 115 | 116 | 117 | -------------------------------------------------------------------------------- /CxdecExtractor/ExtendUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "detours.h" 5 | namespace Engine 6 | { 7 | namespace HookUtils 8 | { 9 | class InlineHook 10 | { 11 | public: 12 | InlineHook() = delete; 13 | InlineHook(const InlineHook&) = delete; 14 | InlineHook(InlineHook&&) = delete; 15 | InlineHook& operator=(const InlineHook&) = delete; 16 | InlineHook& operator=(InlineHook&&) = delete; 17 | ~InlineHook() = delete; 18 | 19 | 20 | template 21 | static void Hook(T& OriginalFunction, T DetourFunction) 22 | { 23 | DetourUpdateThread(GetCurrentThread()); 24 | DetourTransactionBegin(); 25 | DetourAttach(&(PVOID&)OriginalFunction, (PVOID&)DetourFunction); 26 | DetourTransactionCommit(); 27 | } 28 | 29 | template 30 | static void UnHook(T& OriginalFunction, T DetourFunction) 31 | { 32 | DetourUpdateThread(GetCurrentThread()); 33 | DetourTransactionBegin(); 34 | DetourDetach(&(PVOID&)OriginalFunction, (PVOID&)DetourFunction); 35 | DetourTransactionCommit(); 36 | } 37 | }; 38 | } 39 | 40 | namespace StreamUtils 41 | { 42 | class IStreamEx 43 | { 44 | public: 45 | IStreamEx() = delete; 46 | IStreamEx(const IStreamEx&) = delete; 47 | IStreamEx(IStreamEx&&) = delete; 48 | IStreamEx& operator=(const IStreamEx&) = delete; 49 | IStreamEx& operator=(IStreamEx&&) = delete; 50 | ~IStreamEx() = delete; 51 | 52 | /// 53 | /// 获取流长度 54 | /// 55 | /// 56 | static ULONGLONG WINAPI Length(IStream* stream) 57 | { 58 | LARGE_INTEGER pos{ }; 59 | pos.QuadPart = (LONGLONG)IStreamEx::Position(stream); 60 | 61 | ULARGE_INTEGER size; 62 | stream->Seek(LARGE_INTEGER{ }, STREAM_SEEK_END, &size); 63 | stream->Seek(pos, STREAM_SEEK_SET, nullptr); 64 | 65 | return size.QuadPart; 66 | } 67 | /// 68 | /// 获取当前流位置 69 | /// 70 | /// 71 | static ULONGLONG WINAPI Position(IStream* stream) 72 | { 73 | ULARGE_INTEGER pos; 74 | stream->Seek(LARGE_INTEGER{ }, STREAM_SEEK_CUR, &pos); 75 | return pos.QuadPart; 76 | } 77 | /// 78 | /// 设置当前流位置 79 | /// 80 | /// 81 | /// 82 | /// 83 | static void WINAPI Seek(IStream* stream, LONGLONG offset, DWORD seekMode) 84 | { 85 | LARGE_INTEGER move{ }; 86 | move.QuadPart = offset; 87 | stream->Seek(move, seekMode, nullptr); 88 | } 89 | /// 90 | /// 读取流 91 | /// 92 | /// 93 | /// 94 | /// 95 | static ULONG WINAPI Read(IStream* stream, void* buffer, ULONG length) 96 | { 97 | ULONG readLength = 0ul; 98 | stream->Read(buffer, length, &readLength); 99 | return readLength; 100 | } 101 | }; 102 | } 103 | } 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /CxdecExtractor/ExtractCore.cpp: -------------------------------------------------------------------------------- 1 | #include "ExtractCore.h" 2 | #include "pe.h" 3 | #include "file.h" 4 | #include "directory.h" 5 | #include "path.h" 6 | #include "stringhelper.h" 7 | #include "ExtendUtils.h" 8 | 9 | namespace Engine 10 | { 11 | //**********ExtractCore***********// 12 | ExtractCore::ExtractCore() 13 | { 14 | this->mCreateStreamFunc = nullptr; 15 | this->mCreateIndexFunc = nullptr; 16 | } 17 | 18 | ExtractCore::~ExtractCore() 19 | { 20 | } 21 | 22 | void ExtractCore::SetOutputDirectory(const std::wstring& directory) 23 | { 24 | this->mExtractDirectoryPath = Path::Combine(directory, ExtractCore::ExtractorOutFolderName); 25 | } 26 | 27 | void ExtractCore::SetLoggerDirectory(const std::wstring& directory) 28 | { 29 | std::wstring path = Path::Combine(directory, ExtractCore::ExtractorLogFileName); 30 | 31 | File::Delete(path); 32 | this->mLogger.Open(path.c_str()); 33 | } 34 | 35 | void ExtractCore::Initialize(PVOID codeVa, DWORD codeSize) 36 | { 37 | PVOID createStream = PE::SearchPattern(codeVa, codeSize, ExtractCore::CreateStreamSignature, sizeof(ExtractCore::CreateStreamSignature) - 1u); 38 | PVOID createIndex = PE::SearchPattern(codeVa, codeSize, ExtractCore::CreateIndexSignature, sizeof(ExtractCore::CreateIndexSignature) - 1u); 39 | 40 | if (createStream && createIndex) 41 | { 42 | this->mCreateStreamFunc = (tCreateStream)createStream; 43 | this->mCreateIndexFunc = (tCreateIndex)createIndex; 44 | } 45 | } 46 | 47 | bool ExtractCore::IsInitialized() 48 | { 49 | return this->mCreateStreamFunc && this->mCreateIndexFunc; 50 | } 51 | 52 | void ExtractCore::ExtractPackage(const std::wstring& packageFileName) 53 | { 54 | if (!this->IsInitialized()) 55 | { 56 | ::MessageBoxW(nullptr, L"未初始化CxdecV2接口\n请检查是否为无DRM的Wamsoft Hxv4加密游戏", L"错误", MB_OK); 57 | return; 58 | } 59 | 60 | tTJSString tjsXp3PackagePath = TVPGetAppPath() + packageFileName.c_str(); //获取封包标准路径 61 | std::vector entries = std::vector(); 62 | 63 | this->GetEntries(tjsXp3PackagePath, entries); 64 | if (!entries.empty()) 65 | { 66 | Directory::Create(this->mExtractDirectoryPath); //创建资源提取导出文件夹 67 | 68 | std::wstring packageName = Path::GetFileNameWithoutExtension(packageFileName); //封包名 69 | std::wstring extractOutput = Path::Combine(this->mExtractDirectoryPath, packageName); //输出文件夹 70 | std::wstring fileTableOutput = extractOutput + L".alst"; //文件表输出路径 71 | 72 | File::Delete(fileTableOutput); 73 | Log::Logger fileTable = Log::Logger(fileTableOutput.c_str()); 74 | 75 | //写UTF-16LE bom头 76 | { 77 | WORD bom = 0xFEFF; 78 | fileTable.WriteData(&bom, sizeof(bom)); 79 | } 80 | 81 | for (FileEntry& entry : entries) 82 | { 83 | std::wstring dirHash = StringHelper::BytesToHexStringW(entry.DirectoryPathHash, sizeof(entry.DirectoryPathHash)); //文件夹Hash字符串形式 84 | std::wstring fileNameHash = StringHelper::BytesToHexStringW(entry.FileNameHash, sizeof(entry.FileNameHash)); //文件名Hash字符串形式 85 | std::wstring arcOutputPath = extractOutput + L"\\" + dirHash + L"\\" + fileNameHash; //该资源导出路径 86 | 87 | if (IStream* stream = this->CreateStream(entry, packageFileName)) 88 | { 89 | //写表 dirHash[Sign]dirHash[Sign]fileHash[Sign]fileHash[NewLine] 90 | fileTable.WriteUnicode(L"%s%s%s%s%s%s%s\r\n", 91 | dirHash.c_str(), 92 | ExtractCore::Split, 93 | dirHash.c_str(), 94 | ExtractCore::Split, 95 | fileNameHash.c_str(), 96 | ExtractCore::Split, 97 | fileNameHash.c_str()); 98 | //解包 99 | this->ExtractFile(stream, arcOutputPath); 100 | stream->Release(); 101 | } 102 | else 103 | { 104 | this->mLogger.WriteLine(L"File Not Exist: %s/%s/%s", packageName.c_str(), dirHash.c_str(), fileNameHash.c_str()); 105 | } 106 | } 107 | ::MessageBoxW(nullptr, (packageFileName + L"提取成功").c_str(), L"信息", MB_OK); 108 | fileTable.Close(); 109 | } 110 | else 111 | { 112 | ::MessageBoxW(nullptr, L"请选择正确的XP3封包", L"错误", MB_OK); 113 | } 114 | } 115 | 116 | void ExtractCore::GetEntries(const tTJSString& xp3PackagePath, std::vector& retValue) 117 | { 118 | retValue.clear(); 119 | 120 | //加载文件表 121 | tTJSVariant tjsEntries = tTJSVariant(); 122 | tTJSVariant tjsPackagePath = tTJSVariant(xp3PackagePath); 123 | this->mCreateIndexFunc(&tjsEntries, &tjsPackagePath); 124 | 125 | if (tjsEntries.Type() == tvtObject) 126 | { 127 | tTJSVariantClosure& dirEntriesObj = tjsEntries.AsObjectClosureNoAddRef(); 128 | 129 | //读取数组项数 130 | tTJSVariant tjsCount = tTJSVariant(); 131 | tjs_int count = 0; 132 | dirEntriesObj.PropGet(TJS_MEMBERMUSTEXIST, L"count", NULL, &tjsCount, nullptr); 133 | count = tjsCount.AsInteger(); 134 | 135 | constexpr tjs_int DirectoryItemSize = 2; 136 | //获取文件夹项数 (占KR数组2项) 137 | //文件夹路径Hash 138 | //子文件数组 139 | tjs_int dirCount = count / DirectoryItemSize; 140 | 141 | //遍历文件夹 142 | for (tjs_int di = 0; di < dirCount; ++di) 143 | { 144 | //获取文件夹路径Hash与子文件表 145 | tTJSVariant tjsDirHash = tTJSVariant(); 146 | tTJSVariant tjsFileEntries = tTJSVariant(); 147 | dirEntriesObj.PropGetByNum(TJS_CII_GET, di * DirectoryItemSize + 0, &tjsDirHash, nullptr); 148 | dirEntriesObj.PropGetByNum(TJS_CII_GET, di * DirectoryItemSize + 1, &tjsFileEntries, nullptr); 149 | 150 | //获取Hash 151 | tTJSVariantOctet* dirHash = tjsDirHash.AsOctetNoAddRef(); 152 | 153 | //获取子文件表数组 154 | tTJSVariantClosure& fileEntries = tjsFileEntries.AsObjectClosureNoAddRef(); 155 | 156 | //获取子文件数组项数 157 | tjsCount.Clear(); 158 | fileEntries.PropGet(TJS_MEMBERMUSTEXIST, L"count", NULL, &tjsCount, nullptr); 159 | count = tjsCount.AsInteger(); 160 | 161 | constexpr tjs_int FileItemSize = 2; 162 | //获取文件项数 (占KR数组2项) 163 | //文件名Hash 164 | //文件信息 165 | tjs_int fileCount = count / FileItemSize; 166 | 167 | //遍历子文件 168 | for (tjs_int fi = 0; fi < fileCount; ++fi) 169 | { 170 | //获取文件名Hash与文件信息 171 | tTJSVariant tjsFileNameHash = tTJSVariant(); 172 | tTJSVariant tjsFileInfo = tTJSVariant(); 173 | fileEntries.PropGetByNum(TJS_CII_GET, fi * FileItemSize + 0, &tjsFileNameHash, nullptr); 174 | fileEntries.PropGetByNum(TJS_CII_GET, fi * FileItemSize + 1, &tjsFileInfo, nullptr); 175 | 176 | //获取Hash 177 | tTJSVariantOctet* fileNameHash = tjsFileNameHash.AsOctetNoAddRef(); 178 | 179 | //获取文件信息 180 | tTJSVariantClosure& fileInfo = tjsFileInfo.AsObjectClosureNoAddRef(); 181 | 182 | //获取文件信息 183 | __int64 ordinal = 0i64; 184 | __int64 key = 0i64; 185 | 186 | tTJSVariant tjsValue = tTJSVariant(); 187 | fileInfo.PropGetByNum(TJS_CII_GET, 0, &tjsValue, nullptr); 188 | ordinal = tjsValue.AsInteger(); 189 | 190 | tjsValue.Clear(); 191 | fileInfo.PropGetByNum(TJS_CII_GET, 1, &tjsValue, nullptr); 192 | key = tjsValue.AsInteger(); 193 | 194 | //解析后的文件表 195 | FileEntry entry{ }; 196 | memcpy(entry.DirectoryPathHash, dirHash->GetData(), dirHash->GetLength()); 197 | memcpy(entry.FileNameHash, fileNameHash->GetData(), fileNameHash->GetLength()); 198 | 199 | entry.Key = key; 200 | entry.Ordinal = ordinal; 201 | 202 | retValue.push_back(entry); 203 | } 204 | } 205 | } 206 | } 207 | 208 | IStream* ExtractCore::CreateStream(const FileEntry& entry, const std::wstring& packageName) 209 | { 210 | tjs_char fakeName[4]{ }; 211 | entry.GetFakeName(fakeName); 212 | 213 | tTJSString tjsArcPath = TVPGetAppPath(); //获取游戏路径 214 | tjsArcPath += packageName.c_str(); //添加封包 215 | tjsArcPath += L">"; //添加封包分隔符 216 | tjsArcPath += fakeName; //添加资源名 217 | tjsArcPath = TVPNormalizeStorageName(tjsArcPath); 218 | 219 | return this->mCreateStreamFunc(&tjsArcPath, entry.Key, entry.GetEncryptMode()); 220 | } 221 | 222 | void ExtractCore::ExtractFile(IStream* stream, const std::wstring& extractPath) 223 | { 224 | unsigned long long size = StreamUtils::IStreamEx::Length(stream); //获取流长度 225 | 226 | //相对路径 227 | std::wstring relativePath(&extractPath.c_str()[this->mExtractDirectoryPath.length() + 1u]); 228 | if (size > 0) 229 | { 230 | //创建文件夹 231 | { 232 | std::wstring outputDir = Path::GetDirectoryName(extractPath); 233 | if (!outputDir.empty()) 234 | { 235 | Directory::Create(outputDir.c_str()); 236 | } 237 | } 238 | 239 | std::vector buffer; 240 | bool success = false; 241 | 242 | if (ExtractCore::TryDecryptText(stream, buffer)) //尝试解密SimpleEncrypt 243 | { 244 | success = true; 245 | } 246 | else 247 | { 248 | //普通资源 249 | buffer.resize(size); 250 | 251 | stream->Seek(LARGE_INTEGER{ }, STREAM_SEEK_SET, nullptr); 252 | if (StreamUtils::IStreamEx::Read(stream, buffer.data(), size) == size) 253 | { 254 | success = true; 255 | } 256 | } 257 | 258 | if (success && !buffer.empty()) 259 | { 260 | //保存文件 261 | if (File::WriteAllBytes(extractPath, buffer.data(), buffer.size())) 262 | { 263 | this->mLogger.WriteLine(L"Extract Successed: %s", relativePath.c_str()); 264 | } 265 | else 266 | { 267 | this->mLogger.WriteLine(L"Write Error: %s", relativePath.c_str()); 268 | } 269 | } 270 | else 271 | { 272 | this->mLogger.WriteLine(L"Invaild File: %s", relativePath.c_str()); 273 | } 274 | stream->Seek(LARGE_INTEGER{ }, STREAM_SEEK_SET, nullptr); 275 | } 276 | else 277 | { 278 | this->mLogger.WriteLine(L"Empty File: %s", relativePath.c_str()); 279 | } 280 | } 281 | 282 | 283 | bool ExtractCore::TryDecryptText(IStream* stream, std::vector& output) 284 | { 285 | uint8_t mark[2]{ }; 286 | StreamUtils::IStreamEx::Read(stream, mark, 2ul); 287 | 288 | if (mark[0] == 0xfe && mark[1] == 0xfe) //检查加密标记头 289 | { 290 | uint8_t mode; 291 | StreamUtils::IStreamEx::Read(stream, &mode, 1ul); 292 | 293 | if (mode != 0 && mode != 1 && mode != 2) //识别模式 294 | { 295 | return false; 296 | } 297 | 298 | ZeroMemory(mark, sizeof(mark)); 299 | StreamUtils::IStreamEx::Read(stream, mark, 2ul); 300 | 301 | if (mark[0] != 0xff || mark[1] != 0xfe) //Unicode Bom 302 | { 303 | return false; 304 | } 305 | 306 | if (mode == 2) //压缩模式 307 | { 308 | long long compressed = 0; 309 | long long uncompressed = 0; 310 | StreamUtils::IStreamEx::Read(stream, &compressed, sizeof(long long)); 311 | StreamUtils::IStreamEx::Read(stream, &uncompressed, sizeof(long long)); 312 | 313 | if (compressed <= 0 || compressed >= INT_MAX || uncompressed <= 0 || uncompressed >= INT_MAX) 314 | { 315 | return false; 316 | } 317 | 318 | std::vector data = std::vector((size_t)compressed); 319 | 320 | //读取压缩数据 321 | if (StreamUtils::IStreamEx::Read(stream, data.data(), compressed) != compressed) 322 | { 323 | return false; 324 | } 325 | 326 | std::vector buffer = std::vector((size_t)uncompressed + 2u); 327 | 328 | //写入Bom头 329 | buffer[0] = mark[0]; 330 | buffer[1] = mark[1]; 331 | 332 | uint8_t* dest = buffer.data() + 2; 333 | unsigned long destLen = (unsigned long)uncompressed; 334 | 335 | if (ZLIB_uncompress(dest, &destLen, data.data(), compressed) || destLen != (unsigned long)uncompressed) 336 | { 337 | return false; 338 | } 339 | 340 | output = std::move(buffer); 341 | return true; 342 | } 343 | else 344 | { 345 | long long startpos = StreamUtils::IStreamEx::Position(stream); //解密起始位置 346 | long long endpos = StreamUtils::IStreamEx::Length(stream); //解密结束位置 347 | 348 | StreamUtils::IStreamEx::Seek(stream, startpos, STREAM_SEEK_SET); //设置回起始位置 349 | 350 | long long size = endpos - startpos; //解密大小 351 | 352 | if (size <= 0 || size >= INT_MAX) 353 | { 354 | return false; 355 | } 356 | 357 | size_t count = (size_t)(size / sizeof(wchar_t)); 358 | 359 | if (count == 0) 360 | { 361 | return false; 362 | } 363 | 364 | std::vector buffer(count); //存放文本 365 | 366 | StreamUtils::IStreamEx::Read(stream, buffer.data(), size); //读取资源 367 | 368 | if (mode == 0) //模式0 369 | { 370 | for (size_t i = 0; i < count; i++) 371 | { 372 | wchar_t ch = buffer[i]; 373 | if (ch >= 0x20) buffer[i] = ch ^ (((ch & 0xfe) << 8) ^ 1); 374 | } 375 | } 376 | else if (mode == 1) //模式1 377 | { 378 | for (size_t i = 0; i < count; i++) 379 | { 380 | wchar_t ch = buffer[i]; 381 | ch = ((ch & 0xaaaaaaaa) >> 1) | ((ch & 0x55555555) << 1); 382 | buffer[i] = ch; 383 | } 384 | } 385 | 386 | size_t sizeToCopy = count * sizeof(wchar_t); 387 | 388 | output.resize(sizeToCopy + 2u); 389 | 390 | //写入Unicode Bom 391 | output[0] = mark[0]; 392 | output[1] = mark[1]; 393 | 394 | //回写解密后的数据 395 | memcpy(output.data() + 2, buffer.data(), sizeToCopy); 396 | 397 | return true; 398 | } 399 | } 400 | return false; 401 | } 402 | //================================// 403 | } 404 | -------------------------------------------------------------------------------- /CxdecExtractor/ExtractCore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "tp_stub.h" 7 | #include "log.h" 8 | 9 | namespace Engine 10 | { 11 | /// 12 | /// 文件表 13 | /// 14 | class FileEntry 15 | { 16 | public: 17 | /// 18 | /// 文件夹Hash 19 | /// 20 | unsigned __int8 DirectoryPathHash[8]; 21 | /// 22 | /// 文件名Hash 23 | /// 24 | unsigned __int8 FileNameHash[32]; 25 | /// 26 | /// 文件Key 27 | /// 28 | __int64 Key; 29 | /// 30 | /// 文件序号 31 | /// 32 | __int64 Ordinal; 33 | 34 | /// 35 | /// 获取合法性 36 | /// 37 | bool IsVaild() const 38 | { 39 | return this->Ordinal >= 0i64; 40 | } 41 | 42 | /// 43 | /// 获取加密模式 44 | /// 45 | unsigned __int32 GetEncryptMode() const 46 | { 47 | return ((this->Ordinal & 0x0000FFFF00000000i64) >> 32); 48 | } 49 | 50 | /// 51 | /// 获取封包的名字 52 | /// 最多8字节 4个字符 3个Unicode字符 + 0结束符 53 | /// 54 | /// 字符返回值指针 55 | void GetFakeName(wchar_t* retValue) const 56 | { 57 | wchar_t* fakeName = retValue; 58 | 59 | *(__int64*)fakeName = 0i64; //清空8字节 60 | 61 | unsigned __int32 ordinalLow32 = this->Ordinal & 0x00000000FFFFFFFFi64; 62 | 63 | int charIndex = 0; 64 | do 65 | { 66 | unsigned __int32 temp = ordinalLow32; 67 | temp &= 0x00003FFFu; 68 | temp += 0x00005000u; 69 | 70 | fakeName[charIndex] = temp & 0x0000FFFFu; 71 | ++charIndex; 72 | 73 | ordinalLow32 >>= 0x0E; 74 | } while (ordinalLow32 != 0u); 75 | } 76 | }; 77 | 78 | class ExtractCore 79 | { 80 | private: 81 | static constexpr const wchar_t ExtractorOutFolderName[] = L"Extractor_Output"; //提取器输出文件夹名 82 | static constexpr const wchar_t ExtractorLogFileName[] = L"Extractor.log"; //提取器日志文件名 83 | 84 | private: 85 | static constexpr const char CreateStreamSignature[] = "\x55\x8B\xEC\x6A\xFF\x68\x2A\x2A\x2A\x2A\x64\xA1\x00\x00\x00\x00\x50\x51\xA1\x2A\x2A\x2A\x2A\x33\xC5\x50\x8D\x45\xF4\x64\xA3\x00\x00\x00\x00\xA1\x2A\x2A\x2A\x2A\x85\xC0\x75\x32\x68\xB0\x30\x00\x00"; 86 | static constexpr const char CreateIndexSignature[] = "\x55\x8B\xEC\x6A\xFF\x68\x2A\x2A\x2A\x2A\x64\xA1\x00\x00\x00\x00\x50\x83\xEC\x14\x57\xA1\x2A\x2A\x2A\x2A\x33\xC5\x50\x8D\x45\xF4\x64\xA3\x00\x00\x00\x00\x83\x7D\x08\x00\x0F\x84\x2A\x2A\x00\x00\xA1\x2A\x2A\x2A\x2A\x85\xC0\x75\x12\x68\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\x83\xC4\x04\xA3\x2A\x2A\x2A\x2A\xFF\x75\x0C\x8D\x4D\xF0\x51\xFF\xD0\xA1\x2A\x2A\x2A\x2A\xC7\x45\xFC\x00\x00\x00\x00\x85\xC0"; 87 | static constexpr const wchar_t Split[] = L"##YSig##"; //格式分割字符串 88 | 89 | using tCreateStream = IStream* (__cdecl*)(const tTJSString* fakeName, tjs_int64 key, tjs_uint32 encryptMode); 90 | using tCreateIndex = tjs_error (__cdecl*)(tTJSVariant* retValue, const tTJSVariant* tjsXP3Name); 91 | 92 | tCreateStream mCreateStreamFunc; //CxCreateStream打开文件流接口 93 | tCreateIndex mCreateIndexFunc; //CxCreateIndex获取文件表接口 94 | 95 | std::wstring mExtractDirectoryPath; //解包输出文件夹 96 | Log::Logger mLogger; //解包日志 97 | 98 | public: 99 | ExtractCore(); 100 | ExtractCore(const ExtractCore&) = delete; 101 | ExtractCore(ExtractCore&&) = delete; 102 | ExtractCore& operator=(const ExtractCore&) = delete; 103 | ExtractCore& operator=(ExtractCore&&) = delete; 104 | ~ExtractCore(); 105 | 106 | /// 107 | /// 设置资源输出路径 108 | /// 109 | /// 文件夹绝对路径 110 | void SetOutputDirectory(const std::wstring& directory); 111 | 112 | /// 113 | /// 设置日志输出路径 114 | /// 115 | /// 文件夹绝对路径 116 | void SetLoggerDirectory(const std::wstring& directory); 117 | 118 | /// 119 | /// 初始化 (特征码找接口) 120 | /// 121 | /// 代码起始地址 122 | /// 代码大小 123 | void Initialize(PVOID codeVa, DWORD codeSize); 124 | /// 125 | /// 检查是否已经初始化 126 | /// 127 | /// True已初始化 False未初始化 128 | bool IsInitialized(); 129 | /// 130 | /// 解包 131 | /// 132 | /// 封包名称 133 | void ExtractPackage(const std::wstring& packageFileName); 134 | 135 | private: 136 | /// 137 | /// 获取Hxv4文件表 138 | /// 139 | /// 封包绝对路径 140 | /// 文件表数组 141 | void GetEntries(const tTJSString& xp3PackagePath, std::vector& retValue); 142 | 143 | /// 144 | /// 创建资源流 145 | /// 146 | /// 文件表 147 | /// 封包名 148 | /// IStream对象 149 | IStream* CreateStream(const FileEntry& entry, const std::wstring& packageName); 150 | 151 | /// 152 | /// 提取文件 153 | /// 154 | /// 流 155 | /// 提取路径 156 | void ExtractFile(IStream* stream, const std::wstring& extractPath); 157 | 158 | /// 159 | /// 尝试解密文本 160 | /// 161 | /// 资源流 162 | /// 输出缓冲区 163 | /// True解密成功 False不是文本加密 164 | static bool TryDecryptText(IStream* stream, std::vector& output); 165 | }; 166 | } 167 | 168 | 169 | -------------------------------------------------------------------------------- /CxdecExtractor/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include "Application.h" 2 | 3 | #pragma comment(linker, "/MERGE:\".detourd=.data\"") 4 | #pragma comment(linker, "/MERGE:\".detourc=.rdata\"") 5 | 6 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 7 | { 8 | UNREFERENCED_PARAMETER(lpReserved); 9 | switch (ul_reason_for_call) 10 | { 11 | case DLL_PROCESS_ATTACH: 12 | { 13 | Engine::Application::Initialize(hModule); 14 | break; 15 | } 16 | case DLL_THREAD_ATTACH: 17 | case DLL_THREAD_DETACH: 18 | break; 19 | case DLL_PROCESS_DETACH: 20 | { 21 | Engine::Application::Release(); 22 | break; 23 | } 24 | } 25 | return TRUE; 26 | } 27 | 28 | /// 29 | /// 解包 30 | /// 31 | /// 封包名 例如xxx.xp3 32 | extern "C" __declspec(dllexport) void WINAPI ExtractPackage(const wchar_t* packageName) 33 | { 34 | Engine::Application::GetInstance()->GetExtractor()->ExtractPackage(packageName); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /CxdecExtractor/export.def: -------------------------------------------------------------------------------- 1 | LIBRARY 2 | EXPORTS 3 | ExtractPackage -------------------------------------------------------------------------------- /CxdecExtractorLoader/AmetoYuki_1.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YeLikesss/KrkrExtractForCxdecV2/1829eb0abb8740f2db5b7fc497b7483372b5b6a0/CxdecExtractorLoader/AmetoYuki_1.ico -------------------------------------------------------------------------------- /CxdecExtractorLoader/CxdecExtractorLoader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "path.h" 5 | #include "util.h" 6 | #include "directory.h" 7 | #include "encoding.h" 8 | #include "resource.h" 9 | 10 | #pragma comment(linker, "/MERGE:\".detourd=.data\"") 11 | #pragma comment(linker, "/MERGE:\".detourc=.rdata\"") 12 | 13 | #ifdef _UNICODE 14 | #if defined _M_IX86 15 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 16 | #elif defined _M_X64 17 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 18 | #else 19 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 20 | #endif 21 | #endif 22 | 23 | 24 | static std::wstring g_LoaderFullPath; //加载器全路径 25 | static std::wstring g_LoaderCurrentDirectory; //加载器目录 26 | static std::wstring g_KrkrExeFullPath; //Krkr游戏主程序全路径 27 | static std::wstring g_KrkrExeDirectory; //Krkr游戏目录 28 | 29 | /// 30 | /// 主窗体消息循环 31 | /// 32 | /// 窗口句柄 33 | /// 消息 34 | /// 35 | /// 36 | INT_PTR CALLBACK LoaderDialogWindProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 37 | { 38 | switch (msg) 39 | { 40 | case WM_COMMAND: 41 | { 42 | std::wstring injectDllFileName = std::wstring(); 43 | switch (LOWORD(wParam)) 44 | { 45 | case IDC_Extractor: 46 | { 47 | injectDllFileName = L"CxdecExtractorUI.dll"; 48 | break; 49 | } 50 | case IDC_StringDumper: 51 | { 52 | injectDllFileName = L"CxdecStringDumper.dll"; 53 | break; 54 | } 55 | case IDC_KeyDumper: 56 | { 57 | //NotImpl 58 | ::MessageBoxW(nullptr, L"此功能暂未实现", L"错误", MB_OK); 59 | break; 60 | } 61 | } 62 | 63 | if (!injectDllFileName.empty()) 64 | { 65 | std::wstring injectDllFullPath = Path::Combine(g_LoaderCurrentDirectory, injectDllFileName); 66 | std::string injectDllFullPathA = Encoding::UnicodeToAnsi(injectDllFullPath, Encoding::CodePage::ACP); 67 | 68 | STARTUPINFOW si{ }; 69 | si.cb = sizeof(si); 70 | PROCESS_INFORMATION pi{ }; 71 | 72 | if (DetourCreateProcessWithDllW(g_KrkrExeFullPath.c_str(), NULL, NULL, NULL, FALSE, 0u, NULL, g_KrkrExeDirectory.c_str(), &si, &pi, injectDllFullPathA.c_str(), NULL)) 73 | { 74 | ::CloseHandle(pi.hThread); 75 | ::CloseHandle(pi.hProcess); 76 | 77 | //运行成功 关闭窗口 78 | ::PostMessageW(hwnd, WM_CLOSE, 0u, 0u); 79 | } 80 | else 81 | { 82 | ::MessageBoxW(nullptr, L"创建进程错误", L"错误", MB_OK); 83 | } 84 | } 85 | return TRUE; 86 | } 87 | case WM_CLOSE: 88 | { 89 | ::DestroyWindow(hwnd); 90 | return TRUE; 91 | } 92 | case WM_DESTROY: 93 | { 94 | ::PostQuitMessage(0); 95 | return TRUE; 96 | } 97 | } 98 | return FALSE; 99 | } 100 | 101 | 102 | int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) 103 | { 104 | UNREFERENCED_PARAMETER(hPrevInstance); 105 | 106 | std::wstring loaderFullPath = Util::GetAppPathW(); 107 | std::wstring loaderCurrentDirectory = Path::GetDirectoryName(loaderFullPath); 108 | std::wstring krkrExeFullPath = std::wstring(); 109 | std::wstring krkrExeDirectory = std::wstring(); 110 | 111 | //获取启动参数 112 | { 113 | int argc = 0; 114 | LPWSTR* argv = ::CommandLineToArgvW(lpCmdLine, &argc); 115 | if (argc) 116 | { 117 | wchar_t* arg = argv[0]; 118 | 119 | krkrExeFullPath = std::wstring(arg); 120 | krkrExeDirectory = Path::GetDirectoryName(krkrExeFullPath); 121 | } 122 | ::LocalFree(argv); 123 | } 124 | 125 | g_LoaderFullPath = loaderFullPath; 126 | g_LoaderCurrentDirectory = loaderCurrentDirectory; 127 | g_KrkrExeFullPath = krkrExeFullPath; 128 | g_KrkrExeDirectory = krkrExeDirectory; 129 | 130 | //启动加载器 131 | { 132 | if (!krkrExeFullPath.empty() && krkrExeFullPath != loaderFullPath) 133 | { 134 | HWND hwnd = ::CreateDialogParamW((HINSTANCE)hInstance, MAKEINTRESOURCEW(IDD_MainForm), NULL, LoaderDialogWindProc, 0u); 135 | ::ShowWindow(hwnd, SW_NORMAL); 136 | 137 | MSG msg{ }; 138 | while (BOOL ret = ::GetMessageW(&msg, NULL, 0u, 0u)) 139 | { 140 | if (ret == -1) 141 | { 142 | return -1; 143 | } 144 | else 145 | { 146 | ::TranslateMessage(&msg); 147 | ::DispatchMessageW(&msg); 148 | } 149 | } 150 | } 151 | else 152 | { 153 | ::MessageBoxW(nullptr, L"请拖拽游戏主程序到启动器", L"错误", MB_OK); 154 | } 155 | } 156 | return 0; 157 | } 158 | -------------------------------------------------------------------------------- /CxdecExtractorLoader/CxdecExtractorLoader.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YeLikesss/KrkrExtractForCxdecV2/1829eb0abb8740f2db5b7fc497b7483372b5b6a0/CxdecExtractorLoader/CxdecExtractorLoader.rc -------------------------------------------------------------------------------- /CxdecExtractorLoader/CxdecExtractorLoader.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 16.0 15 | Win32Proj 16 | {26785597-4b94-439f-ae7e-4f41a4010be1} 17 | CxdecExtractorLoader 18 | 10.0 19 | CxdecExtractorLoader 20 | 21 | 22 | 23 | Application 24 | true 25 | v143 26 | Unicode 27 | 28 | 29 | Application 30 | false 31 | v143 32 | true 33 | Unicode 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | false 50 | true 51 | true 52 | false 53 | 54 | 55 | false 56 | true 57 | true 58 | false 59 | false 60 | 61 | 62 | 63 | Level3 64 | _WIN32_WINNT=0x601;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 65 | true 66 | stdcpp20 67 | false 68 | false 69 | AdvancedVectorExtensions2 70 | $(SolutionDir)Detours;$(SolutionDir)Common;%(AdditionalIncludeDirectories) 71 | MultiThreadedDebug 72 | false 73 | false 74 | false 75 | false 76 | false 77 | false 78 | false 79 | false 80 | false 81 | false 82 | false 83 | false 84 | false 85 | 86 | 87 | Windows 88 | true 89 | false 90 | false 91 | false 92 | false 93 | false 94 | false 95 | true 96 | false 97 | false 98 | false 99 | 100 | 101 | false 102 | 103 | 104 | 105 | 106 | Level3 107 | true 108 | true 109 | _WIN32_WINNT=0x601;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 110 | true 111 | stdcpp20 112 | false 113 | false 114 | AdvancedVectorExtensions2 115 | $(SolutionDir)Detours;$(SolutionDir)Common;%(AdditionalIncludeDirectories) 116 | MultiThreaded 117 | false 118 | false 119 | false 120 | false 121 | false 122 | false 123 | false 124 | false 125 | false 126 | false 127 | false 128 | 129 | 130 | false 131 | false 132 | false 133 | 134 | 135 | Windows 136 | true 137 | true 138 | false 139 | false 140 | false 141 | false 142 | false 143 | false 144 | 145 | 146 | false 147 | false 148 | false 149 | true 150 | false 151 | 152 | 153 | false 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 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 | -------------------------------------------------------------------------------- /CxdecExtractorLoader/CxdecExtractorLoader.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {1a3862fc-2e9c-40b5-9852-dc494c9ce530} 10 | 11 | 12 | {79160863-3116-4b56-8ddc-4a8907257830} 13 | 14 | 15 | 16 | 17 | 源文件 18 | 19 | 20 | Detours 21 | 22 | 23 | Detours 24 | 25 | 26 | Detours 27 | 28 | 29 | Detours 30 | 31 | 32 | Detours 33 | 34 | 35 | Common 36 | 37 | 38 | Common 39 | 40 | 41 | Common 42 | 43 | 44 | Common 45 | 46 | 47 | Common 48 | 49 | 50 | Common 51 | 52 | 53 | Common 54 | 55 | 56 | Common 57 | 58 | 59 | 60 | 61 | Detours 62 | 63 | 64 | 65 | Common 66 | 67 | 68 | Common 69 | 70 | 71 | Common 72 | 73 | 74 | Common 75 | 76 | 77 | Common 78 | 79 | 80 | Common 81 | 82 | 83 | Common 84 | 85 | 86 | Common 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /CxdecExtractorLoader/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ 生成的包含文件。 3 | // 供 CxdecExtractorLoader.rc 使用 4 | // 5 | #define IDI_MainIcon 101 6 | #define IDD_MainForm 102 7 | #define IDC_Extractor 1001 8 | #define IDC_StringDumper 1002 9 | #define IDC_KeyDumper 1003 10 | 11 | // Next default values for new objects 12 | // 13 | #ifdef APSTUDIO_INVOKED 14 | #ifndef APSTUDIO_READONLY_SYMBOLS 15 | #define _APS_NEXT_RESOURCE_VALUE 104 16 | #define _APS_NEXT_COMMAND_VALUE 40001 17 | #define _APS_NEXT_CONTROL_VALUE 1004 18 | #define _APS_NEXT_SYMED_VALUE 101 19 | #endif 20 | #endif 21 | -------------------------------------------------------------------------------- /CxdecExtractorUI/CxdecExtractorUI.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YeLikesss/KrkrExtractForCxdecV2/1829eb0abb8740f2db5b7fc497b7483372b5b6a0/CxdecExtractorUI/CxdecExtractorUI.rc -------------------------------------------------------------------------------- /CxdecExtractorUI/CxdecExtractorUI.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 16.0 15 | Win32Proj 16 | {313d8951-b8b6-482a-86bc-8c4be0202d97} 17 | CxdecExtractorUI 18 | 10.0 19 | 20 | 21 | 22 | DynamicLibrary 23 | true 24 | v143 25 | Unicode 26 | 27 | 28 | DynamicLibrary 29 | false 30 | v143 31 | true 32 | Unicode 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | false 48 | false 49 | false 50 | false 51 | 52 | 53 | false 54 | false 55 | false 56 | false 57 | false 58 | 59 | 60 | 61 | Level3 62 | false 63 | _WIN32_WINNT=0x601;WIN32;_DEBUG;CXDECEXTRACTORUI_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 64 | true 65 | NotUsing 66 | pch.h 67 | false 68 | AdvancedVectorExtensions2 69 | false 70 | MultiThreadedDebug 71 | false 72 | false 73 | false 74 | false 75 | false 76 | false 77 | false 78 | false 79 | false 80 | 81 | 82 | false 83 | false 84 | false 85 | 86 | 87 | Windows 88 | true 89 | false 90 | false 91 | false 92 | false 93 | false 94 | 95 | 96 | true 97 | false 98 | false 99 | false 100 | false 101 | 102 | 103 | false 104 | 105 | 106 | 107 | 108 | Level3 109 | true 110 | true 111 | false 112 | _WIN32_WINNT=0x601;WIN32;NDEBUG;CXDECEXTRACTORUI_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 113 | true 114 | NotUsing 115 | pch.h 116 | false 117 | AdvancedVectorExtensions2 118 | false 119 | false 120 | MultiThreaded 121 | false 122 | false 123 | false 124 | false 125 | false 126 | false 127 | false 128 | false 129 | false 130 | 131 | 132 | false 133 | false 134 | false 135 | 136 | 137 | Windows 138 | true 139 | true 140 | false 141 | false 142 | false 143 | false 144 | false 145 | false 146 | 147 | 148 | true 149 | false 150 | false 151 | false 152 | false 153 | 154 | 155 | false 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /CxdecExtractorUI/CxdecExtractorUI.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 源文件 20 | 21 | 22 | 23 | 24 | 头文件 25 | 26 | 27 | 28 | 29 | 资源文件 30 | 31 | 32 | -------------------------------------------------------------------------------- /CxdecExtractorUI/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resource.h" 3 | 4 | #include 5 | #pragma comment(lib,"shlwapi.lib") 6 | 7 | /// 8 | /// 最大路径 9 | /// 10 | constexpr size_t MaxPath = 1024u; 11 | 12 | typedef void (WINAPI* tExtractFunc)(const wchar_t* packageName); 13 | static tExtractFunc g_ExtractPackage = nullptr; 14 | 15 | /// 16 | /// 主窗体消息循环 17 | /// 18 | /// 窗口句柄 19 | /// 消息 20 | /// 21 | /// 22 | INT_PTR CALLBACK ExtractorDialogWindProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 23 | { 24 | switch (msg) 25 | { 26 | case WM_DROPFILES: 27 | { 28 | HDROP hDrop = (HDROP)wParam; 29 | wchar_t fullName[MaxPath]; 30 | //只获取第一项 31 | if (UINT strLen = ::DragQueryFileW(hDrop, 0u, fullName, MaxPath)) 32 | { 33 | DWORD attr = ::GetFileAttributesW(fullName); 34 | if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE) 35 | { 36 | const wchar_t* fileName = ::PathFindFileNameW(fullName); 37 | g_ExtractPackage(fileName); 38 | } 39 | ::DragFinish(hDrop); 40 | } 41 | return TRUE; 42 | } 43 | case WM_CLOSE: 44 | { 45 | ::DestroyWindow(hwnd); 46 | return TRUE; 47 | } 48 | case WM_DESTROY: 49 | { 50 | ::PostQuitMessage(0); 51 | return TRUE; 52 | } 53 | } 54 | return FALSE; 55 | } 56 | 57 | 58 | /// 59 | /// 窗口代码 60 | /// 61 | /// 模块基地址 62 | DWORD WINAPI WinExtractorEntry(LPVOID hInstance) 63 | { 64 | HWND hwnd = ::CreateDialogParamW((HINSTANCE)hInstance, MAKEINTRESOURCEW(IDD_MainForm), NULL, ExtractorDialogWindProc, 0u); 65 | ::ShowWindow(hwnd, SW_NORMAL); 66 | 67 | MSG msg{ }; 68 | while (BOOL ret = ::GetMessageW(&msg, NULL, 0u, 0u)) 69 | { 70 | if (ret == -1) 71 | { 72 | return -1; 73 | } 74 | else 75 | { 76 | ::TranslateMessage(&msg); 77 | ::DispatchMessageW(&msg); 78 | } 79 | } 80 | return 0u; 81 | } 82 | 83 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 84 | { 85 | UNREFERENCED_PARAMETER(lpReserved); 86 | switch (ul_reason_for_call) 87 | { 88 | case DLL_PROCESS_ATTACH: 89 | { 90 | constexpr const wchar_t CoreDllNameW[] = L"CxdecExtractor.dll"; 91 | 92 | wchar_t moduleFullPath[MaxPath]; 93 | DWORD strLen = ::GetModuleFileNameW(hModule, moduleFullPath, MaxPath); 94 | wchar_t* dllName = ::PathFindFileNameW(moduleFullPath); 95 | memcpy(dllName, CoreDllNameW, sizeof(CoreDllNameW)); 96 | 97 | if (HMODULE coreBase = ::LoadLibraryW(moduleFullPath)) 98 | { 99 | g_ExtractPackage = (tExtractFunc)::GetProcAddress(coreBase, "ExtractPackage"); 100 | if (HANDLE hThread = ::CreateThread(NULL, 0u, WinExtractorEntry, hModule, 0u, NULL)) 101 | { 102 | ::CloseHandle(hThread); 103 | } 104 | } 105 | else 106 | { 107 | ::MessageBoxW(nullptr, L"CxdecExtractor.dll加载失败", L"错误", MB_OK); 108 | } 109 | break; 110 | } 111 | case DLL_THREAD_ATTACH: 112 | case DLL_THREAD_DETACH: 113 | break; 114 | case DLL_PROCESS_DETACH: 115 | { 116 | break; 117 | } 118 | } 119 | return TRUE; 120 | } 121 | 122 | extern "C" __declspec(dllexport) void Dummy(){} -------------------------------------------------------------------------------- /CxdecExtractorUI/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ 生成的包含文件。 3 | // 供 CxdecExtractorUI.rc 使用 4 | // 5 | #define IDD_MainForm 101 6 | #define IDC_FileDrop 1003 7 | 8 | // Next default values for new objects 9 | // 10 | #ifdef APSTUDIO_INVOKED 11 | #ifndef APSTUDIO_READONLY_SYMBOLS 12 | #define _APS_NEXT_RESOURCE_VALUE 103 13 | #define _APS_NEXT_COMMAND_VALUE 40001 14 | #define _APS_NEXT_CONTROL_VALUE 1004 15 | #define _APS_NEXT_SYMED_VALUE 101 16 | #endif 17 | #endif 18 | -------------------------------------------------------------------------------- /CxdecStringDumper/Application.cpp: -------------------------------------------------------------------------------- 1 | #include "Application.h" 2 | #include "path.h" 3 | #include "util.h" 4 | #include "ExtendUtils.h" 5 | 6 | namespace Engine 7 | { 8 | /// 9 | /// 单实例 10 | /// 11 | static Application* g_Instance = nullptr; 12 | 13 | //Hook插件功能 14 | tTVPV2LinkProc g_V2Link = nullptr; 15 | HRESULT __stdcall HookV2Link(iTVPFunctionExporter* exporter) 16 | { 17 | HRESULT result = g_V2Link(exporter); 18 | HookUtils::InlineHook::UnHook(g_V2Link, HookV2Link); 19 | g_V2Link = nullptr; 20 | 21 | //初始化插件 22 | Application::GetInstance()->InitializeTVPEngine(exporter); 23 | 24 | return result; 25 | } 26 | 27 | //Hook插件加载 28 | auto g_GetProcAddressFunction = ::GetProcAddress; 29 | FARPROC WINAPI HookGetProcAddress(HMODULE hModule, LPCSTR lpProcName) 30 | { 31 | FARPROC result = g_GetProcAddressFunction(hModule, lpProcName); 32 | if (result) 33 | { 34 | // 忽略序号导出 35 | if (HIWORD(lpProcName) != 0) 36 | { 37 | if (strcmp(lpProcName, "V2Link") == 0) 38 | { 39 | //Nt头偏移 40 | PIMAGE_NT_HEADERS ntHeader = PIMAGE_NT_HEADERS((ULONG_PTR)hModule + ((PIMAGE_DOS_HEADER)hModule)->e_lfanew); 41 | //可选头大小 42 | DWORD optionalHeaderSize = ntHeader->FileHeader.SizeOfOptionalHeader; 43 | //第一个节表(代码段) 44 | PIMAGE_SECTION_HEADER codeSectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)ntHeader + sizeof(ntHeader->Signature) + sizeof(IMAGE_FILE_HEADER) + optionalHeaderSize); 45 | 46 | DWORD codeStartRva = codeSectionHeader->VirtualAddress; //代码段起始RVA 47 | DWORD codeSize = codeSectionHeader->SizeOfRawData; //代码段大小 48 | 49 | ULONG_PTR codeStartVa = (ULONG_PTR)hModule + codeStartRva; //代码段起始VA 50 | 51 | //初始化 52 | Application* app = Application::GetInstance(); 53 | if (!app->IsTVPEngineInitialize()) 54 | { 55 | g_V2Link = (tTVPV2LinkProc)result; 56 | HookUtils::InlineHook::Hook(g_V2Link, HookV2Link); 57 | } 58 | 59 | //HashStringDumper接口 60 | HashCore* dumper = app->GetStringDumper(); 61 | if (!dumper->IsInitialized()) 62 | { 63 | dumper->Initialize((PVOID)codeStartVa, codeSize); 64 | } 65 | 66 | //初始化完毕 解除Hook 67 | if (dumper->IsInitialized()) 68 | { 69 | HookUtils::InlineHook::UnHook(g_GetProcAddressFunction, HookGetProcAddress); 70 | } 71 | } 72 | } 73 | } 74 | return result; 75 | } 76 | 77 | 78 | //**********Application***********// 79 | Application::Application() 80 | { 81 | this->mCurrentDirectoryPath = Path::GetDirectoryName(Util::GetModulePathW(::GetModuleHandleW(NULL))); 82 | this->mTVPExporterInitialized = false; 83 | 84 | //单例Dumper 85 | this->mStringDumper = HashCore::GetInstance(); 86 | 87 | //设置解包输出路径 88 | this->mStringDumper->SetOutputDirectory(this->mCurrentDirectoryPath); 89 | } 90 | 91 | Application::~Application() 92 | { 93 | if (this->mStringDumper) 94 | { 95 | //单例Dumper释放 96 | HashCore::Release(); 97 | this->mStringDumper = nullptr; 98 | } 99 | } 100 | 101 | void Application::InitializeModule(HMODULE hModule) 102 | { 103 | this->mModuleDirectoryPath = Path::GetDirectoryName(Util::GetModulePathW(hModule)); 104 | } 105 | 106 | void Application::InitializeTVPEngine(iTVPFunctionExporter* exporter) 107 | { 108 | this->mTVPExporterInitialized = TVPInitImportStub(exporter); 109 | TVPSetCommandLine(L"-debugwin", L"yes"); 110 | } 111 | 112 | bool Application::IsTVPEngineInitialize() 113 | { 114 | return this->mTVPExporterInitialized; 115 | } 116 | 117 | HashCore* Application::GetStringDumper() 118 | { 119 | return this->mStringDumper; 120 | } 121 | 122 | //********====Static====********// 123 | 124 | Application* Application::GetInstance() 125 | { 126 | return g_Instance; 127 | } 128 | 129 | void Application::Initialize(HMODULE hModule) 130 | { 131 | g_Instance = new Application(); 132 | g_Instance->InitializeModule(hModule); 133 | 134 | //Hook 135 | HookUtils::InlineHook::Hook(g_GetProcAddressFunction, HookGetProcAddress); 136 | } 137 | 138 | void Application::Release() 139 | { 140 | if (g_Instance) 141 | { 142 | delete g_Instance; 143 | g_Instance = nullptr; 144 | } 145 | } 146 | 147 | //================================// 148 | } 149 | 150 | -------------------------------------------------------------------------------- /CxdecStringDumper/Application.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "HashCore.h" 5 | 6 | namespace Engine 7 | { 8 | using tTVPV2LinkProc = HRESULT(__stdcall*)(iTVPFunctionExporter*); 9 | using tTVPV2UnlinkProc = HRESULT(__stdcall*)(); 10 | 11 | class Application 12 | { 13 | private: 14 | Application(); 15 | Application(const Application&) = delete; 16 | Application(Application&&) = delete; 17 | Application& operator=(const Application&) = delete; 18 | Application& operator=(Application&&) = delete; 19 | ~Application(); 20 | 21 | private: 22 | 23 | std::wstring mModuleDirectoryPath; //dll目录 24 | std::wstring mCurrentDirectoryPath; //游戏当前目录 25 | HashCore* mStringDumper; //Hash字符串dump 26 | bool mTVPExporterInitialized; //插件初始化成功标志 27 | 28 | public: 29 | 30 | /// 31 | /// 设置模块信息 32 | /// 33 | /// 模块信息 34 | void InitializeModule(HMODULE hModule); 35 | 36 | /// 37 | /// 初始化插件 38 | /// 39 | /// 插件导出函数 40 | void InitializeTVPEngine(iTVPFunctionExporter* exporter); 41 | 42 | /// 43 | /// 获取插件是否初始化完毕 44 | /// 45 | /// True已初始化 False未初始化 46 | bool IsTVPEngineInitialize(); 47 | 48 | /// 49 | /// 获取解包器 50 | /// 51 | /// 52 | HashCore* GetStringDumper(); 53 | 54 | /// 55 | /// 获取对象实例 56 | /// 57 | static Application* GetInstance(); 58 | /// 59 | /// 初始化 60 | /// 61 | /// 模块信息 62 | static void Initialize(HMODULE hModule); 63 | /// 64 | /// 释放 65 | /// 66 | static void Release(); 67 | }; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /CxdecStringDumper/CxdecStringDumper.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 16.0 15 | Win32Proj 16 | {3adcce05-606f-4818-833d-e0e78230e8b0} 17 | CxdecStringDumper 18 | 10.0 19 | 20 | 21 | 22 | DynamicLibrary 23 | true 24 | v143 25 | Unicode 26 | 27 | 28 | DynamicLibrary 29 | false 30 | v143 31 | true 32 | Unicode 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | false 48 | false 49 | false 50 | 51 | 52 | false 53 | false 54 | false 55 | false 56 | 57 | 58 | 59 | Level3 60 | false 61 | _WIN32_WINNT=0x601;WIN32;_DEBUG;CXDECSTRINGDUMPER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 62 | true 63 | NotUsing 64 | pch.h 65 | stdcpp20 66 | false 67 | false 68 | false 69 | false 70 | AdvancedVectorExtensions2 71 | false 72 | false 73 | false 74 | false 75 | false 76 | false 77 | true 78 | $(SolutionDir)Common;$(SolutionDir)KrkrPlugin;$(SolutionDir)Detours;%(AdditionalIncludeDirectories) 79 | MultiThreadedDebug 80 | false 81 | false 82 | false 83 | false 84 | 85 | 86 | Windows 87 | true 88 | false 89 | false 90 | false 91 | false 92 | false 93 | true 94 | false 95 | false 96 | false 97 | false 98 | 99 | 100 | false 101 | 102 | 103 | 104 | 105 | Level3 106 | true 107 | true 108 | false 109 | _WIN32_WINNT=0x601;WIN32;NDEBUG;CXDECSTRINGDUMPER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 110 | true 111 | NotUsing 112 | pch.h 113 | stdcpp20 114 | false 115 | false 116 | false 117 | false 118 | AdvancedVectorExtensions2 119 | false 120 | false 121 | false 122 | false 123 | false 124 | false 125 | false 126 | false 127 | false 128 | MultiThreaded 129 | $(SolutionDir)Common;$(SolutionDir)KrkrPlugin;$(SolutionDir)Detours;%(AdditionalIncludeDirectories) 130 | false 131 | false 132 | 133 | 134 | Windows 135 | true 136 | true 137 | false 138 | false 139 | false 140 | false 141 | false 142 | false 143 | false 144 | false 145 | false 146 | true 147 | false 148 | 149 | 150 | false 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 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 | -------------------------------------------------------------------------------- /CxdecStringDumper/CxdecStringDumper.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {31cfe412-f303-4fd8-917a-2f1b7fd82798} 10 | 11 | 12 | {49ec2efa-ba13-4ca5-9527-c5e8b8e5ea1e} 13 | 14 | 15 | {50a1afa9-bab3-4a98-ab88-687f26d688d5} 16 | 17 | 18 | 19 | 20 | 源文件 21 | 22 | 23 | Common 24 | 25 | 26 | Common 27 | 28 | 29 | Common 30 | 31 | 32 | Common 33 | 34 | 35 | Common 36 | 37 | 38 | Common 39 | 40 | 41 | Common 42 | 43 | 44 | Common 45 | 46 | 47 | Detours 48 | 49 | 50 | Detours 51 | 52 | 53 | Detours 54 | 55 | 56 | Detours 57 | 58 | 59 | Detours 60 | 61 | 62 | KrkrPlugin 63 | 64 | 65 | 源文件 66 | 67 | 68 | 源文件 69 | 70 | 71 | 72 | 73 | Common 74 | 75 | 76 | Common 77 | 78 | 79 | Common 80 | 81 | 82 | Common 83 | 84 | 85 | Common 86 | 87 | 88 | Common 89 | 90 | 91 | Common 92 | 93 | 94 | Common 95 | 96 | 97 | Detours 98 | 99 | 100 | KrkrPlugin 101 | 102 | 103 | 源文件 104 | 105 | 106 | 源文件 107 | 108 | 109 | 源文件 110 | 111 | 112 | -------------------------------------------------------------------------------- /CxdecStringDumper/ExtendUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "detours.h" 5 | namespace Engine 6 | { 7 | namespace HookUtils 8 | { 9 | class InlineHook 10 | { 11 | public: 12 | InlineHook() = delete; 13 | InlineHook(const InlineHook&) = delete; 14 | InlineHook(InlineHook&&) = delete; 15 | InlineHook& operator=(const InlineHook&) = delete; 16 | InlineHook& operator=(InlineHook&&) = delete; 17 | ~InlineHook() = delete; 18 | 19 | 20 | template 21 | static void Hook(T& OriginalFunction, T DetourFunction) 22 | { 23 | DetourUpdateThread(GetCurrentThread()); 24 | DetourTransactionBegin(); 25 | DetourAttach(&(PVOID&)OriginalFunction, (PVOID&)DetourFunction); 26 | DetourTransactionCommit(); 27 | } 28 | 29 | template 30 | static void UnHook(T& OriginalFunction, T DetourFunction) 31 | { 32 | DetourUpdateThread(GetCurrentThread()); 33 | DetourTransactionBegin(); 34 | DetourDetach(&(PVOID&)OriginalFunction, (PVOID&)DetourFunction); 35 | DetourTransactionCommit(); 36 | } 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CxdecStringDumper/HashCore.cpp: -------------------------------------------------------------------------------- 1 | #include "HashCore.h" 2 | #include "pe.h" 3 | #include "file.h" 4 | #include "directory.h" 5 | #include "path.h" 6 | #include "stringhelper.h" 7 | #include "ExtendUtils.h" 8 | 9 | namespace Engine 10 | { 11 | //**********IStringHasher***********// 12 | tjs_int IStringHasher::GetSaltLength() const 13 | { 14 | return this->mSaltSize; 15 | } 16 | 17 | const tjs_uint8* IStringHasher::GetSaltBytes() const 18 | { 19 | return this->mSalt; 20 | } 21 | 22 | IStringHasher::VptrTable* IStringHasher::GetVptrTable() 23 | { 24 | return *(IStringHasher::VptrTable**)this; 25 | } 26 | 27 | void IStringHasher::SetVptrTable(const VptrTable* vt) 28 | { 29 | *(const IStringHasher::VptrTable**)this = vt; 30 | } 31 | //================================// 32 | 33 | 34 | //*****************Dumper******************// 35 | 36 | static HashCore* g_Instance = nullptr; //单实例 37 | HashCore::tCreateCompoundStorageMedia g_CreateStorageMediaFunc = nullptr; //TVPStorageMedia[Cxdec]接口 38 | 39 | const CompoundStorageMedia* g_StorageMedia = nullptr; //封包管理媒体 40 | const IStringHasher::VptrTable* g_PathNameHasherOriginalVtPtr = nullptr; //文件夹路径Hash原虚表指针 41 | const IStringHasher::VptrTable* g_FileNameHasherOriginalVtPtr = nullptr; //文件名Hash原虚表指针 42 | 43 | IStringHasher::VptrTable g_PathNameHasherHookVt; //文件夹路径Hash Hook虚表 44 | IStringHasher::VptrTable g_FileNameHasherHookVt; //文件名Hash Hook虚表 45 | 46 | tjs_int __fastcall HookPathNameHasherCalcute(IStringHasher* thisObj, void* unusedEdx, tTJSVariant* hashValueRet, const tTJSString* str, const tTJSString* seed); 47 | tjs_int __fastcall HookFileNameHasherCalcute(IStringHasher* thisObj, void* unusedEdx, tTJSVariant* hashValueRet, const tTJSString* str, const tTJSString* seed); 48 | 49 | //创建Media Hook 50 | tjs_error __cdecl HookCreateCompoundStorageMedia(CompoundStorageMedia** retTVPStorageMedia, tTJSVariant* tjsVarPrefix, int argc, void* argv) 51 | { 52 | tjs_error result = g_CreateStorageMediaFunc(retTVPStorageMedia, tjsVarPrefix, argc, argv); 53 | if (TJS_SUCCEEDED(result)) 54 | { 55 | //Unhook 56 | HookUtils::InlineHook::UnHook(g_CreateStorageMediaFunc, HookCreateCompoundStorageMedia); 57 | 58 | //获取媒体对象 59 | CompoundStorageMedia* storageMedia = *retTVPStorageMedia; 60 | g_StorageMedia = storageMedia; 61 | 62 | //打印Hash参数 63 | { 64 | HashCore* dumper = g_Instance; 65 | Log::Logger& uniLogger = dumper->mUniversalLogger; 66 | 67 | uniLogger.WriteUnicode(L"Hash Seed:%s\r\n", storageMedia->HasherSeed.c_str()); 68 | uniLogger.WriteUnicode(L"PathNameHasherSalt:%s\r\n", StringHelper::BytesToHexStringW(storageMedia->PathNameHasher->GetSaltBytes(), storageMedia->PathNameHasher->GetSaltLength()).c_str()); 69 | uniLogger.WriteUnicode(L"FileNameHasherSalt:%s\r\n", StringHelper::BytesToHexStringW(storageMedia->FileNameHasher->GetSaltBytes(), storageMedia->FileNameHasher->GetSaltLength()).c_str()); 70 | } 71 | 72 | //文件夹路径Hash虚表Hook 73 | { 74 | IStringHasher::VptrTable* pnHasherVt = storageMedia->PathNameHasher->GetVptrTable(); 75 | g_PathNameHasherOriginalVtPtr = pnHasherVt; 76 | 77 | g_PathNameHasherHookVt = *pnHasherVt; 78 | g_PathNameHasherHookVt.Calculate = HookPathNameHasherCalcute; 79 | storageMedia->PathNameHasher->SetVptrTable(&g_PathNameHasherHookVt); 80 | } 81 | 82 | //文件名Hash虚表Hook 83 | { 84 | IStringHasher::VptrTable* fnHasherVt = storageMedia->FileNameHasher->GetVptrTable(); 85 | g_FileNameHasherOriginalVtPtr = fnHasherVt; 86 | 87 | g_FileNameHasherHookVt = *fnHasherVt; 88 | g_FileNameHasherHookVt.Calculate = HookFileNameHasherCalcute; 89 | storageMedia->FileNameHasher->SetVptrTable(&g_FileNameHasherHookVt); 90 | } 91 | } 92 | return result; 93 | } 94 | 95 | //文件夹路径Hash计算Hook fastcall模拟thiscall 96 | tjs_int __fastcall HookPathNameHasherCalcute(IStringHasher* thisObj, void* unusedEdx, tTJSVariant* hashValueRet, const tTJSString* str, const tTJSString* seed) 97 | { 98 | tjs_int len = g_PathNameHasherOriginalVtPtr->Calculate(thisObj, nullptr, hashValueRet, str, seed); 99 | 100 | const wchar_t* relativeDirPath = str->c_str(); 101 | //空文件夹替换 102 | if (*relativeDirPath == L'\0') 103 | { 104 | relativeDirPath = L"%EmptyString%"; 105 | } 106 | 107 | tTJSVariantOctet* hashValue = hashValueRet->AsOctetNoAddRef(); 108 | //打印 String[Sign]Hash[NewLine] 109 | g_Instance->mDirectoryHashLogger.WriteUnicode(L"%s%s%s\r\n", relativeDirPath, HashCore::Split, StringHelper::BytesToHexStringW(hashValue->GetData(), hashValue->GetLength()).c_str()); 110 | 111 | return len; 112 | } 113 | 114 | //文件名Hash计算Hook fastcall模拟thiscall 115 | tjs_int __fastcall HookFileNameHasherCalcute(IStringHasher* thisObj, void* unusedEdx, tTJSVariant* hashValueRet, const tTJSString* str, const tTJSString* seed) 116 | { 117 | tjs_int len = g_FileNameHasherOriginalVtPtr->Calculate(thisObj, nullptr, hashValueRet, str, seed); 118 | 119 | const wchar_t* fileName = str->c_str(); 120 | 121 | tTJSVariantOctet* hashValue = hashValueRet->AsOctetNoAddRef(); 122 | //打印 String[Sign]Hash[NewLine] 123 | g_Instance->mFileNameHashLogger.WriteUnicode(L"%s%s%s\r\n", fileName, HashCore::Split, StringHelper::BytesToHexStringW(hashValue->GetData(), hashValue->GetLength()).c_str()); 124 | 125 | return len; 126 | } 127 | 128 | //================================// 129 | 130 | 131 | //**********HashCore***********// 132 | HashCore::HashCore() 133 | { 134 | } 135 | 136 | HashCore::~HashCore() 137 | { 138 | } 139 | 140 | void HashCore::SetOutputDirectory(const std::wstring& directory) 141 | { 142 | std::wstring dumpOutDirectory = Path::Combine(directory, HashCore::FolderName); 143 | this->mDumperDirectoryPath = dumpOutDirectory; 144 | 145 | //创建输出目录 146 | Directory::Create(dumpOutDirectory); 147 | 148 | //日志初始化 149 | std::wstring directoryHashLogPath = Path::Combine(dumpOutDirectory, HashCore::DirectoryHashFileName); 150 | std::wstring fileNameHashLogPath = Path::Combine(dumpOutDirectory, HashCore::FileNameHashFileName); 151 | std::wstring universalLogPath = Path::Combine(dumpOutDirectory, HashCore::UniversalFileName); 152 | File::Delete(directoryHashLogPath); 153 | File::Delete(fileNameHashLogPath); 154 | File::Delete(universalLogPath); 155 | this->mDirectoryHashLogger.Open(directoryHashLogPath.c_str()); 156 | this->mFileNameHashLogger.Open(fileNameHashLogPath.c_str()); 157 | this->mUniversalLogger.Open(universalLogPath.c_str()); 158 | 159 | //写UTF-16LE bom头 160 | { 161 | WORD bom = 0xFEFF; 162 | this->mDirectoryHashLogger.WriteData(&bom, sizeof(bom)); 163 | this->mFileNameHashLogger.WriteData(&bom, sizeof(bom)); 164 | this->mUniversalLogger.WriteData(&bom, sizeof(bom)); 165 | } 166 | } 167 | 168 | void HashCore::Initialize(PVOID codeVa, DWORD codeSize) 169 | { 170 | PVOID createMedia = PE::SearchPattern(codeVa, codeSize, HashCore::CreateCompoundStorageMediaSignature, sizeof(HashCore::CreateCompoundStorageMediaSignature) - 1); 171 | if (createMedia) 172 | { 173 | g_CreateStorageMediaFunc = (tCreateCompoundStorageMedia)createMedia; 174 | 175 | //Hook创建媒体接口 176 | HookUtils::InlineHook::Hook(g_CreateStorageMediaFunc, HookCreateCompoundStorageMedia); 177 | } 178 | } 179 | 180 | bool HashCore::IsInitialized() 181 | { 182 | return g_CreateStorageMediaFunc != nullptr; 183 | } 184 | 185 | //************=====Static=====************// 186 | 187 | HashCore* HashCore::GetInstance() 188 | { 189 | if (g_Instance == nullptr) 190 | { 191 | g_Instance = new HashCore(); 192 | } 193 | return g_Instance; 194 | } 195 | 196 | void HashCore::Release() 197 | { 198 | if (g_Instance) 199 | { 200 | delete g_Instance; 201 | g_Instance = nullptr; 202 | } 203 | } 204 | //================================// 205 | } 206 | -------------------------------------------------------------------------------- /CxdecStringDumper/HashCore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "tp_stub.h" 6 | #include "log.h" 7 | 8 | namespace Engine 9 | { 10 | 11 | /// 12 | /// Hash接口 13 | /// 14 | class IStringHasher 15 | { 16 | public: 17 | //IStringHasher::Calculate接口 fastcall模拟thiscall 18 | using tCalculate = tjs_int(__fastcall*)(IStringHasher* thisObj, void* unuseEdx, tTJSVariant* hashValueRet, const tTJSString* str, const tTJSString* seed); 19 | 20 | /// 21 | /// 虚表结构 22 | /// 23 | struct VptrTable 24 | { 25 | void* Destruct; //析构 26 | tCalculate Calculate; //计算Hash 27 | }; 28 | 29 | private: 30 | //vtable offset:0x00 31 | tjs_uint8* mSalt; //盐指针 offset:0x04 32 | tjs_int mSaltSize; //盐大小 offset:0x08 33 | public: 34 | virtual ~IStringHasher() = 0; 35 | /// 36 | /// Hash计算 37 | /// 38 | /// 返回值 39 | /// 字符串 40 | /// 种子 41 | /// Hash长度 42 | virtual tjs_int Calculate(tTJSVariant* hashValueRet, const tTJSString* str, const tTJSString* seed) = 0; 43 | 44 | public: 45 | /// 46 | /// 获取盐的长度 47 | /// 48 | tjs_int GetSaltLength() const; 49 | /// 50 | /// 获取盐数据指针 51 | /// 52 | const tjs_uint8* GetSaltBytes() const; 53 | 54 | /// 55 | /// 获取虚表指针 (Hook用) 56 | /// 57 | VptrTable* GetVptrTable(); 58 | /// 59 | /// 设置虚表指针 (Hook用) 60 | /// 61 | void SetVptrTable(const VptrTable* vt); 62 | 63 | IStringHasher() = delete; 64 | IStringHasher(const IStringHasher&) = delete; 65 | IStringHasher(IStringHasher&&) = delete; 66 | IStringHasher& operator=(const IStringHasher&) = delete; 67 | IStringHasher& operator=(IStringHasher&&) = delete; 68 | }; 69 | 70 | /// 71 | /// 文件路径Hash接口 72 | /// 73 | class PathNameHasher : public IStringHasher 74 | { 75 | private: 76 | //IStringHasher Base offset:0x00 77 | tjs_uint8 mSaltData[0x10]; //盐数据 offset:0x0C 78 | 79 | public: 80 | PathNameHasher() = delete; 81 | PathNameHasher(const PathNameHasher&) = delete; 82 | PathNameHasher(PathNameHasher&&) = delete; 83 | PathNameHasher& operator=(const PathNameHasher&) = delete; 84 | PathNameHasher& operator=(PathNameHasher&&) = delete; 85 | }; 86 | 87 | /// 88 | /// 文件名Hash接口 89 | /// 90 | class FileNameHasher : public IStringHasher 91 | { 92 | private: 93 | //IStringHasher Base offset:0x00 94 | tjs_uint8 mSaltData[0x20]; //盐数据 offset:0x0C 95 | 96 | public: 97 | FileNameHasher() = delete; 98 | FileNameHasher(const FileNameHasher&) = delete; 99 | FileNameHasher(FileNameHasher&&) = delete; 100 | FileNameHasher& operator=(const FileNameHasher&) = delete; 101 | FileNameHasher& operator=(FileNameHasher&&) = delete; 102 | }; 103 | 104 | 105 | //Cxdec插件储存管理 size = 0x60 106 | //CompoundStorageMedia : public iTVPStorageMedia 107 | struct CompoundStorageMedia 108 | { 109 | void* VptrTable; //0x00 110 | int RefCount; //0x04 111 | DWORD field_8; //0x08 112 | tTJSString PreFix; //0x0C 资源前缀 113 | tTJSString HasherSeed; //0x10 Hash盐 114 | CRITICAL_SECTION CriticalSection; //0x14 临界区锁 115 | 116 | //0x2C 已加载资源表 117 | //std::unorder_map ArchiveEntryLoaded 118 | BYTE Reserve[0x20]; 119 | 120 | //0x4C 已加载封包名字 121 | //std::vector PackageLoaded 122 | tTJSString* Start; 123 | tTJSString* Position; 124 | tTJSString* End; 125 | 126 | //0x58 路径Hash对象 127 | IStringHasher* PathNameHasher; 128 | 129 | //0x5C 文件名Hash对象 130 | IStringHasher* FileNameHasher; 131 | }; 132 | 133 | 134 | class HashCore 135 | { 136 | public: 137 | using tCreateCompoundStorageMedia = tjs_error(__cdecl*)(CompoundStorageMedia** retTVPStorageMedia, tTJSVariant* tjsVarPrefix, int argc, void* argv); 138 | 139 | //Hook TVPStorageMedia创建 140 | friend tjs_error __cdecl HookCreateCompoundStorageMedia(CompoundStorageMedia** retTVPStorageMedia, tTJSVariant* tjsVarPrefix, int argc, void* argv); 141 | //Hook 文件夹路径计算 142 | friend tjs_int __fastcall HookPathNameHasherCalcute(IStringHasher* thisObj, void* unusedEdx, tTJSVariant* hashValueRet, const tTJSString* str, const tTJSString* seed); 143 | //Hook 文件名计算 144 | friend tjs_int __fastcall HookFileNameHasherCalcute(IStringHasher* thisObj, void* unusedEdx, tTJSVariant* hashValueRet, const tTJSString* str, const tTJSString* seed); 145 | 146 | private: 147 | static constexpr const wchar_t FolderName[] = L"StringHashDumper_Output"; //字符串Dump文件夹 148 | static constexpr const wchar_t DirectoryHashFileName[] = L"DirectoryHash.log"; //Dump路径日志文件名 149 | static constexpr const wchar_t FileNameHashFileName[] = L"FileNameHash.log"; //Dump文件名日志文件名 150 | static constexpr const wchar_t UniversalFileName[] = L"Universal.log"; //通用日志文件名 151 | 152 | private: 153 | static constexpr const char CreateCompoundStorageMediaSignature[] = "\x55\x8B\xEC\x6A\xFF\x68\x2A\x2A\x2A\x2A\x64\xA1\x00\x00\x00\x00\x50\x83\xEC\x08\x56\xA1\x2A\x2A\x2A\x2A\x33\xC5\x50\x8D\x45\xF4\x64\xA3\x00\x00\x00\x00\xA1\x2A\x2A\x2A\x2A\x85\xC0\x75\x12\x68\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\x83\xC4\x04\xA3\x2A\x2A\x2A\x2A\x8B\x75\x0C\x56\xFF\xD0\x83\xF8\x02\x74\x2A\xB8\x15\xFC\xFF\xFF\x8B\x4D\xF4\x64\x89\x0D\x00\x00\x00\x00\x59\x5E\x8B\xE5\x5D\xC3"; 154 | static constexpr const wchar_t Split[] = L"##YSig##"; //输出分隔符 155 | 156 | std::wstring mDumperDirectoryPath; //Dump输出目录 157 | Log::Logger mDirectoryHashLogger; //文件夹Hash日志 158 | Log::Logger mFileNameHashLogger; //文件名Hash日志 159 | Log::Logger mUniversalLogger; //通用日志 160 | 161 | private: 162 | HashCore(); 163 | HashCore(const HashCore&) = delete; 164 | HashCore(HashCore&&) = delete; 165 | HashCore& operator=(const HashCore&) = delete; 166 | HashCore& operator=(HashCore&&) = delete; 167 | ~HashCore(); 168 | 169 | public: 170 | 171 | /// 172 | /// 设置Hash表输出路径 173 | /// 174 | /// 文件夹绝对路径 175 | void SetOutputDirectory(const std::wstring& directory); 176 | 177 | /// 178 | /// 初始化 (特征码找接口) 179 | /// 180 | /// 代码起始地址 181 | /// 代码大小 182 | void Initialize(PVOID codeVa, DWORD codeSize); 183 | /// 184 | /// 检查是否已经初始化 185 | /// 186 | /// True已初始化 False未初始化 187 | bool IsInitialized(); 188 | 189 | 190 | /// 191 | /// 获取对象实例 192 | /// 193 | static HashCore* GetInstance(); 194 | /// 195 | /// 释放 196 | /// 197 | static void Release(); 198 | }; 199 | } 200 | 201 | 202 | -------------------------------------------------------------------------------- /CxdecStringDumper/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include "Application.h" 2 | 3 | #pragma comment(linker, "/MERGE:\".detourd=.data\"") 4 | #pragma comment(linker, "/MERGE:\".detourc=.rdata\"") 5 | 6 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 7 | { 8 | UNREFERENCED_PARAMETER(lpReserved); 9 | switch (ul_reason_for_call) 10 | { 11 | case DLL_PROCESS_ATTACH: 12 | { 13 | Engine::Application::Initialize(hModule); 14 | break; 15 | } 16 | case DLL_THREAD_ATTACH: 17 | case DLL_THREAD_DETACH: 18 | break; 19 | case DLL_PROCESS_DETACH: 20 | { 21 | Engine::Application::Release(); 22 | break; 23 | } 24 | } 25 | return TRUE; 26 | } 27 | 28 | extern "C" __declspec(dllexport) void Dummy() {} -------------------------------------------------------------------------------- /Detours/uimports.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Add DLLs to a module import table (uimports.cpp of detours.lib) 4 | // 5 | // Microsoft Research Detours Package, Version 4.0.1 6 | // 7 | // Copyright (c) Microsoft Corporation. All rights reserved. 8 | // 9 | // Note that this file is included into creatwth.cpp one or more times 10 | // (once for each supported module format). 11 | // 12 | 13 | #if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH 14 | #error detours.h version mismatch 15 | #endif 16 | 17 | // UpdateImports32 aka UpdateImports64 18 | static BOOL UPDATE_IMPORTS_XX(HANDLE hProcess, 19 | HMODULE hModule, 20 | __in_ecount(nDlls) LPCSTR *plpDlls, 21 | DWORD nDlls) 22 | { 23 | BOOL fSucceeded = FALSE; 24 | DWORD cbNew = 0; 25 | 26 | BYTE * pbNew = NULL; 27 | DWORD i; 28 | SIZE_T cbRead; 29 | DWORD n; 30 | 31 | PBYTE pbModule = (PBYTE)hModule; 32 | 33 | IMAGE_DOS_HEADER idh; 34 | ZeroMemory(&idh, sizeof(idh)); 35 | if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), &cbRead) 36 | || cbRead < sizeof(idh)) { 37 | 38 | DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n", 39 | pbModule, pbModule + sizeof(idh), GetLastError())); 40 | 41 | finish: 42 | if (pbNew != NULL) { 43 | delete[] pbNew; 44 | pbNew = NULL; 45 | } 46 | return fSucceeded; 47 | } 48 | 49 | IMAGE_NT_HEADERS_XX inh; 50 | ZeroMemory(&inh, sizeof(inh)); 51 | 52 | if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), &cbRead) 53 | || cbRead < sizeof(inh)) { 54 | DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n", 55 | pbModule + idh.e_lfanew, 56 | pbModule + idh.e_lfanew + sizeof(inh), 57 | GetLastError())); 58 | goto finish; 59 | } 60 | 61 | if (inh.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC_XX) { 62 | DETOUR_TRACE(("Wrong size image (%04x != %04x).\n", 63 | inh.OptionalHeader.Magic, IMAGE_NT_OPTIONAL_HDR_MAGIC_XX)); 64 | SetLastError(ERROR_INVALID_BLOCK); 65 | goto finish; 66 | } 67 | 68 | // Zero out the bound table so loader doesn't use it instead of our new table. 69 | inh.BOUND_DIRECTORY.VirtualAddress = 0; 70 | inh.BOUND_DIRECTORY.Size = 0; 71 | 72 | // Find the size of the mapped file. 73 | DWORD dwSec = idh.e_lfanew + 74 | FIELD_OFFSET(IMAGE_NT_HEADERS_XX, OptionalHeader) + 75 | inh.FileHeader.SizeOfOptionalHeader; 76 | 77 | for (i = 0; i < inh.FileHeader.NumberOfSections; i++) { 78 | IMAGE_SECTION_HEADER ish; 79 | ZeroMemory(&ish, sizeof(ish)); 80 | 81 | if (!ReadProcessMemory(hProcess, pbModule + dwSec + sizeof(ish) * i, &ish, 82 | sizeof(ish), &cbRead) 83 | || cbRead < sizeof(ish)) { 84 | 85 | DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %lu\n", 86 | pbModule + dwSec + sizeof(ish) * i, 87 | pbModule + dwSec + sizeof(ish) * (i + 1), 88 | GetLastError())); 89 | goto finish; 90 | } 91 | 92 | DETOUR_TRACE(("ish[%lu] : va=%08lx sr=%lu\n", i, ish.VirtualAddress, ish.SizeOfRawData)); 93 | 94 | // If the linker didn't suggest an IAT in the data directories, the 95 | // loader will look for the section of the import directory to be used 96 | // for this instead. Since we put out new IMPORT_DIRECTORY outside any 97 | // section boundary, the loader will not find it. So we provide one 98 | // explicitly to avoid the search. 99 | // 100 | if (inh.IAT_DIRECTORY.VirtualAddress == 0 && 101 | inh.IMPORT_DIRECTORY.VirtualAddress >= ish.VirtualAddress && 102 | inh.IMPORT_DIRECTORY.VirtualAddress < ish.VirtualAddress + ish.SizeOfRawData) { 103 | 104 | inh.IAT_DIRECTORY.VirtualAddress = ish.VirtualAddress; 105 | inh.IAT_DIRECTORY.Size = ish.SizeOfRawData; 106 | } 107 | } 108 | 109 | if (inh.IMPORT_DIRECTORY.VirtualAddress != 0 && inh.IMPORT_DIRECTORY.Size == 0) { 110 | 111 | // Don't worry about changing the PE file, 112 | // because the load information of the original PE header has been saved and will be restored. 113 | // The change here is just for the following code to work normally 114 | 115 | PIMAGE_IMPORT_DESCRIPTOR pImageImport = (PIMAGE_IMPORT_DESCRIPTOR)(pbModule + inh.IMPORT_DIRECTORY.VirtualAddress); 116 | 117 | do { 118 | IMAGE_IMPORT_DESCRIPTOR ImageImport; 119 | if (!ReadProcessMemory(hProcess, pImageImport, &ImageImport, sizeof(ImageImport), NULL)) { 120 | DETOUR_TRACE(("ReadProcessMemory failed: %lu\n", GetLastError())); 121 | goto finish; 122 | } 123 | inh.IMPORT_DIRECTORY.Size += sizeof(IMAGE_IMPORT_DESCRIPTOR); 124 | if (!ImageImport.Name) { 125 | break; 126 | } 127 | ++pImageImport; 128 | } while (TRUE); 129 | 130 | DWORD dwLastError = GetLastError(); 131 | OutputDebugString(TEXT("[This PE file has an import table, but the import table size is marked as 0. This is an error.") 132 | TEXT("If it is not repaired, the launched program will not work properly, Detours has automatically repaired its import table size for you! ! !]\r\n")); 133 | if (GetLastError() != dwLastError) { 134 | SetLastError(dwLastError); 135 | } 136 | } 137 | 138 | DETOUR_TRACE((" Imports: %p..%p\n", 139 | pbModule + inh.IMPORT_DIRECTORY.VirtualAddress, 140 | pbModule + inh.IMPORT_DIRECTORY.VirtualAddress + 141 | inh.IMPORT_DIRECTORY.Size)); 142 | 143 | // Calculate new import directory size. Note that since inh is from another 144 | // process, inh could have been corrupted. We need to protect against 145 | // integer overflow in allocation calculations. 146 | DWORD nOldDlls = inh.IMPORT_DIRECTORY.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR); 147 | DWORD obRem; 148 | if (DWordMult(sizeof(IMAGE_IMPORT_DESCRIPTOR), nDlls, &obRem) != S_OK) { 149 | DETOUR_TRACE(("too many new DLLs.\n")); 150 | goto finish; 151 | } 152 | DWORD obOld; 153 | if (DWordAdd(obRem, sizeof(IMAGE_IMPORT_DESCRIPTOR) * nOldDlls, &obOld) != S_OK) { 154 | DETOUR_TRACE(("DLL entries overflow.\n")); 155 | goto finish; 156 | } 157 | DWORD obTab = PadToDwordPtr(obOld); 158 | // Check for integer overflow. 159 | if (obTab < obOld) { 160 | DETOUR_TRACE(("DLL entries padding overflow.\n")); 161 | goto finish; 162 | } 163 | DWORD stSize; 164 | if (DWordMult(sizeof(DWORD_XX) * 4, nDlls, &stSize) != S_OK) { 165 | DETOUR_TRACE(("String table overflow.\n")); 166 | goto finish; 167 | } 168 | DWORD obDll; 169 | if (DWordAdd(obTab, stSize, &obDll) != S_OK) { 170 | DETOUR_TRACE(("Import table size overflow\n")); 171 | goto finish; 172 | } 173 | DWORD obStr = obDll; 174 | cbNew = obStr; 175 | for (n = 0; n < nDlls; n++) { 176 | if (DWordAdd(cbNew, PadToDword((DWORD)strlen(plpDlls[n]) + 1), &cbNew) != S_OK) { 177 | DETOUR_TRACE(("Overflow adding string table entry\n")); 178 | goto finish; 179 | } 180 | } 181 | pbNew = new BYTE [cbNew]; 182 | if (pbNew == NULL) { 183 | DETOUR_TRACE(("new BYTE [cbNew] failed.\n")); 184 | goto finish; 185 | } 186 | ZeroMemory(pbNew, cbNew); 187 | 188 | PBYTE pbBase = pbModule; 189 | PBYTE pbNext = pbBase 190 | + inh.OptionalHeader.BaseOfCode 191 | + inh.OptionalHeader.SizeOfCode 192 | + inh.OptionalHeader.SizeOfInitializedData 193 | + inh.OptionalHeader.SizeOfUninitializedData; 194 | if (pbBase < pbNext) { 195 | pbBase = pbNext; 196 | } 197 | DETOUR_TRACE(("pbBase = %p\n", pbBase)); 198 | 199 | PBYTE pbNewIid = FindAndAllocateNearBase(hProcess, pbModule, pbBase, cbNew); 200 | if (pbNewIid == NULL) { 201 | DETOUR_TRACE(("FindAndAllocateNearBase failed.\n")); 202 | goto finish; 203 | } 204 | 205 | PIMAGE_IMPORT_DESCRIPTOR piid = (PIMAGE_IMPORT_DESCRIPTOR)pbNew; 206 | IMAGE_THUNK_DATAXX *pt = NULL; 207 | 208 | DWORD obBase = (DWORD)(pbNewIid - pbModule); 209 | DWORD dwProtect = 0; 210 | 211 | if (inh.IMPORT_DIRECTORY.VirtualAddress != 0) { 212 | // Read the old import directory if it exists. 213 | DETOUR_TRACE(("IMPORT_DIRECTORY perms=%lx\n", dwProtect)); 214 | 215 | if (!ReadProcessMemory(hProcess, 216 | pbModule + inh.IMPORT_DIRECTORY.VirtualAddress, 217 | &piid[nDlls], 218 | nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR), &cbRead) 219 | || cbRead < nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR)) { 220 | 221 | DETOUR_TRACE(("ReadProcessMemory(imports) failed: %lu\n", GetLastError())); 222 | goto finish; 223 | } 224 | } 225 | 226 | for (n = 0; n < nDlls; n++) { 227 | HRESULT hrRet = StringCchCopyA((char*)pbNew + obStr, cbNew - obStr, plpDlls[n]); 228 | if (FAILED(hrRet)) { 229 | DETOUR_TRACE(("StringCchCopyA failed: %08lx\n", hrRet)); 230 | goto finish; 231 | } 232 | 233 | // After copying the string, we patch up the size "??" bits if any. 234 | hrRet = ReplaceOptionalSizeA((char*)pbNew + obStr, 235 | cbNew - obStr, 236 | DETOURS_STRINGIFY(DETOURS_BITS_XX)); 237 | if (FAILED(hrRet)) { 238 | DETOUR_TRACE(("ReplaceOptionalSizeA failed: %08lx\n", hrRet)); 239 | goto finish; 240 | } 241 | 242 | DWORD nOffset = obTab + (sizeof(IMAGE_THUNK_DATAXX) * (4 * n)); 243 | piid[n].OriginalFirstThunk = obBase + nOffset; 244 | 245 | // We need 2 thunks for the import table and 2 thunks for the IAT. 246 | // One for an ordinal import and one to mark the end of the list. 247 | pt = ((IMAGE_THUNK_DATAXX*)(pbNew + nOffset)); 248 | pt[0].u1.Ordinal = IMAGE_ORDINAL_FLAG_XX + 1; 249 | pt[1].u1.Ordinal = 0; 250 | 251 | nOffset = obTab + (sizeof(IMAGE_THUNK_DATAXX) * ((4 * n) + 2)); 252 | piid[n].FirstThunk = obBase + nOffset; 253 | pt = ((IMAGE_THUNK_DATAXX*)(pbNew + nOffset)); 254 | pt[0].u1.Ordinal = IMAGE_ORDINAL_FLAG_XX + 1; 255 | pt[1].u1.Ordinal = 0; 256 | piid[n].TimeDateStamp = 0; 257 | piid[n].ForwarderChain = 0; 258 | piid[n].Name = obBase + obStr; 259 | 260 | obStr += PadToDword((DWORD)strlen(plpDlls[n]) + 1); 261 | } 262 | _Analysis_assume_(obStr <= cbNew); 263 | 264 | #if 0 265 | for (i = 0; i < nDlls + nOldDlls; i++) { 266 | DETOUR_TRACE(("%8d. Look=%08x Time=%08x Fore=%08x Name=%08x Addr=%08x\n", 267 | i, 268 | piid[i].OriginalFirstThunk, 269 | piid[i].TimeDateStamp, 270 | piid[i].ForwarderChain, 271 | piid[i].Name, 272 | piid[i].FirstThunk)); 273 | if (piid[i].OriginalFirstThunk == 0 && piid[i].FirstThunk == 0) { 274 | break; 275 | } 276 | } 277 | #endif 278 | 279 | if (!WriteProcessMemory(hProcess, pbNewIid, pbNew, obStr, NULL)) { 280 | DETOUR_TRACE(("WriteProcessMemory(iid) failed: %lu\n", GetLastError())); 281 | goto finish; 282 | } 283 | 284 | DETOUR_TRACE(("obBaseBef = %08lx..%08lx\n", 285 | inh.IMPORT_DIRECTORY.VirtualAddress, 286 | inh.IMPORT_DIRECTORY.VirtualAddress + inh.IMPORT_DIRECTORY.Size)); 287 | DETOUR_TRACE(("obBaseAft = %08lx..%08lx\n", obBase, obBase + obStr)); 288 | 289 | // In this case the file didn't have an import directory in first place, 290 | // so we couldn't fix the missing IAT above. We still need to explicitly 291 | // provide an IAT to prevent to loader from looking for one. 292 | // 293 | if (inh.IAT_DIRECTORY.VirtualAddress == 0) { 294 | inh.IAT_DIRECTORY.VirtualAddress = obBase; 295 | inh.IAT_DIRECTORY.Size = cbNew; 296 | } 297 | 298 | inh.IMPORT_DIRECTORY.VirtualAddress = obBase; 299 | inh.IMPORT_DIRECTORY.Size = cbNew; 300 | 301 | /////////////////////// Update the NT header for the new import directory. 302 | // 303 | if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders, 304 | PAGE_EXECUTE_READWRITE, &dwProtect)) { 305 | DETOUR_TRACE(("VirtualProtectEx(inh) write failed: %lu\n", GetLastError())); 306 | goto finish; 307 | } 308 | 309 | inh.OptionalHeader.CheckSum = 0; 310 | 311 | if (!WriteProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) { 312 | DETOUR_TRACE(("WriteProcessMemory(idh) failed: %lu\n", GetLastError())); 313 | goto finish; 314 | } 315 | DETOUR_TRACE(("WriteProcessMemory(idh:%p..%p)\n", pbModule, pbModule + sizeof(idh))); 316 | 317 | if (!WriteProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), NULL)) { 318 | DETOUR_TRACE(("WriteProcessMemory(inh) failed: %lu\n", GetLastError())); 319 | goto finish; 320 | } 321 | DETOUR_TRACE(("WriteProcessMemory(inh:%p..%p)\n", 322 | pbModule + idh.e_lfanew, 323 | pbModule + idh.e_lfanew + sizeof(inh))); 324 | 325 | if (!VirtualProtectEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders, 326 | dwProtect, &dwProtect)) { 327 | DETOUR_TRACE(("VirtualProtectEx(idh) restore failed: %lu\n", GetLastError())); 328 | goto finish; 329 | } 330 | 331 | fSucceeded = TRUE; 332 | goto finish; 333 | } 334 | -------------------------------------------------------------------------------- /KrkrZCxdecV2.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.6.33801.468 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CxdecExtractor", "CxdecExtractor\CxdecExtractor.vcxproj", "{C43A55E8-8D0C-479E-BD44-A648D550E594}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CxdecExtractorLoader", "CxdecExtractorLoader\CxdecExtractorLoader.vcxproj", "{26785597-4B94-439F-AE7E-4F41A4010BE1}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CxdecExtractorUI", "CxdecExtractorUI\CxdecExtractorUI.vcxproj", "{313D8951-B8B6-482A-86BC-8C4BE0202D97}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CxdecStringDumper", "CxdecStringDumper\CxdecStringDumper.vcxproj", "{3ADCCE05-606F-4818-833D-E0E78230E8B0}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|x86 = Debug|x86 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {C43A55E8-8D0C-479E-BD44-A648D550E594}.Debug|x86.ActiveCfg = Debug|Win32 21 | {C43A55E8-8D0C-479E-BD44-A648D550E594}.Debug|x86.Build.0 = Debug|Win32 22 | {C43A55E8-8D0C-479E-BD44-A648D550E594}.Release|x86.ActiveCfg = Release|Win32 23 | {C43A55E8-8D0C-479E-BD44-A648D550E594}.Release|x86.Build.0 = Release|Win32 24 | {26785597-4B94-439F-AE7E-4F41A4010BE1}.Debug|x86.ActiveCfg = Debug|Win32 25 | {26785597-4B94-439F-AE7E-4F41A4010BE1}.Debug|x86.Build.0 = Debug|Win32 26 | {26785597-4B94-439F-AE7E-4F41A4010BE1}.Release|x86.ActiveCfg = Release|Win32 27 | {26785597-4B94-439F-AE7E-4F41A4010BE1}.Release|x86.Build.0 = Release|Win32 28 | {313D8951-B8B6-482A-86BC-8C4BE0202D97}.Debug|x86.ActiveCfg = Debug|Win32 29 | {313D8951-B8B6-482A-86BC-8C4BE0202D97}.Debug|x86.Build.0 = Debug|Win32 30 | {313D8951-B8B6-482A-86BC-8C4BE0202D97}.Release|x86.ActiveCfg = Release|Win32 31 | {313D8951-B8B6-482A-86BC-8C4BE0202D97}.Release|x86.Build.0 = Release|Win32 32 | {3ADCCE05-606F-4818-833D-E0E78230E8B0}.Debug|x86.ActiveCfg = Debug|Win32 33 | {3ADCCE05-606F-4818-833D-E0E78230E8B0}.Debug|x86.Build.0 = Debug|Win32 34 | {3ADCCE05-606F-4818-833D-E0E78230E8B0}.Release|x86.ActiveCfg = Release|Win32 35 | {3ADCCE05-606F-4818-833D-E0E78230E8B0}.Release|x86.Build.0 = Release|Win32 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {3C6BC094-81D3-4BCF-97F0-B13138A67353} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KrkrExtractV2 (ForCxdecV2) 动态工具集 2 |   适用于Wamsoft(Hxv4 2021.11+)加密解包/字符串<->Hash提取 3 | 4 | ## 环境 5 |   系统: Windows 7 SP1 x64 6 | 7 |   IDE: Visual Sudio 2022 8 | 9 |   编译器: MSVC2022 x86 10 | 11 | ## 与老版本区别 12 |   1.重构代码, 稍微看着没那么屎山 13 | 14 |   2.功能模块拆分为不同Dll 15 | 16 |   3.修复许多bug 17 | 18 | ## 如何使用 19 |   1. `CxdecExtractorLoader.exe`, `CxdecExtractor.dll`, `CxdecExtractorUI.dll`, `CxdecStringDumper.dll`保持同一目录 20 | 21 |   2. 保证你的游戏是Wamsoft KrkrZ Hxv4加密类型且加密认证已移除 22 | 23 |   3. 拖拽游戏exe到`CxdecExtractorLoader.exe`启动, 弹出模块选择对话框 24 | 25 |   4. 选择`加载解包模块`, 弹出解包对话框, 拖拽`xxx.xp3`到框内解包 26 | 27 |    4.1 `游戏目录\Extractor_Output\`为输出目录, 包含`xxx文件夹`的封包资源与`xxx.alst`的文件表 28 | 29 |    4.2 `工具目录\Extractor.log`为日志信息 30 | 31 |   5. 选择`加载字符串Hash提取模块`, 自动提取游戏运行时的字符串Hash映射表 32 | 33 |    5.1 `游戏目录\StringHashDumper_Output\`为输出目录 34 | 35 |    5.2 `DirectoryHash.log`为文件夹路径Hash映射表 36 | 37 |    5.3 `FileNameHash.log`为文件名Hash映射表 38 | 39 |    5.4 `Universal.log`为通用信息(Hash加密参数) 40 | 41 |   6. 选择`加载Key提取模块`(功能暂未实现) 42 | 43 |   7. 工具不会申请管理员权限进行弹出UAC提权, 游戏与工具务必不要放在C盘 44 | 45 |   8. 如出现错误标题的弹窗报错, 请检查上述步骤 46 | 47 | ## 常见问题 48 |   Q: 为什么没有资源文件名 49 | 50 |   A: 封包里面本来就没有文件名 51 | 52 |   Q: 解包对话框支持批量拖拽解包吗 53 | 54 |   A: 不支持, 仅支持单个封包逐个拖拽提取 55 | 56 |   Q: 解包响应框解包时候无响应 57 | 58 |   A: 没做多线程支持, 等它慢慢解完就好 59 | 60 |   Q: Hash映射表能一次性提取所有吗 61 | 62 |   A: 不能, 名字在脚本里面散落到处都是, 且不全 63 | 64 |   Q: 兼容Win7以外的系统吗 65 | 66 |   A: 理论上兼容, 不过没有测试, 有问题我也不知道 67 | 68 | ## 同类工具推荐 69 | 70 | * KrkrExtractV2 (ForCxdecV2)(本工具)  类型: 动态   解包: 一次性  文件名: 运行时 71 | 72 | * [KrkrDump](https://github.com/crskycode/KrkrDump)  类型: 动态   解包: 运行时  文件名: 运行时 73 | 74 | * [GARBro](https://github.com/crskycode/GARbro)  类型: 静态(需人工装填)   解包: 一次性  文件名: 无 --------------------------------------------------------------------------------