Repository: YeLikesss/CNGALTools
Branch: main
Commit: 8f389b0d1fdf
Files: 773
Total size: 16.0 MB
Directory structure:
gitextract_0qf8lc79/
├── .gitattributes
├── .gitignore
├── 001.NVL/
│ ├── BKEngine/
│ │ ├── BKEFileNameDumper/
│ │ │ ├── BKEFileNameDumper/
│ │ │ │ ├── BKEFileNameDumper.vcxproj
│ │ │ │ ├── BKEFileNameDumper.vcxproj.filters
│ │ │ │ └── dllmain.cpp
│ │ │ ├── BKEFileNameDumper.sln
│ │ │ ├── Common/
│ │ │ │ ├── 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
│ │ │ ├── Detours/
│ │ │ │ ├── creatwth.cpp
│ │ │ │ ├── detours.cpp
│ │ │ │ ├── detours.h
│ │ │ │ ├── disasm.cpp
│ │ │ │ ├── image.cpp
│ │ │ │ ├── modules.cpp
│ │ │ │ └── uimports.cpp
│ │ │ └── Loader/
│ │ │ ├── Loader.cpp
│ │ │ ├── Loader.vcxproj
│ │ │ └── Loader.vcxproj.filters
│ │ ├── BKEngine/
│ │ │ ├── BKEngine.sln
│ │ │ ├── BKEngineStatic/
│ │ │ │ ├── BKEngine/
│ │ │ │ │ ├── BKARCFileBase.cs
│ │ │ │ │ ├── BKARCFileV20.cs
│ │ │ │ │ ├── BKARCFileV21.cs
│ │ │ │ │ ├── BKARCFileV40.cs
│ │ │ │ │ ├── BZip2Helper.cs
│ │ │ │ │ └── ZstdHelper.cs
│ │ │ │ └── BKEngineStatic.csproj
│ │ │ └── ExtractGUI/
│ │ │ ├── ExtractGUI.csproj
│ │ │ ├── MainForm.Designer.cs
│ │ │ ├── MainForm.cs
│ │ │ ├── MainForm.resx
│ │ │ ├── Program.cs
│ │ │ └── Properties/
│ │ │ └── PublishProfiles/
│ │ │ └── FolderProfile64.pubxml
│ │ └── Manual.md
│ ├── NVLKrkr2/
│ │ ├── DataBase/
│ │ │ └── FileNameList/
│ │ │ ├── Conspiracy Field Fog Shadow.lst
│ │ │ ├── Conspiracy Field Snow Trap.lst
│ │ │ └── the Melody of Iris.lst
│ │ ├── Manual.md
│ │ ├── NVLKR2Extract/
│ │ │ ├── ExtractGUI/
│ │ │ │ ├── ExtractGUI.csproj
│ │ │ │ ├── MainForm.Designer.cs
│ │ │ │ ├── MainForm.cs
│ │ │ │ ├── MainForm.resx
│ │ │ │ ├── Program.cs
│ │ │ │ └── Properties/
│ │ │ │ └── PublishProfiles/
│ │ │ │ └── FolderProfile64.pubxml
│ │ │ ├── HashDecoder/
│ │ │ │ ├── BinaryDataConvert.cs
│ │ │ │ ├── HashDecoder.csproj
│ │ │ │ ├── MainForm.Designer.cs
│ │ │ │ ├── MainForm.cs
│ │ │ │ ├── MainForm.resx
│ │ │ │ ├── PathUtil.cs
│ │ │ │ ├── Program.cs
│ │ │ │ ├── Properties/
│ │ │ │ │ └── PublishProfiles/
│ │ │ │ │ └── FolderProfile64.pubxml
│ │ │ │ ├── TextCreator.Designer.cs
│ │ │ │ ├── TextCreator.cs
│ │ │ │ └── TextCreator.resx
│ │ │ ├── NVLKR2.sln
│ │ │ └── NVLKR2Static/
│ │ │ ├── NVLKR2Static.csproj
│ │ │ └── NvlKr2/
│ │ │ ├── GameKey.cs
│ │ │ ├── XP3Archive.cs
│ │ │ ├── XP3Filter.cs
│ │ │ └── Zlib.cs
│ │ └── NVLKrkrDump/
│ │ ├── Common/
│ │ │ ├── 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
│ │ ├── Detours/
│ │ │ ├── creatwth.cpp
│ │ │ ├── detours.cpp
│ │ │ ├── detours.h
│ │ │ ├── disasm.cpp
│ │ │ ├── image.cpp
│ │ │ ├── modules.cpp
│ │ │ └── uimports.cpp
│ │ ├── KrkrPlugin/
│ │ │ ├── tp_stub.cpp
│ │ │ └── tp_stub.h
│ │ ├── NVLKrkrDump/
│ │ │ ├── NVLKrkrDump.vcxproj
│ │ │ ├── NVLKrkrDump.vcxproj.filters
│ │ │ └── dllmain.cpp
│ │ ├── NVLKrkrDump.sln
│ │ └── NVLKrkrDumpLoader/
│ │ ├── KrkrDumpLoader.cpp
│ │ ├── KrkrDumpLoader.vcxproj
│ │ └── KrkrDumpLoader.vcxproj.filters
│ ├── NVLUnity/
│ │ ├── Manual.md
│ │ ├── NVLUnityDecryptor/
│ │ │ ├── DecryptorGui/
│ │ │ │ ├── DecryptorGui.csproj
│ │ │ │ ├── MainForm.Designer.cs
│ │ │ │ ├── MainForm.cs
│ │ │ │ ├── MainForm.resx
│ │ │ │ ├── Program.cs
│ │ │ │ └── Properties/
│ │ │ │ └── PublishProfiles/
│ │ │ │ └── FolderProfile64.pubxml
│ │ │ ├── NvlUnity.sln
│ │ │ └── NvlUnityDecrypt/
│ │ │ ├── NvlUnity/
│ │ │ │ └── ArchiveCrypto.cs
│ │ │ ├── NvlUnity.V1/
│ │ │ │ ├── GameDBV1.cs
│ │ │ │ └── NVLFilterV1.cs
│ │ │ └── NvlUnityDecryptor.csproj
│ │ └── NVLUnityScriptDumper/
│ │ ├── Common/
│ │ │ ├── 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
│ │ ├── Detours/
│ │ │ ├── creatwth.cpp
│ │ │ ├── detours.cpp
│ │ │ ├── detours.h
│ │ │ ├── disasm.cpp
│ │ │ ├── image.cpp
│ │ │ ├── modules.cpp
│ │ │ └── uimports.cpp
│ │ ├── DumperGUI/
│ │ │ ├── DumperGUI.rc
│ │ │ ├── DumperGUI.vcxproj
│ │ │ ├── DumperGUI.vcxproj.filters
│ │ │ ├── dllmain.cpp
│ │ │ └── resource.h
│ │ ├── Il2Cpp/
│ │ │ ├── Il2CppAPI.cpp
│ │ │ ├── Il2CppAPI.h
│ │ │ └── Il2CppClass.h
│ │ ├── Loader/
│ │ │ ├── Loader.cpp
│ │ │ ├── Loader.vcxproj
│ │ │ └── Loader.vcxproj.filters
│ │ ├── NVLUnityScriptDumper.sln
│ │ └── ScriptDumper/
│ │ ├── Export.def
│ │ ├── ScriptDumper.vcxproj
│ │ ├── ScriptDumper.vcxproj.filters
│ │ └── dllmain.cpp
│ └── NVLWeb/
│ ├── ConsoleTest/
│ │ ├── ConsoleTest.csproj
│ │ └── Program.cs
│ ├── EndOfTheWorldExtractor/
│ │ ├── EndOfTheWorldExtractor.csproj
│ │ └── Program.cs
│ ├── Manual.md
│ ├── NVLWeb.sln
│ └── NVLWebStatic/
│ ├── ASARPackage.cs
│ ├── Crypto.cs
│ ├── EndOfTheWorld.cs
│ ├── EntryProcess.cs
│ └── NVLWebStatic.csproj
├── 002.Strrationalism/
│ └── Snowing/
│ ├── Manual.md
│ └── SnowingExtract/
│ ├── ConsoleExecute/
│ │ ├── ConsoleExecute.csproj
│ │ └── Program.cs
│ ├── SnowingExtract.sln
│ ├── SnowingStatic/
│ │ ├── Snowing/
│ │ │ ├── AesHelper.cs
│ │ │ ├── Archive.cs
│ │ │ ├── ArchiveFile.cs
│ │ │ ├── MemorySearch.cs
│ │ │ ├── ScenarioArchive.cs
│ │ │ ├── StructureConvert.cs
│ │ │ └── TextureArchive.cs
│ │ ├── Snowing.Games/
│ │ │ └── GameKeys.cs
│ │ └── SnowingStatic.csproj
│ └── VainRiserExtractor/
│ ├── Program.cs
│ └── VainRiserExtractor.csproj
├── 003.BlueAngel/
│ ├── BlueAngelExtract/
│ │ ├── BlueAngelExtract.sln
│ │ ├── BlueAngelStaticExtract/
│ │ │ ├── BlueAngel/
│ │ │ │ ├── AssemblyEmulator.cs
│ │ │ │ ├── LZ4.cs
│ │ │ │ └── XP3Archive.cs
│ │ │ ├── BlueAngel.StarlightofAeons/
│ │ │ │ ├── Archive.cs
│ │ │ │ └── Key.cs
│ │ │ ├── BlueAngel.V1/
│ │ │ │ └── ArchiveCrypto.cs
│ │ │ └── BlueAngelStaticExtract.csproj
│ │ ├── ConsoleExecute/
│ │ │ ├── ConsoleExecute.csproj
│ │ │ └── Program.cs
│ │ └── StarlightofAeonsExtractor/
│ │ ├── Program.cs
│ │ └── StarlightofAeonsExtractor.csproj
│ ├── Manual.md
│ ├── TheCardinalMemoryNotch/
│ │ ├── ConsoleExecute/
│ │ │ ├── ConsoleExecute.csproj
│ │ │ └── Program.cs
│ │ ├── EngineCoreStatic/
│ │ │ ├── EngineCoreStatic.csproj
│ │ │ ├── SPKArchive.cs
│ │ │ ├── XP3Struct.cs
│ │ │ └── Zlib.cs
│ │ ├── ExtractorV1/
│ │ │ ├── ExtractorV1.csproj
│ │ │ └── Program.cs
│ │ ├── ProtectorFileDumper/
│ │ │ ├── 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
│ │ │ ├── ProtectorFileDumper.vcxproj
│ │ │ ├── ProtectorFileDumper.vcxproj.filters
│ │ │ └── dllmain.cpp
│ │ └── TheCardinalMemoryNotch.sln
│ └── TheCardinalMemoryNotchV2/
│ ├── KrkrFileDumper/
│ │ ├── 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
│ │ ├── Exports.def
│ │ ├── KrkrFileDumper.vcxproj
│ │ ├── KrkrFileDumper.vcxproj.filters
│ │ ├── KrkrPlugin/
│ │ │ ├── tp_stub.cpp
│ │ │ └── tp_stub.h
│ │ └── dllmain.cpp
│ ├── KrkrFileDumperLoader/
│ │ ├── 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
│ │ ├── KrkrFileDumperLoader.cpp
│ │ ├── KrkrFileDumperLoader.vcxproj
│ │ ├── KrkrFileDumperLoader.vcxproj.filters
│ │ ├── Ntdll/
│ │ │ ├── ntdll.h
│ │ │ ├── ntdll_x64.lib
│ │ │ ├── ntdll_x86.lib
│ │ │ ├── ntdllp_x64.lib
│ │ │ └── ntdllp_x86.lib
│ │ └── inlinestring.h
│ └── TheCardinalMemoryNotchV2.sln
├── 004.Fontainebleau/
│ ├── Manual.md
│ └── MeetInParisDumper/
│ ├── Fontainebleau.sln
│ ├── MeetInParisDumper/
│ │ ├── BaseType.h
│ │ ├── MeetInParisDumper.vcxproj
│ │ ├── MeetInParisDumper.vcxproj.filters
│ │ ├── Path.cpp
│ │ ├── Path.h
│ │ ├── StringHelper.cpp
│ │ ├── StringHelper.h
│ │ └── dllmain.cpp
│ └── Rename/
│ ├── Program.cs
│ └── Rename.csproj
├── 005.ZixSolution/
│ ├── ConsoleExecute/
│ │ ├── ConsoleExecute.csproj
│ │ └── Program.cs
│ ├── Extractor/
│ │ ├── Extractor.csproj
│ │ ├── Untils/
│ │ │ ├── Pickle.cs
│ │ │ └── Zlib.cs
│ │ ├── ZixRenpy7V1/
│ │ │ ├── Archive.cs
│ │ │ └── Crypto.cs
│ │ └── ZixRenpy8V1/
│ │ ├── Archive.cs
│ │ └── Crypto.cs
│ ├── Manual.md
│ ├── ZixExtractorR7/
│ │ ├── Program.cs
│ │ └── ZixExtractorR7.csproj
│ ├── ZixExtractorR8/
│ │ ├── Program.cs
│ │ └── ZixExtractorR8.csproj
│ └── zedraxloRenpy.sln
├── 006.iFAction/
│ └── iFActionTool/
│ ├── ConsoleExecute/
│ │ ├── ConsoleExecute.csproj
│ │ └── Program.cs
│ ├── Manual.md
│ ├── UnitTest/
│ │ ├── Program.cs
│ │ └── UnitTest.csproj
│ ├── iFActionExtractor/
│ │ ├── IFAction.V1/
│ │ │ └── Archive.cs
│ │ └── iFActionExtractor.csproj
│ └── iFActionTool.sln
├── 007.AsicxArt/
│ ├── AsicxArtTool/
│ │ ├── AsicxArt.sln
│ │ ├── AsicxArtStatic/
│ │ │ ├── AsicxArt/
│ │ │ │ ├── MemoryExtension.cs
│ │ │ │ ├── SQLite3.cs
│ │ │ │ └── SQLite3Command.cs
│ │ │ ├── AsicxArt.V1/
│ │ │ │ ├── Archive.cs
│ │ │ │ └── Games.cs
│ │ │ └── AsicxArtStatic.csproj
│ │ └── ExtractorGui/
│ │ ├── ExtractorGui.csproj
│ │ ├── MainForm.Designer.cs
│ │ ├── MainForm.cs
│ │ ├── MainForm.resx
│ │ ├── Program.cs
│ │ └── Properties/
│ │ └── PublishProfiles/
│ │ └── FolderProfile86.pubxml
│ └── Manual.md
├── 008.XinYvanGames/
│ ├── Manual.md
│ ├── SapphireMoonForeverMemories/
│ │ └── ExtractorCode.cs
│ ├── ShadowOfTwelveKeyGen/
│ │ ├── Program.cs
│ │ └── ShadowOfTwelveKeyGen.csproj
│ └── XinYvanGames.sln
├── 009.SoraPlayer/
│ ├── Manual.md
│ └── SOAExtract/
│ ├── ConsoleExecute/
│ │ ├── ConsoleExecute.csproj
│ │ └── Program.cs
│ ├── SoraPlayer.sln
│ ├── SoraPlayerExtractorV1/
│ │ ├── Program.cs
│ │ └── SoraPlayerExtractorV1.csproj
│ └── SoraPlayerStatic/
│ ├── Archive.cs
│ ├── SoraPlayerStatic.csproj
│ ├── XP3Archive.cs
│ └── Zlib.cs
├── 010.UniversalXP3DecFilter/
│ ├── MainFrom/
│ │ ├── MainFrom.Designer.cs
│ │ ├── MainFrom.cs
│ │ ├── MainFrom.csproj
│ │ ├── MainFrom.resx
│ │ ├── Program.cs
│ │ └── Properties/
│ │ └── PublishProfiles/
│ │ └── FolderProfile64.pubxml
│ ├── Manual.md
│ ├── XP3Archive/
│ │ ├── Archive.cs
│ │ ├── XP3Archive.cs
│ │ ├── XP3Archive.csproj
│ │ ├── XP3Filter.cs
│ │ └── Zlib.cs
│ └── XP3DecTPM.sln
├── 011.Irregulars/
│ ├── ConsoleTest/
│ │ ├── ConsoleTest.csproj
│ │ └── Program.cs
│ ├── Irregulars.sln
│ ├── IrregularsExtractorV1/
│ │ ├── IrregularsExtractorV1.csproj
│ │ └── Program.cs
│ ├── IrregularsStatic/
│ │ ├── Crypto.cs
│ │ ├── IrregularsStatic.csproj
│ │ └── PathUtil.cs
│ └── Manual.md
├── 012.VisualNovelMaker/
│ ├── ConsoleTest/
│ │ ├── ConsoleTest.csproj
│ │ └── Program.cs
│ ├── Manual.md
│ ├── VNMakerCore/
│ │ ├── Crypto.V1/
│ │ │ └── Games/
│ │ │ └── GameInfo.cs
│ │ ├── General/
│ │ │ ├── CryptoFilter.cs
│ │ │ ├── NWPath.cs
│ │ │ └── NWResource.cs
│ │ └── VNMakerCore.csproj
│ ├── VNMakerGUI/
│ │ ├── MainForm.Designer.cs
│ │ ├── MainForm.cs
│ │ ├── MainForm.resx
│ │ ├── Program.cs
│ │ ├── Properties/
│ │ │ └── PublishProfiles/
│ │ │ └── FolderProfile64.pubxml
│ │ └── VNMakerGUI.csproj
│ └── VisualNovelMaker.sln
├── 013.GameCreatorTool/
│ ├── GCExtractorGUI/
│ │ ├── GCExtractorGUI.csproj
│ │ ├── MainForm.Designer.cs
│ │ ├── MainForm.cs
│ │ ├── MainForm.resx
│ │ ├── Program.cs
│ │ └── Properties/
│ │ └── PublishProfiles/
│ │ └── FolderProfile64.pubxml
│ ├── GameCreatorStatic/
│ │ ├── Commom.cs
│ │ ├── Extractor.V1/
│ │ │ ├── GCCryptoV1.cs
│ │ │ ├── GCGameV1.cs
│ │ │ └── GCStructureV1.cs
│ │ ├── GameCreatorStatic.csproj
│ │ └── Utils.cs
│ ├── GameCreatorTool.sln
│ ├── Manual.md
│ └── UnitTest/
│ ├── Program.cs
│ └── UnitTest.csproj
├── 014.OrangeStudio/
│ ├── Lover/
│ │ ├── ConsoleExecute/
│ │ │ ├── ConsoleExecute.csproj
│ │ │ ├── ImageDecoder.cs
│ │ │ ├── Program.cs
│ │ │ └── StreamExtend.cs
│ │ └── Lover.sln
│ └── Manual.md
├── 015.SeparateHearts/
│ └── SeparateHeartsEngineExtractor/
│ ├── ConsoleExecute/
│ │ ├── ConsoleExecute.csproj
│ │ └── Program.cs
│ ├── EngineCoreStatic/
│ │ ├── EngineCoreStatic.csproj
│ │ ├── HACDecompressor.cs
│ │ ├── HACDirectFile.cs
│ │ ├── HACImageDecoder.cs
│ │ ├── HACPackage.cs
│ │ ├── HACStreamExtend.cs
│ │ ├── HTPImageDecoder.cs
│ │ └── ImageProcessUtils.cs
│ ├── Manual.md
│ ├── SeparateHeartsEngineExtractor.sln
│ └── SeparateHeartsExtractorV1/
│ ├── Program.cs
│ └── SeparateHeartsExtractorV1.csproj
├── 016.NekoNovel/
│ └── NekoNovel/
│ ├── ConsoleExecute/
│ │ ├── ConsoleExecute.csproj
│ │ └── Program.cs
│ ├── Manual.md
│ ├── NekoNovel.sln
│ ├── NekoNovelExtractorV1/
│ │ ├── NekoNovelExtractorV1.csproj
│ │ └── Program.cs
│ └── NekoNovelStatic/
│ ├── NekoNovelStatic.csproj
│ ├── NekoPackage.cs
│ ├── StreamExtend.cs
│ └── Zlib.cs
├── 017.OurshowGames/
│ ├── ConsoleTest/
│ │ ├── ConsoleTest.csproj
│ │ └── Program.cs
│ ├── Manual.md
│ ├── OurshowExtractorV1/
│ │ ├── OurshowExtractorV1.csproj
│ │ └── Program.cs
│ ├── OurshowGames.sln
│ └── OurshowStatic/
│ ├── AGPArchiveV1.cs
│ ├── BitReader.cs
│ ├── CompressLZ77.cs
│ └── OurshowStatic.csproj
├── 018.CaramelMochaStudio/
│ └── TheStreetOfAdriftToolkit/
│ ├── Manual.md
│ ├── TSOACheat/
│ │ ├── BulletHellCheat.cpp
│ │ ├── BulletHellCheat.h
│ │ ├── 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
│ │ ├── Detours/
│ │ │ ├── creatwth.cpp
│ │ │ ├── detours.cpp
│ │ │ ├── detours.h
│ │ │ ├── disasm.cpp
│ │ │ ├── image.cpp
│ │ │ ├── modules.cpp
│ │ │ └── uimports.cpp
│ │ ├── ExtendUtils.h
│ │ ├── GameDotCheat.cpp
│ │ ├── GameDotCheat.h
│ │ ├── Il2Cpp/
│ │ │ ├── Il2Cpp.cpp
│ │ │ ├── Il2Cpp.h
│ │ │ ├── Il2CppAPI.cpp
│ │ │ ├── Il2CppAPI.h
│ │ │ └── Il2CppHeader.h
│ │ ├── Ntdll/
│ │ │ ├── ntdll.h
│ │ │ ├── ntdll_x64.lib
│ │ │ ├── ntdll_x86.lib
│ │ │ ├── ntdllp_x64.lib
│ │ │ └── ntdllp_x86.lib
│ │ ├── NtdllExtend.cpp
│ │ ├── NtdllExtend.h
│ │ ├── System.cpp
│ │ ├── System.h
│ │ ├── TSOACheat.vcxproj
│ │ ├── TSOACheat.vcxproj.filters
│ │ ├── UnityEngine.cpp
│ │ ├── UnityEngine.h
│ │ └── dllmain.cpp
│ ├── TSOALoader/
│ │ ├── 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
│ │ ├── Detours/
│ │ │ ├── creatwth.cpp
│ │ │ ├── detours.cpp
│ │ │ ├── detours.h
│ │ │ ├── disasm.cpp
│ │ │ ├── image.cpp
│ │ │ ├── modules.cpp
│ │ │ └── uimports.cpp
│ │ ├── TSOALoader.vcxproj
│ │ ├── TSOALoader.vcxproj.filters
│ │ └── winmain.cpp
│ └── TheStreetOfAdriftToolkit.sln
├── 019.PygmaGame/
│ ├── ExtractorV1/
│ │ ├── ExtractorV1.csproj
│ │ └── Program.cs
│ ├── ExtractorV2/
│ │ ├── ExtractorV2.csproj
│ │ └── Program.cs
│ ├── Manual.md
│ ├── PygmaGame.sln
│ ├── PygmaGameStatic/
│ │ ├── HLXRenpyPackageV2.cs
│ │ ├── Misc/
│ │ │ └── DeobfuscatorV1.cs
│ │ ├── Pickle.cs
│ │ ├── PygmaGameStatic.csproj
│ │ ├── WJZRenpyPackageV1.cs
│ │ └── Zlib.cs
│ └── UnitTest/
│ ├── Program.cs
│ └── UnitTest.csproj
├── 020.Xso/
│ ├── ExtractorV1/
│ │ ├── ExtractorV1.csproj
│ │ └── Program.cs
│ ├── Manual.md
│ ├── UnitTest/
│ │ ├── Program.cs
│ │ └── UnitTest.csproj
│ ├── Xso.sln
│ └── XsoStatic/
│ ├── Pickle.cs
│ ├── XsoRenpyV1.cs
│ ├── XsoStatic.csproj
│ └── Zlib.cs
├── 021.UniversalRPAExtractor/
│ ├── Manual.md
│ ├── RPAArchive/
│ │ ├── RPAArchive.csproj
│ │ ├── RenpyRPA.cs
│ │ ├── RenpyRPAv3.cs
│ │ └── Utils/
│ │ ├── Pickle.cs
│ │ └── Zlib.cs
│ ├── RPAExtractorGUI/
│ │ ├── MainForm.Designer.cs
│ │ ├── MainForm.cs
│ │ ├── MainForm.resx
│ │ ├── Program.cs
│ │ ├── Properties/
│ │ │ └── PublishProfiles/
│ │ │ └── FolderProfile64.pubxml
│ │ └── RPAExtractorGUI.csproj
│ └── UniversalRPAExtractor.sln
├── 022.XiangShe/
│ ├── AGreatScientistDecryptor/
│ │ ├── AGreatScientistDecryptor.csproj
│ │ └── Program.cs
│ ├── Manual.md
│ ├── UnitTest/
│ │ ├── Program.cs
│ │ └── UnitTest.csproj
│ ├── XiangShe.sln
│ └── XiangSheStatic/
│ ├── Crypto.V1/
│ │ ├── Crypto.cs
│ │ ├── GameDB.cs
│ │ └── ResourceExtractor.cs
│ ├── Utils/
│ │ └── GZip.cs
│ └── XiangSheStatic.csproj
├── 023.YuriAVGEngine/
│ ├── EngineCore/
│ │ ├── EngineCore.csproj
│ │ ├── YuriCrypto.cs
│ │ ├── YuriGames.cs
│ │ ├── YuriPackage.cs
│ │ ├── YuriScenario.cs
│ │ └── YuriSerializer.cs
│ ├── ExtractorGUI/
│ │ ├── ExtractorGUI.csproj
│ │ ├── MainForm.Designer.cs
│ │ ├── MainForm.cs
│ │ ├── MainForm.resx
│ │ ├── Program.cs
│ │ └── Properties/
│ │ └── PublishProfiles/
│ │ └── FolderProfile64.pubxml
│ ├── Manual.md
│ └── YuriAVGEngine.sln
├── 024.SanHuaMiao/
│ ├── Manual.md
│ └── SanHuaMiaoStudio/
│ ├── 3001Pages/
│ │ ├── 3001Pages.csproj
│ │ └── Program.cs
│ └── SanHuaMiaoStudio.sln
├── 025.SugarRush/
│ ├── IdolForging/
│ │ ├── IdolForging.csproj
│ │ └── Program.cs
│ ├── Manual.md
│ └── SugarRush.sln
├── 994.AleCubicSoft/
│ └── UndercoverAgent/
│ ├── Manual.md
│ ├── UALoader/
│ │ ├── 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
│ │ ├── Detours/
│ │ │ ├── creatwth.cpp
│ │ │ ├── detours.cpp
│ │ │ ├── detours.h
│ │ │ ├── disasm.cpp
│ │ │ ├── image.cpp
│ │ │ ├── modules.cpp
│ │ │ └── uimports.cpp
│ │ ├── UALoader.vcxproj
│ │ ├── UALoader.vcxproj.filters
│ │ └── winmain.cpp
│ ├── UAPatch/
│ │ ├── 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
│ │ ├── Detours/
│ │ │ ├── creatwth.cpp
│ │ │ ├── detours.cpp
│ │ │ ├── detours.h
│ │ │ ├── disasm.cpp
│ │ │ ├── image.cpp
│ │ │ ├── modules.cpp
│ │ │ └── uimports.cpp
│ │ ├── ExtendUtils.h
│ │ ├── Il2Cpp/
│ │ │ ├── Il2Cpp.cpp
│ │ │ ├── Il2Cpp.h
│ │ │ ├── Il2CppApi.cpp
│ │ │ ├── Il2CppApi.h
│ │ │ └── Il2CppHeader.h
│ │ ├── Ntdll/
│ │ │ ├── ntdll.h
│ │ │ ├── ntdll_x64.lib
│ │ │ ├── ntdll_x86.lib
│ │ │ ├── ntdllp_x64.lib
│ │ │ └── ntdllp_x86.lib
│ │ ├── NtdllExtend.cpp
│ │ ├── NtdllExtend.h
│ │ ├── SaveDataPatch.cpp
│ │ ├── SaveDataPatch.h
│ │ ├── TSKTContainer.h
│ │ ├── UAPatch.vcxproj
│ │ ├── UAPatch.vcxproj.filters
│ │ └── dllmain.cpp
│ └── UndercoverAgent.sln
├── 995.Chatte Noire/
│ ├── 01.Nie no Hakoniwa/
│ │ ├── FileExtractor/
│ │ │ ├── ExfsPackage.cs
│ │ │ ├── FileExtractor.csproj
│ │ │ └── Program.cs
│ │ └── Nie no Hakoniwa.sln
│ └── Manual.md
├── 996.LightVN/
│ └── LightVN/
│ ├── ConsoleExecute/
│ │ ├── ConsoleExecute.csproj
│ │ └── Program.cs
│ ├── LightVN.sln
│ ├── LightVNExtractorV1/
│ │ ├── LightVNExtractorV1.csproj
│ │ └── Program.cs
│ ├── LightVNExtractorV2/
│ │ ├── LightVNExtractorV2.csproj
│ │ └── Program.cs
│ ├── LightVNStatic/
│ │ ├── CryptoFilterV1.cs
│ │ ├── CryptoFilterV2.cs
│ │ ├── GameV1.cs
│ │ ├── GameV2.cs
│ │ ├── LightVNStatic.csproj
│ │ ├── PackageV1.cs
│ │ └── PackageV2.cs
│ └── Manual.md
├── 997.SyawaseWorks/
│ ├── HamidashiCreative/
│ │ ├── Common/
│ │ │ ├── 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
│ │ ├── ConsoleExecute/
│ │ │ ├── ConsoleExecute.csproj
│ │ │ └── Program.cs
│ │ ├── Detours/
│ │ │ ├── creatwth.cpp
│ │ │ ├── detours.cpp
│ │ │ ├── detours.h
│ │ │ ├── disasm.cpp
│ │ │ ├── image.cpp
│ │ │ ├── modules.cpp
│ │ │ └── uimports.cpp
│ │ ├── HamidashiCreative.sln
│ │ ├── HamidashiCreativeStatic/
│ │ │ ├── HamidashiCreativeStatic.csproj
│ │ │ ├── SWArchive.cs
│ │ │ ├── SWFilter.cs
│ │ │ └── SWHash.cs
│ │ ├── HamidashiPatch/
│ │ │ ├── HamidashiPatch.vcxproj
│ │ │ ├── HamidashiPatch.vcxproj.filters
│ │ │ └── dllmain.cpp
│ │ ├── Ntdll/
│ │ │ ├── ntdll.h
│ │ │ ├── ntdll_x64.lib
│ │ │ ├── ntdll_x86.lib
│ │ │ ├── ntdllp_x64.lib
│ │ │ └── ntdllp_x86.lib
│ │ └── SteamPatch/
│ │ └── ds.ini
│ ├── HappyLiveShowUp/
│ │ ├── Common/
│ │ │ ├── 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
│ │ ├── ConsoleExecute/
│ │ │ ├── ConsoleExecute.csproj
│ │ │ └── Program.cs
│ │ ├── Detours/
│ │ │ ├── creatwth.cpp
│ │ │ ├── detours.cpp
│ │ │ ├── detours.h
│ │ │ ├── disasm.cpp
│ │ │ ├── image.cpp
│ │ │ ├── modules.cpp
│ │ │ └── uimports.cpp
│ │ ├── HappyLiveShowUp.sln
│ │ ├── HappyLiveShowUpStatic/
│ │ │ ├── Enc_Key_V102
│ │ │ ├── HappyLiveShowUpStatic.csproj
│ │ │ ├── Patch_Key_V102
│ │ │ ├── SWCrypto.cs
│ │ │ ├── SWDataPack.cs
│ │ │ └── SWGameData.cs
│ │ ├── Ntdll/
│ │ │ ├── ntdll.h
│ │ │ ├── ntdll_x64.lib
│ │ │ ├── ntdll_x86.lib
│ │ │ ├── ntdllp_x64.lib
│ │ │ └── ntdllp_x86.lib
│ │ ├── Patch/
│ │ │ ├── Patch.vcxproj
│ │ │ ├── Patch.vcxproj.filters
│ │ │ └── dllmain.cpp
│ │ ├── SteamPatch/
│ │ │ └── ds.ini
│ │ └── TestExecute/
│ │ ├── Program.cs
│ │ └── TestExecute.csproj
│ └── Manual.md
├── 998.HikariField/
│ ├── FutureRadio/
│ │ ├── ConsoleExecute/
│ │ │ ├── ConsoleExecute.csproj
│ │ │ └── Program.cs
│ │ ├── FutureRadio.sln
│ │ ├── FutureRadioStatic/
│ │ │ ├── BinArchive.cs
│ │ │ ├── FutureRadioStatic.csproj
│ │ │ └── PidaArchive.cs
│ │ └── Manual.md
│ ├── HFUnityV1/
│ │ ├── AonatsuLine/
│ │ │ ├── AonatsuLine.csproj
│ │ │ └── Program.cs
│ │ ├── EngineCore/
│ │ │ ├── EngineCore.csproj
│ │ │ ├── PacArchive.cs
│ │ │ ├── QuickLZ.cs
│ │ │ └── Until.cs
│ │ ├── HFUnityV1.sln
│ │ ├── MakingLoverFHD/
│ │ │ ├── MakingLoverFHD.csproj
│ │ │ └── Program.cs
│ │ └── Manual.md
│ └── NekoNyan/
│ ├── ConsoleExecute/
│ │ ├── ConsoleExecute.csproj
│ │ └── Program.cs
│ ├── ExtractorGUI/
│ │ ├── ExtractorGUI.csproj
│ │ ├── MainForm.Designer.cs
│ │ ├── MainForm.cs
│ │ ├── MainForm.resx
│ │ ├── Program.cs
│ │ └── Properties/
│ │ └── PublishProfiles/
│ │ └── FolderProfile64.pubxml
│ ├── Manual.md
│ ├── NekoNyan.sln
│ └── NekoNyanStatic/
│ ├── Crypto/
│ │ ├── ArchiveCryptoBase.cs
│ │ └── DataManager.cs
│ ├── Crypto.V1/
│ │ ├── ArchiveCryptoV10.cs
│ │ ├── ArchiveCryptoV11.cs
│ │ ├── ArchiveCryptoV12.cs
│ │ └── ArchiveCryptoV13.cs
│ └── NekoNyanStatic.csproj
├── LICENSE
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
*.DotSettings
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
opencoverCoverage.xml
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/BKEFileNameDumper/BKEFileNameDumper.vcxproj
================================================
Debug
Win32
Release
Win32
16.0
Win32Proj
{5a11c975-a301-4700-b5d2-13e027255948}
BKEFileNameDumper
10.0
DynamicLibrary
true
v143
Unicode
DynamicLibrary
false
v143
true
Unicode
false
false
Level3
false
_WIN32_WINNT=0x601;_WIN32;_DEBUG;BKEFILENAMEDUMPER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
true
NotUsing
pch.h
false
false
false
AdvancedVectorExtensions2
false
MultiThreadedDebug
$(SolutionDir)Common;
stdcpp20
Windows
true
false
false
false
false
false
false
true
false
/FILEALIGN:0x1000 %(AdditionalOptions)
Level3
true
true
false
_WIN32_WINNT=0x601;WIN32;NDEBUG;BKEFILENAMEDUMPER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
true
NotUsing
pch.h
false
false
false
AdvancedVectorExtensions2
false
MultiThreaded
$(SolutionDir)Common;
stdcpp20
Windows
true
true
false
false
false
false
false
false
false
true
false
/FILEALIGN:0x1000 %(AdditionalOptions)
false
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/BKEFileNameDumper/BKEFileNameDumper.vcxproj.filters
================================================
{4FC737F1-C7A5-4376-A066-2A32D752A2FF}
cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
{93995380-89BD-4b04-88EB-625FBE52EBFB}
h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
{731d0d0e-aab8-4952-a2cc-6a31a6bb8f12}
Common
Common
Common
Common
Common
Common
Common
源文件
Common
Common
Common
Common
Common
Common
Common
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/BKEFileNameDumper/dllmain.cpp
================================================
#include
#include
#include "log.h"
#include "stringhelper.h"
#include "util.h"
#include "path.h"
#include "file.h"
#include "pe.h"
//仅仅《十二色的季节》使用了Hash
#define BKARC_GetFileEntry_Vptr_RVA 0x29633C
struct StdWStringMSVC2013
{
union
{
wchar_t* LongString;
wchar_t ShortString[8];
} String;
int StringLength;
int MaxStringLength;
const wchar_t* ConstString()
{
if (this->MaxStringLength >= 8)
{
return this->String.LongString;
}
else
{
return this->String.ShortString;
}
}
int Length()
{
return this->StringLength;
}
};
typedef void* (_fastcall* tBKARC_GetFileEntry)(void*, void*, StdWStringMSVC2013*);
static tBKARC_GetFileEntry g_BKARC_GetFileEntry_FuncPtr = NULL;
static Log::Logger g_FileNameDumper;
//Hook 封包查找函数
__declspec(noinline)
void* __fastcall HookBKARCGetFileEntry(void* thisObj, void* unusedEdx,StdWStringMSVC2013* string)
{
void* entry = g_BKARC_GetFileEntry_FuncPtr(thisObj, NULL, string);
//文件存在
if (entry)
{
g_FileNameDumper.Write(L"%s\n", string->ConstString());
}
return entry;
}
void InstallHook()
{
PVOID gameBase = GetModuleHandleW(NULL);
PVOID bkarc_GetFileEntry_Vptr_VA = (BYTE*)gameBase + BKARC_GetFileEntry_Vptr_RVA;
//保存原函数指针
g_BKARC_GetFileEntry_FuncPtr = *(tBKARC_GetFileEntry*)bkarc_GetFileEntry_Vptr_VA;
//虚表Hook
PVOID HookBKARC_GetFileEntry_FuncPtr = HookBKARCGetFileEntry;
PE::WriteMemory(bkarc_GetFileEntry_Vptr_VA, &HookBKARC_GetFileEntry_FuncPtr, sizeof(PVOID));
}
void Initialize()
{
std::wstring gameDirectory = Util::GetAppDirectoryW();
std::wstring fileNameOutPath = gameDirectory + L"\\FileName.lst";
File::Delete(fileNameOutPath);
g_FileNameDumper.Open(fileNameOutPath.c_str());
InstallHook();
}
void ShutDown()
{
g_FileNameDumper.Close();
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
Initialize();
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
{
ShutDown();
break;
}
}
return TRUE;
}
extern "C" __declspec(dllexport) void Dummy() {}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/BKEFileNameDumper.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33403.182
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BKEFileNameDumper", "BKEFileNameDumper\BKEFileNameDumper.vcxproj", "{5A11C975-A301-4700-B5D2-13E027255948}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Loader", "Loader\Loader.vcxproj", "{1514DC6A-FF75-4B71-8565-9A721C6B07E4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5A11C975-A301-4700-B5D2-13E027255948}.Debug|x86.ActiveCfg = Debug|Win32
{5A11C975-A301-4700-B5D2-13E027255948}.Debug|x86.Build.0 = Debug|Win32
{5A11C975-A301-4700-B5D2-13E027255948}.Release|x86.ActiveCfg = Release|Win32
{5A11C975-A301-4700-B5D2-13E027255948}.Release|x86.Build.0 = Release|Win32
{1514DC6A-FF75-4B71-8565-9A721C6B07E4}.Debug|x86.ActiveCfg = Debug|Win32
{1514DC6A-FF75-4B71-8565-9A721C6B07E4}.Debug|x86.Build.0 = Debug|Win32
{1514DC6A-FF75-4B71-8565-9A721C6B07E4}.Release|x86.ActiveCfg = Release|Win32
{1514DC6A-FF75-4B71-8565-9A721C6B07E4}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {54E8DE13-1F44-4EA1-AB87-ED4B26B886BE}
EndGlobalSection
EndGlobal
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/encoding.cpp
================================================
// encoding.cpp
#include
#include "encoding.h"
#undef max
namespace Encoding
{
std::wstring AnsiToUnicode(const std::string& source, int codePage)
{
if (source.length() == 0)
{
return std::wstring();
}
if (source.length() > (size_t)std::numeric_limits::max())
{
return std::wstring();
}
int length = MultiByteToWideChar(codePage, 0, source.c_str(), (int)source.length(), NULL, 0);
if (length <= 0)
{
return std::wstring();
}
std::wstring output(length, L'\0');
if (MultiByteToWideChar(codePage, 0, source.c_str(), (int)source.length(), (LPWSTR)output.data(), (int)output.length() + 1) == 0)
{
return std::wstring();
}
return output;
}
std::string UnicodeToAnsi(const std::wstring& source, int codePage)
{
if (source.length() == 0)
{
return std::string();
}
if (source.length() > (size_t)std::numeric_limits::max())
{
return std::string();
}
int length = WideCharToMultiByte(codePage, 0, source.c_str(), (int)source.length(), NULL, 0, NULL, NULL);
if (length <= 0)
{
return std::string();
}
std::string output(length, '\0');
if (WideCharToMultiByte(codePage, 0, source.c_str(), (int)source.length(), (LPSTR)output.data(), (int)output.length() + 1, NULL, NULL) == 0)
{
return std::string();
}
return output;
}
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/encoding.h
================================================
// encoding.h
#pragma once
#include
namespace Encoding
{
enum CodePage
{
ACP = 0,
UTF_8 = 65001,
SHIFT_JIS = 932,
GBK = 936,
};
std::wstring AnsiToUnicode(const std::string& source, int codePage);
std::string UnicodeToAnsi(const std::wstring& source, int codePage);
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/file.cpp
================================================
// file.cpp
#include
#include
#include
#include
namespace File
{
std::string ReadAllText(const std::string& path)
{
FILE* fp;
long long size;
size_t length;
unsigned char buf[3];
bool utf8bom;
std::string output;
if (fopen_s(&fp, path.c_str(), "rb") != 0)
{
goto error;
}
if (_fseeki64(fp, 0, SEEK_END) != 0)
{
goto error;
}
size = _ftelli64(fp);
if (size <= 0)
{
goto error;
}
if (static_cast(size) > std::numeric_limits::max())
{
goto error;
}
if (_fseeki64(fp, 0, SEEK_SET) != 0)
{
goto error;
}
length = static_cast(size);
// Check UTF-8 BOM
utf8bom = false;
if (fread(buf, 3, 1, fp) == 1)
{
if (buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
{
utf8bom = true;
}
}
if (utf8bom)
{
length -= 3;
}
else
{
if (_fseeki64(fp, 0, SEEK_SET) != 0)
{
goto error;
}
}
if (length == 0)
{
goto error;
}
output.resize(length);
if (fread(output.data(), length, 1, fp) != 1)
{
goto error;
}
fclose(fp);
return output;
error:
if (fp)
{
fclose(fp);
}
return std::string();
}
std::string ReadAllText(const std::wstring& path)
{
FILE* fp;
long long size;
size_t length;
unsigned char buf[3];
bool utf8bom;
std::string output;
if (_wfopen_s(&fp, path.c_str(), L"rb") != 0)
{
goto error;
}
if (fp == nullptr)
{
goto error;
}
if (_fseeki64(fp, 0, SEEK_END) != 0)
{
goto error;
}
size = _ftelli64(fp);
if (size <= 0)
{
goto error;
}
if (static_cast(size) > std::numeric_limits::max())
{
goto error;
}
if (_fseeki64(fp, 0, SEEK_SET) != 0)
{
goto error;
}
length = static_cast(size);
// Check UTF-8 BOM
utf8bom = false;
if (fread(buf, 3, 1, fp) == 1)
{
if (buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
{
utf8bom = true;
}
}
if (utf8bom)
{
length -= 3;
}
else
{
if (_fseeki64(fp, 0, SEEK_SET) != 0)
{
goto error;
}
}
if (length == 0)
{
goto error;
}
output.resize(length);
if (fread(output.data(), length, 1, fp) != 1)
{
goto error;
}
fclose(fp);
return output;
error:
if (fp)
{
fclose(fp);
}
return std::string();
}
bool WriteAllBytes(const std::string& path, const void* buffer, size_t size)
{
FILE* fp;
if (fopen_s(&fp, path.c_str(), "wb") != 0)
{
goto error;
}
if (buffer == nullptr)
{
goto error;
}
if (size == 0)
{
goto error;
}
if (fwrite(buffer, size, 1, fp) != 1)
{
goto error;
}
fflush(fp);
fclose(fp);
return true;
error:
if (fp)
{
fclose(fp);
}
return false;
}
bool WriteAllBytes(const std::wstring& path, const void* buffer, size_t size)
{
FILE* fp;
if (_wfopen_s(&fp, path.c_str(), L"wb") != 0)
{
goto error;
}
if (fp == nullptr)
{
goto error;
}
if (buffer == nullptr)
{
goto error;
}
if (size == 0)
{
goto error;
}
if (fwrite(buffer, size, 1, fp) != 1)
{
goto error;
}
fflush(fp);
fclose(fp);
return true;
error:
if (fp)
{
fclose(fp);
}
return false;
}
void Delete(const std::string& path)
{
remove(path.c_str());
}
void Delete(const std::wstring& path)
{
_wremove(path.c_str());
}
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/file.h
================================================
// file.h
#pragma once
#include
namespace File
{
std::string ReadAllText(const std::string& path);
std::string ReadAllText(const std::wstring& path);
bool WriteAllBytes(const std::string& path, const void* buffer, size_t size);
bool WriteAllBytes(const std::wstring& path, const void* buffer, size_t size);
void Delete(const std::string& path);
void Delete(const std::wstring& path);
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/log.cpp
================================================
// log.cpp
#include
#include
#include "log.h"
#include "stringhelper.h"
#include "encoding.h"
namespace Log
{
Logger::Logger() : m_pOutput{}
{
}
Logger::Logger(const wchar_t* lpFileName)
: m_pOutput{}
{
Open(lpFileName);
}
Logger::~Logger()
{
Close();
}
void Logger::Open(const wchar_t* lpFileName)
{
m_pOutput = _wfsopen(lpFileName, L"at", _SH_DENYWR);
}
void Logger::Close()
{
Flush();
if (m_pOutput)
{
fclose(m_pOutput);
m_pOutput = nullptr;
}
}
void Logger::Flush()
{
if (m_pOutput)
{
fflush(m_pOutput);
}
}
static std::string GetTimeString()
{
time_t tv;
struct tm tm;
char buf[32];
time(&tv);
localtime_s(&tm, &tv);
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
return std::string(buf);
}
void Logger::WriteAnsi(int iCodePage, const char* lpFormat, ...)
{
if (!m_pOutput)
{
return;
}
va_list ap;
va_start(ap, lpFormat);
auto content = StringHelper::VFormat(lpFormat, ap);
va_end(ap);
auto unicode = Encoding::AnsiToUnicode(content, iCodePage);
auto output = Encoding::UnicodeToAnsi(unicode, Encoding::CodePage::UTF_8);
fwrite(output.data(), output.length(), 1, m_pOutput);
fflush(m_pOutput);
}
void Logger::WriteLineAnsi(int iCodePage, const char* lpFormat, ...)
{
if (!m_pOutput)
{
return;
}
va_list ap;
va_start(ap, lpFormat);
auto content = StringHelper::VFormat(lpFormat, ap);
va_end(ap);
auto unicode = Encoding::AnsiToUnicode(content, iCodePage);
auto utf = Encoding::UnicodeToAnsi(unicode, Encoding::CodePage::UTF_8);
auto timestamp = GetTimeString();
auto output = timestamp + " | " + utf + "\n";
fwrite(output.data(), output.length(), 1, m_pOutput);
fflush(m_pOutput);
}
void Logger::Write(const wchar_t* lpFormat, ...)
{
if (!m_pOutput)
{
return;
}
va_list ap;
va_start(ap, lpFormat);
auto content = StringHelper::VFormat(lpFormat, ap);
va_end(ap);
auto output = Encoding::UnicodeToAnsi(content, Encoding::CodePage::UTF_8);
fwrite(output.data(), output.length(), 1, m_pOutput);
fflush(m_pOutput);
}
void Logger::WriteLine(const wchar_t* lpFormat, ...)
{
if (!m_pOutput)
{
return;
}
va_list ap;
va_start(ap, lpFormat);
auto content = StringHelper::VFormat(lpFormat, ap);
va_end(ap);
auto utf = Encoding::UnicodeToAnsi(content, Encoding::CodePage::UTF_8);
auto timestamp = GetTimeString();
auto output = timestamp + " | " + utf + "\n";
fwrite(output.data(), output.length(), 1, m_pOutput);
fflush(m_pOutput);
}
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/log.h
================================================
// log.h
#pragma once
#include
namespace Log
{
class Logger
{
public:
Logger();
Logger(const wchar_t* lpFileName);
~Logger();
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
void Open(const wchar_t* lpFileName);
void Close();
void Flush();
void WriteAnsi(int iCodePage, const char* lpFormat, ...);
void WriteLineAnsi(int iCodePage, const char* lpFormat, ...);
void Write(const wchar_t* lpFormat, ...);
void WriteLine(const wchar_t* lpFormat, ...);
private:
FILE* m_pOutput;
};
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/path.cpp
================================================
// path.cpp
#include
#include
namespace Path
{
std::string GetFileName(const std::string& path)
{
size_t pos;
pos = path.find_last_of('\\');
if (pos != std::string::npos)
{
return path.substr(pos + 1);
}
pos = path.find_last_of('/');
if (pos != std::string::npos)
{
return path.substr(pos + 1);
}
return path;
}
std::wstring GetFileName(const std::wstring& path)
{
size_t pos;
pos = path.find_last_of(L'\\');
if (pos != std::wstring::npos)
{
return path.substr(pos + 1);
}
pos = path.find_last_of(L'/');
if (pos != std::wstring::npos)
{
return path.substr(pos + 1);
}
return path;
}
std::string GetFileNameWithoutExtension(const std::string& path)
{
std::string name = GetFileName(path);
size_t pos = name.find_last_of('.');
if (pos != std::string::npos && pos > 0)
{
return name.substr(0, pos);
}
return name;
}
std::wstring GetFileNameWithoutExtension(const std::wstring& path)
{
std::wstring name = GetFileName(path);
size_t pos = name.find_last_of(L'.');
if (pos != std::wstring::npos && pos > 0)
{
return name.substr(0, pos);
}
return name;
}
std::string GetDirectoryName(const std::string& path)
{
size_t pos;
pos = path.find_last_of('\\');
if (pos != std::string::npos && pos > 0)
{
return path.substr(0, pos);
}
pos = path.find_last_of('/');
if (pos != std::string::npos && pos > 0)
{
return path.substr(0, pos);
}
return std::string();
}
std::wstring GetDirectoryName(const std::wstring& path)
{
size_t pos;
pos = path.find_last_of(L'\\');
if (pos != std::wstring::npos && pos > 0)
{
return path.substr(0, pos);
}
pos = path.find_last_of(L'/');
if (pos != std::wstring::npos && pos > 0)
{
return path.substr(0, pos);
}
return std::wstring();
}
std::string GetExtension(const std::string& path)
{
int length = static_cast(path.length());
for (int i = length - 1; i >= 0; i--)
{
char ch = path[i];
if (ch == '.')
{
if (i != length - 1)
{
return path.substr(i, length - i);
}
else
{
return std::string();
}
}
if (ch == '\\' || ch == '/')
{
break;
}
}
return std::string();
}
std::wstring GetExtension(const std::wstring& path)
{
int length = static_cast(path.length());
for (int i = length - 1; i >= 0; i--)
{
wchar_t ch = path[i];
if (ch == '.')
{
if (i != length - 1)
{
return path.substr(i, length - i);
}
else
{
return std::wstring();
}
}
if (ch == L'\\' || ch == L'/')
{
break;
}
}
return std::wstring();
}
std::string ChangeExtension(const std::string& path, const std::string& ext)
{
int length = static_cast(path.length());
if (length == 0)
{
return std::string();
}
int subLength = static_cast(path.length());
for (int i = length - 1; i >= 0; i--)
{
char ch = path[i];
if (ch == '.')
{
subLength = i;
break;
}
if (ch == '\\' || ch == '/')
{
break;
}
}
std::string subPath = path.substr(0, subLength);
if (ext.length() == 0)
{
return subPath;
}
if (ext.front() != '.')
{
return subPath + "." + ext;
}
else
{
return subPath + ext;
}
}
std::wstring ChangeExtension(const std::wstring& path, const std::wstring& ext)
{
int length = static_cast(path.length());
if (length == 0)
{
return std::wstring();
}
int subLength = static_cast(path.length());
for (int i = length - 1; i >= 0; i--)
{
wchar_t ch = path[i];
if (ch == L'.')
{
subLength = i;
break;
}
if (ch == L'\\' || ch == L'/')
{
break;
}
}
std::wstring subPath = path.substr(0, subLength);
if (ext.length() == 0)
{
return subPath;
}
if (ext.front() != L'.')
{
return subPath + L'.' + ext;
}
else
{
return subPath + ext;
}
}
std::string GetFullPath(const std::string& path)
{
DWORD dwBufferSize = MAX_PATH;
std::string output;
while (dwBufferSize < USHRT_MAX)
{
output.resize(dwBufferSize);
DWORD nSize = GetFullPathNameA(path.c_str(), dwBufferSize, const_cast(output.data()), NULL);
if (nSize == 0)
{
return std::string();
}
if (nSize < dwBufferSize)
{
return output.substr(0, nSize);
}
else
{
dwBufferSize *= 2;
}
}
return std::string();
}
std::wstring GetFullPath(const std::wstring& path)
{
DWORD dwBufferSize = MAX_PATH;
std::wstring output;
while (dwBufferSize < USHRT_MAX)
{
output.resize(dwBufferSize);
DWORD nSize = GetFullPathNameW(path.c_str(), dwBufferSize, const_cast(output.data()), NULL);
if (nSize == 0)
{
return std::wstring();
}
if (nSize < dwBufferSize)
{
return output.substr(0, nSize);
}
else
{
dwBufferSize *= 2;
}
}
return std::wstring();
}
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/path.h
================================================
// path.h
#pragma once
#include
namespace Path
{
std::string GetFileName(const std::string& path);
std::wstring GetFileName(const std::wstring& path);
std::string GetFileNameWithoutExtension(const std::string& path);
std::wstring GetFileNameWithoutExtension(const std::wstring& path);
std::string GetDirectoryName(const std::string& path);
std::wstring GetDirectoryName(const std::wstring& path);
std::string GetExtension(const std::string& path);
std::wstring GetExtension(const std::wstring& path);
std::string ChangeExtension(const std::string& path, const std::string& ext);
std::wstring ChangeExtension(const std::wstring& path, const std::wstring& ext);
std::string GetFullPath(const std::string& path);
std::wstring GetFullPath(const std::wstring& path);
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/pe.cpp
================================================
// pe.cpp
#include "pe.h"
namespace PE
{
PVOID GetModuleBase(HMODULE hModule)
{
MEMORY_BASIC_INFORMATION mem;
if (!VirtualQuery(hModule, &mem, sizeof(mem)))
return 0;
return mem.AllocationBase;
}
DWORD GetModuleSize(HMODULE hModule)
{
return ((PIMAGE_NT_HEADERS)((ULONG_PTR)hModule + ((PIMAGE_DOS_HEADER)hModule)->e_lfanew))->OptionalHeader.SizeOfImage;
}
PIMAGE_SECTION_HEADER GetSectionHeader(HMODULE hModule, PCSTR lpName)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
return NULL;
if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0)
return NULL;
PIMAGE_SECTION_HEADER pSectionHeaders = (PIMAGE_SECTION_HEADER)((PBYTE)pNtHeader + sizeof(pNtHeader->Signature) + sizeof(pNtHeader->FileHeader) + pNtHeader->FileHeader.SizeOfOptionalHeader);
for (DWORD n = 0; n < pNtHeader->FileHeader.NumberOfSections; n++)
{
if (strcmp((PCSTR)pSectionHeaders[n].Name, lpName) == 0)
{
if (pSectionHeaders[n].VirtualAddress == 0 || pSectionHeaders[n].SizeOfRawData == 0)
return NULL;
return &pSectionHeaders[n];
}
}
return NULL;
}
static inline PBYTE RvaAdjust(PIMAGE_DOS_HEADER pDosHeader, DWORD raddr)
{
if (raddr != NULL)
{
return ((PBYTE)pDosHeader) + raddr;
}
return NULL;
}
PVOID GetImportAddress(HMODULE hModule, LPCSTR lpModuleName, LPCSTR lpProcName)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
return NULL;
if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0)
return NULL;
PIMAGE_IMPORT_DESCRIPTOR iidp = (PIMAGE_IMPORT_DESCRIPTOR)RvaAdjust(pDosHeader, pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
if (iidp == NULL)
return NULL;
for (; iidp->OriginalFirstThunk != 0; iidp++)
{
LPCSTR lpszModule = (LPCSTR)RvaAdjust(pDosHeader, iidp->Name);
if (lpszModule == NULL)
return NULL;
if (_stricmp(lpszModule, lpModuleName) != 0)
continue;
PIMAGE_THUNK_DATA pThunks = (PIMAGE_THUNK_DATA)RvaAdjust(pDosHeader, iidp->OriginalFirstThunk);
PVOID* pAddrs = (PVOID*)RvaAdjust(pDosHeader, iidp->FirstThunk);
if (pThunks == NULL)
continue;
for (DWORD i = 0; pThunks[i].u1.Ordinal; i++)
{
if (IMAGE_SNAP_BY_ORDINAL(pThunks[i].u1.Ordinal))
continue;
LPCSTR lpszProc = (PCSTR)RvaAdjust(pDosHeader, (DWORD)pThunks[i].u1.AddressOfData + 2);
if (lpszProc == NULL)
continue;
if (strcmp(lpszProc, lpProcName) == 0)
return &pAddrs[i];
}
}
return NULL;
}
PVOID SearchPattern(PVOID lpStartSearch, DWORD dwSearchLen, const char* lpPattern, DWORD dwPatternLen)
{
ULONG_PTR dwStartAddr = (ULONG_PTR)lpStartSearch;
ULONG_PTR dwEndAddr = dwStartAddr + dwSearchLen - dwPatternLen;
while (dwStartAddr < dwEndAddr)
{
bool found = true;
for (DWORD i = 0; i < dwPatternLen; i++)
{
char code = *(char*)(dwStartAddr + i);
if (lpPattern[i] != 0x2A && lpPattern[i] != code)
{
found = false;
break;
}
}
if (found)
return (PVOID)dwStartAddr;
dwStartAddr++;
}
return 0;
}
BOOL WriteMemory(PVOID lpAddress, PVOID lpBuffer, DWORD nSize)
{
DWORD dwProtect;
if (VirtualProtect(lpAddress, nSize, PAGE_EXECUTE_READWRITE, &dwProtect))
{
memcpy(lpAddress, lpBuffer, nSize);
VirtualProtect(lpAddress, nSize, dwProtect, &dwProtect);
return TRUE;
}
return FALSE;
}
BOOL IATHook(HMODULE hModule, LPCSTR lpModuleName, LPCSTR lpProcName, PVOID lpNewProc, PVOID* lpOriginalProc)
{
PVOID lpAddress = GetImportAddress(hModule, lpModuleName, lpProcName);
if (lpAddress == NULL)
{
return FALSE;
}
if (lpOriginalProc)
{
*lpOriginalProc = *(PVOID*)lpAddress;
}
return WriteValue(lpAddress, lpNewProc);
}
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/pe.h
================================================
// pe.h
#pragma once
#include
#include
namespace PE
{
// Get the base address of the specified module.
PVOID GetModuleBase(HMODULE hModule);
// Get the size of the specified module.
DWORD GetModuleSize(HMODULE hModule);
// Get the section with the specified name.
PIMAGE_SECTION_HEADER GetSectionHeader(HMODULE hModule, PCSTR lpName);
// Get the address of the imported function in the import table.
PVOID GetImportAddress(HMODULE hModule, LPCSTR lpModuleName, LPCSTR lpProcName);
// Searche memory for the specified pattern.
PVOID SearchPattern(PVOID lpStartSearch, DWORD dwSearchLen, const char* lpPattern, DWORD dwPatternLen);
// Write data to the specified address.
BOOL WriteMemory(PVOID lpAddress, PVOID lpBuffer, DWORD nSize);
// Writes a scalar value to the specified address.
// If you pass a pointer, the value of that pointer is written.
template, bool> = true>
BOOL WriteValue(PVOID lpAddress, T tValue)
{
return WriteMemory(lpAddress, &tValue, sizeof(T));
}
// Replace imported function in the import table.
BOOL IATHook(HMODULE hModule, LPCSTR lpModuleName, LPCSTR lpProcName, PVOID lpNewProc, PVOID* lpOriginalProc);
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/stringhelper.cpp
================================================
// stringhelper.cpp
#include
#include
#include
namespace StringHelper
{
bool StartsWith(const char* source, const char* sub)
{
std::string_view vsource(source);
std::string_view vsub(sub);
if (vsource.length() == 0 || vsub.length() == 0 || vsource.length() < vsub.length())
{
return false;
}
return vsource.compare(0, vsub.length(), sub) == 0;
}
bool StartsWith(const wchar_t* source, const wchar_t* sub)
{
std::wstring_view vsource(source);
std::wstring_view vsub(sub);
if (vsource.length() == 0 || vsub.length() == 0 || vsource.length() < vsub.length())
{
return false;
}
return vsource.compare(0, vsub.length(), sub) == 0;
}
bool StartsWith(const std::string& source, const std::string& sub)
{
if (source.length() == 0 || sub.length() == 0 || source.length() < sub.length())
{
return false;
}
return source.compare(0, sub.length(), sub) == 0;
}
bool StartsWith(const std::wstring& source, const std::wstring& sub)
{
if (source.length() == 0 || sub.length() == 0 || source.length() < sub.length())
{
return false;
}
return source.compare(0, sub.length(), sub) == 0;
}
bool EndsWith(const char* source, const char* sub)
{
std::string_view vsource(source);
std::string_view vsub(sub);
if (vsource.length() == 0 || vsub.length() == 0 || vsource.length() < vsub.length())
{
return false;
}
return vsource.compare(vsource.length() - vsub.length(), vsub.length(), sub) == 0;
}
bool EndsWith(const wchar_t* source, const wchar_t* sub)
{
std::wstring_view vsource(source);
std::wstring_view vsub(sub);
if (vsource.length() == 0 || vsub.length() == 0 || vsource.length() < vsub.length())
{
return false;
}
return vsource.compare(vsource.length() - vsub.length(), vsub.length(), sub) == 0;
}
bool EndsWith(const std::string& source, const std::string& sub)
{
if (source.length() == 0 || sub.length() == 0 || source.length() < sub.length())
{
return false;
}
return source.compare(source.length() - sub.length(), sub.length(), sub) == 0;
}
bool EndsWith(const std::wstring& source, const std::wstring& sub)
{
if (source.length() == 0 || sub.length() == 0 || source.length() < sub.length())
{
return false;
}
return source.compare(source.length() - sub.length(), sub.length(), sub) == 0;
}
std::string ToLower(const std::string& source)
{
std::string output = source;
std::transform(output.begin(), output.end(), output.begin(), [](auto c) { return (std::string::value_type)std::tolower(c); });
return output;
}
std::wstring ToLower(const std::wstring& source)
{
std::wstring output = source;
std::transform(output.begin(), output.end(), output.begin(), [](auto c) { return (std::wstring::value_type)std::tolower(c); });
return output;
}
std::string ToUpper(const std::string& source)
{
std::string output = source;
std::transform(output.begin(), output.end(), output.begin(), [](auto c) { return (std::string::value_type)std::toupper(c); });
return output;
}
std::wstring ToUpper(const std::wstring& source)
{
std::wstring output = source;
std::transform(output.begin(), output.end(), output.begin(), [](auto c) { return (std::wstring::value_type)std::toupper(c); });
return output;
}
std::string Format(const char* format, ...)
{
char buf[1024];
int count;
va_list ap;
// Try to print to a small buffer first.
// We don't need to allocate a large buffer if it's enough to hold all the characters.
va_start(ap, format);
count = vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);
if (count <= 0)
{
// Something error happened, We return an empty string.
return std::string();
}
if (count < sizeof(buf))
{
// All characters have been written to the small buffer.
return std::string(buf, count);
}
// Allocate a buffer large enough to hold all characters.
std::string output(count, '\0');
// Try to print
va_start(ap, format);
count = vsnprintf(const_cast(output.data()), output.size() + 1, format, ap);
va_end(ap);
if (count <= 0)
{
// Something error happened, We return an empty string.
return std::string();
}
return output;
}
std::string VFormat(const char* format, va_list ap)
{
char buf[1024];
int count;
// Try to print to a small buffer first.
// We don't need to allocate a large buffer if it's enough to hold all the characters.
count = vsnprintf(buf, sizeof(buf), format, ap);
if (count <= 0)
{
// Something error happened, We return an empty string.
return std::string();
}
if (count < sizeof(buf))
{
// All characters have been written to the small buffer.
return std::string(buf, count);
}
// Allocate a buffer large enough to hold all characters.
std::string output(count, '\0');
// Try to print
count = vsnprintf(const_cast(output.data()), output.size() + 1, format, ap);
if (count <= 0)
{
// Something error happened, We return an empty string.
return std::string();
}
return output;
}
std::wstring Format(const wchar_t* format, ...)
{
wchar_t buf[1024];
int count;
va_list ap;
// Try to print to a small buffer first.
// We don't need to allocate a large buffer if it's enough to hold all the characters.
va_start(ap, format);
count = _vsnwprintf_s(buf, _countof(buf), format, ap);
va_end(ap);
if (count <= 0)
{
// Something error happened, We return an empty string.
return std::wstring();
}
if (count < sizeof(buf))
{
// All characters have been written to the small buffer.
return std::wstring(buf, count);
}
// Allocate a buffer large enough to hold all characters.
std::wstring output(count, '\0');
// Try to print
va_start(ap, format);
count = _vsnwprintf_s(const_cast(output.data()), output.size() + 1, output.size() + 1, format, ap);
va_end(ap);
if (count <= 0)
{
// Something error happened, We return an empty string.
return std::wstring();
}
return output;
}
std::wstring VFormat(const wchar_t* format, va_list ap)
{
wchar_t buf[1024];
int count;
// Try to print to a small buffer first.
// We don't need to allocate a large buffer if it's enough to hold all the characters.
count = _vsnwprintf_s(buf, _countof(buf), format, ap);
if (count <= 0)
{
// Something error happened, We return an empty string.
return std::wstring();
}
if (count < sizeof(buf))
{
// All characters have been written to the small buffer.
return std::wstring(buf, count);
}
// Allocate a buffer large enough to hold all characters.
std::wstring output(count, '\0');
// Try to print
count = _vsnwprintf_s(const_cast(output.data()), output.size() + 1, output.size() + 1, format, ap);
if (count <= 0)
{
// Something error happened, We return an empty string.
return std::wstring();
}
return output;
}
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/stringhelper.h
================================================
// stringhelper.h
#pragma once
#include
namespace StringHelper
{
bool StartsWith(const char* source, const char* sub);
bool StartsWith(const wchar_t* source, const wchar_t* sub);
bool StartsWith(const std::string& source, const std::string& sub);
bool StartsWith(const std::wstring& source, const std::wstring& sub);
bool EndsWith(const char* source, const char* sub);
bool EndsWith(const wchar_t* source, const wchar_t* sub);
bool EndsWith(const std::string& source, const std::string& sub);
bool EndsWith(const std::wstring& source, const std::wstring& sub);
std::string ToLower(const std::string& source);
std::wstring ToLower(const std::wstring& source);
std::string ToUpper(const std::string& source);
std::wstring ToUpper(const std::wstring& source);
std::string Format(const char* format, ...);
std::string VFormat(const char* format, va_list ap);
std::wstring Format(const wchar_t* format, ...);
std::wstring VFormat(const wchar_t* format, va_list ap);
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/util.cpp
================================================
// util.cpp
#include
#include
#include "stringhelper.h"
namespace Util
{
std::string GetModulePathA(HMODULE hModule)
{
DWORD dwBufferSize = MAX_PATH;
std::string output;
// Maximum file path limitation
// @see https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
while (dwBufferSize < USHRT_MAX)
{
output.resize(dwBufferSize);
// Try to get the file name.
DWORD nSize = GetModuleFileNameA(hModule, const_cast(output.data()), dwBufferSize);
DWORD dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_SUCCESS && dwErrorCode != ERROR_INSUFFICIENT_BUFFER)
{
// Something unexpected happened.
return std::string();
}
if (dwErrorCode == ERROR_SUCCESS && nSize < dwBufferSize)
{
// All characters have been written into the buffer.
return output.substr(0, nSize);
}
if (dwErrorCode == ERROR_INSUFFICIENT_BUFFER || nSize == dwBufferSize)
{
// Expand the buffer.
dwBufferSize *= 2;
}
}
return std::string();
}
std::wstring GetModulePathW(HMODULE hModule)
{
DWORD dwBufferSize = MAX_PATH;
std::wstring output;
// Maximum file path limitation
// @see https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
while (dwBufferSize < USHRT_MAX)
{
output.resize(dwBufferSize);
// Try to get the file name.
DWORD nSize = GetModuleFileNameW(hModule, const_cast(output.data()), dwBufferSize);
DWORD dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_SUCCESS && dwErrorCode != ERROR_INSUFFICIENT_BUFFER)
{
// Something unexpected happened.
return std::wstring();
}
if (dwErrorCode == ERROR_SUCCESS && nSize < dwBufferSize)
{
// All characters have been written into the buffer.
return output.substr(0, nSize);
}
if (dwErrorCode == ERROR_INSUFFICIENT_BUFFER || nSize == dwBufferSize)
{
// Expand the buffer.
dwBufferSize *= 2;
}
}
return std::wstring();
}
std::string GetAppPathA()
{
return GetModulePathA(GetModuleHandleW(NULL));
}
std::wstring GetAppPathW()
{
return GetModulePathW(GetModuleHandleW(NULL));
}
std::string GetAppDirectoryA()
{
std::string path = GetAppPathA();
size_t pos = path.find_last_of('\\');
if (pos != std::string::npos && pos > 0)
{
return path.substr(0, pos);
}
return path;
}
std::wstring GetAppDirectoryW()
{
std::wstring path = GetAppPathW();
size_t pos = path.find_last_of('\\');
if (pos != std::wstring::npos && pos > 0)
{
return path.substr(0, pos);
}
return path;
}
std::string GetLastErrorMessageA()
{
DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM;
DWORD dwErrorCode = GetLastError();
DWORD dwLanguageId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
LPSTR pBuffer = NULL;
if (FormatMessageA(dwFlags, NULL, dwErrorCode, dwLanguageId, (LPSTR)&pBuffer, 0, NULL) == 0)
{
return std::string();
}
std::string message(pBuffer);
LocalFree(pBuffer);
return message;
}
std::wstring GetLastErrorMessageW()
{
DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM;
DWORD dwErrorCode = GetLastError();
DWORD dwLanguageId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
PWSTR pBuffer = NULL;
if (FormatMessageW(dwFlags, NULL, dwErrorCode, dwLanguageId, (PWSTR)&pBuffer, 0, NULL) == 0)
{
return std::wstring();
}
std::wstring message(pBuffer);
LocalFree(pBuffer);
return message;
}
__declspec(noreturn) void ThrowError(const char* format, ...)
{
va_list ap;
va_start(ap, format);
auto message = StringHelper::VFormat(format, ap);
va_end(ap);
MessageBoxA(NULL, message.c_str(), "Fatal Error", MB_ICONERROR | MB_OK);
ExitProcess(1);
}
__declspec(noreturn) void ThrowError(const wchar_t* format, ...)
{
va_list ap;
va_start(ap, format);
auto message = StringHelper::VFormat(format, ap);
va_end(ap);
MessageBoxW(NULL, message.c_str(), L"Fatal Error", MB_ICONERROR | MB_OK);
ExitProcess(1);
}
void WriteDebugMessage(const char* format, ...)
{
va_list ap;
va_start(ap, format);
auto message = StringHelper::VFormat(format, ap);
va_end(ap);
OutputDebugStringA(message.c_str());
}
void WriteDebugMessage(const wchar_t* format, ...)
{
va_list ap;
va_start(ap, format);
auto message = StringHelper::VFormat(format, ap);
va_end(ap);
OutputDebugStringW(message.c_str());
}
std::string OpenFolderDialog(const std::string& title)
{
char buf[MAX_PATH]{};
BROWSEINFOA bi{};
bi.hwndOwner = GetActiveWindow();
bi.pidlRoot = NULL;
bi.pszDisplayName = buf;
bi.lpszTitle = title.c_str();
bi.ulFlags = BIF_NEWDIALOGSTYLE;
bi.lpfn = NULL;
bi.lParam = NULL;
bi.iImage = 0;
LPITEMIDLIST idl = SHBrowseForFolderA(&bi);
if (idl == NULL)
{
return std::string();
}
if (SHGetPathFromIDListA(idl, buf) == FALSE)
{
return std::string();
}
return std::string(buf);
}
std::wstring OpenFolderDialog(const std::wstring& title)
{
WCHAR buf[MAX_PATH]{};
BROWSEINFOW bi{};
bi.hwndOwner = GetActiveWindow();
bi.pidlRoot = NULL;
bi.pszDisplayName = buf;
bi.lpszTitle = title.c_str();
bi.ulFlags = BIF_NEWDIALOGSTYLE;
bi.lpfn = NULL;
bi.lParam = NULL;
bi.iImage = 0;
LPITEMIDLIST idl = SHBrowseForFolderW(&bi);
if (idl == NULL)
{
return std::wstring();
}
if (SHGetPathFromIDListW(idl, buf) == FALSE)
{
return std::wstring();
}
return std::wstring(buf);
}
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Common/util.h
================================================
// util.h
#pragma once
#include
#include
namespace Util
{
// Get the full path of the specified module.
std::string GetModulePathA(HMODULE hModule);
// Get the full path of the specified module.
std::wstring GetModulePathW(HMODULE hModule);
// Get the full path to the executable.
std::string GetAppPathA();
// Get the full path to the executable.
std::wstring GetAppPathW();
// Get the directory path to the executable.
std::string GetAppDirectoryA();
// Get the directory path to the executable.
std::wstring GetAppDirectoryW();
// Get message from Win32 last error code.
std::string GetLastErrorMessageA();
// Get message from Win32 last error code.
std::wstring GetLastErrorMessageW();
// Display an error message then close the application.
__declspec(noreturn) void ThrowError(const char* format, ...);
// Display an error message then close the application.
__declspec(noreturn) void ThrowError(const wchar_t* format, ...);
// Sends a string to the debugger for display.
void WriteDebugMessage(const char* format, ...);
// Sends a string to the debugger for display.
void WriteDebugMessage(const wchar_t* format, ...);
// Display a folder select dialog.
std::string OpenFolderDialog(const std::string& title);
// Display a folder select dialog.
std::wstring OpenFolderDialog(const std::wstring& title);
}
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Detours/creatwth.cpp
================================================
//////////////////////////////////////////////////////////////////////////////
//
// Create a process with a DLL (creatwth.cpp of detours.lib)
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// #define DETOUR_DEBUG 1
#define DETOURS_INTERNAL
#include "detours.h"
#include
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
#error detours.h version mismatch
#endif
#define IMPORT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
#define BOUND_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]
#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]
#define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT]
//////////////////////////////////////////////////////////////////////////////
//
const GUID DETOUR_EXE_HELPER_GUID = { /* ea0251b9-5cde-41b5-98d0-2af4a26b0fee */
0xea0251b9, 0x5cde, 0x41b5,
{ 0x98, 0xd0, 0x2a, 0xf4, 0xa2, 0x6b, 0x0f, 0xee }};
//////////////////////////////////////////////////////////////////////////////
//
// Enumerate through modules in the target process.
//
static PVOID LoadNtHeaderFromProcess(_In_ HANDLE hProcess,
_In_ HMODULE hModule,
_Out_ PIMAGE_NT_HEADERS32 pNtHeader)
{
ZeroMemory(pNtHeader, sizeof(*pNtHeader));
PBYTE pbModule = (PBYTE)hModule;
if (pbModule == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi, sizeof(mbi));
if (VirtualQueryEx(hProcess, hModule, &mbi, sizeof(mbi)) == 0) {
return NULL;
}
IMAGE_DOS_HEADER idh;
if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n",
pbModule, pbModule + sizeof(idh), GetLastError()));
return NULL;
}
if (idh.e_magic != IMAGE_DOS_SIGNATURE ||
(DWORD)idh.e_lfanew > mbi.RegionSize ||
(DWORD)idh.e_lfanew < sizeof(idh)) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew,
pNtHeader, sizeof(*pNtHeader), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p:%p) failed: %lu\n",
pbModule + idh.e_lfanew,
pbModule + idh.e_lfanew + sizeof(*pNtHeader),
pbModule,
GetLastError()));
return NULL;
}
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
return pbModule + idh.e_lfanew;
}
static HMODULE EnumerateModulesInProcess(_In_ HANDLE hProcess,
_In_opt_ HMODULE hModuleLast,
_Out_ PIMAGE_NT_HEADERS32 pNtHeader,
_Out_opt_ PVOID *pRemoteNtHeader)
{
ZeroMemory(pNtHeader, sizeof(*pNtHeader));
if (pRemoteNtHeader) {
*pRemoteNtHeader = NULL;
}
PBYTE pbLast = (PBYTE)hModuleLast + MM_ALLOCATION_GRANULARITY;
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi, sizeof(mbi));
// Find the next memory region that contains a mapped PE image.
//
for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) {
if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) {
break;
}
// Usermode address space has such an unaligned region size always at the
// end and only at the end.
//
if ((mbi.RegionSize & 0xfff) == 0xfff) {
break;
}
if (((PBYTE)mbi.BaseAddress + mbi.RegionSize) < pbLast) {
break;
}
// Skip uncommitted regions and guard pages.
//
if ((mbi.State != MEM_COMMIT) ||
((mbi.Protect & 0xff) == PAGE_NOACCESS) ||
(mbi.Protect & PAGE_GUARD)) {
continue;
}
PVOID remoteHeader
= LoadNtHeaderFromProcess(hProcess, (HMODULE)pbLast, pNtHeader);
if (remoteHeader) {
if (pRemoteNtHeader) {
*pRemoteNtHeader = remoteHeader;
}
return (HMODULE)pbLast;
}
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////////
//
// Find payloads in target process.
//
static PVOID FindDetourSectionInRemoteModule(_In_ HANDLE hProcess,
_In_ HMODULE hModule,
_In_ const IMAGE_NT_HEADERS32 *pNtHeader,
_In_ PVOID pRemoteNtHeader)
{
if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return NULL;
}
PIMAGE_SECTION_HEADER pRemoteSectionHeaders
= (PIMAGE_SECTION_HEADER)((PBYTE)pRemoteNtHeader
+ sizeof(pNtHeader->Signature)
+ sizeof(pNtHeader->FileHeader)
+ pNtHeader->FileHeader.SizeOfOptionalHeader);
IMAGE_SECTION_HEADER header;
for (DWORD n = 0; n < pNtHeader->FileHeader.NumberOfSections; ++n) {
if (!ReadProcessMemory(hProcess, pRemoteSectionHeaders + n, &header, sizeof(header), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %lu\n",
pRemoteSectionHeaders + n,
(PBYTE)(pRemoteSectionHeaders + n) + sizeof(header),
GetLastError()));
return NULL;
}
if (strcmp((PCHAR)header.Name, ".detour") == 0) {
if (header.VirtualAddress == 0 ||
header.SizeOfRawData == 0) {
break;
}
SetLastError(NO_ERROR);
return (PBYTE)hModule + header.VirtualAddress;
}
}
SetLastError(ERROR_EXE_MARKED_INVALID);
return NULL;
}
static PVOID FindPayloadInRemoteDetourSection(_In_ HANDLE hProcess,
_In_ REFGUID rguid,
_Out_opt_ DWORD *pcbData,
_In_ PVOID pvRemoteDetoursSection)
{
if (pcbData) {
*pcbData = 0;
}
PBYTE pbData = (PBYTE)pvRemoteDetoursSection;
DETOUR_SECTION_HEADER header;
if (!ReadProcessMemory(hProcess, pbData, &header, sizeof(header), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(dsh@%p..%p) failed: %lu\n",
pbData,
pbData + sizeof(header),
GetLastError()));
return NULL;
}
if (header.cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) ||
header.nSignature != DETOUR_SECTION_HEADER_SIGNATURE) {
SetLastError(ERROR_EXE_MARKED_INVALID);
return NULL;
}
if (header.nDataOffset == 0) {
header.nDataOffset = header.cbHeaderSize;
}
for (PVOID pvSection = pbData + header.nDataOffset; pvSection < pbData + header.cbDataSize;) {
DETOUR_SECTION_RECORD section;
if (!ReadProcessMemory(hProcess, pvSection, §ion, sizeof(section), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(dsr@%p..%p) failed: %lu\n",
pvSection,
(PBYTE)pvSection + sizeof(section),
GetLastError()));
return NULL;
}
if (DetourAreSameGuid(section.guid, rguid)) {
if (pcbData) {
*pcbData = section.cbBytes - sizeof(section);
}
SetLastError(NO_ERROR);
return (DETOUR_SECTION_RECORD *)pvSection + 1;
}
pvSection = (PBYTE)pvSection + section.cbBytes;
}
return NULL;
}
_Success_(return != NULL)
PVOID WINAPI DetourFindRemotePayload(_In_ HANDLE hProcess,
_In_ REFGUID rguid,
_Out_opt_ DWORD *pcbData)
{
if (hProcess == NULL) {
SetLastError(ERROR_INVALID_HANDLE);
return NULL;
}
IMAGE_NT_HEADERS32 header;
PVOID pvRemoteHeader;
for (HMODULE hMod = NULL; (hMod = EnumerateModulesInProcess(hProcess, hMod, &header, &pvRemoteHeader)) != NULL;) {
PVOID pvData = FindDetourSectionInRemoteModule(hProcess, hMod, &header, pvRemoteHeader);
if (pvData != NULL) {
pvData = FindPayloadInRemoteDetourSection(hProcess, rguid, pcbData, pvData);
if (pvData != NULL) {
return pvData;
}
}
}
SetLastError(ERROR_MOD_NOT_FOUND);
return NULL;
}
//////////////////////////////////////////////////////////////////////////////
//
// Find a region of memory in which we can create a replacement import table.
//
static PBYTE FindAndAllocateNearBase(HANDLE hProcess, PBYTE pbModule, PBYTE pbBase, DWORD cbAlloc)
{
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi, sizeof(mbi));
PBYTE pbLast = pbBase;
for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) {
ZeroMemory(&mbi, sizeof(mbi));
if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) {
if (GetLastError() == ERROR_INVALID_PARAMETER) {
break;
}
DETOUR_TRACE(("VirtualQueryEx(%p) failed: %lu\n",
pbLast, GetLastError()));
break;
}
// Usermode address space has such an unaligned region size always at the
// end and only at the end.
//
if ((mbi.RegionSize & 0xfff) == 0xfff) {
break;
}
// Skip anything other than a pure free region.
//
if (mbi.State != MEM_FREE) {
continue;
}
// Use the max of mbi.BaseAddress and pbBase, in case mbi.BaseAddress < pbBase.
PBYTE pbAddress = (PBYTE)mbi.BaseAddress > pbBase ? (PBYTE)mbi.BaseAddress : pbBase;
// Round pbAddress up to the nearest MM allocation boundary.
const DWORD_PTR mmGranularityMinusOne = (DWORD_PTR)(MM_ALLOCATION_GRANULARITY -1);
pbAddress = (PBYTE)(((DWORD_PTR)pbAddress + mmGranularityMinusOne) & ~mmGranularityMinusOne);
#ifdef _WIN64
// The offset from pbModule to any replacement import must fit into 32 bits.
// For simplicity, we check that the offset to the last byte fits into 32 bits,
// instead of the largest offset we'll actually use. The values are very similar.
const size_t GB4 = ((((size_t)1) << 32) - 1);
if ((size_t)(pbAddress + cbAlloc - 1 - pbModule) > GB4) {
DETOUR_TRACE(("FindAndAllocateNearBase(1) failing due to distance >4GB %p\n", pbAddress));
return NULL;
}
#else
UNREFERENCED_PARAMETER(pbModule);
#endif
DETOUR_TRACE(("Free region %p..%p\n",
mbi.BaseAddress,
(PBYTE)mbi.BaseAddress + mbi.RegionSize));
for (; pbAddress < (PBYTE)mbi.BaseAddress + mbi.RegionSize; pbAddress += MM_ALLOCATION_GRANULARITY) {
PBYTE pbAlloc = (PBYTE)VirtualAllocEx(hProcess, pbAddress, cbAlloc,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pbAlloc == NULL) {
DETOUR_TRACE(("VirtualAllocEx(%p) failed: %lu\n", pbAddress, GetLastError()));
continue;
}
#ifdef _WIN64
// The offset from pbModule to any replacement import must fit into 32 bits.
if ((size_t)(pbAddress + cbAlloc - 1 - pbModule) > GB4) {
DETOUR_TRACE(("FindAndAllocateNearBase(2) failing due to distance >4GB %p\n", pbAddress));
return NULL;
}
#endif
DETOUR_TRACE(("[%p..%p] Allocated for import table.\n",
pbAlloc, pbAlloc + cbAlloc));
return pbAlloc;
}
}
return NULL;
}
static inline DWORD PadToDword(DWORD dw)
{
return (dw + 3) & ~3u;
}
static inline DWORD PadToDwordPtr(DWORD dw)
{
return (dw + 7) & ~7u;
}
static inline HRESULT ReplaceOptionalSizeA(_Inout_z_count_(cchDest) LPSTR pszDest,
_In_ size_t cchDest,
_In_z_ LPCSTR pszSize)
{
if (cchDest == 0 || pszDest == NULL || pszSize == NULL ||
pszSize[0] == '\0' || pszSize[1] == '\0' || pszSize[2] != '\0') {
// can not write into empty buffer or with string other than two chars.
return ERROR_INVALID_PARAMETER;
}
for (; cchDest >= 2; cchDest--, pszDest++) {
if (pszDest[0] == '?' && pszDest[1] == '?') {
pszDest[0] = pszSize[0];
pszDest[1] = pszSize[1];
break;
}
}
return S_OK;
}
static BOOL RecordExeRestore(HANDLE hProcess, HMODULE hModule, DETOUR_EXE_RESTORE& der)
{
// Save the various headers for DetourRestoreAfterWith.
ZeroMemory(&der, sizeof(der));
der.cb = sizeof(der);
der.pidh = (PBYTE)hModule;
der.cbidh = sizeof(der.idh);
if (!ReadProcessMemory(hProcess, der.pidh, &der.idh, sizeof(der.idh), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n",
der.pidh, der.pidh + der.cbidh, GetLastError()));
return FALSE;
}
DETOUR_TRACE(("IDH: %p..%p\n", der.pidh, der.pidh + der.cbidh));
// We read the NT header in two passes to get the full size.
// First we read just the Signature and FileHeader.
der.pinh = der.pidh + der.idh.e_lfanew;
der.cbinh = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader);
if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) {
DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n",
der.pinh, der.pinh + der.cbinh, GetLastError()));
return FALSE;
}
// Second we read the OptionalHeader and Section headers.
der.cbinh = (FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +
der.inh.FileHeader.SizeOfOptionalHeader +
der.inh.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
if (der.cbinh > sizeof(der.raw)) {
return FALSE;
}
if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) {
DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n",
der.pinh, der.pinh + der.cbinh, GetLastError()));
return FALSE;
}
DETOUR_TRACE(("INH: %p..%p\n", der.pinh, der.pinh + der.cbinh));
// Third, we read the CLR header
if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
if (der.inh32.CLR_DIRECTORY.VirtualAddress != 0 &&
der.inh32.CLR_DIRECTORY.Size != 0) {
DETOUR_TRACE(("CLR32.VirtAddr=%08lx, CLR.Size=%lu\n",
der.inh32.CLR_DIRECTORY.VirtualAddress,
der.inh32.CLR_DIRECTORY.Size));
der.pclr = ((PBYTE)hModule) + der.inh32.CLR_DIRECTORY.VirtualAddress;
}
}
else if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
if (der.inh64.CLR_DIRECTORY.VirtualAddress != 0 &&
der.inh64.CLR_DIRECTORY.Size != 0) {
DETOUR_TRACE(("CLR64.VirtAddr=%08lx, CLR.Size=%lu\n",
der.inh64.CLR_DIRECTORY.VirtualAddress,
der.inh64.CLR_DIRECTORY.Size));
der.pclr = ((PBYTE)hModule) + der.inh64.CLR_DIRECTORY.VirtualAddress;
}
}
if (der.pclr != 0) {
der.cbclr = sizeof(der.clr);
if (!ReadProcessMemory(hProcess, der.pclr, &der.clr, der.cbclr, NULL)) {
DETOUR_TRACE(("ReadProcessMemory(clr@%p..%p) failed: %lu\n",
der.pclr, der.pclr + der.cbclr, GetLastError()));
return FALSE;
}
DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr));
}
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
//
#if DETOURS_32BIT
#define DWORD_XX DWORD32
#define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS32
#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR32_MAGIC
#define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG32
#define IMAGE_THUNK_DATAXX IMAGE_THUNK_DATA32
#define UPDATE_IMPORTS_XX UpdateImports32
#define DETOURS_BITS_XX 32
#include "uimports.cpp"
#undef DETOUR_EXE_RESTORE_FIELD_XX
#undef DWORD_XX
#undef IMAGE_NT_HEADERS_XX
#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX
#undef IMAGE_ORDINAL_FLAG_XX
#undef UPDATE_IMPORTS_XX
#endif // DETOURS_32BIT
#if DETOURS_64BIT
#define DWORD_XX DWORD64
#define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS64
#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR64_MAGIC
#define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG64
#define IMAGE_THUNK_DATAXX IMAGE_THUNK_DATA64
#define UPDATE_IMPORTS_XX UpdateImports64
#define DETOURS_BITS_XX 64
#include "uimports.cpp"
#undef DETOUR_EXE_RESTORE_FIELD_XX
#undef DWORD_XX
#undef IMAGE_NT_HEADERS_XX
#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX
#undef IMAGE_ORDINAL_FLAG_XX
#undef UPDATE_IMPORTS_XX
#endif // DETOURS_64BIT
//////////////////////////////////////////////////////////////////////////////
//
#if DETOURS_64BIT
C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == sizeof(IMAGE_NT_HEADERS32) + 16);
static BOOL UpdateFrom32To64(HANDLE hProcess, HMODULE hModule, WORD machine,
DETOUR_EXE_RESTORE& der)
{
IMAGE_DOS_HEADER idh;
IMAGE_NT_HEADERS32 inh32;
IMAGE_NT_HEADERS64 inh64;
IMAGE_SECTION_HEADER sects[32];
PBYTE pbModule = (PBYTE)hModule;
DWORD n;
ZeroMemory(&inh32, sizeof(inh32));
ZeroMemory(&inh64, sizeof(inh64));
ZeroMemory(sects, sizeof(sects));
DETOUR_TRACE(("UpdateFrom32To64(%04x)\n", machine));
//////////////////////////////////////////////////////// Read old headers.
//
if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n",
pbModule, pbModule + sizeof(idh), GetLastError()));
return FALSE;
}
DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p)\n",
pbModule, pbModule + sizeof(idh)));
PBYTE pnh = pbModule + idh.e_lfanew;
if (!ReadProcessMemory(hProcess, pnh, &inh32, sizeof(inh32), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n",
pnh, pnh + sizeof(inh32), GetLastError()));
return FALSE;
}
DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh32)));
if (inh32.FileHeader.NumberOfSections > (sizeof(sects)/sizeof(sects[0]))) {
return FALSE;
}
PBYTE psects = pnh +
FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +
inh32.FileHeader.SizeOfOptionalHeader;
ULONG cb = inh32.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
if (!ReadProcessMemory(hProcess, psects, §s, cb, NULL)) {
DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %lu\n",
psects, psects + cb, GetLastError()));
return FALSE;
}
DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p)\n", psects, psects + cb));
////////////////////////////////////////////////////////// Convert header.
//
inh64.Signature = inh32.Signature;
inh64.FileHeader = inh32.FileHeader;
inh64.FileHeader.Machine = machine;
inh64.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64);
inh64.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC;
inh64.OptionalHeader.MajorLinkerVersion = inh32.OptionalHeader.MajorLinkerVersion;
inh64.OptionalHeader.MinorLinkerVersion = inh32.OptionalHeader.MinorLinkerVersion;
inh64.OptionalHeader.SizeOfCode = inh32.OptionalHeader.SizeOfCode;
inh64.OptionalHeader.SizeOfInitializedData = inh32.OptionalHeader.SizeOfInitializedData;
inh64.OptionalHeader.SizeOfUninitializedData = inh32.OptionalHeader.SizeOfUninitializedData;
inh64.OptionalHeader.AddressOfEntryPoint = inh32.OptionalHeader.AddressOfEntryPoint;
inh64.OptionalHeader.BaseOfCode = inh32.OptionalHeader.BaseOfCode;
inh64.OptionalHeader.ImageBase = inh32.OptionalHeader.ImageBase;
inh64.OptionalHeader.SectionAlignment = inh32.OptionalHeader.SectionAlignment;
inh64.OptionalHeader.FileAlignment = inh32.OptionalHeader.FileAlignment;
inh64.OptionalHeader.MajorOperatingSystemVersion
= inh32.OptionalHeader.MajorOperatingSystemVersion;
inh64.OptionalHeader.MinorOperatingSystemVersion
= inh32.OptionalHeader.MinorOperatingSystemVersion;
inh64.OptionalHeader.MajorImageVersion = inh32.OptionalHeader.MajorImageVersion;
inh64.OptionalHeader.MinorImageVersion = inh32.OptionalHeader.MinorImageVersion;
inh64.OptionalHeader.MajorSubsystemVersion = inh32.OptionalHeader.MajorSubsystemVersion;
inh64.OptionalHeader.MinorSubsystemVersion = inh32.OptionalHeader.MinorSubsystemVersion;
inh64.OptionalHeader.Win32VersionValue = inh32.OptionalHeader.Win32VersionValue;
inh64.OptionalHeader.SizeOfImage = inh32.OptionalHeader.SizeOfImage;
inh64.OptionalHeader.SizeOfHeaders = inh32.OptionalHeader.SizeOfHeaders;
inh64.OptionalHeader.CheckSum = inh32.OptionalHeader.CheckSum;
inh64.OptionalHeader.Subsystem = inh32.OptionalHeader.Subsystem;
inh64.OptionalHeader.DllCharacteristics = inh32.OptionalHeader.DllCharacteristics;
inh64.OptionalHeader.SizeOfStackReserve = inh32.OptionalHeader.SizeOfStackReserve;
inh64.OptionalHeader.SizeOfStackCommit = inh32.OptionalHeader.SizeOfStackCommit;
inh64.OptionalHeader.SizeOfHeapReserve = inh32.OptionalHeader.SizeOfHeapReserve;
inh64.OptionalHeader.SizeOfHeapCommit = inh32.OptionalHeader.SizeOfHeapCommit;
inh64.OptionalHeader.LoaderFlags = inh32.OptionalHeader.LoaderFlags;
inh64.OptionalHeader.NumberOfRvaAndSizes = inh32.OptionalHeader.NumberOfRvaAndSizes;
for (n = 0; n < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; n++) {
inh64.OptionalHeader.DataDirectory[n] = inh32.OptionalHeader.DataDirectory[n];
}
/////////////////////////////////////////////////////// Write new headers.
//
DWORD dwProtect = 0;
if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders,
PAGE_EXECUTE_READWRITE, &dwProtect)) {
return FALSE;
}
if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) {
DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %lu\n",
pnh, pnh + sizeof(inh64), GetLastError()));
return FALSE;
}
DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh64)));
psects = pnh +
FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +
inh64.FileHeader.SizeOfOptionalHeader;
cb = inh64.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
if (!WriteProcessMemory(hProcess, psects, §s, cb, NULL)) {
DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p) failed: %lu\n",
psects, psects + cb, GetLastError()));
return FALSE;
}
DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p)\n", psects, psects + cb));
// Record the updated headers.
if (!RecordExeRestore(hProcess, hModule, der)) {
return FALSE;
}
// Remove the import table.
if (der.pclr != NULL && (der.clr.Flags & COMIMAGE_FLAGS_ILONLY)) {
inh64.IMPORT_DIRECTORY.VirtualAddress = 0;
inh64.IMPORT_DIRECTORY.Size = 0;
if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) {
DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %lu\n",
pnh, pnh + sizeof(inh64), GetLastError()));
return FALSE;
}
}
DWORD dwOld = 0;
if (!VirtualProtectEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders,
dwProtect, &dwOld)) {
return FALSE;
}
return TRUE;
}
#endif // DETOURS_64BIT
typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
static BOOL IsWow64ProcessHelper(HANDLE hProcess,
PBOOL Wow64Process)
{
#ifdef _X86_
if (Wow64Process == NULL) {
return FALSE;
}
// IsWow64Process is not available on all supported versions of Windows.
//
HMODULE hKernel32 = LoadLibraryW(L"KERNEL32.DLL");
if (hKernel32 == NULL) {
DETOUR_TRACE(("LoadLibraryW failed: %lu\n", GetLastError()));
return FALSE;
}
LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(
hKernel32, "IsWow64Process");
if (pfnIsWow64Process == NULL) {
DETOUR_TRACE(("GetProcAddress failed: %lu\n", GetLastError()));
return FALSE;
}
return pfnIsWow64Process(hProcess, Wow64Process);
#else
return IsWow64Process(hProcess, Wow64Process);
#endif
}
//////////////////////////////////////////////////////////////////////////////
//
BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_ DWORD nDlls)
{
// Find the next memory region that contains a mapped PE image.
//
BOOL bIs32BitProcess;
BOOL bIs64BitOS = FALSE;
HMODULE hModule = NULL;
HMODULE hLast = NULL;
DETOUR_TRACE(("DetourUpdateProcessWithDll(%p,dlls=%lu)\n", hProcess, nDlls));
for (;;) {
IMAGE_NT_HEADERS32 inh;
if ((hLast = EnumerateModulesInProcess(hProcess, hLast, &inh, NULL)) == NULL) {
break;
}
DETOUR_TRACE(("%p machine=%04x magic=%04x\n",
hLast, inh.FileHeader.Machine, inh.OptionalHeader.Magic));
if ((inh.FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) {
hModule = hLast;
DETOUR_TRACE(("%p Found EXE\n", hLast));
}
}
if (hModule == NULL) {
SetLastError(ERROR_INVALID_OPERATION);
return FALSE;
}
// Determine if the target process is 32bit or 64bit. This is a two-stop process:
//
// 1. First, determine if we're running on a 64bit operating system.
// - If we're running 64bit code (i.e. _WIN64 is defined), this is trivially true.
// - If we're running 32bit code (i.e. _WIN64 is not defined), test if
// we're running under Wow64. If so, it implies that the operating system
// is 64bit.
//
#ifdef _WIN64
bIs64BitOS = TRUE;
#else
if (!IsWow64ProcessHelper(GetCurrentProcess(), &bIs64BitOS)) {
return FALSE;
}
#endif
// 2. With the operating system bitness known, we can now consider the target process:
// - If we're running on a 64bit OS, the target process is 32bit in case
// it is running under Wow64. Otherwise, it's 64bit, running natively
// (without Wow64).
// - If we're running on a 32bit OS, the target process must be 32bit, too.
//
if (bIs64BitOS) {
if (!IsWow64ProcessHelper(hProcess, &bIs32BitProcess)) {
return FALSE;
}
} else {
bIs32BitProcess = TRUE;
}
DETOUR_TRACE((" 32BitProcess=%d\n", bIs32BitProcess));
return DetourUpdateProcessWithDllEx(hProcess,
hModule,
bIs32BitProcess,
rlpDlls,
nDlls);
}
BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess,
_In_ HMODULE hModule,
_In_ BOOL bIs32BitProcess,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_ DWORD nDlls)
{
// Find the next memory region that contains a mapped PE image.
//
BOOL bIs32BitExe = FALSE;
DETOUR_TRACE(("DetourUpdateProcessWithDllEx(%p,%p,dlls=%lu)\n", hProcess, hModule, nDlls));
IMAGE_NT_HEADERS32 inh;
if (hModule == NULL || !LoadNtHeaderFromProcess(hProcess, hModule, &inh)) {
SetLastError(ERROR_INVALID_OPERATION);
return FALSE;
}
if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
&& inh.FileHeader.Machine != 0) {
bIs32BitExe = TRUE;
}
DETOUR_TRACE((" 32BitExe=%d\n", bIs32BitExe));
if (hModule == NULL) {
SetLastError(ERROR_INVALID_OPERATION);
return FALSE;
}
// Save the various headers for DetourRestoreAfterWith.
//
DETOUR_EXE_RESTORE der;
if (!RecordExeRestore(hProcess, hModule, der)) {
return FALSE;
}
#if defined(DETOURS_64BIT)
// Try to convert a neutral 32-bit managed binary to a 64-bit managed binary.
if (bIs32BitExe && !bIs32BitProcess) {
if (!der.pclr // Native binary
|| (der.clr.Flags & COMIMAGE_FLAGS_ILONLY) == 0 // Or mixed-mode MSIL
|| (der.clr.Flags & COMIMAGE_FLAGS_32BITREQUIRED) != 0) { // Or 32BIT Required MSIL
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (!UpdateFrom32To64(hProcess, hModule,
#if defined(DETOURS_X64)
IMAGE_FILE_MACHINE_AMD64,
#elif defined(DETOURS_IA64)
IMAGE_FILE_MACHINE_IA64,
#elif defined(DETOURS_ARM64)
IMAGE_FILE_MACHINE_ARM64,
#else
#error Must define one of DETOURS_X64 or DETOURS_IA64 or DETOURS_ARM64 on 64-bit.
#endif
der)) {
return FALSE;
}
bIs32BitExe = FALSE;
}
#endif // DETOURS_64BIT
// Now decide if we can insert the detour.
#if defined(DETOURS_32BIT)
if (bIs32BitProcess) {
// 32-bit native or 32-bit managed process on any platform.
if (!UpdateImports32(hProcess, hModule, rlpDlls, nDlls)) {
return FALSE;
}
}
else {
// 64-bit native or 64-bit managed process.
//
// Can't detour a 64-bit process with 32-bit code.
// Note: This happens for 32-bit PE binaries containing only
// manage code that have been marked as 64-bit ready.
//
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
#elif defined(DETOURS_64BIT)
if (bIs32BitProcess || bIs32BitExe) {
// Can't detour a 32-bit process with 64-bit code.
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
else {
// 64-bit native or 64-bit managed process on any platform.
if (!UpdateImports64(hProcess, hModule, rlpDlls, nDlls)) {
return FALSE;
}
}
#else
#pragma Must define one of DETOURS_32BIT or DETOURS_64BIT.
#endif // DETOURS_64BIT
/////////////////////////////////////////////////// Update the CLR header.
//
if (der.pclr != NULL) {
DETOUR_CLR_HEADER clr;
CopyMemory(&clr, &der.clr, sizeof(clr));
clr.Flags &= ~COMIMAGE_FLAGS_ILONLY; // Clear the IL_ONLY flag.
DWORD dwProtect;
if (!DetourVirtualProtectSameExecuteEx(hProcess, der.pclr, sizeof(clr), PAGE_READWRITE, &dwProtect)) {
DETOUR_TRACE(("VirtualProtectEx(clr) write failed: %lu\n", GetLastError()));
return FALSE;
}
if (!WriteProcessMemory(hProcess, der.pclr, &clr, sizeof(clr), NULL)) {
DETOUR_TRACE(("WriteProcessMemory(clr) failed: %lu\n", GetLastError()));
return FALSE;
}
if (!VirtualProtectEx(hProcess, der.pclr, sizeof(clr), dwProtect, &dwProtect)) {
DETOUR_TRACE(("VirtualProtectEx(clr) restore failed: %lu\n", GetLastError()));
return FALSE;
}
DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr));
#if DETOURS_64BIT
if (der.clr.Flags & COMIMAGE_FLAGS_32BITREQUIRED) { // Is the 32BIT Required Flag set?
// X64 never gets here because the process appears as a WOW64 process.
// However, on IA64, it doesn't appear to be a WOW process.
DETOUR_TRACE(("CLR Requires 32-bit\n"));
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
#endif // DETOURS_64BIT
}
//////////////////////////////// Save the undo data to the target process.
//
if (!DetourCopyPayloadToProcess(hProcess, DETOUR_EXE_RESTORE_GUID, &der, sizeof(der))) {
DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError()));
return FALSE;
}
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
//
BOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ LPCSTR lpDllName,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
{
DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED);
PROCESS_INFORMATION pi;
BOOL fResult = FALSE;
if (pfCreateProcessA == NULL) {
pfCreateProcessA = CreateProcessA;
}
fResult = pfCreateProcessA(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwMyCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
&pi);
if (lpProcessInformation != NULL) {
CopyMemory(lpProcessInformation, &pi, sizeof(pi));
}
if (!fResult) {
return FALSE;
}
LPCSTR rlpDlls[2];
DWORD nDlls = 0;
if (lpDllName != NULL) {
rlpDlls[nDlls++] = lpDllName;
}
if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) {
TerminateProcess(pi.hProcess, ~0u);
return FALSE;
}
if (!(dwCreationFlags & CREATE_SUSPENDED)) {
ResumeThread(pi.hThread);
}
return TRUE;
}
BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ LPCSTR lpDllName,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
{
DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED);
PROCESS_INFORMATION pi;
if (pfCreateProcessW == NULL) {
pfCreateProcessW = CreateProcessW;
}
BOOL fResult = pfCreateProcessW(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwMyCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
&pi);
if (lpProcessInformation) {
CopyMemory(lpProcessInformation, &pi, sizeof(pi));
}
if (!fResult) {
return FALSE;
}
LPCSTR rlpDlls[2];
DWORD nDlls = 0;
if (lpDllName != NULL) {
rlpDlls[nDlls++] = lpDllName;
}
if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) {
TerminateProcess(pi.hProcess, ~0u);
return FALSE;
}
if (!(dwCreationFlags & CREATE_SUSPENDED)) {
ResumeThread(pi.hThread);
}
return TRUE;
}
BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess,
_In_ REFGUID rguid,
_In_reads_bytes_(cbData) LPCVOID pvData,
_In_ DWORD cbData)
{
return DetourCopyPayloadToProcessEx(hProcess, rguid, pvData, cbData) != NULL;
}
_Success_(return != NULL)
PVOID WINAPI DetourCopyPayloadToProcessEx(_In_ HANDLE hProcess,
_In_ REFGUID rguid,
_In_reads_bytes_(cbData) LPCVOID pvData,
_In_ DWORD cbData)
{
if (hProcess == NULL) {
SetLastError(ERROR_INVALID_HANDLE);
return NULL;
}
DWORD cbTotal = (sizeof(IMAGE_DOS_HEADER) +
sizeof(IMAGE_NT_HEADERS) +
sizeof(IMAGE_SECTION_HEADER) +
sizeof(DETOUR_SECTION_HEADER) +
sizeof(DETOUR_SECTION_RECORD) +
cbData);
PBYTE pbBase = (PBYTE)VirtualAllocEx(hProcess, NULL, cbTotal,
MEM_COMMIT, PAGE_READWRITE);
if (pbBase == NULL) {
DETOUR_TRACE(("VirtualAllocEx(%lu) failed: %lu\n", cbTotal, GetLastError()));
return NULL;
}
// As you can see in the following code,
// the memory layout of the payload range "[pbBase, pbBase+cbTotal]" is a PE executable file,
// so DetourFreePayload can use "DetourGetContainingModule(Payload pointer)" to get the above "pbBase" pointer,
// pbBase: the memory block allocated by VirtualAllocEx will be released in DetourFreePayload by VirtualFree.
PBYTE pbTarget = pbBase;
IMAGE_DOS_HEADER idh;
IMAGE_NT_HEADERS inh;
IMAGE_SECTION_HEADER ish;
DETOUR_SECTION_HEADER dsh;
DETOUR_SECTION_RECORD dsr;
SIZE_T cbWrote = 0;
ZeroMemory(&idh, sizeof(idh));
idh.e_magic = IMAGE_DOS_SIGNATURE;
idh.e_lfanew = sizeof(idh);
if (!WriteProcessMemory(hProcess, pbTarget, &idh, sizeof(idh), &cbWrote) ||
cbWrote != sizeof(idh)) {
DETOUR_TRACE(("WriteProcessMemory(idh) failed: %lu\n", GetLastError()));
return NULL;
}
pbTarget += sizeof(idh);
ZeroMemory(&inh, sizeof(inh));
inh.Signature = IMAGE_NT_SIGNATURE;
inh.FileHeader.SizeOfOptionalHeader = sizeof(inh.OptionalHeader);
inh.FileHeader.Characteristics = IMAGE_FILE_DLL;
inh.FileHeader.NumberOfSections = 1;
inh.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC;
if (!WriteProcessMemory(hProcess, pbTarget, &inh, sizeof(inh), &cbWrote) ||
cbWrote != sizeof(inh)) {
return NULL;
}
pbTarget += sizeof(inh);
ZeroMemory(&ish, sizeof(ish));
memcpy(ish.Name, ".detour", sizeof(ish.Name));
ish.VirtualAddress = (DWORD)((pbTarget + sizeof(ish)) - pbBase);
ish.SizeOfRawData = (sizeof(DETOUR_SECTION_HEADER) +
sizeof(DETOUR_SECTION_RECORD) +
cbData);
if (!WriteProcessMemory(hProcess, pbTarget, &ish, sizeof(ish), &cbWrote) ||
cbWrote != sizeof(ish)) {
return NULL;
}
pbTarget += sizeof(ish);
ZeroMemory(&dsh, sizeof(dsh));
dsh.cbHeaderSize = sizeof(dsh);
dsh.nSignature = DETOUR_SECTION_HEADER_SIGNATURE;
dsh.nDataOffset = sizeof(DETOUR_SECTION_HEADER);
dsh.cbDataSize = (sizeof(DETOUR_SECTION_HEADER) +
sizeof(DETOUR_SECTION_RECORD) +
cbData);
if (!WriteProcessMemory(hProcess, pbTarget, &dsh, sizeof(dsh), &cbWrote) ||
cbWrote != sizeof(dsh)) {
return NULL;
}
pbTarget += sizeof(dsh);
ZeroMemory(&dsr, sizeof(dsr));
dsr.cbBytes = cbData + sizeof(DETOUR_SECTION_RECORD);
dsr.nReserved = 0;
dsr.guid = rguid;
if (!WriteProcessMemory(hProcess, pbTarget, &dsr, sizeof(dsr), &cbWrote) ||
cbWrote != sizeof(dsr)) {
return NULL;
}
pbTarget += sizeof(dsr);
if (!WriteProcessMemory(hProcess, pbTarget, pvData, cbData, &cbWrote) ||
cbWrote != cbData) {
return NULL;
}
DETOUR_TRACE(("Copied %lu byte payload into target process at %p\n",
cbData, pbTarget));
SetLastError(NO_ERROR);
return pbTarget;
}
static BOOL s_fSearchedForHelper = FALSE;
static PDETOUR_EXE_HELPER s_pHelper = NULL;
VOID CALLBACK DetourFinishHelperProcess(_In_ HWND,
_In_ HINSTANCE,
_In_ LPSTR,
_In_ INT)
{
LPCSTR * rlpDlls = NULL;
DWORD Result = 9900;
DWORD cOffset = 0;
DWORD cSize = 0;
HANDLE hProcess = NULL;
if (s_pHelper == NULL) {
DETOUR_TRACE(("DetourFinishHelperProcess called with s_pHelper = NULL.\n"));
Result = 9905;
goto Cleanup;
}
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, s_pHelper->pid);
if (hProcess == NULL) {
DETOUR_TRACE(("OpenProcess(pid=%lu) failed: %lu\n",
s_pHelper->pid, GetLastError()));
Result = 9901;
goto Cleanup;
}
rlpDlls = new NOTHROW LPCSTR [s_pHelper->nDlls];
cSize = s_pHelper->cb - sizeof(DETOUR_EXE_HELPER);
for (DWORD n = 0; n < s_pHelper->nDlls; n++) {
size_t cchDest = 0;
HRESULT hr = StringCchLengthA(&s_pHelper->rDlls[cOffset], cSize - cOffset, &cchDest);
if (!SUCCEEDED(hr)) {
Result = 9902;
goto Cleanup;
}
rlpDlls[n] = &s_pHelper->rDlls[cOffset];
cOffset += (DWORD)cchDest + 1;
}
if (!DetourUpdateProcessWithDll(hProcess, rlpDlls, s_pHelper->nDlls)) {
DETOUR_TRACE(("DetourUpdateProcessWithDll(pid=%lu) failed: %lu\n",
s_pHelper->pid, GetLastError()));
Result = 9903;
goto Cleanup;
}
Result = 0;
Cleanup:
if (rlpDlls != NULL) {
delete[] rlpDlls;
rlpDlls = NULL;
}
// Note: s_pHelper is allocated as part of injecting the payload in DetourCopyPayloadToProcess(..),
// it's a fake section and not data allocated by the system PE loader.
// Delete the payload after execution to release the memory occupied by it
if (s_pHelper != NULL) {
DetourFreePayload(s_pHelper);
s_pHelper = NULL;
}
ExitProcess(Result);
}
BOOL WINAPI DetourIsHelperProcess(VOID)
{
PVOID pvData;
DWORD cbData;
if (s_fSearchedForHelper) {
return (s_pHelper != NULL);
}
s_fSearchedForHelper = TRUE;
pvData = DetourFindPayloadEx(DETOUR_EXE_HELPER_GUID, &cbData);
if (pvData == NULL || cbData < sizeof(DETOUR_EXE_HELPER)) {
return FALSE;
}
s_pHelper = (PDETOUR_EXE_HELPER)pvData;
if (s_pHelper->cb < sizeof(*s_pHelper)) {
s_pHelper = NULL;
return FALSE;
}
return TRUE;
}
static
BOOL WINAPI AllocExeHelper(_Out_ PDETOUR_EXE_HELPER *pHelper,
_In_ DWORD dwTargetPid,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls)
{
PDETOUR_EXE_HELPER Helper = NULL;
BOOL Result = FALSE;
_Field_range_(0, cSize - 4) DWORD cOffset = 0;
DWORD cSize = 4;
if (pHelper == NULL) {
goto Cleanup;
}
*pHelper = NULL;
if (nDlls < 1 || nDlls > 4096) {
SetLastError(ERROR_INVALID_PARAMETER);
goto Cleanup;
}
for (DWORD n = 0; n < nDlls; n++) {
HRESULT hr;
size_t cchDest = 0;
hr = StringCchLengthA(rlpDlls[n], 4096, &cchDest);
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
cSize += (DWORD)cchDest + 1;
}
Helper = (PDETOUR_EXE_HELPER) new NOTHROW BYTE[sizeof(DETOUR_EXE_HELPER) + cSize];
if (Helper == NULL) {
goto Cleanup;
}
Helper->cb = sizeof(DETOUR_EXE_HELPER) + cSize;
Helper->pid = dwTargetPid;
Helper->nDlls = nDlls;
for (DWORD n = 0; n < nDlls; n++) {
HRESULT hr;
size_t cchDest = 0;
if (cOffset > 0x10000 || cSize > 0x10000 || cOffset + 2 >= cSize) {
goto Cleanup;
}
if (cOffset + 2 >= cSize || cOffset + 65536 < cSize) {
goto Cleanup;
}
_Analysis_assume_(cOffset + 1 < cSize);
_Analysis_assume_(cOffset < 0x10000);
_Analysis_assume_(cSize < 0x10000);
PCHAR psz = &Helper->rDlls[cOffset];
hr = StringCchCopyA(psz, cSize - cOffset, rlpDlls[n]);
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
// REVIEW 28020 The expression '1<=_Param_(2)& &_Param_(2)<=2147483647' is not true at this call.
// REVIEW 28313 Analysis will not proceed past this point because of annotation evaluation. The annotation expression *_Param_(3)<_Param_(2)&&*_Param_(3)<=stringLength$(_Param_(1)) cannot be true under any assumptions at this point in the program.
#pragma warning(suppress:28020 28313)
hr = StringCchLengthA(psz, cSize - cOffset, &cchDest);
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
// Replace "32." with "64." or "64." with "32."
for (DWORD c = (DWORD)cchDest + 1; c > 3; c--) {
#if DETOURS_32BIT
if (psz[c - 3] == '3' && psz[c - 2] == '2' && psz[c - 1] == '.') {
psz[c - 3] = '6'; psz[c - 2] = '4';
break;
}
#else
if (psz[c - 3] == '6' && psz[c - 2] == '4' && psz[c - 1] == '.') {
psz[c - 3] = '3'; psz[c - 2] = '2';
break;
}
#endif
}
cOffset += (DWORD)cchDest + 1;
}
*pHelper = Helper;
Helper = NULL;
Result = TRUE;
Cleanup:
if (Helper != NULL) {
delete[] (PBYTE)Helper;
Helper = NULL;
}
return Result;
}
static
VOID WINAPI FreeExeHelper(PDETOUR_EXE_HELPER *pHelper)
{
if (*pHelper != NULL) {
delete[] (PBYTE)*pHelper;
*pHelper = NULL;
}
}
BOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid,
_In_ LPCSTR lpDllName,
_In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
{
return DetourProcessViaHelperDllsA(dwTargetPid, 1, &lpDllName, pfCreateProcessA);
}
BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
{
BOOL Result = FALSE;
PROCESS_INFORMATION pi;
STARTUPINFOA si;
CHAR szExe[MAX_PATH];
CHAR szCommand[MAX_PATH];
PDETOUR_EXE_HELPER helper = NULL;
HRESULT hr;
DWORD nLen = GetEnvironmentVariableA("WINDIR", szExe, ARRAYSIZE(szExe));
DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%lu,dlls=%lu)\n", dwTargetPid, nDlls));
if (nDlls < 1 || nDlls > 4096) {
SetLastError(ERROR_INVALID_PARAMETER);
goto Cleanup;
}
if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) {
goto Cleanup;
}
if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) {
goto Cleanup;
}
#if DETOURS_OPTION_BITS
#if DETOURS_32BIT
hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\sysnative\\rundll32.exe");
#else // !DETOURS_32BIT
hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\syswow64\\rundll32.exe");
#endif // !DETOURS_32BIT
#else // DETOURS_OPTIONS_BITS
hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\system32\\rundll32.exe");
#endif // DETOURS_OPTIONS_BITS
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
//for East Asia languages and so on, like Chinese, print format with "%hs" can not work fine before user call _tsetlocale(LC_ALL,_T(".ACP"));
//so we can't use "%hs" in format string, because the dll that contain this code would inject to any process, even not call _tsetlocale(LC_ALL,_T(".ACP")) before
hr = StringCchPrintfA(szCommand, ARRAYSIZE(szCommand),
"rundll32.exe \"%s\",#1", &helper->rDlls[0]);
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
ZeroMemory(&pi, sizeof(pi));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%hs\", \"%hs\")\n", szExe, szCommand));
if (pfCreateProcessA(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED,
NULL, NULL, &si, &pi)) {
if (!DetourCopyPayloadToProcess(pi.hProcess,
DETOUR_EXE_HELPER_GUID,
helper, helper->cb)) {
DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError()));
TerminateProcess(pi.hProcess, ~0u);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
goto Cleanup;
}
ResumeThread(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwResult = 500;
GetExitCodeProcess(pi.hProcess, &dwResult);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if (dwResult != 0) {
DETOUR_TRACE(("Rundll32.exe failed: result=%lu\n", dwResult));
goto Cleanup;
}
Result = TRUE;
}
else {
DETOUR_TRACE(("CreateProcess failed: %lu\n", GetLastError()));
goto Cleanup;
}
Cleanup:
FreeExeHelper(&helper);
return Result;
}
BOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid,
_In_ LPCSTR lpDllName,
_In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
{
return DetourProcessViaHelperDllsW(dwTargetPid, 1, &lpDllName, pfCreateProcessW);
}
BOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
{
BOOL Result = FALSE;
PROCESS_INFORMATION pi;
STARTUPINFOW si;
WCHAR szExe[MAX_PATH];
WCHAR szCommand[MAX_PATH];
PDETOUR_EXE_HELPER helper = NULL;
HRESULT hr;
WCHAR szDllName[MAX_PATH];
int cchWrittenWideChar;
DWORD nLen = GetEnvironmentVariableW(L"WINDIR", szExe, ARRAYSIZE(szExe));
DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%lu,dlls=%lu)\n", dwTargetPid, nDlls));
if (nDlls < 1 || nDlls > 4096) {
SetLastError(ERROR_INVALID_PARAMETER);
goto Cleanup;
}
if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) {
goto Cleanup;
}
if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) {
goto Cleanup;
}
#if DETOURS_OPTION_BITS
#if DETOURS_32BIT
hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\sysnative\\rundll32.exe");
#else // !DETOURS_32BIT
hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\syswow64\\rundll32.exe");
#endif // !DETOURS_32BIT
#else // DETOURS_OPTIONS_BITS
hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\system32\\rundll32.exe");
#endif // DETOURS_OPTIONS_BITS
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
//for East Asia languages and so on, like Chinese, print format with "%hs" can not work fine before user call _tsetlocale(LC_ALL,_T(".ACP"));
//so we can't use "%hs" in format string, because the dll that contain this code would inject to any process, even not call _tsetlocale(LC_ALL,_T(".ACP")) before
cchWrittenWideChar = MultiByteToWideChar(CP_ACP, 0, &helper->rDlls[0], -1, szDllName, ARRAYSIZE(szDllName));
if (cchWrittenWideChar >= ARRAYSIZE(szDllName) || cchWrittenWideChar <= 0) {
goto Cleanup;
}
hr = StringCchPrintfW(szCommand, ARRAYSIZE(szCommand),
L"rundll32.exe \"%s\",#1", szDllName);
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
ZeroMemory(&pi, sizeof(pi));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%ls\", \"%ls\")\n", szExe, szCommand));
if (pfCreateProcessW(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED,
NULL, NULL, &si, &pi)) {
if (!DetourCopyPayloadToProcess(pi.hProcess,
DETOUR_EXE_HELPER_GUID,
helper, helper->cb)) {
DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError()));
TerminateProcess(pi.hProcess, ~0u);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
goto Cleanup;
}
ResumeThread(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwResult = 500;
GetExitCodeProcess(pi.hProcess, &dwResult);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if (dwResult != 0) {
DETOUR_TRACE(("Rundll32.exe failed: result=%lu\n", dwResult));
goto Cleanup;
}
Result = TRUE;
}
else {
DETOUR_TRACE(("CreateProcess failed: %lu\n", GetLastError()));
goto Cleanup;
}
Cleanup:
FreeExeHelper(&helper);
return Result;
}
BOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ LPCSTR lpDllName,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
{
if (pfCreateProcessA == NULL) {
pfCreateProcessA = CreateProcessA;
}
PROCESS_INFORMATION backup;
if (lpProcessInformation == NULL) {
lpProcessInformation = &backup;
ZeroMemory(&backup, sizeof(backup));
}
if (!pfCreateProcessA(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags | CREATE_SUSPENDED,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation)) {
return FALSE;
}
LPCSTR szDll = lpDllName;
if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &szDll, 1) &&
!DetourProcessViaHelperA(lpProcessInformation->dwProcessId,
lpDllName,
pfCreateProcessA)) {
TerminateProcess(lpProcessInformation->hProcess, ~0u);
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
return FALSE;
}
if (!(dwCreationFlags & CREATE_SUSPENDED)) {
ResumeThread(lpProcessInformation->hThread);
}
if (lpProcessInformation == &backup) {
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
}
return TRUE;
}
BOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ LPCSTR lpDllName,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
{
if (pfCreateProcessW == NULL) {
pfCreateProcessW = CreateProcessW;
}
PROCESS_INFORMATION backup;
if (lpProcessInformation == NULL) {
lpProcessInformation = &backup;
ZeroMemory(&backup, sizeof(backup));
}
if (!pfCreateProcessW(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags | CREATE_SUSPENDED,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation)) {
return FALSE;
}
LPCSTR sz = lpDllName;
if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &sz, 1) &&
!DetourProcessViaHelperW(lpProcessInformation->dwProcessId,
lpDllName,
pfCreateProcessW)) {
TerminateProcess(lpProcessInformation->hProcess, ~0u);
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
return FALSE;
}
if (!(dwCreationFlags & CREATE_SUSPENDED)) {
ResumeThread(lpProcessInformation->hThread);
}
if (lpProcessInformation == &backup) {
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
}
return TRUE;
}
BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
{
if (pfCreateProcessA == NULL) {
pfCreateProcessA = CreateProcessA;
}
PROCESS_INFORMATION backup;
if (lpProcessInformation == NULL) {
lpProcessInformation = &backup;
ZeroMemory(&backup, sizeof(backup));
}
if (!pfCreateProcessA(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags | CREATE_SUSPENDED,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation)) {
return FALSE;
}
if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) &&
!DetourProcessViaHelperDllsA(lpProcessInformation->dwProcessId,
nDlls,
rlpDlls,
pfCreateProcessA)) {
TerminateProcess(lpProcessInformation->hProcess, ~0u);
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
return FALSE;
}
if (!(dwCreationFlags & CREATE_SUSPENDED)) {
ResumeThread(lpProcessInformation->hThread);
}
if (lpProcessInformation == &backup) {
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
}
return TRUE;
}
BOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
{
if (pfCreateProcessW == NULL) {
pfCreateProcessW = CreateProcessW;
}
PROCESS_INFORMATION backup;
if (lpProcessInformation == NULL) {
lpProcessInformation = &backup;
ZeroMemory(&backup, sizeof(backup));
}
if (!pfCreateProcessW(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags | CREATE_SUSPENDED,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation)) {
return FALSE;
}
if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) &&
!DetourProcessViaHelperDllsW(lpProcessInformation->dwProcessId,
nDlls,
rlpDlls,
pfCreateProcessW)) {
TerminateProcess(lpProcessInformation->hProcess, ~0u);
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
return FALSE;
}
if (!(dwCreationFlags & CREATE_SUSPENDED)) {
ResumeThread(lpProcessInformation->hThread);
}
if (lpProcessInformation == &backup) {
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
}
return TRUE;
}
//
///////////////////////////////////////////////////////////////// End of File.
================================================
FILE: 001.NVL/BKEngine/BKEFileNameDumper/Detours/detours.cpp
================================================
//////////////////////////////////////////////////////////////////////////////
//
// Core Detours Functionality (detours.cpp of detours.lib)
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//#define DETOUR_DEBUG 1
#define DETOURS_INTERNAL
#include "detours.h"
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
#error detours.h version mismatch
#endif
#define NOTHROW
//////////////////////////////////////////////////////////////////////////////
//
#ifdef _DEBUG
extern "C" IMAGE_DOS_HEADER __ImageBase;
int Detour_AssertExprWithFunctionName(int reportType, const char* filename, int linenumber, const char* FunctionName, const char* msg)
{
int nRet = 0;
DWORD dwLastError = GetLastError();
CHAR szModuleNameWithFunctionName[MAX_PATH * 2];
szModuleNameWithFunctionName[0] = 0;
GetModuleFileNameA((HMODULE)&__ImageBase, szModuleNameWithFunctionName, ARRAYSIZE(szModuleNameWithFunctionName));
StringCchCatNA(szModuleNameWithFunctionName, ARRAYSIZE(szModuleNameWithFunctionName), ",", ARRAYSIZE(szModuleNameWithFunctionName) - strlen(szModuleNameWithFunctionName) - 1);
StringCchCatNA(szModuleNameWithFunctionName, ARRAYSIZE(szModuleNameWithFunctionName), FunctionName, ARRAYSIZE(szModuleNameWithFunctionName) - strlen(szModuleNameWithFunctionName) - 1);
SetLastError(dwLastError);
nRet = _CrtDbgReport(reportType, filename, linenumber, szModuleNameWithFunctionName, msg);
SetLastError(dwLastError);
return nRet;
}
#endif// _DEBUG
//////////////////////////////////////////////////////////////////////////////
//
struct _DETOUR_ALIGN
{
BYTE obTarget : 3;
BYTE obTrampoline : 5;
};
C_ASSERT(sizeof(_DETOUR_ALIGN) == 1);
//////////////////////////////////////////////////////////////////////////////
//
// Region reserved for system DLLs, which cannot be used for trampolines.
//
static PVOID s_pSystemRegionLowerBound = (PVOID)(ULONG_PTR)0x70000000;
static PVOID s_pSystemRegionUpperBound = (PVOID)(ULONG_PTR)0x80000000;
//////////////////////////////////////////////////////////////////////////////
//
static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)
{
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi));
__try {
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return false;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
return false;
}
if (pbAddress >= ((PBYTE)pDosHeader +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&
pbAddress < ((PBYTE)pDosHeader +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {
return true;
}
}
#pragma prefast(suppress:28940, "A bad pointer means this probably isn't a PE header.")
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
return false;
}
return false;
}
inline ULONG_PTR detour_2gb_below(ULONG_PTR address)
{
return (address > (ULONG_PTR)0x7ff80000) ? address - 0x7ff80000 : 0x80000;
}
inline ULONG_PTR detour_2gb_above(ULONG_PTR address)
{
#if defined(DETOURS_64BIT)
return (address < (ULONG_PTR)0xffffffff80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfffffffffff80000;
#else
return (address < (ULONG_PTR)0x80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfff80000;
#endif
}
///////////////////////////////////////////////////////////////////////// X86.
//
#ifdef DETOURS_X86
struct _DETOUR_TRAMPOLINE
{
BYTE rbCode[30]; // target code + jmp to pbRemain
BYTE cbCode; // size of moved target code.
BYTE cbCodeBreak; // padding to make debugging easier.
BYTE rbRestore[22]; // original target code.
BYTE cbRestore; // size of original target code.
BYTE cbRestoreBreak; // padding to make debugging easier.
_DETOUR_ALIGN rAlign[8]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
};
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 72);
enum {
SIZE_OF_JMP = 5
};
inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)
{
PBYTE pbJmpSrc = pbCode + 5;
*pbCode++ = 0xE9; // jmp +imm32
*((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)
{
*pbCode++ = 0xff; // jmp [+imm32]
*pbCode++ = 0x25;
*((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal);
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
*pbCode++ = 0xcc; // brk;
}
return pbCode;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
// First, skip over the import vector if there is one.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
// Then, skip over a patch jump
if (pbCode[0] == 0xeb) { // jmp +imm8
PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
pbCode = pbNew;
// First, skip over the import vector if there is one.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
pbNew = *(UNALIGNED PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
// Finally, skip over a long jump if it is the target of the patch jump.
else if (pbCode[0] == 0xe9) { // jmp +imm32
pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
// We have to place trampolines within +/- 2GB of code.
ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
DETOUR_TRACE(("[%p..%p..%p]\n", (PVOID)lo, pbCode, (PVOID)hi));
// And, within +/- 2GB of relative jmp targets.
if (pbCode[0] == 0xe9) { // jmp +imm32
PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
if (pbNew < pbCode) {
hi = detour_2gb_above((ULONG_PTR)pbNew);
}
else {
lo = detour_2gb_below((ULONG_PTR)pbNew);
}
DETOUR_TRACE(("[%p..%p..%p] +imm32\n", (PVOID)lo, pbCode, (PVOID)hi));
}
*ppLower = (PDETOUR_TRAMPOLINE)lo;
*ppUpper = (PDETOUR_TRAMPOLINE)hi;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
if (pbCode[0] == 0xeb || // jmp +imm8
pbCode[0] == 0xe9 || // jmp +imm32
pbCode[0] == 0xe0 || // jmp eax
pbCode[0] == 0xc2 || // ret +imm8
pbCode[0] == 0xc3 || // ret
pbCode[0] == 0xcc) { // brk
return TRUE;
}
else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret
return TRUE;
}
else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
return TRUE;
}
else if ((pbCode[0] == 0x26 || // jmp es:
pbCode[0] == 0x2e || // jmp cs:
pbCode[0] == 0x36 || // jmp ss:
pbCode[0] == 0x3e || // jmp ds:
pbCode[0] == 0x64 || // jmp fs:
pbCode[0] == 0x65) && // jmp gs:
pbCode[1] == 0xff && // jmp [+imm32]
pbCode[2] == 0x25) {
return TRUE;
}
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
// 1-byte through 11-byte NOPs.
if (pbCode[0] == 0x90) {
return 1;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x90) {
return 2;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) {
return 3;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 &&
pbCode[3] == 0x00) {
return 4;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00) {
return 5;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) {
return 6;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00) {
return 7;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00) {
return 8;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) {
return 9;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F &&
pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
pbCode[9] == 0x00) {
return 10;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 &&
pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
pbCode[9] == 0x00 && pbCode[10] == 0x00) {
return 11;
}
// int 3.
if (pbCode[0] == 0xcc) {
return 1;
}
return 0;
}
#endif // DETOURS_X86
///////////////////////////////////////////////////////////////////////// X64.
//
#ifdef DETOURS_X64
struct _DETOUR_TRAMPOLINE
{
// An X64 instuction can be 15 bytes long.
// In practice 11 seems to be the limit.
BYTE rbCode[30]; // target code + jmp to pbRemain.
BYTE cbCode; // size of moved target code.
BYTE cbCodeBreak; // padding to make debugging easier.
BYTE rbRestore[30]; // original target code.
BYTE cbRestore; // size of original target code.
BYTE cbRestoreBreak; // padding to make debugging easier.
_DETOUR_ALIGN rAlign[8]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
BYTE rbCodeIn[8]; // jmp [pbDetour]
};
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 96);
enum {
SIZE_OF_JMP = 5
};
inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)
{
PBYTE pbJmpSrc = pbCode + 5;
*pbCode++ = 0xE9; // jmp +imm32
*((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)
{
PBYTE pbJmpSrc = pbCode + 6;
*pbCode++ = 0xff; // jmp [+imm32]
*pbCode++ = 0x25;
*((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
*pbCode++ = 0xcc; // brk;
}
return pbCode;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
// First, skip over the import vector if there is one.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
// Then, skip over a patch jump
if (pbCode[0] == 0xeb) { // jmp +imm8
PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
pbCode = pbNew;
// First, skip over the import vector if there is one.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
pbNew = *(UNALIGNED PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
// Finally, skip over a long jump if it is the target of the patch jump.
else if (pbCode[0] == 0xe9) { // jmp +imm32
pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
// We have to place trampolines within +/- 2GB of code.
ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
DETOUR_TRACE(("[%p..%p..%p]\n", (PVOID)lo, pbCode, (PVOID)hi));
// And, within +/- 2GB of relative jmp vectors.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
PBYTE pbNew = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];
if (pbNew < pbCode) {
hi = detour_2gb_above((ULONG_PTR)pbNew);
}
else {
lo = detour_2gb_below((ULONG_PTR)pbNew);
}
DETOUR_TRACE(("[%p..%p..%p] [+imm32]\n", (PVOID)lo, pbCode, (PVOID)hi));
}
// And, within +/- 2GB of relative jmp targets.
else if (pbCode[0] == 0xe9) { // jmp +imm32
PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
if (pbNew < pbCode) {
hi = detour_2gb_above((ULONG_PTR)pbNew);
}
else {
lo = detour_2gb_below((ULONG_PTR)pbNew);
}
DETOUR_TRACE(("[%p..%p..%p] +imm32\n", (PVOID)lo, pbCode, (PVOID)hi));
}
*ppLower = (PDETOUR_TRAMPOLINE)lo;
*ppUpper = (PDETOUR_TRAMPOLINE)hi;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
if (pbCode[0] == 0xeb || // jmp +imm8
pbCode[0] == 0xe9 || // jmp +imm32
pbCode[0] == 0xe0 || // jmp eax
pbCode[0] == 0xc2 || // ret +imm8
pbCode[0] == 0xc3 || // ret
pbCode[0] == 0xcc) { // brk
return TRUE;
}
else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret
return TRUE;
}
else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
return TRUE;
}
else if ((pbCode[0] == 0x26 || // jmp es:
pbCode[0] == 0x2e || // jmp cs:
pbCode[0] == 0x36 || // jmp ss:
pbCode[0] == 0x3e || // jmp ds:
pbCode[0] == 0x64 || // jmp fs:
pbCode[0] == 0x65) && // jmp gs:
pbCode[1] == 0xff && // jmp [+imm32]
pbCode[2] == 0x25) {
return TRUE;
}
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
// 1-byte through 11-byte NOPs.
if (pbCode[0] == 0x90) {
return 1;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x90) {
return 2;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) {
return 3;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 &&
pbCode[3] == 0x00) {
return 4;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00) {
return 5;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) {
return 6;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00) {
return 7;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00) {
return 8;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) {
return 9;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F &&
pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
pbCode[9] == 0x00) {
return 10;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 &&
pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
pbCode[9] == 0x00 && pbCode[10] == 0x00) {
return 11;
}
// int 3.
if (pbCode[0] == 0xcc) {
return 1;
}
return 0;
}
#endif // DETOURS_X64
//////////////////////////////////////////////////////////////////////// IA64.
//
#ifdef DETOURS_IA64
struct _DETOUR_TRAMPOLINE
{
// On the IA64, a trampoline is used for both incoming and outgoing calls.
//
// The trampoline contains the following bundles for the outgoing call:
// movl gp=target_gp;
//
// brl target_code;
//
// The trampoline contains the following bundles for the incoming call:
// alloc r41=ar.pfs, b, 0, 8, 0
// mov r40=rp
//
// adds r50=0, r39
// adds r49=0, r38
// adds r48=0, r37 ;;
//
// adds r47=0, r36
// adds r46=0, r35
// adds r45=0, r34
//
// adds r44=0, r33
// adds r43=0, r32
// adds r42=0, gp ;;
//
// movl gp=ffffffff`ffffffff ;;
//
// brl.call.sptk.few rp=disas!TestCodes+20e0 (00000000`00404ea0) ;;
//
// adds gp=0, r42
// mov rp=r40, +0 ;;
// mov.i ar.pfs=r41
//
// br.ret.sptk.many rp ;;
//
// This way, we only have to relocate a single bundle.
//
// The complicated incoming trampoline is required because we have to
// create an additional stack frame so that we save and restore the gp.
// We must do this because gp is a caller-saved register, but not saved
// if the caller thinks the target is in the same DLL, which changes
// when we insert a detour.
//
DETOUR_IA64_BUNDLE bMovlTargetGp; // Bundle which sets target GP
BYTE rbCode[sizeof(DETOUR_IA64_BUNDLE)]; // moved bundle.
DETOUR_IA64_BUNDLE bBrlRemainEip; // Brl to pbRemain
// This must be adjacent to bBranchIslands.
// Each instruction in the moved bundle could be a IP-relative chk or branch or call.
// Any such instructions are changed to point to a brl in bBranchIslands.
// This must be adjacent to bBrlRemainEip -- see "pbPool".
DETOUR_IA64_BUNDLE bBranchIslands[DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE];
// Target of brl inserted in target function
DETOUR_IA64_BUNDLE bAllocFrame; // alloc frame
DETOUR_IA64_BUNDLE bSave37to39; // save r37, r38, r39.
DETOUR_IA64_BUNDLE bSave34to36; // save r34, r35, r36.
DETOUR_IA64_BUNDLE bSaveGPto33; // save gp, r32, r33.
DETOUR_IA64_BUNDLE bMovlDetourGp; // set detour GP.
DETOUR_IA64_BUNDLE bCallDetour; // call detour.
DETOUR_IA64_BUNDLE bPopFrameGp; // pop frame and restore gp.
DETOUR_IA64_BUNDLE bReturn; // return to caller.
PLABEL_DESCRIPTOR pldTrampoline;
BYTE rbRestore[sizeof(DETOUR_IA64_BUNDLE)]; // original target bundle.
BYTE cbRestore; // size of original target code.
BYTE cbCode; // size of moved target code.
_DETOUR_ALIGN rAlign[14]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
PPLABEL_DESCRIPTOR ppldDetour; // [pbDetour,gpDetour]
PPLABEL_DESCRIPTOR ppldTarget; // [pbTarget,gpDetour]
};
C_ASSERT(sizeof(DETOUR_IA64_BUNDLE) == 16);
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 256 + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * 16);
enum {
SIZE_OF_JMP = sizeof(DETOUR_IA64_BUNDLE)
};
inline PBYTE detour_skip_jmp(PBYTE pPointer, PVOID *ppGlobals)
{
PBYTE pGlobals = NULL;
PBYTE pbCode = NULL;
if (pPointer != NULL) {
PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)pPointer;
pbCode = (PBYTE)ppld->EntryPoint;
pGlobals = (PBYTE)ppld->GlobalPointer;
}
if (ppGlobals != NULL) {
*ppGlobals = pGlobals;
}
if (pbCode == NULL) {
return NULL;
}
DETOUR_IA64_BUNDLE *pb = (DETOUR_IA64_BUNDLE *)pbCode;
// IA64 Local Import Jumps look like:
// addl r2=ffffffff`ffe021c0, gp ;;
// ld8 r2=[r2]
// nop.i 0 ;;
//
// ld8 r3=[r2], 8 ;;
// ld8 gp=[r2]
// mov b6=r3, +0
//
// nop.m 0
// nop.i 0
// br.cond.sptk.few b6
//
// 002024000200100b
if ((pb[0].wide[0] & 0xfffffc000603ffff) == 0x002024000200100b &&
pb[0].wide[1] == 0x0004000000203008 &&
pb[1].wide[0] == 0x001014180420180a &&
pb[1].wide[1] == 0x07000830c0203008 &&
pb[2].wide[0] == 0x0000000100000010 &&
pb[2].wide[1] == 0x0080006000000200) {
ULONG64 offset =
((pb[0].wide[0] & 0x0000000001fc0000) >> 18) | // imm7b
((pb[0].wide[0] & 0x000001ff00000000) >> 25) | // imm9d
((pb[0].wide[0] & 0x00000000f8000000) >> 11); // imm5c
if (pb[0].wide[0] & 0x0000020000000000) { // sign
offset |= 0xffffffffffe00000;
}
PBYTE pbTarget = pGlobals + offset;
DETOUR_TRACE(("%p: potential import jump, target=%p\n", pb, pbTarget));
if (detour_is_imported(pbCode, pbTarget) && *(PBYTE*)pbTarget != NULL) {
DETOUR_TRACE(("%p: is import jump, label=%p\n", pb, *(PBYTE *)pbTarget));
PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)*(PBYTE *)pbTarget;
pbCode = (PBYTE)ppld->EntryPoint;
pGlobals = (PBYTE)ppld->GlobalPointer;
if (ppGlobals != NULL) {
*ppGlobals = pGlobals;
}
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
(void)pbCode;
*ppLower = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0x0000000000080000;
*ppUpper = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0xfffffffffff80000;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
// Routine not needed on IA64.
(void)pbCode;
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
// Routine not needed on IA64.
(void)pbCode;
return 0;
}
#endif // DETOURS_IA64
#ifdef DETOURS_ARM
struct _DETOUR_TRAMPOLINE
{
// A Thumb-2 instruction can be 2 or 4 bytes long.
BYTE rbCode[62]; // target code + jmp to pbRemain
BYTE cbCode; // size of moved target code.
BYTE cbCodeBreak; // padding to make debugging easier.
BYTE rbRestore[22]; // original target code.
BYTE cbRestore; // size of original target code.
BYTE cbRestoreBreak; // padding to make debugging easier.
_DETOUR_ALIGN rAlign[8]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
};
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 104);
enum {
SIZE_OF_JMP = 8
};
inline PBYTE align4(PBYTE pValue)
{
return (PBYTE)(((ULONG)pValue) & ~(ULONG)3u);
}
inline ULONG fetch_thumb_opcode(PBYTE pbCode)
{
ULONG Opcode = *(UINT16 *)&pbCode[0];
if (Opcode >= 0xe800) {
Opcode = (Opcode << 16) | *(UINT16 *)&pbCode[2];
}
return Opcode;
}
inline void write_thumb_opcode(PBYTE &pbCode, ULONG Opcode)
{
if (Opcode >= 0x10000) {
*((UINT16*&)pbCode)++ = Opcode >> 16;
}
*((UINT16*&)pbCode)++ = (UINT16)Opcode;
}
PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal)
{
PBYTE pbLiteral;
if (ppPool != NULL) {
*ppPool = *ppPool - 4;
pbLiteral = *ppPool;
}
else {
pbLiteral = align4(pbCode + 6);
}
*((PBYTE*&)pbLiteral) = DETOURS_PBYTE_TO_PFUNC(pbJmpVal);
LONG delta = pbLiteral - align4(pbCode + 4);
write_thumb_opcode(pbCode, 0xf8dff000 | delta); // LDR PC,[PC+n]
if (ppPool == NULL) {
if (((ULONG)pbCode & 2) != 0) {
write_thumb_opcode(pbCode, 0xdefe); // BREAK
}
pbCode += 4;
}
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
write_thumb_opcode(pbCode, 0xdefe);
}
return pbCode;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
// Skip over the import jump if there is one.
pbCode = (PBYTE)DETOURS_PFUNC_TO_PBYTE(pbCode);
ULONG Opcode = fetch_thumb_opcode(pbCode);
if ((Opcode & 0xfbf08f00) == 0xf2400c00) { // movw r12,#xxxx
ULONG Opcode2 = fetch_thumb_opcode(pbCode+4);
if ((Opcode2 & 0xfbf08f00) == 0xf2c00c00) { // movt r12,#xxxx
ULONG Opcode3 = fetch_thumb_opcode(pbCode+8);
if (Opcode3 == 0xf8dcf000) { // ldr pc,[r12]
PBYTE pbTarget = (PBYTE)(((Opcode2 << 12) & 0xf7000000) |
((Opcode2 << 1) & 0x08000000) |
((Opcode2 << 16) & 0x00ff0000) |
((Opcode >> 4) & 0x0000f700) |
((Opcode >> 15) & 0x00000800) |
((Opcode >> 0) & 0x000000ff));
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(PBYTE *)pbTarget;
pbNew = DETOURS_PFUNC_TO_PBYTE(pbNew);
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
return pbNew;
}
}
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
// We have to place trampolines within +/- 2GB of code.
ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
DETOUR_TRACE(("[%p..%p..%p]\n", (PVOID)lo, pbCode, (PVOID)hi));
*ppLower = (PDETOUR_TRAMPOLINE)lo;
*ppUpper = (PDETOUR_TRAMPOLINE)hi;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
ULONG Opcode = fetch_thumb_opcode(pbCode);
if ((Opcode & 0xffffff87) == 0x4700 || // bx
(Opcode & 0xf800d000) == 0xf0009000) { // b
return TRUE;
}
if ((Opcode & 0xffff8000) == 0xe8bd8000) { // pop {...,pc}
__debugbreak();
return TRUE;
}
if ((Opcode & 0xffffff00) == 0x0000bd00) { // pop {...,pc}
__debugbreak();
return TRUE;
}
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
if (pbCode[0] == 0x00 && pbCode[1] == 0xbf) { // nop.
return 2;
}
if (pbCode[0] == 0x00 && pbCode[1] == 0x00) { // zero-filled padding.
return 2;
}
return 0;
}
#endif // DETOURS_ARM
#ifdef DETOURS_ARM64
struct _DETOUR_TRAMPOLINE
{
// An ARM64 instruction is 4 bytes long.
//
// The overwrite is always composed of 3 instructions (12 bytes) which perform an indirect jump
// using _DETOUR_TRAMPOLINE::pbDetour as the address holding the target location.
//
// Copied instructions can expand.
//
// The scheme using MovImmediate can cause an instruction
// to grow as much as 6 times.
// That would be Bcc or Tbz with a large address space:
// 4 instructions to form immediate
// inverted tbz/bcc
// br
//
// An expansion of 4 is not uncommon -- bl/blr and small address space:
// 3 instructions to form immediate
// br or brl
//
// A theoretical maximum for rbCode is thefore 4*4*6 + 16 = 112 (another 16 for jmp to pbRemain).
//
// With literals, the maximum expansion is 5, including the literals: 4*4*5 + 16 = 96.
//
// The number is rounded up to 128. m_rbScratchDst should match this.
//
BYTE rbCode[128]; // target code + jmp to pbRemain
BYTE cbCode; // size of moved target code.
BYTE cbCodeBreak[3]; // padding to make debugging easier.
BYTE rbRestore[24]; // original target code.
BYTE cbRestore; // size of original target code.
BYTE cbRestoreBreak[3]; // padding to make debugging easier.
_DETOUR_ALIGN rAlign[8]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
};
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 184);
enum {
SIZE_OF_JMP = 12
};
inline ULONG fetch_opcode(PBYTE pbCode)
{
return *(ULONG *)pbCode;
}
inline void write_opcode(PBYTE &pbCode, ULONG Opcode)
{
*(ULONG *)pbCode = Opcode;
pbCode += 4;
}
struct ARM64_INDIRECT_JMP {
struct {
ULONG Rd : 5;
ULONG immhi : 19;
ULONG iop : 5;
ULONG immlo : 2;
ULONG op : 1;
} ardp;
struct {
ULONG Rt : 5;
ULONG Rn : 5;
ULONG imm : 12;
ULONG opc : 2;
ULONG iop1 : 2;
ULONG V : 1;
ULONG iop2 : 3;
ULONG size : 2;
} ldr;
ULONG br;
};
#pragma warning(push)
#pragma warning(disable:4201)
union ARM64_INDIRECT_IMM {
struct {
ULONG64 pad : 12;
ULONG64 adrp_immlo : 2;
ULONG64 adrp_immhi : 19;
};
LONG64 value;
};
#pragma warning(pop)
PBYTE detour_gen_jmp_indirect(BYTE *pbCode, ULONG64 *pbJmpVal)
{
// adrp x17, [jmpval]
// ldr x17, [x17, jmpval]
// br x17
struct ARM64_INDIRECT_JMP *pIndJmp;
union ARM64_INDIRECT_IMM jmpIndAddr;
jmpIndAddr.value = (((LONG64)pbJmpVal) & 0xFFFFFFFFFFFFF000) -
(((LONG64)pbCode) & 0xFFFFFFFFFFFFF000);
pIndJmp = (struct ARM64_INDIRECT_JMP *)pbCode;
pbCode = (BYTE *)(pIndJmp + 1);
pIndJmp->ardp.Rd = 17;
pIndJmp->ardp.immhi = jmpIndAddr.adrp_immhi;
pIndJmp->ardp.iop = 0x10;
pIndJmp->ardp.immlo = jmpIndAddr.adrp_immlo;
pIndJmp->ardp.op = 1;
pIndJmp->ldr.Rt = 17;
pIndJmp->ldr.Rn = 17;
pIndJmp->ldr.imm = (((ULONG64)pbJmpVal) & 0xFFF) / 8;
pIndJmp->ldr.opc = 1;
pIndJmp->ldr.iop1 = 1;
pIndJmp->ldr.V = 0;
pIndJmp->ldr.iop2 = 7;
pIndJmp->ldr.size = 3;
pIndJmp->br = 0xD61F0220;
return pbCode;
}
PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal)
{
PBYTE pbLiteral;
if (ppPool != NULL) {
*ppPool = *ppPool - 8;
pbLiteral = *ppPool;
}
else {
pbLiteral = pbCode + 8;
}
*((PBYTE*&)pbLiteral) = pbJmpVal;
LONG delta = (LONG)(pbLiteral - pbCode);
write_opcode(pbCode, 0x58000011 | ((delta / 4) << 5)); // LDR X17,[PC+n]
write_opcode(pbCode, 0xd61f0000 | (17 << 5)); // BR X17
if (ppPool == NULL) {
pbCode += 8;
}
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
write_opcode(pbCode, 0xd4100000 | (0xf000 << 5));
}
return pbCode;
}
inline INT64 detour_sign_extend(UINT64 value, UINT bits)
{
const UINT left = 64 - bits;
const INT64 m1 = -1;
const INT64 wide = (INT64)(value << left);
const INT64 sign = (wide < 0) ? (m1 << left) : 0;
return value | sign;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
// Skip over the import jump if there is one.
pbCode = (PBYTE)pbCode;
ULONG Opcode = fetch_opcode(pbCode);
if ((Opcode & 0x9f00001f) == 0x90000010) { // adrp x16, IAT
ULONG Opcode2 = fetch_opcode(pbCode + 4);
if ((Opcode2 & 0xffe003ff) == 0xf9400210) { // ldr x16, [x16, IAT]
ULONG Opcode3 = fetch_opcode(pbCode + 8);
if (Opcode3 == 0xd61f0200) { // br x16
/* https://static.docs.arm.com/ddi0487/bb/DDI0487B_b_armv8_arm.pdf
The ADRP instruction shifts a signed, 21-bit immediate left by 12 bits, adds it to the value of the program counter with
the bottom 12 bits cleared to zero, and then writes the result to a general-purpose register. This permits the
calculation of the address at a 4KB aligned memory region. In conjunction with an ADD (immediate) instruction, or
a Load/Store instruction with a 12-bit immediate offset, this allows for the calculation of, or access to, any address
within +/- 4GB of the current PC.
PC-rel. addressing
This section describes the encoding of the PC-rel. addressing instruction class. The encodings in this section are
decoded from Data Processing -- Immediate on page C4-226.
Add/subtract (immediate)
This section describes the encoding of the Add/subtract (immediate) instruction class. The encodings in this section
are decoded from Data Processing -- Immediate on page C4-226.
Decode fields
Instruction page
op
0 ADR
1 ADRP
C6.2.10 ADRP
Form PC-relative address to 4KB page adds an immediate value that is shifted left by 12 bits, to the PC value to
form a PC-relative address, with the bottom 12 bits masked out, and writes the result to the destination register.
ADRP ,