From e4d32f54fdb094e9b1259c8d8ee7b6f43b730ddb Mon Sep 17 00:00:00 2001 From: Ironhammer Std <2482911962@qq.com> Date: Thu, 14 May 2026 11:48:00 +0800 Subject: [PATCH 1/6] Disable Warning C4456 for a certain part of code --- SyringeDebugger.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/SyringeDebugger.h b/SyringeDebugger.h index 88797c2..0e9e0e4 100644 --- a/SyringeDebugger.h +++ b/SyringeDebugger.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #define WIN32_LEAN_AND_MEAN // WIN32_FAT_AND_STUPID @@ -45,6 +45,10 @@ class SyringeDebugger { std::string_view const flagView = flag; +//Disable Warning C4456 +#pragma warning( push ) +#pragma warning( disable : 4456 ) + // parse all -i=filename_to_inject from flags if (auto const pos = flagView.find(INCLUDE_FLAG); pos != std::string_view::npos) { @@ -68,6 +72,8 @@ class SyringeDebugger } } +#pragma warning( pop ) + if (dlls.empty()) { dlls.emplace_back("*.dll"); From 5475e50e98e3d52acda9fa8615e735290de815b3 Mon Sep 17 00:00:00 2001 From: Ironhammer Std <2482911962@qq.com> Date: Thu, 14 May 2026 17:54:16 +0800 Subject: [PATCH 2/6] Basic Hook Analyzer with INJ generation --- Debugger.vcxproj | 3 + Debugger.vcxproj.filters | 12 ++++ HookAnalyzer.cpp | 145 +++++++++++++++++++++++++++++++++++++++ HookAnalyzer.h | 50 ++++++++++++++ SyringeDebugger.cpp | 40 ++++++++++- SyringeDebugger.h | 20 ++++++ ToolFunctions.cpp | 77 +++++++++++++++++++++ 7 files changed, 345 insertions(+), 2 deletions(-) create mode 100644 HookAnalyzer.cpp create mode 100644 HookAnalyzer.h create mode 100644 ToolFunctions.cpp diff --git a/Debugger.vcxproj b/Debugger.vcxproj index 120d7c3..7c48406 100644 --- a/Debugger.vcxproj +++ b/Debugger.vcxproj @@ -148,15 +148,18 @@ + + + diff --git a/Debugger.vcxproj.filters b/Debugger.vcxproj.filters index c4cd35f..1039ce5 100644 --- a/Debugger.vcxproj.filters +++ b/Debugger.vcxproj.filters @@ -13,6 +13,9 @@ {e85625c5-48c3-4acf-a47a-873697970105} ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + {0a7cbdbd-6556-4c84-b288-4ffa95f8120b} + @@ -30,6 +33,12 @@ Source Files + + Components + + + Components + @@ -56,6 +65,9 @@ Header Files + + Components + diff --git a/HookAnalyzer.cpp b/HookAnalyzer.cpp new file mode 100644 index 0000000..714a71e --- /dev/null +++ b/HookAnalyzer.cpp @@ -0,0 +1,145 @@ +#include "HookAnalyzer.h" +#include "Handle.h" +//#include "Setting.h" +#include +#include "Log.h" + +const std::string& ExecutableDirectoryPath(); + +void HookAnalyzer::Add(HookAnalyzeData&& Data, bool Show) +{ + if (Show) + { + ByLibName[Data.Lib].push_back(Data); + HookMap[Data.Lib + AnalyzerDelim + Data.Proc] = Data; + ByAddress[Data.Addr].push_back(std::move(Data)); + } + + HookMapEx[Data.Lib + AnalyzerDelim + Data.Proc] = Data; + ByLibNameEx[Data.Lib].push_back(Data); + ByAddressEx[Data.RelLib][Data.Addr].push_back(std::move(Data)); +} + +//bool HookAnalyzer::Report() +//{ +// FileHandle File = FileHandle(fopen("HookAnalysis.log", "w")); +// if (!File)return false; +// fprintf(File, "%s 将分析获取到的钩子。\n", VersionString); +// if (ShowHookAnalysis_ByAddr) +// { +// fputs("========================\n", File); +// fputs("按照钩子位置分析:(每个地址处按照钩子执行序)\n", File); +// for (auto& p : ByAddress) +// { +// fprintf(File, "在 %08X :\n", p.first); +// for (auto v : p.second) +// { +// fprintf(File, "钩子\"%s,相对于\"%s\",来自\"%s\",长度%d,优先级 %d,次优先级 \"%s\"\n", v.Proc.c_str(), v.RelLib.c_str(), v.Lib.c_str(), v.Len, v.Priority, v.SubPriority.c_str()); +// } +// } +// } +// +// if (ShowHookAnalysis_ByLib) +// { +// fputs("========================\n", File); +// fputs("按照钩子来源分析:\n", File); +// for (auto& p : ByLibName) +// { +// fprintf(File, "正在分析 DLL :\"%s\" ……\n", p.first.c_str()); +// for (auto v : p.second) +// { +// fprintf(File, "钩子\"%s\",相对于\"%s\",位于%08X,长度%d,优先级 %d,次优先级 \"%s\"\n", v.Proc.c_str(), v.RelLib.c_str(), v.Addr, v.Len, v.Priority, v.SubPriority.c_str()); +// } +// } +// } +// +// fputs("========================\n", File); +// fprintf(File, "%s 分析完毕。\n", VersionString); +// return true; +//} +// +//bool HookAnalyzer::HasHookConflict() +//{ +// //check if there are conflicting hooks +// bool Conflict = false; +// for (auto& [lib, byaddr] : ByAddressEx) +// { +// std::vector*> SortedHooks; +// for (auto& p : byaddr) +// SortedHooks.push_back(&p.second); +// std::sort(SortedHooks.begin(), SortedHooks.end(), [](const auto& lhs, const auto& rhs) -> bool +// { +// return lhs->front().Addr < rhs->front().Addr; +// }); +// for (size_t i = 0; i < SortedHooks.size() - 1; i++) +// { +// auto Addr1 = SortedHooks[i]->front().Addr; +// auto Addr2 = SortedHooks[i + 1]->front().Addr; +// auto Len1 = std::max_element(SortedHooks[i]->begin(), SortedHooks[i]->end(), [](const auto& lhs, const auto& rhs) -> bool +// { +// return lhs.Len < rhs.Len; +// })->Len; +// Len1 = std::max(Len1, 5);//a JMP is 5 bytes +// if (Addr1 + Len1 > Addr2) +// { +// Log::WriteLine("检测到钩子冲突:"); +// for (auto& h : *SortedHooks[i]) +// Log::WriteLine("钩子\"%s\",来自\"%s\",相对于\"%s\",位于%08X,长度%d,优先级 %d,次优先级 \"%s\"", +// h.Proc.c_str(), +// h.Lib.c_str(), +// h.RelLib.c_str(), +// h.Addr, +// h.Len, +// h.Priority, +// h.SubPriority.c_str()); +// for (auto& h : *SortedHooks[i + 1]) +// Log::WriteLine("钩子\"%s\",来自\"%s\",相对于\"%s\",位于%08X,长度%d,优先级 %d,次优先级 \"%s\"", +// h.Proc.c_str(), +// h.Lib.c_str(), +// h.RelLib.c_str(), +// h.Addr, +// h.Len, +// h.Priority, +// h.SubPriority.c_str()); +// if (!Conflict && ShowHookConflictPopup) +// { +// wchar_t ErrorStr[1000]; +// swprintf_s(ErrorStr, 1000, L"检测到位于 0x%08X 和 0x%08X 的钩子冲突,详见 Syringe.log 。", Addr1, Addr2); +// MessageBoxW(NULL, ErrorStr, VersionLString, MB_OK | MB_ICONERROR); +// } +// Conflict = true; +// } +// } +// return Conflict; +// } +//} + +bool HookAnalyzer::GenerateINJ() +{ + //Log::WriteLine(ExecutableDirectoryPath().c_str()); + auto path = ExecutableDirectoryPath() + "\\INJ"; + auto pp = CreateDirectoryA(path.c_str(), NULL); + if (pp || GetLastError() == ERROR_ALREADY_EXISTS) + { + //Log::WriteLine((path + "\\").c_str()); + for (auto& p : ByLibNameEx) + { + //Log::WriteLine((path + "\\" + p.first).c_str()); + FileHandle File = FileHandle(fopen((path + "\\" + p.first + ".inj").c_str(), "w")); + if (!File)return false; + for (auto& h : p.second) + { + if (!h.RelLib.empty()) + fputs(";Relative Hook Found ,failed to Generate", File); + else if (!h.SubPriority.empty()) + fprintf(File, "%X=%s,%X,%d,%s\n", h.Addr, h.Proc.c_str(), h.Len, h.Priority, h.SubPriority.c_str()); + else if (h.Priority == 100000) + fprintf(File, "%X=%s,%X\n", h.Addr, h.Proc.c_str(), h.Len); + else + fprintf(File, "%X=%s,%X,%d\n", h.Addr, h.Proc.c_str(), h.Len, h.Priority); + } + } + return true; + } + return false; +} \ No newline at end of file diff --git a/HookAnalyzer.h b/HookAnalyzer.h new file mode 100644 index 0000000..562cb74 --- /dev/null +++ b/HookAnalyzer.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include + +const std::string AnalyzerDelim = "\\*^*\\"; + +struct HookAnalyzeData +{ + std::string Lib; + std::string Proc; + int Addr; + int Len; + + int Priority{ 100000 }; + std::string SubPriority{ "" }; + std::string RelLib{ "" }; +}; + +class HookAnalyzer +{ +private: + std::unordered_map> ByLibName; + std::unordered_map> ByLibNameEx; +public: + std::unordered_map HookMap; + std::unordered_map> ByAddress; + std::unordered_map HookMapEx; + std::unordered_map>> ByAddressEx; + + void Add(HookAnalyzeData&& , bool Show); + bool Report(); + bool GenerateINJ(); + bool HasHookConflict(); +}; + +//static constexpr size_t MaxNameLength = 0x100u; +// +//struct Hook +//{ +// char lib[MaxNameLength]; +// char proc[MaxNameLength]; +// void* proc_address; +// +// size_t num_overridden; +// int Priority; +// char SubPriority[MaxNameLength]; +// char RelativeLib[MaxNameLength]; +//}; \ No newline at end of file diff --git a/SyringeDebugger.cpp b/SyringeDebugger.cpp index 5715a6f..2b6d7a8 100644 --- a/SyringeDebugger.cpp +++ b/SyringeDebugger.cpp @@ -1,4 +1,4 @@ -#include "SyringeDebugger.h" +#include "SyringeDebugger.h" #include "CRC32.h" #include "FindFile.h" @@ -788,8 +788,28 @@ DWORD SyringeDebugger::HandleException(DEBUG_EVENT const& dbgEvent) return DBG_CONTINUE; } -void SyringeDebugger::Run(std::string_view const arguments) +void SyringeDebugger::Run(std::string_view arguments) { + if(bDryRun) + { + Log::WriteLine(__FUNCTION__ ": Entering dry run mode, not actually running the process."); + DryRun(arguments); + } + else + { + RealRun(arguments); + } +} + +void SyringeDebugger::DryRun(std::string_view const arguments) +{ + PreLoadData(); +} + +void SyringeDebugger::RealRun(std::string_view const arguments) +{ + PreLoadData(); + constexpr auto AllocDataSize = sizeof(AllocData); Log::WriteLine( @@ -937,6 +957,17 @@ void SyringeDebugger::Run(std::string_view const arguments) Log::WriteLine(); } +void SyringeDebugger::PreLoadData() +{ + if (bGenerateINJ) + { + Log::WriteLine(__FUNCTION__ ": Creating INJ files..."); + if (analyzer.GenerateINJ()) + Log::WriteLine(__FUNCTION__ ": Complete."); + else Log::WriteLine(__FUNCTION__ ": Failed."); + } +} + void SyringeDebugger::RemoveBP(LPVOID const address, bool const restoreOpcode) { if (auto const i = Breakpoints.find(address); i != Breakpoints.end()) @@ -1108,6 +1139,11 @@ void SyringeDebugger::FindDLLs() { for (auto& i : it.second.hooks) { + std::string_view filename = i.lib; + auto sz = filename.find_last_of('\\'); + auto sv = (sz != std::string_view::npos) ? filename.substr(sz + 1, filename.size() - sz - 1) : filename; + analyzer.Add(HookAnalyzeData{ sv.data(), i.proc, (int)it.first, (int)i.num_overridden }, false); + v_AllHooks.push_back(&i); } } diff --git a/SyringeDebugger.h b/SyringeDebugger.h index 0e9e0e4..36368dc 100644 --- a/SyringeDebugger.h +++ b/SyringeDebugger.h @@ -22,6 +22,8 @@ #include #pragma warning(pop) +#include "HookAnalyzer.h" + using std::operator""sv; class SyringeDebugger @@ -36,6 +38,8 @@ class SyringeDebugger static constexpr std::string_view NODETACH_FLAG = "--nodetach"; static constexpr std::string_view NOWAIT_FLAG = "--nowait"; static constexpr std::string_view HANDSHAKES_FLAG = "--handshakes"; + static constexpr std::string_view DRYRUN_FLAG = "--dryrun"; + static constexpr std::string_view GENERATEINJ_FLAG = "--generate-inj"; public: SyringeDebugger(std::string_view filename, std::vector flags = {}) @@ -66,6 +70,14 @@ class SyringeDebugger { bHandshakes = true; } + else if (auto const pos = flagView.find(DRYRUN_FLAG); pos != std::string_view::npos) + { + bDryRun = true; + } + else if (auto const pos = flagView.find(GENERATEINJ_FLAG); pos != std::string_view::npos) + { + bGenerateINJ = true; + } else { Log::WriteLine(__FUNCTION__ ": Unknown flag \"%.*s\", skipping.", printable(flagView)); @@ -83,6 +95,8 @@ class SyringeDebugger } // debugger + void DryRun(std::string_view arguments); + void RealRun(std::string_view arguments); void Run(std::string_view arguments); DWORD HandleException(DEBUG_EVENT const& dbgEvent); @@ -195,12 +209,17 @@ class SyringeDebugger bool bDetachWhenDone{ true }; bool bWaitForProcessEnd{ true }; bool bHandshakes{ false }; + bool bDryRun{ false }; + bool bGenerateINJ{ false }; bool bDLLsLoaded{ false }; bool bHooksCreated{ false }; bool bAVLogged{ false }; + // components + HookAnalyzer analyzer; + // data addresses struct AllocData { @@ -250,6 +269,7 @@ class SyringeDebugger bool CanHostDLL(PortableExecutable const& DLL, IMAGE_SECTION_HEADER const& hosts) const; bool ParseHooksSection(PortableExecutable const& DLL, IMAGE_SECTION_HEADER const& hooks, HookBuffer& buffer); std::optional Handshake(char const* lib, int hooks, unsigned int crc); + void PreLoadData(); }; // disable "structures padded due to alignment specifier" diff --git a/ToolFunctions.cpp b/ToolFunctions.cpp new file mode 100644 index 0000000..47d84d6 --- /dev/null +++ b/ToolFunctions.cpp @@ -0,0 +1,77 @@ +#include +#include +#include + +const std::string& ExecutableDirectoryPath() +{ + static std::string ss; + if (!ss.empty())return ss; + std::vector full_path_exe(MAX_PATH); + + for (;;) + { + const DWORD result = GetModuleFileNameA(NULL, + &full_path_exe[0], + full_path_exe.size()); + + if (result == 0) + { + // Report failure to caller. + } + else if (full_path_exe.size() == result) + { + // Buffer too small: increase size. + full_path_exe.resize(full_path_exe.size() * 2); + } + else + { + // Success. + break; + } + } + + // Remove executable name. + std::string result(full_path_exe.begin(), full_path_exe.end()); + std::string::size_type i = result.find_last_of("\\/"); + if (std::string::npos != i) result.erase(i); + + ss = result; + return ss; +} + +const std::wstring& ExecutableDirectoryPathW() +{ + static std::wstring ss; + if (!ss.empty())return ss; + std::vector full_path_exe(MAX_PATH); + + for (;;) + { + const DWORD result = GetModuleFileNameW(NULL, + &full_path_exe[0], + full_path_exe.size()); + + if (result == 0) + { + // Report failure to caller. + } + else if (full_path_exe.size() == result) + { + // Buffer too small: increase size. + full_path_exe.resize(full_path_exe.size() * 2); + } + else + { + // Success. + break; + } + } + + // Remove executable name. + std::wstring result(full_path_exe.begin(), full_path_exe.end()); + std::wstring::size_type i = result.find_last_of(L"\\/"); + if (std::string::npos != i) result.erase(i); + + ss = result; + return ss; +} From 7d140541521c203f67618ca360c7a6ac099a8253 Mon Sep 17 00:00:00 2001 From: Ironhammer Std <2482911962@qq.com> Date: Thu, 14 May 2026 17:56:19 +0800 Subject: [PATCH 3/6] Set DefaultPriority --- HookAnalyzer.cpp | 2 +- HookAnalyzer.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/HookAnalyzer.cpp b/HookAnalyzer.cpp index 714a71e..496855a 100644 --- a/HookAnalyzer.cpp +++ b/HookAnalyzer.cpp @@ -133,7 +133,7 @@ bool HookAnalyzer::GenerateINJ() fputs(";Relative Hook Found ,failed to Generate", File); else if (!h.SubPriority.empty()) fprintf(File, "%X=%s,%X,%d,%s\n", h.Addr, h.Proc.c_str(), h.Len, h.Priority, h.SubPriority.c_str()); - else if (h.Priority == 100000) + else if (h.Priority == DefaultPriority) fprintf(File, "%X=%s,%X\n", h.Addr, h.Proc.c_str(), h.Len); else fprintf(File, "%X=%s,%X,%d\n", h.Addr, h.Proc.c_str(), h.Len, h.Priority); diff --git a/HookAnalyzer.h b/HookAnalyzer.h index 562cb74..35c3f83 100644 --- a/HookAnalyzer.h +++ b/HookAnalyzer.h @@ -5,6 +5,7 @@ #include const std::string AnalyzerDelim = "\\*^*\\"; +const int DefaultPriority = 100000; struct HookAnalyzeData { @@ -13,7 +14,7 @@ struct HookAnalyzeData int Addr; int Len; - int Priority{ 100000 }; + int Priority{ DefaultPriority }; std::string SubPriority{ "" }; std::string RelLib{ "" }; }; From bf6668eecfeebd7699e239e0e52fe7b173f6bd24 Mon Sep 17 00:00:00 2001 From: Ironhammer Std <2482911962@qq.com> Date: Thu, 14 May 2026 17:58:00 +0800 Subject: [PATCH 4/6] Temp show all hooks in HookAnalysis (until rule sets implemented) --- SyringeDebugger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SyringeDebugger.cpp b/SyringeDebugger.cpp index 2b6d7a8..61276cf 100644 --- a/SyringeDebugger.cpp +++ b/SyringeDebugger.cpp @@ -1142,7 +1142,7 @@ void SyringeDebugger::FindDLLs() std::string_view filename = i.lib; auto sz = filename.find_last_of('\\'); auto sv = (sz != std::string_view::npos) ? filename.substr(sz + 1, filename.size() - sz - 1) : filename; - analyzer.Add(HookAnalyzeData{ sv.data(), i.proc, (int)it.first, (int)i.num_overridden }, false); + analyzer.Add(HookAnalyzeData{ sv.data(), i.proc, (int)it.first, (int)i.num_overridden }, true); v_AllHooks.push_back(&i); } From f677f56f681d8bad640a9375768a7a524b3ecd57 Mon Sep 17 00:00:00 2001 From: Ironhammer Std <2482911962@qq.com> Date: Thu, 14 May 2026 20:59:31 +0800 Subject: [PATCH 5/6] ReportLOG & DetectConflict ReportJSON not yet implemented --- HookAnalyzer.cpp | 190 ++++++++++++++++++++++---------------------- HookAnalyzer.h | 5 +- SyringeDebugger.cpp | 19 +++++ SyringeDebugger.h | 40 ++++++++++ 4 files changed, 158 insertions(+), 96 deletions(-) diff --git a/HookAnalyzer.cpp b/HookAnalyzer.cpp index 496855a..5f3988a 100644 --- a/HookAnalyzer.cpp +++ b/HookAnalyzer.cpp @@ -3,6 +3,7 @@ //#include "Setting.h" #include #include "Log.h" +#include "resource.h" const std::string& ExecutableDirectoryPath(); @@ -12,7 +13,7 @@ void HookAnalyzer::Add(HookAnalyzeData&& Data, bool Show) { ByLibName[Data.Lib].push_back(Data); HookMap[Data.Lib + AnalyzerDelim + Data.Proc] = Data; - ByAddress[Data.Addr].push_back(std::move(Data)); + ByAddress[Data.Addr].push_back(Data); } HookMapEx[Data.Lib + AnalyzerDelim + Data.Proc] = Data; @@ -20,99 +21,100 @@ void HookAnalyzer::Add(HookAnalyzeData&& Data, bool Show) ByAddressEx[Data.RelLib][Data.Addr].push_back(std::move(Data)); } -//bool HookAnalyzer::Report() -//{ -// FileHandle File = FileHandle(fopen("HookAnalysis.log", "w")); -// if (!File)return false; -// fprintf(File, "%s 将分析获取到的钩子。\n", VersionString); -// if (ShowHookAnalysis_ByAddr) -// { -// fputs("========================\n", File); -// fputs("按照钩子位置分析:(每个地址处按照钩子执行序)\n", File); -// for (auto& p : ByAddress) -// { -// fprintf(File, "在 %08X :\n", p.first); -// for (auto v : p.second) -// { -// fprintf(File, "钩子\"%s,相对于\"%s\",来自\"%s\",长度%d,优先级 %d,次优先级 \"%s\"\n", v.Proc.c_str(), v.RelLib.c_str(), v.Lib.c_str(), v.Len, v.Priority, v.SubPriority.c_str()); -// } -// } -// } -// -// if (ShowHookAnalysis_ByLib) -// { -// fputs("========================\n", File); -// fputs("按照钩子来源分析:\n", File); -// for (auto& p : ByLibName) -// { -// fprintf(File, "正在分析 DLL :\"%s\" ……\n", p.first.c_str()); -// for (auto v : p.second) -// { -// fprintf(File, "钩子\"%s\",相对于\"%s\",位于%08X,长度%d,优先级 %d,次优先级 \"%s\"\n", v.Proc.c_str(), v.RelLib.c_str(), v.Addr, v.Len, v.Priority, v.SubPriority.c_str()); -// } -// } -// } -// -// fputs("========================\n", File); -// fprintf(File, "%s 分析完毕。\n", VersionString); -// return true; -//} -// -//bool HookAnalyzer::HasHookConflict() -//{ -// //check if there are conflicting hooks -// bool Conflict = false; -// for (auto& [lib, byaddr] : ByAddressEx) -// { -// std::vector*> SortedHooks; -// for (auto& p : byaddr) -// SortedHooks.push_back(&p.second); -// std::sort(SortedHooks.begin(), SortedHooks.end(), [](const auto& lhs, const auto& rhs) -> bool -// { -// return lhs->front().Addr < rhs->front().Addr; -// }); -// for (size_t i = 0; i < SortedHooks.size() - 1; i++) -// { -// auto Addr1 = SortedHooks[i]->front().Addr; -// auto Addr2 = SortedHooks[i + 1]->front().Addr; -// auto Len1 = std::max_element(SortedHooks[i]->begin(), SortedHooks[i]->end(), [](const auto& lhs, const auto& rhs) -> bool -// { -// return lhs.Len < rhs.Len; -// })->Len; -// Len1 = std::max(Len1, 5);//a JMP is 5 bytes -// if (Addr1 + Len1 > Addr2) -// { -// Log::WriteLine("检测到钩子冲突:"); -// for (auto& h : *SortedHooks[i]) -// Log::WriteLine("钩子\"%s\",来自\"%s\",相对于\"%s\",位于%08X,长度%d,优先级 %d,次优先级 \"%s\"", -// h.Proc.c_str(), -// h.Lib.c_str(), -// h.RelLib.c_str(), -// h.Addr, -// h.Len, -// h.Priority, -// h.SubPriority.c_str()); -// for (auto& h : *SortedHooks[i + 1]) -// Log::WriteLine("钩子\"%s\",来自\"%s\",相对于\"%s\",位于%08X,长度%d,优先级 %d,次优先级 \"%s\"", -// h.Proc.c_str(), -// h.Lib.c_str(), -// h.RelLib.c_str(), -// h.Addr, -// h.Len, -// h.Priority, -// h.SubPriority.c_str()); -// if (!Conflict && ShowHookConflictPopup) -// { -// wchar_t ErrorStr[1000]; -// swprintf_s(ErrorStr, 1000, L"检测到位于 0x%08X 和 0x%08X 的钩子冲突,详见 Syringe.log 。", Addr1, Addr2); -// MessageBoxW(NULL, ErrorStr, VersionLString, MB_OK | MB_ICONERROR); -// } -// Conflict = true; -// } -// } -// return Conflict; -// } -//} +bool HookAnalyzer::ReportLOG(bool ByAddr, bool ByLib) +{ + constexpr auto const VersionString = "SyringeEx " SYRINGEEX_VER_TEXT ", based on Syringe 0.7.2.0"; + + FileHandle File = FileHandle(fopen("HookAnalysis.log", "w")); + if (!File)return false; + fprintf(File, "%s will analyze the specified hooks.\n", VersionString); + if (ByAddr) + { + fputs("========================\n", File); + fputs("By Hook Position: (Execution order for each address)\n", File); + for (auto& p : ByAddress) + { + fprintf(File, "At %08X : \n", p.first); + for (auto v : p.second) + { + //fprintf(File, "Hook\"%s, Relative to\"%s\", From\"%s\", %d Bytes Overridden ,Priority %d, Sub Priority \"%s\"\n", v.Proc.c_str(), v.RelLib.c_str(), v.Lib.c_str(), v.Len, v.Priority, v.SubPriority.c_str()); + fprintf(File, "Hook\"%s, From\"%s\", %d Bytes Overridden\n", v.Proc.c_str(), v.Lib.c_str(), v.Len); + } + } + } + + if (ByLib) + { + fputs("========================\n", File); + fputs("By Hook Source: \n", File); + for (auto& p : ByLibName) + { + fprintf(File, "Analyzing DLL : \"%s\" ……\n", p.first.c_str()); + for (auto v : p.second) + { + //fprintf(File, "Hook\"%s, Relative to\"%s\", From\"%s\", %d Bytes Overridden ,Priority %d, Sub Priority \"%s\"\n", v.Proc.c_str(), v.RelLib.c_str(), v.Lib.c_str(), v.Len, v.Priority, v.SubPriority.c_str()); + fprintf(File, "Hook\"%s, From\"%s\", %d Bytes Overridden\n", v.Proc.c_str(), v.Lib.c_str(), v.Len); + } + } + } + + fputs("========================\n", File); + fprintf(File, "%s : Complete.\n", VersionString); + return true; +} + +bool HookAnalyzer::ReportNDJSON() +{ + //TODO + //Until a JSON library is chosen and imported + //A format may work : + //every line : + //Hook Address / Name / Source / Bytes Overridden +} + +bool HookAnalyzer::HasHookConflict(bool ShowHookConflictPopup) +{ + //check if there are conflicting hooks + bool Conflict = false; + for (auto& [lib, byaddr] : ByAddressEx) + { + std::vector*> SortedHooks; + for (auto& p : byaddr) + SortedHooks.push_back(&p.second); + std::sort(SortedHooks.begin(), SortedHooks.end(), [](const auto& lhs, const auto& rhs) -> bool + { + return lhs->front().Addr < rhs->front().Addr; + }); + for (size_t i = 0; i < SortedHooks.size() - 1; i++) + { + auto Addr1 = SortedHooks[i]->front().Addr; + auto Addr2 = SortedHooks[i + 1]->front().Addr; + auto Len1 = std::max_element(SortedHooks[i]->begin(), SortedHooks[i]->end(), [](const auto& lhs, const auto& rhs) -> bool + { + return lhs.Len < rhs.Len; + })->Len; + Len1 = std::max(Len1, 5);//a JMP is 5 bytes + if (Addr1 + Len1 > Addr2) + { + Log::WriteLine("Hook Conflict Detected:"); + for (auto& v : *SortedHooks[i]) + //Log::WriteLine("Hook\"%s, Relative to\"%s\", From\"%s\", %d Bytes Overridden ,Priority %d, Sub Priority \"%s\"\n", v.Proc.c_str(), v.RelLib.c_str(), v.Lib.c_str(), v.Len, v.Priority, v.SubPriority.c_str()); + Log::WriteLine("Hook\"%s, From\"%s\", %d Bytes Overridden\n", v.Proc.c_str(), v.Lib.c_str(), v.Len); + for (auto& v : *SortedHooks[i + 1]) + //Log::WriteLine("Hook\"%s, Relative to\"%s\", From\"%s\", %d Bytes Overridden ,Priority %d, Sub Priority \"%s\"\n", v.Proc.c_str(), v.RelLib.c_str(), v.Lib.c_str(), v.Len, v.Priority, v.SubPriority.c_str()); + Log::WriteLine("Hook\"%s, From\"%s\", %d Bytes Overridden\n", v.Proc.c_str(), v.Lib.c_str(), v.Len); + if (!Conflict && ShowHookConflictPopup) + { + wchar_t ErrorStr[1000]; + swprintf_s(ErrorStr, 1000, L"Hook Conflict detected at 0x%08X and 0x%08X , see details in Syringe.log.", Addr1, Addr2); + MessageBoxW(NULL, ErrorStr, L"SyringeEx", MB_OK | MB_ICONERROR); + } + Conflict = true; + } + } + return Conflict; + } +} bool HookAnalyzer::GenerateINJ() { diff --git a/HookAnalyzer.h b/HookAnalyzer.h index 35c3f83..bfb7c6b 100644 --- a/HookAnalyzer.h +++ b/HookAnalyzer.h @@ -31,9 +31,10 @@ class HookAnalyzer std::unordered_map>> ByAddressEx; void Add(HookAnalyzeData&& , bool Show); - bool Report(); + bool ReportLOG(bool ByAddr, bool ByLib); + bool ReportNDJSON();//TODO bool GenerateINJ(); - bool HasHookConflict(); + bool HasHookConflict(bool ShowHookConflictPopup); }; //static constexpr size_t MaxNameLength = 0x100u; diff --git a/SyringeDebugger.cpp b/SyringeDebugger.cpp index 61276cf..8dd6848 100644 --- a/SyringeDebugger.cpp +++ b/SyringeDebugger.cpp @@ -959,6 +959,25 @@ void SyringeDebugger::RealRun(std::string_view const arguments) void SyringeDebugger::PreLoadData() { + if (bReportLOG) + { + Log::WriteLine(__FUNCTION__ ": Writing Hook Analysis Report LOG……", v_AllHooks.size()); + if(analyzer.ReportLOG(bReportLogByAddress, bReportLogByLibrary))Log::WriteLine(__FUNCTION__ ": Complete, see HookAnalysis.log 。", v_AllHooks.size()); + else Log::WriteLine(__FUNCTION__ ": Failed to generate.", v_AllHooks.size()); + } + + if (bReportJSON) + { + Log::WriteLine(__FUNCTION__ ": Writing Hook Analysis Report JSON……", v_AllHooks.size()); + if (analyzer.ReportNDJSON())Log::WriteLine(__FUNCTION__ ": Complete, see HookAnalysis.json 。", v_AllHooks.size()); + else Log::WriteLine(__FUNCTION__ ": Failed to generate.", v_AllHooks.size()); + } + + if (bDetectConflict) + { + analyzer.HasHookConflict(bShowHookConflictPopup); + } + if (bGenerateINJ) { Log::WriteLine(__FUNCTION__ ": Creating INJ files..."); diff --git a/SyringeDebugger.h b/SyringeDebugger.h index 36368dc..0619940 100644 --- a/SyringeDebugger.h +++ b/SyringeDebugger.h @@ -40,6 +40,15 @@ class SyringeDebugger static constexpr std::string_view HANDSHAKES_FLAG = "--handshakes"; static constexpr std::string_view DRYRUN_FLAG = "--dryrun"; static constexpr std::string_view GENERATEINJ_FLAG = "--generate-inj"; + static constexpr std::string_view REPORT_LOG_FLAG = "--report-log"; + static constexpr std::string_view REPORT_JSON_FLAG = "--report-json"; + static constexpr std::string_view DETECT_CONFLICT_FLAG = "--detect-conflict"; + static constexpr std::string_view SHOW_HOOK_CONFLICT_POPUP_FLAG = "--show-hook-conflict-popup"; + static constexpr std::string_view NO_BY_ADDRESS_FLAG = "--no-by-address"; + static constexpr std::string_view NO_BY_LIBRARY_FLAG = "--no-by-library"; + + + public: SyringeDebugger(std::string_view filename, std::vector flags = {}) @@ -78,6 +87,30 @@ class SyringeDebugger { bGenerateINJ = true; } + if (auto const pos = flagView.find(REPORT_LOG_FLAG); pos != std::string_view::npos) + { + bReportLOG = true; + } + else if (auto const pos = flagView.find(REPORT_JSON_FLAG); pos != std::string_view::npos) + { + bReportJSON = true; + } + else if (auto const pos = flagView.find(DETECT_CONFLICT_FLAG); pos != std::string_view::npos) + { + bDetectConflict = true; + } + else if (auto const pos = flagView.find(SHOW_HOOK_CONFLICT_POPUP_FLAG); pos != std::string_view::npos) + { + bShowHookConflictPopup = true; + } + else if (auto const pos = flagView.find(NO_BY_ADDRESS_FLAG); pos != std::string_view::npos) + { + bReportLogByAddress = false; + } + else if (auto const pos = flagView.find(NO_BY_LIBRARY_FLAG); pos != std::string_view::npos) + { + bReportLogByLibrary = false; + } else { Log::WriteLine(__FUNCTION__ ": Unknown flag \"%.*s\", skipping.", printable(flagView)); @@ -211,6 +244,13 @@ class SyringeDebugger bool bHandshakes{ false }; bool bDryRun{ false }; bool bGenerateINJ{ false }; + bool bReportLOG{ false }; + bool bReportJSON{ false }; + bool bDetectConflict{ false }; + bool bShowHookConflictPopup{ false }; + bool bReportLogByAddress{ true }; + bool bReportLogByLibrary{ true }; + bool bDLLsLoaded{ false }; bool bHooksCreated{ false }; From d25421557aea9767b4fcd3d5f5e1b1852afa9a37 Mon Sep 17 00:00:00 2001 From: Ironhammer Std <2482911962@qq.com> Date: Fri, 15 May 2026 11:47:21 +0800 Subject: [PATCH 6/6] fix a return of HookAnalyzer::HasHookConflict --- HookAnalyzer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HookAnalyzer.cpp b/HookAnalyzer.cpp index 5f3988a..c729d9e 100644 --- a/HookAnalyzer.cpp +++ b/HookAnalyzer.cpp @@ -112,8 +112,8 @@ bool HookAnalyzer::HasHookConflict(bool ShowHookConflictPopup) Conflict = true; } } - return Conflict; } + return Conflict; } bool HookAnalyzer::GenerateINJ()