Skip to content

Add HookAnalyzer Logic#20

Open
IronHammer-Std wants to merge 6 commits into
Phobos-developers:masterfrom
IronHammer-Std:master
Open

Add HookAnalyzer Logic#20
IronHammer-Std wants to merge 6 commits into
Phobos-developers:masterfrom
IronHammer-Std:master

Conversation

@IronHammer-Std
Copy link
Copy Markdown

Context

See Phobos PR #2201

NOTE

all the flags are temporarily stored in the SyringeDebugger class, and maybe we need to move it to another subclass.
HookAnalyzer class is moved from SyringeIH here as it was except a few translations.
HookAnalyzer::ReportNDJSON is not yet implemented until we import a JSON library,and I place the function here just as Kerbiter's wish.
some of the flags are set to default because the coupled components are not here in SyringeEx, so now every hook are set to be shown in the hook analysis.

New & Enhanced Logic :

Components (1) :
HookAnalyzer

New Flags (8) :

    bool bDryRun{ false };
    bool bGenerateINJ{ false };
    bool bReportLOG{ false };
    bool bReportJSON{ false };
    bool bDetectConflict{ false };
    bool bShowHookConflictPopup{ false };
    bool bReportLogByAddress{ true };
    bool bReportLogByLibrary{ true };

Launch Arguments (8) :

--dryrun : run syringe without launching game.
--generate-inj : generate INJ files and place them in .\INJ folder
--report-log : generate a HookAnalysis.log, as it functions in SyringeIH but in English
--no-by-address : Disable the "By Address" part in HookAnalysis.log.
--no-by-library : Disable the "By Library" part in HookAnalysis.log.
--report-json : generate a HookAnalysis.json TODO
--detect-conflict : Detect hook conflict and output to Syringe.log, as it functions in SyringeIH but in English
--show-hook-conflict-popup : Show a popup when --detect-conflict is enabled and a conflict is detected.

In the dry run mode all other 7 flags are available.

@ZivDero
Copy link
Copy Markdown

ZivDero commented May 14, 2026

Can you elaborate on what this does?
--detect-conflict seems straghtforward and useful for cross-library problems.
But it's unclear what any of the rest is.

@IronHammer-Std
Copy link
Copy Markdown
Author

OK let me explain the rest flags.
the design simply copies SyringeIH and most of the design reserved.

Dry Run Mode --dryrun

The dry-run mode is a new one.
It came from a request of Kerbiter which simply run the analysis without starting the game
It collects hook data from DLLs, performs selected analyses, then output results and exit.

Generate INJ File --generate-inj

This calls GenerateINJ() to export all recognized hooks into standard INJ files (like ares.dll.inj) under the .\INJ folder.
Maybe useful for backing up hook configurations or migrating to other tools that read the INJ format.

Hook Analysis Report --report-log/--no-by-address/--no-by-library

Produces a HookAnalysis.log file (in English, equivalent to SyringeIH's HookAnalysis.log). By default, it includes both "By Address" and "By Library" sections. (See Phobos PR #2201, TaranDahl's SKILL.md depend on this log) You can also use --no-by-address and --no-by-library to selectively disable either section.

Hook Analysis in NDJSON ( TODO ) --report-json

Reserved flag for generating a HookAnalysis.json in the future NDJSON format requested by Kerbiter. Implementation is pending the integration of a JSON library.

--detect-conflict/--show-hook-conflict-popup

When --detect-conflict is active, this flag also shows a message box warning the user about any detected conflicts when enabled.

Comment thread HookAnalyzer.cpp
Comment on lines +24 to +64
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;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to write this in a separate log other than syringe.log?

Comment thread HookAnalyzer.cpp
Comment on lines +119 to +147
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 == 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);
}
}
return true;
}
return false;
} No newline at end of file
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the INJ generation for? is there some other tool that uses INJ files, or what's the premise for it?

Comment thread SyringeDebugger.h
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";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it a slow operation? is there a reason to make it optional?

I would expect the conflict detection to be always on and outputting to syringe.log

Comment thread HookAnalyzer.cpp
Comment on lines +24 to +64
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;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this output all hooks or only conflicts? I would expect only conflicts to be outputted, and I feel like they should always be sorted by address? not sure how the by DLL one functions, since the conflicts can (and will) be cross-dll. do we even need any sorting modes?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK let me explain :

Sorry to say that I did not redesign the hook analysis module so there might be something that didnt meet your expectations.
If you think these changes are needed, I will make a new commit for it.
Another thing is that what JSON library should we import ?
the NDJSON format output won't be there until a JSON library is chosen.

Details :

1.

why do we need to write this in a separate log other than syringe.log?

of course we can if you think this is necessary. If you want a why, it is just as it was.
If you think it should be in Syringe.log, we can change the target.

2.

what's the INJ generation for? is there some other tool that uses INJ files, or what's the premise for it?
For there was a INJ generation function.
If you think this is meaningless, a removal will be considered.

3.

is it a slow operation? is there a reason to make it optional?

I would expect the conflict detection to be always on and outputting to syringe.log

I'm sorry to say that again but it is optional because it was optional.
It's OK to let it enabled all the time.

4.

does this output all hooks or only conflicts? I would expect only conflicts to be outputted, and I feel like they should always be sorted by address? not sure how the by DLL one functions, since the conflicts can (and will) be cross-dll. do we even need any sorting modes?

this ReportLOG function is not the thing that detects conflicts.
there should be flags to control the range that ReportLOG outputs, however it isn't here.
" by DLL "just output these hooks DLL after DLL.
Sorting modes are not currently configurable.

Copy link
Copy Markdown
Author

@IronHammer-Std IronHammer-Std May 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

about --report-log :
Another thing is that TaranDahl's SKILL.md still depends on the HookAnalysis.log now and we need the adjusted logic functions similarly when rewritting it.
It enumerates hooks in the configured range. (still cannot configure it in this PR)
(an output format or output target change is OK)

about --detect-conflict :
Now the result is written in Syringe.log.
also you can change it's format and target.
It detects all cross-dll hook conflicts.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to be sorry, it's alright.

On 1 and 3 let's do it then (though I wonder if having it always on will impact startup time a lot, have you checked it?);

on 2: I mean I don't mind it in principle, I just wonder what's the specific use case?

on 4, sorry, I didn't understand what you mean. I was asking about the ByAddr and ByLib function, and I was also wondering whether the output is only conflicts or something else too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants