feat: add iSphere live probe recorder
This commit is contained in:
332
native/ISphereWinHelper/RuntimeProbe.cs
Normal file
332
native/ISphereWinHelper/RuntimeProbe.cs
Normal file
@@ -0,0 +1,332 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ISphereWinHelper
|
||||
{
|
||||
internal static class RuntimeProbe
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process);
|
||||
|
||||
public static Dictionary<string, object> ProbeClientRuntime(Dictionary<string, object> args)
|
||||
{
|
||||
var processNames = GetStringList(args, "process_names", new[] { "IMPlatformClient", "IMPP.ISphere", "iSphere", "importal" });
|
||||
var moduleHints = GetStringList(args, "module_hints", new[]
|
||||
{
|
||||
"IMPlatformClient.exe",
|
||||
"smack.dll",
|
||||
"IMPP.Interface.dll",
|
||||
"IMPP.ServiceBase.dll",
|
||||
"IMPP.Service.dll",
|
||||
"IMPP.Common.dll",
|
||||
"IMPP.Helper.dll",
|
||||
"IMPP.Model.dll",
|
||||
"IMPP.Util.dll",
|
||||
"IMPP.UI.dll",
|
||||
"TcpFileTransfer.dll",
|
||||
"HYHC.IMPP.DAL.dll",
|
||||
"INetwork.dll",
|
||||
"IOClientNetwork.dll"
|
||||
});
|
||||
bool includeModules = HelperProtocol.GetBool(args, "include_modules", true);
|
||||
int maxModules = HelperProtocol.GetInt(args, "max_modules", 120);
|
||||
if (maxModules < 0)
|
||||
{
|
||||
maxModules = 0;
|
||||
}
|
||||
|
||||
var targets = new List<Dictionary<string, object>>();
|
||||
foreach (Process process in Process.GetProcesses())
|
||||
{
|
||||
using (process)
|
||||
{
|
||||
string processName = SafeProcessName(process);
|
||||
if (!MatchesAny(processName, processNames))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
targets.Add(DescribeProcess(process, moduleHints, includeModules, maxModules));
|
||||
}
|
||||
}
|
||||
|
||||
targets.Sort(delegate(Dictionary<string, object> left, Dictionary<string, object> right)
|
||||
{
|
||||
int leftScore = Convert.ToInt32(left["score"]);
|
||||
int rightScore = Convert.ToInt32(right["score"]);
|
||||
int scoreCompare = rightScore.CompareTo(leftScore);
|
||||
if (scoreCompare != 0)
|
||||
{
|
||||
return scoreCompare;
|
||||
}
|
||||
return Convert.ToInt32(left["pid"]).CompareTo(Convert.ToInt32(right["pid"]));
|
||||
});
|
||||
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
{ "probe_mode", "read_only_process_module_inventory" },
|
||||
{ "host", new Dictionary<string, object>
|
||||
{
|
||||
{ "is_64_bit_os", Environment.Is64BitOperatingSystem },
|
||||
{ "is_64_bit_process", Environment.Is64BitProcess },
|
||||
{ "machine_name", Environment.MachineName }
|
||||
}
|
||||
},
|
||||
{ "process_names", processNames },
|
||||
{ "module_hints", moduleHints },
|
||||
{ "target_count", targets.Count },
|
||||
{ "targets", targets }
|
||||
};
|
||||
}
|
||||
|
||||
private static Dictionary<string, object> DescribeProcess(Process process, List<string> moduleHints, bool includeModules, int maxModules)
|
||||
{
|
||||
string executablePath = "";
|
||||
string mainModuleName = "";
|
||||
string mainModuleVersion = "";
|
||||
string mainModuleError = "";
|
||||
|
||||
try
|
||||
{
|
||||
ProcessModule mainModule = process.MainModule;
|
||||
if (mainModule != null)
|
||||
{
|
||||
executablePath = mainModule.FileName ?? "";
|
||||
mainModuleName = mainModule.ModuleName ?? "";
|
||||
FileVersionInfo versionInfo = mainModule.FileVersionInfo;
|
||||
if (versionInfo != null)
|
||||
{
|
||||
mainModuleVersion = versionInfo.FileVersion ?? "";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
mainModuleError = ex.GetType().Name + ": " + ex.Message;
|
||||
}
|
||||
|
||||
bool? isWow64 = GetIsWow64(process);
|
||||
string bitness = "unknown";
|
||||
if (Environment.Is64BitOperatingSystem && isWow64.HasValue)
|
||||
{
|
||||
bitness = isWow64.Value ? "x86" : "x64";
|
||||
}
|
||||
else if (!Environment.Is64BitOperatingSystem)
|
||||
{
|
||||
bitness = "x86";
|
||||
}
|
||||
|
||||
string moduleProbeError = "";
|
||||
var modules = new List<Dictionary<string, object>>();
|
||||
var assemblyPresence = NewPresenceMap(moduleHints);
|
||||
if (includeModules)
|
||||
{
|
||||
try
|
||||
{
|
||||
int count = 0;
|
||||
foreach (ProcessModule module in process.Modules)
|
||||
{
|
||||
if (count >= maxModules)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
string moduleName = module.ModuleName ?? "";
|
||||
string modulePath = module.FileName ?? "";
|
||||
string matchedHint = FindMatchedHint(moduleName, modulePath, moduleHints);
|
||||
if (!string.IsNullOrEmpty(matchedHint))
|
||||
{
|
||||
assemblyPresence[matchedHint] = true;
|
||||
}
|
||||
|
||||
modules.Add(new Dictionary<string, object>
|
||||
{
|
||||
{ "name", moduleName },
|
||||
{ "path", modulePath },
|
||||
{ "matched_hint", matchedHint }
|
||||
});
|
||||
count++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
moduleProbeError = ex.GetType().Name + ": " + ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(mainModuleName))
|
||||
{
|
||||
string matchedMain = FindMatchedHint(mainModuleName, executablePath, moduleHints);
|
||||
if (!string.IsNullOrEmpty(matchedMain))
|
||||
{
|
||||
assemblyPresence[matchedMain] = true;
|
||||
}
|
||||
}
|
||||
|
||||
int score = Score(process.ProcessName, executablePath, assemblyPresence);
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
{ "pid", process.Id },
|
||||
{ "process_name", process.ProcessName },
|
||||
{ "main_window_title", SafeMainWindowTitle(process) },
|
||||
{ "main_window_handle", WindowScanner.FormatHwnd(process.MainWindowHandle) },
|
||||
{ "has_main_window", process.MainWindowHandle != IntPtr.Zero },
|
||||
{ "executable_path", executablePath },
|
||||
{ "main_module_name", mainModuleName },
|
||||
{ "main_module_version", mainModuleVersion },
|
||||
{ "main_module_error", mainModuleError },
|
||||
{ "bitness", bitness },
|
||||
{ "is_wow64", isWow64.HasValue ? (object)isWow64.Value : null },
|
||||
{ "module_probe_ok", string.IsNullOrEmpty(moduleProbeError) },
|
||||
{ "module_probe_error", moduleProbeError },
|
||||
{ "assembly_presence", assemblyPresence },
|
||||
{ "modules", modules },
|
||||
{ "score", score }
|
||||
};
|
||||
}
|
||||
|
||||
private static Dictionary<string, object> NewPresenceMap(List<string> moduleHints)
|
||||
{
|
||||
var presence = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (string hint in moduleHints)
|
||||
{
|
||||
if (!presence.ContainsKey(hint))
|
||||
{
|
||||
presence.Add(hint, false);
|
||||
}
|
||||
}
|
||||
return presence;
|
||||
}
|
||||
|
||||
private static int Score(string processName, string executablePath, Dictionary<string, object> assemblyPresence)
|
||||
{
|
||||
int score = 0;
|
||||
string combined = ((processName ?? "") + " " + (executablePath ?? "")).ToLowerInvariant();
|
||||
if (combined.Contains("implatformclient")) score += 80;
|
||||
if (combined.Contains("isphere")) score += 40;
|
||||
if (combined.Contains("impp")) score += 20;
|
||||
if (combined.Contains("importal")) score += 10;
|
||||
foreach (object value in assemblyPresence.Values)
|
||||
{
|
||||
if (value is bool && (bool)value)
|
||||
{
|
||||
score += 5;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
private static bool? GetIsWow64(Process process)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isWow64;
|
||||
if (IsWow64Process(process.Handle, out isWow64))
|
||||
{
|
||||
return isWow64;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string SafeProcessName(Process process)
|
||||
{
|
||||
try
|
||||
{
|
||||
return process.ProcessName ?? "";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static string SafeMainWindowTitle(Process process)
|
||||
{
|
||||
try
|
||||
{
|
||||
return process.MainWindowTitle ?? "";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static bool MatchesAny(string processName, List<string> candidates)
|
||||
{
|
||||
foreach (string candidate in candidates)
|
||||
{
|
||||
if (string.Equals(processName, candidate, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string FindMatchedHint(string moduleName, string modulePath, List<string> hints)
|
||||
{
|
||||
foreach (string hint in hints)
|
||||
{
|
||||
if (string.Equals(moduleName, hint, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return hint;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(modulePath) &&
|
||||
modulePath.EndsWith("\\" + hint, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return hint;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static List<string> GetStringList(Dictionary<string, object> source, string key, string[] defaults)
|
||||
{
|
||||
var values = new List<string>();
|
||||
object raw;
|
||||
if (source != null && source.TryGetValue(key, out raw) && raw != null)
|
||||
{
|
||||
object[] rawArray = raw as object[];
|
||||
if (rawArray != null)
|
||||
{
|
||||
foreach (object item in rawArray)
|
||||
{
|
||||
string value = Convert.ToString(item);
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
values.Add(value.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string single = Convert.ToString(raw);
|
||||
if (!string.IsNullOrWhiteSpace(single))
|
||||
{
|
||||
foreach (string part in single.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
string value = part.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
values.Add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (values.Count == 0)
|
||||
{
|
||||
values.AddRange(defaults);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user