using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.IO; using System.Linq; using System.Management; using System.Net.Http; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; using System.Timers; using System.Xml.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace MPELicenseAgent { public partial class Service1 : ServiceBase { private string homeBaseAddress = "apilicenses.mpe.ca"; private string apiKey = "&v94gt8ZHFTTTeuT"; private string[] requestedPrograms = new string[30]; private Timer pollRateTimer; private int pollRate; private bool canPhoneHome = false; public Service1() { InitializeComponent(); } // Function that starts when the service is run protected override async void OnStart(string[] args) { this.WriteToFile("\n*******************************************************\nMPE License Tracker\nMade by: Donavon McDowell\n"); try { await this.getHealth(); if (this.canPhoneHome) { this.WriteToFile(DateTime.Now.ToString() + " Connected to home base " + this.homeBaseAddress); await this.getPollRate(); await this.getRequestedPrograms(); await this.getClientPrograms(); } } catch (Exception ex1) { Exception ex = ex1; } this.pollRateTimer = new Timer(); this.pollRateTimer.Interval = (double)this.pollRate; this.pollRateTimer.Elapsed += new ElapsedEventHandler(this.OnElapsedTime); this.pollRateTimer.Start(); } protected override void OnStop() { if (this.pollRateTimer == null) return; this.pollRateTimer.Stop(); this.pollRateTimer.Dispose(); } // Params: STRING data // Description: Takes a string and writes it to an output / log file // Location: Where the exe file for this service exists public void WriteToFile(string data) { string str1 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"); if (!Directory.Exists(str1)) Directory.CreateDirectory(str1); string str2 = Path.Combine(str1, "ServiceLog.txt"); if (File.Exists(str2) && new FileInfo(str2).Length / 1024L > 100L) { File.Delete(str2); using (StreamWriter text = File.CreateText(str2)) text.WriteLine(data); } else { using (StreamWriter streamWriter = File.AppendText(str2)) streamWriter.WriteLine(data); } } private async void OnElapsedTime(object source, ElapsedEventArgs e) { await this.getHealth(); if (!this.canPhoneHome) return; await this.getRequestedPrograms(); await this.getPollRate(); await this.getClientPrograms(); this.pollRateTimer.Interval = (double)this.pollRate; } // Checks the health of the api, if there are any issues it will wait and probe again in a little bit // This is used so that we don't DDOS the reverse proxy private async Task getHealth() { string baseUrl = "https://" + this.homeBaseAddress; string endpoint = "/api/healthcheck"; HttpMethod method = HttpMethod.Get; HttpClient httpClient = new HttpClient() { Timeout = TimeSpan.FromSeconds(4.0) }; try { HttpResponseMessage response = await httpClient.GetAsync(baseUrl + endpoint); if (response.IsSuccessStatusCode) { string responses = await response.Content.ReadAsStringAsync(); if (!string.IsNullOrEmpty(responses)) { this.canPhoneHome = true; } else { this.canPhoneHome = false; this.WriteToFile(DateTime.Now.ToString() + " Disconnected from home base " + this.homeBaseAddress); } responses = (string)null; } else { this.canPhoneHome = false; this.WriteToFile(DateTime.Now.ToString() + " Disconnected from home base " + this.homeBaseAddress); } response = (HttpResponseMessage)null; baseUrl = (string)null; endpoint = (string)null; method = (HttpMethod)null; httpClient = (HttpClient)null; } catch (TaskCanceledException ex) { this.canPhoneHome = false; this.WriteToFile(DateTime.Now.ToString() + " [Error]: Connection timed out while trying to phone home to " + this.homeBaseAddress); baseUrl = (string)null; endpoint = (string)null; method = (HttpMethod)null; httpClient = (HttpClient)null; } catch (Exception ex) { this.canPhoneHome = false; this.WriteToFile(DateTime.Now.ToString() + " [Error]: There was an error in the getHealth() function. " + ex.Message); baseUrl = (string)null; endpoint = (string)null; method = (HttpMethod)null; httpClient = (HttpClient)null; } } // This talks to the backend and gets all of the programs that we are interested in monitoring private async Task getRequestedPrograms() { string baseUrl = "https://" + this.homeBaseAddress; string endpoint = "/api/programs"; HttpMethod method = HttpMethod.Get; try { string responses = await Service1.ApiClient.hitApi(baseUrl, endpoint, method); string cleanedResponses = responses.Trim('[', ']'); string[] responseArray = cleanedResponses.Split(new string[1] { "\",\"" }, StringSplitOptions.None); responseArray[0] = responseArray[0].Trim('"'); responseArray[responseArray.Length - 1] = responseArray[responseArray.Length - 1].Trim('"'); this.requestedPrograms = responseArray; responses = (string)null; cleanedResponses = (string)null; responseArray = (string[])null; baseUrl = (string)null; endpoint = (string)null; method = (HttpMethod)null; } catch (Exception ex) { this.WriteToFile(DateTime.Now.ToString() + " [Error]: There was an error in the getRequestedPrograms() function. " + ex?.ToString()); baseUrl = (string)null; endpoint = (string)null; method = (HttpMethod)null; } } // This looks at the computer itself and determines any applications that match our requested programs private async Task getClientPrograms() { Process[] processes = Process.GetProcesses(); string[] matchingProcesses = processes .Select(p => p.ProcessName) .Where(pn => this.requestedPrograms.Contains(pn, StringComparer.OrdinalIgnoreCase)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToArray(); foreach (string processName in matchingProcesses) { Process[] processList = processes.Where(p => p.ProcessName.Equals(processName, StringComparison.OrdinalIgnoreCase)).ToArray(); foreach (Process process in processList) { string userName = GetProcessOwner(process.Id); await this.sendMatchingPrograms(processName, userName); } } processes = null; matchingProcesses = null; } // Method to get the username of the process owner private string GetProcessOwner(int processId) { try { string query = "Select * From Win32_Process Where ProcessID = " + processId; using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query)) using (ManagementObjectCollection processList = searcher.Get()) { foreach (ManagementObject obj in processList) { object[] argList = new object[2] { string.Empty, string.Empty }; int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList)); if (returnVal == 0) { return argList[0]?.ToString(); // Return the username } } } } catch { // Handle any exceptions } return "Unknown"; // Return 'Unknown' if unable to retrieve username } // This actually sends the matching applications to the backend // This actually sends the matching applications to the backend private async Task sendMatchingPrograms(string applicationName, string userName) { string baseUrl = "https://" + this.homeBaseAddress; string endpoint = "/api/currentPrograms"; HttpMethod method = HttpMethod.Post; try { var payload = new { applicationName = applicationName, machineName = userName, // Replacing currentMachineName with userName apiKey = this.apiKey }; string response = await Service1.ApiClient.hitApi(baseUrl, endpoint, method, (object)payload); Console.WriteLine("Response from server: " + response); } catch (HttpRequestException ex) { Console.WriteLine("Network error: " + ex.Message); } catch (JsonException ex) { Console.WriteLine("JSON parsing error: " + ex.Message); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); } } private async Task getPollRate() { string baseUrl = "https://" + this.homeBaseAddress; string endpoint = "/api/pollrate"; HttpMethod method = HttpMethod.Get; try { string response = await Service1.ApiClient.hitApi(baseUrl, endpoint, method); JObject jsonObject = JObject.Parse(response); string pollrateString = (string)jsonObject["pollrate"]; this.pollRate = int.Parse(pollrateString); response = (string)null; jsonObject = (JObject)null; pollrateString = (string)null; baseUrl = (string)null; endpoint = (string)null; method = (HttpMethod)null; } catch (Exception ex) { Console.WriteLine(ex.Message); baseUrl = (string)null; endpoint = (string)null; method = (HttpMethod)null; } } public static class ApiClient { public static async Task hitApi( string baseUrl, string endpoint, HttpMethod method, object data = null) { string str; using (HttpClient client = new HttpClient()) { client.BaseAddress = new Uri(baseUrl); HttpResponseMessage response; if (method == HttpMethod.Get) { response = await client.GetAsync(endpoint); } else { if (!(method == HttpMethod.Post)) throw new NotSupportedException(string.Format("HTTP method {0} is not supported.", (object)method)); string jsonData = JsonConvert.SerializeObject(data); StringContent content = new StringContent(jsonData, Encoding.UTF8, "application/json"); response = await client.PostAsync(endpoint, (HttpContent)content); jsonData = (string)null; content = (StringContent)null; } response.EnsureSuccessStatusCode(); str = await response.Content.ReadAsStringAsync(); } return str; } } } }