CLI tool for running Playbooks
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

486 lines
22 KiB

1 year ago
10 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
10 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
6 months ago
6 months ago
1 year ago
6 months ago
1 year ago
10 months ago
6 months ago
10 months ago
1 year ago
6 months ago
6 months ago
6 months ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Management;
  6. using System.Runtime.InteropServices;
  7. using System.Text.RegularExpressions;
  8. using System.Threading.Tasks;
  9. using TrustedUninstaller.Shared.Tasks;
  10. using YamlDotNet.Serialization;
  11. namespace TrustedUninstaller.Shared.Actions
  12. {
  13. class TaskKillAction : TaskAction, ITaskAction
  14. {
  15. public void RunTaskOnMainThread() { throw new NotImplementedException(); }
  16. [DllImport("kernel32.dll", SetLastError=true)]
  17. [return: MarshalAs(UnmanagedType.Bool)]
  18. static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
  19. [DllImport("kernel32.dll", SetLastError = true)]
  20. private static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess,
  21. bool bInheritHandle, int dwProcessId);
  22. public enum ProcessAccessFlags : uint
  23. {
  24. QueryLimitedInformation = 0x1000
  25. }
  26. [DllImport("kernel32.dll", SetLastError = true)]
  27. [return: MarshalAs(UnmanagedType.Bool)]
  28. static extern bool CloseHandle(IntPtr hObject);
  29. [YamlMember(typeof(string), Alias = "name")]
  30. public string? ProcessName { get; set; }
  31. [YamlMember(typeof(string), Alias = "pathContains")]
  32. public string? PathContains { get; set; }
  33. [YamlMember(typeof(string), Alias = "weight")]
  34. public int ProgressWeight { get; set; } = 2;
  35. public int GetProgressWeight() => ProgressWeight;
  36. private bool InProgress { get; set; }
  37. public void ResetProgress() => InProgress = false;
  38. public int? ProcessID { get; set; }
  39. public string ErrorString()
  40. {
  41. string text = $"TaskKillAction failed to kill processes matching '{ProcessName}'.";
  42. try
  43. {
  44. var processes = GetProcess().Select(process => process.ProcessName).Distinct().ToList();
  45. if (processes.Count > 1)
  46. {
  47. text = $"TaskKillAction failed to kill processes:";
  48. foreach (var process in processes)
  49. {
  50. text += "|NEWLINE|" + process;
  51. }
  52. }
  53. else if (processes.Count == 1) text = $"TaskKillAction failed to kill process {processes[0]}.";
  54. } catch (Exception) { }
  55. return text;
  56. }
  57. public UninstallTaskStatus GetStatus()
  58. {
  59. if (InProgress)
  60. {
  61. return UninstallTaskStatus.InProgress;
  62. }
  63. List<Process> processToTerminate = new List<Process>();
  64. if (ProcessID.HasValue)
  65. {
  66. try { processToTerminate.Add(Process.GetProcessById((int)ProcessID)); } catch (Exception) { }
  67. }
  68. else
  69. {
  70. processToTerminate = GetProcess().ToList();
  71. }
  72. return processToTerminate.Any() ? UninstallTaskStatus.ToDo : UninstallTaskStatus.Completed;
  73. }
  74. private List<Process> GetProcess()
  75. {
  76. if (ProcessID.HasValue)
  77. {
  78. var list = new List<Process>();
  79. try
  80. {
  81. var process = Process.GetProcessById(ProcessID.Value);
  82. if (ProcessName == null || process.ProcessName.Equals(ProcessName, StringComparison.OrdinalIgnoreCase))
  83. list.Add(process);
  84. else
  85. return list;
  86. }
  87. catch (Exception e)
  88. {
  89. return list;
  90. }
  91. }
  92. if (ProcessName == null)
  93. {
  94. return new List<Process>();
  95. }
  96. if (ProcessName.EndsWith("*") && ProcessName.StartsWith("*")) return Process.GetProcesses().ToList()
  97. .Where(process => process.ProcessName.IndexOf(ProcessName.Trim('*'), StringComparison.CurrentCultureIgnoreCase) >= 0).ToList();
  98. if (ProcessName.EndsWith("*")) return Process.GetProcesses()
  99. .Where(process => process.ProcessName.StartsWith(ProcessName.TrimEnd('*'), StringComparison.CurrentCultureIgnoreCase)).ToList();
  100. if (ProcessName.StartsWith("*")) return Process.GetProcesses()
  101. .Where(process => process.ProcessName.EndsWith(ProcessName.TrimStart('*'), StringComparison.CurrentCultureIgnoreCase)).ToList();
  102. return Process.GetProcessesByName(ProcessName).ToList();
  103. }
  104. [DllImport("kernel32.dll", SetLastError=true)]
  105. static extern bool IsProcessCritical(IntPtr hProcess, ref bool Critical);
  106. private readonly string[] RegexNoKill = { "lsass", "csrss", "winlogon", "TrustedUninstaller\\.CLI", "dwm", "conhost", "ame.?wizard", "ame.?assassin" };
  107. // These processes give access denied errors when getting their handle for IsProcessCritical.
  108. // TODO: Investigate how to properly acquire permissions.
  109. private readonly string[] RegexNotCritical = { "SecurityHealthService", "wscsvc", "MsMpEng", "SgrmBroker" };
  110. public async Task<bool> RunTask()
  111. {
  112. InProgress = true;
  113. if (string.IsNullOrEmpty(ProcessName) && ProcessID.HasValue)
  114. {
  115. Console.WriteLine($"Killing process with PID '{ProcessID.Value}'...");
  116. }
  117. else
  118. {
  119. if (ProcessName != null && RegexNoKill.Any(regex => Regex.Match(ProcessName, regex, RegexOptions.IgnoreCase).Success))
  120. {
  121. Console.WriteLine($"Skipping {ProcessName}...");
  122. return false;
  123. }
  124. Console.WriteLine($"Killing processes matching '{ProcessName}'...");
  125. }
  126. var cmdAction = new CmdAction();
  127. if (ProcessName != null)
  128. {
  129. //If the service is svchost, we stop the service instead of killing it.
  130. if (ProcessName.Equals("svchost", StringComparison.OrdinalIgnoreCase))
  131. {
  132. try
  133. {
  134. if (ProcessID.HasValue)
  135. {
  136. foreach (var serviceName in Win32.ServiceEx.GetServicesFromProcessId(ProcessID.Value))
  137. {
  138. try
  139. {
  140. var stopServ = new ServiceAction()
  141. {
  142. ServiceName = serviceName,
  143. Operation = ServiceOperation.Stop
  144. };
  145. await stopServ.RunTask();
  146. }
  147. catch (Exception e)
  148. {
  149. Console.WriteLine($"Could not kill service " + serviceName + ": " + e.Message);
  150. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "Could not kill service " + serviceName + ": " + e.Message);
  151. }
  152. }
  153. }
  154. else
  155. {
  156. foreach (var process in GetProcess())
  157. {
  158. foreach (var serviceName in Win32.ServiceEx.GetServicesFromProcessId(process.Id))
  159. {
  160. try
  161. {
  162. var stopServ = new ServiceAction()
  163. {
  164. ServiceName = serviceName,
  165. Operation = ServiceOperation.Stop
  166. };
  167. await stopServ.RunTask();
  168. }
  169. catch (Exception e)
  170. {
  171. Console.WriteLine($"Could not kill service " + serviceName + ": " + e.Message);
  172. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "Could not kill service " + serviceName + ": " + e.Message);
  173. }
  174. }
  175. }
  176. }
  177. }
  178. catch (NullReferenceException e)
  179. {
  180. Console.WriteLine($"A service with PID: {ProcessID.Value} could not be found.");
  181. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, $"Could not find service with PID {ProcessID.Value}.");
  182. }
  183. int i;
  184. for (i = 0; i <= 6 && GetProcess().Any(); i++)
  185. {
  186. await Task.Delay(100 * i);
  187. }
  188. if (i < 6)
  189. {
  190. InProgress = false;
  191. return true;
  192. }
  193. }
  194. if (PathContains != null && !ProcessID.HasValue)
  195. {
  196. var processes = GetProcess();
  197. if (processes.Count > 0) Console.WriteLine("Processes:");
  198. foreach (var process in processes.Where(x =>
  199. {
  200. try
  201. {
  202. return x.MainModule.FileName.Contains(PathContains);
  203. }
  204. catch (Exception e)
  205. {
  206. return false;
  207. }
  208. }))
  209. {
  210. Console.WriteLine(process.ProcessName + " - " + process.Id);
  211. if (!RegexNotCritical.Any(x => Regex.Match(process.ProcessName, x, RegexOptions.IgnoreCase).Success))
  212. {
  213. bool isCritical = false;
  214. IntPtr hprocess = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, process.Id);
  215. IsProcessCritical(hprocess, ref isCritical);
  216. CloseHandle(hprocess);
  217. if (isCritical)
  218. {
  219. Console.WriteLine($"{process.ProcessName} is a critical process, skipping...");
  220. continue;
  221. }
  222. }
  223. try
  224. {
  225. if (!TerminateProcess(process.Handle, 1))
  226. ErrorLogger.WriteToErrorLog("TerminateProcess failed with error code: " + Marshal.GetLastWin32Error(), Environment.StackTrace, "TaskKill Error", ProcessName);
  227. }
  228. catch (Exception e)
  229. {
  230. ErrorLogger.WriteToErrorLog("Could not open process handle: " + e.Message, e.StackTrace, "TaskKillAction Error", process.ProcessName);
  231. }
  232. try
  233. {
  234. process.WaitForExit(1000);
  235. }
  236. catch (Exception e)
  237. {
  238. ErrorLogger.WriteToErrorLog("Error waiting for process exit: " + e.Message, e.StackTrace, "TaskKillAction Error", process.ProcessName);
  239. }
  240. if (process.ProcessName == "explorer") continue;
  241. cmdAction.Command = Environment.Is64BitOperatingSystem ?
  242. $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.Id} -caction terminate" :
  243. $"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.Id} -caction terminate";
  244. if (AmeliorationUtil.UseKernelDriver && process.ProcessName != "explorer") cmdAction.RunTaskOnMainThread();
  245. int i = 0;
  246. while (i <= 3 && GetProcess().Any(x => x.Id == process.Id && x.ProcessName == process.ProcessName))
  247. {
  248. try
  249. {
  250. try
  251. {
  252. if (AmeliorationUtil.UseKernelDriver)
  253. cmdAction.RunTaskOnMainThread();
  254. else
  255. TerminateProcess(process.Handle, 1);
  256. }
  257. catch (Exception e) { }
  258. process.WaitForExit(500);
  259. }
  260. catch (Exception e) { }
  261. await Task.Delay(100);
  262. i++;
  263. }
  264. if (i >= 3) ErrorLogger.WriteToErrorLog($"Task kill timeout exceeded.", Environment.StackTrace, "TaskKillAction Error");
  265. }
  266. InProgress = false;
  267. return true;
  268. }
  269. }
  270. if (ProcessID.HasValue)
  271. {
  272. var process = Process.GetProcessById(ProcessID.Value);
  273. if (ProcessName != null && ProcessName.Equals("explorer", StringComparison.OrdinalIgnoreCase))
  274. {
  275. try {
  276. if (!TerminateProcess(process.Handle, 1))
  277. ErrorLogger.WriteToErrorLog("TerminateProcess failed with error code: " + Marshal.GetLastWin32Error(), Environment.StackTrace, "TaskKill Error", ProcessName);
  278. try
  279. {
  280. process.WaitForExit(1000);
  281. }
  282. catch (Exception e)
  283. {
  284. ErrorLogger.WriteToErrorLog("Error waiting for process exit: " + e.Message, e.StackTrace, "TaskKillAction Error", process.ProcessName);
  285. }
  286. }
  287. catch (Exception e)
  288. {
  289. ErrorLogger.WriteToErrorLog("Could not open process handle: " + e.Message, e.StackTrace, "TaskKillAction Error", process.ProcessName);
  290. }
  291. }
  292. else
  293. {
  294. if (!RegexNotCritical.Any(x => Regex.Match(process.ProcessName, x, RegexOptions.IgnoreCase).Success))
  295. {
  296. bool isCritical = false;
  297. try
  298. {
  299. IntPtr hprocess = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, process.Id);
  300. IsProcessCritical(hprocess, ref isCritical);
  301. CloseHandle(hprocess);
  302. }
  303. catch (InvalidOperationException e)
  304. {
  305. ErrorLogger.WriteToErrorLog("Could not check if process is critical.", e.StackTrace, "TaskKillAction Error", process.ProcessName);
  306. return false;
  307. }
  308. if (isCritical)
  309. {
  310. Console.WriteLine($"{process.ProcessName} is a critical process, skipping...");
  311. return false;
  312. }
  313. }
  314. try
  315. {
  316. if (!TerminateProcess(process.Handle, 1))
  317. ErrorLogger.WriteToErrorLog("TerminateProcess failed with error code: " + Marshal.GetLastWin32Error(), Environment.StackTrace, "TaskKill Error", ProcessName);
  318. }
  319. catch (Exception e)
  320. {
  321. ErrorLogger.WriteToErrorLog("Could not open process handle: " + e.Message, e.StackTrace, "TaskKillAction Error", process.ProcessName);
  322. }
  323. try
  324. {
  325. process.WaitForExit(1000);
  326. }
  327. catch (Exception e)
  328. {
  329. ErrorLogger.WriteToErrorLog("Error waiting for process exit: " + e.Message, e.StackTrace, "TaskKillAction Error", process.ProcessName);
  330. }
  331. cmdAction.Command = Environment.Is64BitOperatingSystem ?
  332. $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {ProcessID.Value} -caction terminate" :
  333. $"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {ProcessID.Value} -caction terminate";
  334. if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
  335. }
  336. int i = 0;
  337. while (i <= 3 && GetProcess().Any(x => x.Id == process.Id && x.ProcessName == process.ProcessName))
  338. {
  339. try
  340. {
  341. try
  342. {
  343. if (AmeliorationUtil.UseKernelDriver)
  344. cmdAction.RunTaskOnMainThread();
  345. else
  346. TerminateProcess(process.Handle, 1);
  347. }
  348. catch (Exception e)
  349. {
  350. }
  351. process.WaitForExit(500);
  352. }
  353. catch (Exception e)
  354. {
  355. }
  356. await Task.Delay(100);
  357. i++;
  358. }
  359. if (i >= 3) ErrorLogger.WriteToErrorLog($"Task kill timeout exceeded.", Environment.StackTrace, "TaskKillAction Error");
  360. }
  361. else
  362. {
  363. var processes = GetProcess();
  364. if (processes.Count > 0) Console.WriteLine("Processes:");
  365. foreach (var process in processes)
  366. {
  367. Console.WriteLine(process.ProcessName + " - " + process.Id);
  368. if (!RegexNotCritical.Any(x => Regex.Match(process.ProcessName, x, RegexOptions.IgnoreCase).Success))
  369. {
  370. bool isCritical = false;
  371. try
  372. {
  373. IntPtr hprocess = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, process.Id);
  374. IsProcessCritical(hprocess, ref isCritical);
  375. CloseHandle(hprocess);
  376. }
  377. catch (InvalidOperationException e)
  378. {
  379. ErrorLogger.WriteToErrorLog("Could not check if process is critical.", e.StackTrace, "TaskKillAction Error", process.ProcessName);
  380. continue;
  381. }
  382. if (isCritical)
  383. {
  384. Console.WriteLine($"{process.ProcessName} is a critical process, skipping...");
  385. continue;
  386. }
  387. }
  388. try
  389. {
  390. if (!TerminateProcess(process.Handle, 1))
  391. ErrorLogger.WriteToErrorLog("TerminateProcess failed with error code: " + Marshal.GetLastWin32Error(), Environment.StackTrace, "TaskKill Error", ProcessName);
  392. }
  393. catch (Exception e)
  394. {
  395. ErrorLogger.WriteToErrorLog("Could not open process handle: " + e.Message, e.StackTrace, "TaskKillAction Error", process.ProcessName);
  396. }
  397. try
  398. {
  399. process.WaitForExit(1000);
  400. }
  401. catch (Exception e)
  402. {
  403. ErrorLogger.WriteToErrorLog("Error waiting for process exit: " + e.Message, e.StackTrace, "TaskKillAction Error", process.ProcessName);
  404. }
  405. if (process.ProcessName == "explorer") continue;
  406. cmdAction.Command = Environment.Is64BitOperatingSystem ?
  407. $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.Id} -caction terminate" :
  408. $"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.Id} -caction terminate";
  409. if (AmeliorationUtil.UseKernelDriver && process.ProcessName != "explorer") cmdAction.RunTaskOnMainThread();
  410. int i = 0;
  411. while (i <= 3 && GetProcess().Any(x => x.Id == process.Id && x.ProcessName == process.ProcessName))
  412. {
  413. try
  414. {
  415. try
  416. {
  417. if (AmeliorationUtil.UseKernelDriver)
  418. cmdAction.RunTaskOnMainThread();
  419. else
  420. TerminateProcess(process.Handle, 1);
  421. }
  422. catch (Exception e)
  423. {
  424. }
  425. process.WaitForExit(500);
  426. }
  427. catch (Exception e)
  428. {
  429. }
  430. await Task.Delay(100);
  431. i++;
  432. }
  433. if (i >= 3) ErrorLogger.WriteToErrorLog($"Task kill timeout exceeded.", Environment.StackTrace, "TaskKillAction Error");
  434. }
  435. }
  436. InProgress = false;
  437. return true;
  438. }
  439. }
  440. }