From d8e47230e094a506406a83eb78916abf2668b29c Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 17:12:34 -0700 Subject: Move Integration into test --- src/test/burn/TestExe/NetfxTask.cs | 295 +++++++++++++++++++++++++++++++ src/test/burn/TestExe/Program.cs | 74 ++++++++ src/test/burn/TestExe/Task.cs | 209 ++++++++++++++++++++++ src/test/burn/TestExe/TestExe.csproj | 20 +++ src/test/burn/TestExe/TestExe_x64.csproj | 17 ++ src/test/burn/TestExe/app.config | 10 ++ 6 files changed, 625 insertions(+) create mode 100644 src/test/burn/TestExe/NetfxTask.cs create mode 100644 src/test/burn/TestExe/Program.cs create mode 100644 src/test/burn/TestExe/Task.cs create mode 100644 src/test/burn/TestExe/TestExe.csproj create mode 100644 src/test/burn/TestExe/TestExe_x64.csproj create mode 100644 src/test/burn/TestExe/app.config (limited to 'src/test/burn/TestExe') diff --git a/src/test/burn/TestExe/NetfxTask.cs b/src/test/burn/TestExe/NetfxTask.cs new file mode 100644 index 00000000..35b1ea95 --- /dev/null +++ b/src/test/burn/TestExe/NetfxTask.cs @@ -0,0 +1,295 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#if NET35 +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Management; +using Microsoft.Win32; + +namespace TestExe +{ + public class ProcessInfoTask : Task + { + public ProcessInfoTask(string Data) : base(Data) { } + + public override void RunTask() + { + try + { + string processInfoXml = ""; + + // Get information about the process and who is running it + Process thisProc = Process.GetCurrentProcess(); + string username = thisProc.StartInfo.EnvironmentVariables["username"].ToString(); + + int parentProcId = GetParentProcess(thisProc.Id); + Process parentProc = Process.GetProcessById(parentProcId); + string parentUsername = parentProc.StartInfo.EnvironmentVariables["username"].ToString(); + + int grandparentProcId = GetParentProcess(parentProc.Id); + Process grandparentProc = Process.GetProcessById(grandparentProcId); + string grandparentUsername = grandparentProc.StartInfo.EnvironmentVariables["username"].ToString(); + + processInfoXml += ""; + processInfoXml += " " + thisProc.ProcessName + ""; + processInfoXml += " " + thisProc.Id.ToString() + ""; + processInfoXml += " " + thisProc.SessionId.ToString() + ""; + processInfoXml += " " + thisProc.MachineName + ""; + // this stuff isn't set since we didn't start the process and tell it what to use. So don't bother + //processInfoXml += " "; + //processInfoXml += " " + thisProc.StartInfo.FileName + ""; + //processInfoXml += " " + thisProc.StartInfo.UserName + ""; + //processInfoXml += " " + thisProc.StartInfo.WorkingDirectory + ""; + //processInfoXml += " " + thisProc.StartInfo.Arguments + ""; + //processInfoXml += " "; + processInfoXml += " " + thisProc.StartTime.ToString() + ""; + processInfoXml += " " + username + ""; + processInfoXml += " "; + processInfoXml += " " + parentProc.ProcessName + ""; + processInfoXml += " " + parentProc.Id.ToString() + ""; + processInfoXml += " " + parentProc.StartTime.ToString() + ""; + processInfoXml += " " + parentUsername + ""; + processInfoXml += " "; + processInfoXml += " "; + processInfoXml += " " + grandparentProc.ProcessName + ""; + processInfoXml += " " + grandparentProc.Id.ToString() + ""; + processInfoXml += " " + grandparentProc.StartTime.ToString() + ""; + processInfoXml += " " + grandparentUsername + ""; + processInfoXml += " "; + processInfoXml += ""; + + string logFile = System.Environment.ExpandEnvironmentVariables(this.data); + Console.WriteLine("Creating Process Info data file: " + logFile); + StreamWriter textFile = File.CreateText(logFile); + textFile.WriteLine(processInfoXml); + textFile.Close(); + } + catch (Exception eX) + { + Console.WriteLine("Creating Process Info data file failed"); + Console.WriteLine(eX.Message); + } + + + } + + private static int GetParentProcess(int Id) + { + int parentPid = 0; + using (ManagementObject mo = new ManagementObject("win32_process.handle='" + Id.ToString() + "'")) + { + mo.Get(); + parentPid = Convert.ToInt32(mo["ParentProcessId"]); + } + return parentPid; + } + } + + /// + /// Task class that will create a registry key and write a name and value in it + /// + public class RegistryWriterTask : Task + { + private string hive; + private string keyPath; + private string[] keyPathArray; + private string name; + private RegistryValueKind regValueKind; + private object value; + + public RegistryWriterTask(string Data) : base(Data) { } + + public override void RunTask() + { + if (this.parseRegKeyNameTypeValue(System.Environment.ExpandEnvironmentVariables(this.data))) + { + RegistryKey rk = Registry.LocalMachine; + + if (this.hive == "HKCU") { rk = Microsoft.Win32.Registry.CurrentUser; } + if (this.hive == "HKCC") { rk = Microsoft.Win32.Registry.CurrentConfig; } + if (this.hive == "HKLM") { rk = Microsoft.Win32.Registry.LocalMachine; } + + foreach (string key in this.keyPathArray) + { + rk = rk.CreateSubKey(key, RegistryKeyPermissionCheck.ReadWriteSubTree); + } + + rk.SetValue(this.name, this.value, this.regValueKind); + Console.WriteLine("Created registry key: '{0}' name: '{1}' value: '{2}' of type: '{3}'", + this.hive + "\\" + this.keyPath, + this.name, + this.value.ToString(), + this.regValueKind.ToString()); + } + else + { + Console.WriteLine("Unable to write registry key."); + } + + } + + private bool parseRegKeyNameTypeValue(string delimittedData) + { + string[] splitString = delimittedData.Split(new string[] { "," }, StringSplitOptions.None); + if (splitString.Length != 4) + { + Console.WriteLine("Invalid regkey. Unable to parse key,name,type,value from: \"" + delimittedData + "\""); + return false; + } + else + { + this.keyPath = splitString[0]; + this.name = splitString[1]; + string datatype = splitString[2]; + if (datatype == "DWord") + { + this.value = UInt32.Parse(splitString[3]); + } + else if (datatype == "QWord") + { + this.value = UInt64.Parse(splitString[3]); + } + else + { + this.value = splitString[3]; + } + + if (this.keyPath.ToUpper().StartsWith("HKLM\\")) + { + this.hive = "HKLM"; + this.keyPath = this.keyPath.Replace("HKLM\\", ""); + } + else if (this.keyPath.ToUpper().StartsWith("HKCC\\")) + { + this.hive = "HKCC"; + this.keyPath = this.keyPath.Replace("HKCC\\", ""); + } + else if (this.keyPath.ToUpper().StartsWith("HKCU\\")) + { + this.hive = "HKCU"; + this.keyPath = this.keyPath.Replace("HKCU\\", ""); + } + else + { + Console.WriteLine("Invalid regkey. Unable to determin hive. regkey must start with either: [HKLM], [HKCU], or [HKCC]"); + return false; + } + this.keyPathArray = this.keyPath.Split(new string[] { "\\" }, StringSplitOptions.None); + + try + { + this.regValueKind = (RegistryValueKind)System.Enum.Parse(typeof(RegistryValueKind), datatype); + } + catch (Exception ex) + { + Console.WriteLine("Invalid datatype. It must be: String, DWord, or QWord (case sensitive)"); + Console.WriteLine(ex.Message); + return false; + } + } + return true; + } + } + + /// + /// Task class that will delete a registry key value or registry key and all of its children + /// + public class RegistryDeleterTask : Task + { + private string hive; + private string keyPath; + private string[] keyPathArray; + private string name; + + public RegistryDeleterTask(string Data) : base(Data) { } + + public override void RunTask() + { + if (this.parseRegKeyName(System.Environment.ExpandEnvironmentVariables(this.data))) + { + try + { + RegistryKey rk = Registry.LocalMachine; + + if (this.hive == "HKCU") { rk = Microsoft.Win32.Registry.CurrentUser; } + if (this.hive == "HKCC") { rk = Microsoft.Win32.Registry.CurrentConfig; } + if (this.hive == "HKLM") { rk = Microsoft.Win32.Registry.LocalMachine; } + + RegistryKey rkParent = null; + foreach (string key in this.keyPathArray) + { + rkParent = rk; + rk = rk.OpenSubKey(key, true); + } + + if (String.IsNullOrEmpty(this.name)) + { + // delete the key and all of its children + string subkeyToDelete = this.keyPathArray[this.keyPathArray.Length - 1]; + rkParent.DeleteSubKeyTree(subkeyToDelete); + Console.WriteLine("Deleted registry key: '{0}'", this.hive + "\\" + this.keyPath); + } + else + { + // just delete this value + rk.DeleteValue(this.name); + Console.WriteLine("Deleted registry key: '{0}' name: '{1}'", this.hive + "\\" + this.keyPath, this.name); + } + } + catch (Exception ex) + { + Console.WriteLine("Unable to delete registry key: '{0}'", this.hive + "\\" + this.keyPath); + Console.WriteLine(ex.Message); + } + } + else + { + Console.WriteLine("Unable to delete registry key."); + } + + } + + private bool parseRegKeyName(string delimittedData) + { + string[] splitString = delimittedData.Split(new string[] { "," }, StringSplitOptions.None); + + if (splitString.Length > 2) + { + Console.WriteLine("Unable to parse registry key and name."); + return false; + } + + this.keyPath = splitString[0]; + if (splitString.Length == 2) + { + this.name = splitString[1]; + } + + if (this.keyPath.ToUpper().StartsWith("HKLM\\")) + { + this.hive = "HKLM"; + this.keyPath = this.keyPath.Replace("HKLM\\", ""); + } + else if (this.keyPath.ToUpper().StartsWith("HKCC\\")) + { + this.hive = "HKCC"; + this.keyPath = this.keyPath.Replace("HKCC\\", ""); + } + else if (this.keyPath.ToUpper().StartsWith("HKCU\\")) + { + this.hive = "HKCU"; + this.keyPath = this.keyPath.Replace("HKCU\\", ""); + } + else + { + Console.WriteLine("Invalid regkey. Unable to determine hive. regkey must start with either: [HKLM], [HKCU], or [HKCC]"); + return false; + } + this.keyPathArray = this.keyPath.Split(new string[] { "\\" }, StringSplitOptions.None); + return true; + } + } +} +#endif diff --git a/src/test/burn/TestExe/Program.cs b/src/test/burn/TestExe/Program.cs new file mode 100644 index 00000000..e92c413b --- /dev/null +++ b/src/test/burn/TestExe/Program.cs @@ -0,0 +1,74 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TestExe +{ + class Program + { + static List tasks; + static int exitCodeToReturn = 0; + + static int Main(string[] args) + { + Usage(); + tasks = TaskParser.ParseTasks(args); + + foreach (Task t in tasks) + { + // special case for the ExitCodeTask + if (t.GetType() == typeof(ExitCodeTask)) + { + exitCodeToReturn = int.Parse(t.data); + } + else + { + t.RunTask(); + } + } + + Console.WriteLine("Exiting with ExitCode = {0}", exitCodeToReturn); + return exitCodeToReturn; + } + + static void Usage() + { + Console.WriteLine(@"TestExe.exe"); + Console.WriteLine(@""); + Console.WriteLine(@"TestExe can be passed various switches to define how it will behave and what tasks it will perform."); + Console.WriteLine(@"All switches are optional."); + Console.WriteLine(@"Any # of switches can be combined in any order."); + Console.WriteLine(@"Switches can be specified multiple times."); + Console.WriteLine(@"The order of the switches listed is the order they will be processed."); + Console.WriteLine(@"Info is written to stdout to describe what tasks are being performed as they are executed."); + Console.WriteLine(@""); + Console.WriteLine(@"Usage: TestExe.exe [tasks...]"); + Console.WriteLine(@""); + Console.WriteLine(@""); + Console.WriteLine(@"/ec # Exit code to return. Can only be specified once. If not specified, 0 will be returned. Example: “/ec 3010” would return 3010"); + Console.WriteLine(@"/s # Milliseconds to sleep before continuing. Example: “/s 5000” would sleep 5 seconds."); + Console.WriteLine(@"/sr #-# Random range of Milliseconds to sleep before continuing. Example: “/sr 5000-10000” would sleep between 5-10 seconds."); + Console.WriteLine(@"/log filename Create a log file called filename. Contents of the log are static text. Example: “/log %temp%\test.log” would create a %temp%\test.log file."); + Console.WriteLine(@"/Pinfo filename Create an xml file containing information about the process: PID, start time, user running the process, etc."); + Console.WriteLine(@"/fe filename Wait for a file to exist before continuing. Example: “/fe %temp%\cache\file.msi” would wait until %temp%\cache\file.msi exists."); + Console.WriteLine(@"/regw regkey,name,type,value (Re)writes a registry key with the specified value"); + Console.WriteLine(@"/regd regkey,[name] Deletes registry key name or key and all of its children (subkeys and values)"); + Console.WriteLine(@""); + Console.WriteLine(@"Example: "); + Console.WriteLine(@""); + Console.WriteLine(@"TestExe.exe /ec 1603 /Pinfo %temp%\Pinfo1.xml /s 1000 /log %temp%\log1.log /sr 5000-10000 /log %temp%\log2.log"); + Console.WriteLine(@""); + Console.WriteLine(@"This would result in the following execution:"); + Console.WriteLine(@" - Create an xml file with the current process info in it."); + Console.WriteLine(@" - Sleep 1 seconds"); + Console.WriteLine(@" - Create log1.log"); + Console.WriteLine(@" - Sleep between 5-10 seconds"); + Console.WriteLine(@" - Create log2.log"); + Console.WriteLine(@" - Exit with 1603"); + Console.WriteLine(@""); + } + } +} diff --git a/src/test/burn/TestExe/Task.cs b/src/test/burn/TestExe/Task.cs new file mode 100644 index 00000000..7d39bfd9 --- /dev/null +++ b/src/test/burn/TestExe/Task.cs @@ -0,0 +1,209 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using Microsoft.Win32; + +namespace TestExe +{ + public abstract class Task + { + public string data; + + public Task(string Data) + { + this.data = Data; + } + + public abstract void RunTask(); + + } + + public class ExitCodeTask : Task + { + public ExitCodeTask(string Data) : base(Data) { } + + public override void RunTask() + { + // this task does nothing. Just stores data about what exit code to return. + } + } + + public class SleepTask : Task + { + public SleepTask(string Data) : base(Data) { } + + public override void RunTask() + { + int milliseconds = int.Parse(this.data); + Console.WriteLine("Starting to sleep for {0} milliseconds", milliseconds); + System.Threading.Thread.Sleep(milliseconds); + } + } + + public class SleepRandomTask : Task + { + public SleepRandomTask(string Data) : base(Data) { } + + public override void RunTask() + { + int low = int.Parse(this.data.Split(new string[] { ":" }, 2, StringSplitOptions.None)[0]); + int high = int.Parse(this.data.Split(new string[] { ":" }, 2, StringSplitOptions.None)[1]); + + Random r = new Random(); + int milliseconds = r.Next(high - low) + low; + Console.WriteLine("Starting to sleep for {0} milliseconds", milliseconds); + System.Threading.Thread.Sleep(milliseconds); + } + } + + public class LargeFileTask : Task + { + public LargeFileTask(string Data) : base(Data) { } + + public override void RunTask() + { + string[] tokens = this.data.Split(new char[] { '|' }, 2); + string filePath = System.Environment.ExpandEnvironmentVariables(tokens[0]); + long size = long.Parse(tokens[1]); + using (var stream = File.Create(filePath)) + { + stream.Seek(size - 1, SeekOrigin.Begin); + stream.WriteByte(1); + } + } + } + + public class LogTask : Task + { + string[] argsUsed; + public LogTask(string Data, string[] args) + : base(Data) + { + this.argsUsed = args; + } + + public override void RunTask() + { + string logFile = ""; + string argsUsedString = ""; + + foreach (string a in this.argsUsed) + { + argsUsedString += a + " "; + } + + try + { + logFile = System.Environment.ExpandEnvironmentVariables(this.data); + Console.WriteLine("creating log file: " + logFile); + StreamWriter textFile = File.CreateText(logFile); + textFile.WriteLine("This is a log file created by TestExe.exe"); + textFile.WriteLine("Args used: " + argsUsedString); + textFile.Close(); + } + catch + { + Console.WriteLine("creating a log file failed for: {0}", logFile); + } + + } + } + + public class FileExistsTask : Task + { + public FileExistsTask(string Data) : base(Data) { } + + public override void RunTask() + { + string fileToExist = System.Environment.ExpandEnvironmentVariables(this.data); + + if (!String.IsNullOrEmpty(fileToExist)) + { + Console.WriteLine("Waiting for this file to exist: \"" + fileToExist + "\""); + while (!System.IO.File.Exists(fileToExist)) + { + System.Threading.Thread.Sleep(250); + } + Console.WriteLine("Found: \"" + fileToExist + "\""); + } + + } + } + + public class TaskParser + { + + public static List ParseTasks(string[] args) + { + List tasks = new List(); + + try + { + // for invalid args. return empty list + if (args.Length % 2 == 0) + { + Task t; + + for (int i = 0; i < args.Length; i += 2) + { + switch (args[i].ToLower()) + { + case "/ec": + t = new ExitCodeTask(args[i + 1]); + tasks.Add(t); + break; + case "/s": + t = new SleepTask(args[i + 1]); + tasks.Add(t); + break; + case "/sr": + t = new SleepRandomTask(args[i + 1]); + tasks.Add(t); + break; + case "/lf": + t = new LargeFileTask(args[i + 1]); + tasks.Add(t); + break; + case "/log": + t = new LogTask(args[i + 1], args); + tasks.Add(t); + break; + case "/fe": + t = new FileExistsTask(args[i + 1]); + tasks.Add(t); + break; +#if NET35 + case "/pinfo": + t = new ProcessInfoTask(args[i + 1]); + tasks.Add(t); + break; + case "/regw": + t = new RegistryWriterTask(args[i + 1]); + tasks.Add(t); + break; + case "/regd": + t = new RegistryDeleterTask(args[i + 1]); + tasks.Add(t); + break; +#endif + + default: + Console.WriteLine("Error: Invalid switch specified."); + return new List(); + } + } + } + } + catch + { + Console.WriteLine("Error: Invalid switch data specified. Couldn't parse the data."); + return new List(); + } + + return tasks; + } + } +} diff --git a/src/test/burn/TestExe/TestExe.csproj b/src/test/burn/TestExe/TestExe.csproj new file mode 100644 index 00000000..5a130422 --- /dev/null +++ b/src/test/burn/TestExe/TestExe.csproj @@ -0,0 +1,20 @@ + + + + + + net35;netcoreapp3.1 + TestExe + TestExe + Exe + embedded + win-x86 + false + true + Major + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestExe/TestExe_x64.csproj b/src/test/burn/TestExe/TestExe_x64.csproj new file mode 100644 index 00000000..1dd2d8e6 --- /dev/null +++ b/src/test/burn/TestExe/TestExe_x64.csproj @@ -0,0 +1,17 @@ + + + + + + net35 + TestExe + TestExe + Exe + embedded + win-x64 + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestExe/app.config b/src/test/burn/TestExe/app.config new file mode 100644 index 00000000..f9811b74 --- /dev/null +++ b/src/test/burn/TestExe/app.config @@ -0,0 +1,10 @@ + + + + + + + + + + -- cgit v1.2.3-55-g6feb