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 ,