diff --git a/Cargo.lock b/Cargo.lock index a9b2e78..a7265d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2488,7 +2488,7 @@ checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" [[package]] name = "pumpbin" -version = "0.2.1" +version = "0.3.0" dependencies = [ "aes-gcm", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 26df82d..dfa084f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pumpbin" -version = "0.2.1" +version = "0.3.0" authors = ["b1n "] edition = "2021" description = "PumpBin is an Implant Generation Platform." diff --git a/src/bin/maker.rs b/src/bin/maker.rs index ef02894..59b4424 100644 --- a/src/bin/maker.rs +++ b/src/bin/maker.rs @@ -1,6 +1,6 @@ #![windows_subsystem = "windows"] -use std::{fs, path::PathBuf, time::Duration, usize}; +use std::{fs, path::PathBuf, usize}; use dirs::{desktop_dir, home_dir}; use iced::{ @@ -13,14 +13,15 @@ use iced::{ Alignment, Length, Renderer, Task, Theme, }; use pumpbin::{ + error_dialog, plugin::{Bins, Plugin}, - svg_style, ShellcodeSaveType, FONT, + settings, svg_style, ShellcodeSaveType, JETBRAINS_MONO_FONT, }; use pumpbin::{ plugin::{EncryptType, Platforms}, - Pumpbin, + show_message, }; -use rfd::AsyncFileDialog; +use rfd::{AsyncFileDialog, MessageLevel}; #[derive(Debug)] struct Maker { @@ -39,7 +40,7 @@ struct Maker { darwin_exe: String, darwin_lib: String, desc: text_editor::Content, - message: String, + pumpbin_version: String, selected_theme: Theme, } @@ -61,21 +62,13 @@ impl Default for Maker { darwin_exe: Default::default(), darwin_lib: Default::default(), desc: text_editor::Content::new(), - message: "Welcom to PumpBin Maker.".to_string(), + pumpbin_version: env!("CARGO_PKG_VERSION").into(), selected_theme: Theme::CatppuccinMacchiato, } } } impl Maker { - fn show_message(&mut self, message: String) -> Task { - self.message = message; - let wait = async { - tokio::time::sleep(Duration::from_secs(3)).await; - }; - Task::perform(wait, MakerMessage::ClearMessage) - } - fn plugin_name(&self) -> &str { &self.plugin_name } @@ -147,6 +140,10 @@ impl Maker { fn selected_theme(&self) -> Theme { self.selected_theme.clone() } + + fn pumpbin_version(&self) -> &str { + &self.pumpbin_version + } } #[derive(Debug, Clone, Copy)] @@ -191,7 +188,6 @@ enum MakerMessage { B1nClicked, GithubClicked, ThemeChanged(Theme), - ClearMessage(()), } impl Application for Maker { @@ -292,39 +288,50 @@ impl Application for Maker { } MakerMessage::GenerateClicked => { if self.plugin_name().is_empty() { - return self.show_message("Plugin Name is empty.".to_string()); + show_message("Plugin Name is empty.".to_string(), MessageLevel::Error); + return Task::none(); } if self.prefix().is_empty() { - return self.show_message("Prefix is empty.".to_string()); + show_message("Prefix is empty.".to_string(), MessageLevel::Error); + return Task::none(); + } + + if self.max_len().is_empty() { + show_message("MaxLen is empty.".to_string(), MessageLevel::Error); + return Task::none(); + } + + let max_len; + if let Ok(max) = self.max_len().parse::() { + max_len = max; + } else { + show_message("MaxLen numeric only.".to_string(), MessageLevel::Error); + return Task::none(); } if let ShellcodeSaveType::Local = self.shellcode_save_type() { if self.size_holder().is_empty() { - return self.show_message("Size Holder is empty.".to_string()); + show_message("Size Holder is empty.".to_string(), MessageLevel::Error); + return Task::none(); } } - let max_len; - - if let Ok(max) = self.max_len().parse::() { - max_len = max; - } else { - return self.show_message("MaxLen numeric only.".to_string()); - } - match self.encrypt_type() { EncryptType::None => (), EncryptType::Xor(x) => { if x.is_empty() { - return self.show_message("Xor Pass is empty.".to_string()); + show_message("Xor Pass is empty.".to_string(), MessageLevel::Error); + return Task::none(); } } EncryptType::AesGcm(x) => { if x.key_holder().is_empty() { - return self.show_message("AesGcm Key is empty.".to_string()); + show_message("AesGcm Key is empty.".to_string(), MessageLevel::Error); + return Task::none(); } else if x.nonce_holder().is_empty() { - return self.show_message("AesGcm Nonce is empty.".to_string()); + show_message("AesGcm Nonce is empty.".to_string(), MessageLevel::Error); + return Task::none(); } } } @@ -440,10 +447,13 @@ impl Application for Maker { Task::perform(make_plugin, MakerMessage::GenerateDone) } - MakerMessage::GenerateDone(x) => self.show_message(match x { - Ok(_) => "Generate done.".to_string(), - Err(e) => e, - }), + MakerMessage::GenerateDone(x) => { + match x { + Ok(_) => show_message("Generate done.".to_string(), MessageLevel::Info), + Err(e) => show_message(e, MessageLevel::Error), + }; + Task::none() + } MakerMessage::ChooseFileClicked(x) => { let choose_file = async move { AsyncFileDialog::new() @@ -468,56 +478,56 @@ impl Application for Maker { ) } MakerMessage::WindowsExeChooseDone(x) => { - match x { - Ok(x) => self.windows_exe = x, - Err(x) => return self.show_message(x), + if let Ok(path) = x { + self.windows_exe = path; } + Task::none() } MakerMessage::WindowsLibChooseDone(x) => { - match x { - Ok(x) => self.windows_lib = x, - Err(x) => return self.show_message(x), + if let Ok(path) = x { + self.windows_lib = path; } + Task::none() } MakerMessage::LinuxExeChooseDone(x) => { - match x { - Ok(x) => self.linux_exe = x, - Err(x) => return self.show_message(x), + if let Ok(path) = x { + self.linux_exe = path; } + Task::none() } MakerMessage::LinuxLibChooseDone(x) => { - match x { - Ok(x) => self.linux_lib = x, - Err(x) => return self.show_message(x), + if let Ok(path) = x { + self.linux_lib = path; } + Task::none() } MakerMessage::DarwinExeChooseDone(x) => { - match x { - Ok(x) => self.darwin_exe = x, - Err(x) => return self.show_message(x), + if let Ok(path) = x { + self.darwin_exe = path; } + Task::none() } MakerMessage::DarwinLibChooseDone(x) => { - match x { - Ok(x) => self.darwin_lib = x, - Err(x) => return self.show_message(x), + if let Ok(path) = x { + self.darwin_lib = path; } + Task::none() } MakerMessage::B1nClicked => { if open::that(env!("CARGO_PKG_HOMEPAGE")).is_err() { - return self.show_message("Open home failed.".into()); + show_message("Open home failed.".into(), MessageLevel::Error); } Task::none() } MakerMessage::GithubClicked => { if open::that(env!("CARGO_PKG_REPOSITORY")).is_err() { - return self.show_message("Open repo failed.".into()); + show_message("Open repo failed.".into(), MessageLevel::Error); } Task::none() } @@ -525,10 +535,6 @@ impl Application for Maker { self.selected_theme = x; Task::none() } - MakerMessage::ClearMessage(_) => { - self.message = "Welcom to PumpBin Maker.".to_string(); - Task::none() - } } } @@ -544,14 +550,14 @@ impl Application for Maker { let pick_list_handle = || pick_list::Handle::Dynamic { closed: pick_list::Icon { - font: FONT, + font: JETBRAINS_MONO_FONT, code_point: '', size: None, line_height: text::LineHeight::Relative(1.0), shaping: text::Shaping::Basic, }, open: pick_list::Icon { - font: FONT, + font: JETBRAINS_MONO_FONT, code_point: '', size: None, line_height: text::LineHeight::Relative(1.0), @@ -724,7 +730,9 @@ impl Application for Maker { .padding(20) .spacing(10); - let message = row![text(" ").size(25), text(&self.message)].align_items(Alignment::Center); + let version = text(format!("PumpBin  v{}", self.pumpbin_version())) + .color(self.theme().extended_palette().primary.base.color); + let b1n = button( Svg::new(Handle::from_memory(include_bytes!( "../../assets/svg/house-heart-fill.svg" @@ -755,14 +763,14 @@ impl Application for Maker { let footer = column![ horizontal_rule(0), row![ - column![message] - .width(Length::FillPortion(1)) + column![version] + .width(Length::Fill) .align_items(Alignment::Start), column![row![b1n, github].align_items(Alignment::Center)] .width(Length::Shrink) .align_items(Alignment::Center), column![theme_list] - .width(Length::FillPortion(1)) + .width(Length::Fill) .align_items(Alignment::End) ] .padding([0, 20]) @@ -778,6 +786,16 @@ impl Application for Maker { } } -fn main() -> iced::Result { - Maker::run(Pumpbin::settings()) +fn main() { + match try_main() { + Ok(_) => (), + Err(e) => { + error_dialog(e); + } + } +} + +fn try_main() -> anyhow::Result<()> { + Maker::run(settings())?; + Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 8914642..01ad36f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ mod button_style; pub mod plugin; pub mod svg_style; -use std::{fmt::Display, fs, iter, ops::Not, path::PathBuf, time::Duration}; +use std::{fmt::Display, fs, iter, ops::Not, path::PathBuf}; use dirs::{desktop_dir, home_dir}; use iced::{ @@ -19,9 +19,63 @@ use iced::{ use memchr::memmem; use plugin::{EncryptType, Plugin, Plugins}; use rand::RngCore; -use rfd::AsyncFileDialog; +use rfd::{ + AsyncFileDialog, AsyncMessageDialog, MessageButtons, MessageDialog, MessageDialogResult, + MessageLevel, +}; -pub const FONT: Font = Font::with_name("JetBrainsMono NF"); +pub const JETBRAINS_MONO_FONT: Font = Font::with_name("JetBrainsMono NF"); + +pub fn error_dialog(error: anyhow::Error) { + MessageDialog::new() + .set_buttons(MessageButtons::Ok) + .set_description(error.to_string()) + .set_level(MessageLevel::Error) + .set_title("PumpBin") + .show(); +} + +pub fn show_message(message: String, level: MessageLevel) -> Task { + let dialog = AsyncMessageDialog::new() + .set_buttons(MessageButtons::Ok) + .set_description(message) + .set_level(level) + .set_title("PumpBin") + .show(); + Task::future(dialog) +} + +pub fn settings() -> Settings { + let size = Size::new(1000.0, 600.0); + + Settings { + id: Some(env!("CARGO_PKG_NAME").into()), + window: window::Settings { + size, + position: Position::Centered, + min_size: Some(size), + visible: true, + resizable: true, + decorations: true, + transparent: false, + level: Level::Normal, + icon: match window::icon::from_file_data( + include_bytes!("../logo/icon.png"), + Some(ImageFormat::Png), + ) { + Ok(x) => Some(x), + Err(_) => None, + }, + exit_on_close_request: true, + ..Default::default() + }, + fonts: vec![include_bytes!("../assets/JetBrainsMonoNerdFont-Regular.ttf").into()], + default_font: JETBRAINS_MONO_FONT, + default_text_size: Pixels(13.0), + antialiasing: true, + ..Default::default() + } +} #[derive(Debug)] pub struct Pumpbin { @@ -29,7 +83,7 @@ pub struct Pumpbin { shellcode_save_type: ShellcodeSaveType, supported_binary_types: Vec, selected_binary_type: Option, - message: String, + version: String, supported_platforms: Vec, selected_platform: Option, plugins: Plugins, @@ -46,7 +100,7 @@ impl Default for Pumpbin { shellcode_save_type: ShellcodeSaveType::Local, supported_binary_types: Default::default(), selected_binary_type: None, - message: "Welcome to PumpBin!".into(), + version: env!("CARGO_PKG_VERSION").into(), supported_platforms: Default::default(), selected_platform: None, plugins: if let Ok(plugins) = Plugins::reade_plugins() { @@ -63,38 +117,6 @@ impl Default for Pumpbin { } impl Pumpbin { - pub fn settings() -> Settings { - let size = Size::new(1000.0, 600.0); - - Settings { - id: Some(env!("CARGO_PKG_NAME").into()), - window: window::Settings { - size, - position: Position::Centered, - min_size: Some(size), - visible: true, - resizable: true, - decorations: true, - transparent: false, - level: Level::Normal, - icon: match window::icon::from_file_data( - include_bytes!("../logo/icon.png"), - Some(ImageFormat::Png), - ) { - Ok(x) => Some(x), - Err(_) => None, - }, - exit_on_close_request: true, - ..Default::default() - }, - fonts: vec![include_bytes!("../assets/JetBrainsMonoNerdFont-Regular.ttf").into()], - default_font: FONT, - default_text_size: Pixels(13.0), - antialiasing: true, - ..Default::default() - } - } - fn random_encrypt_pass(&mut self) { match self.encrypt_type() { EncryptType::None => self.encrypt_type = EncryptType::None, @@ -112,14 +134,6 @@ impl Pumpbin { } } - fn show_message(&mut self, message: String) -> Task { - self.message = message; - let wait = async { - tokio::time::sleep(Duration::from_secs(3)).await; - }; - Task::perform(wait, Message::ClearMessage) - } - pub fn shellcode_src(&self) -> &str { &self.shellcode_src } @@ -136,8 +150,8 @@ impl Pumpbin { self.selected_binary_type } - pub fn message(&self) -> &str { - &self.message + pub fn version(&self) -> &str { + &self.version } pub fn supported_platforms(&self) -> &[Platform] { @@ -189,7 +203,6 @@ pub enum Message { B1nClicked, GithubClicked, ThemeChanged(Theme), - ClearMessage(()), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -281,8 +294,6 @@ impl Application for Pumpbin { Message::ChooseShellcodeDone(x) => { if let Some(path) = x { self.shellcode_src = path.to_string_lossy().to_string(); - } else { - return self.show_message("Canceled shellcode selection.".into()); } Task::none() } @@ -361,23 +372,28 @@ impl Application for Pumpbin { Ok(()) }; - Task::perform(write_encrypted, Message::EncryptShellcodeDone) - } else { - self.show_message("Canceled shellcode selection.".into()) - } + return Task::perform(write_encrypted, Message::EncryptShellcodeDone); + }; + Task::none() + } + Message::EncryptShellcodeDone(x) => { + match x { + Ok(_) => show_message("Saved encrypted shellcode.".into(), MessageLevel::Info), + Err(e) => show_message(e, MessageLevel::Error), + }; + + Task::none() } - Message::EncryptShellcodeDone(x) => self.show_message(match x { - Ok(_) => "Saved encrypted shellcode.".into(), - Err(e) => e, - }), Message::GenerateClicked => { // verify path if local mode let path = PathBuf::from(self.shellcode_src()); if self.shellcode_save_type() == ShellcodeSaveType::Local { if path.exists().not() { - return self.show_message("Shellcode path not exists.".into()); + show_message("Shellcode path not exists.".into(), MessageLevel::Error); + return Task::none(); } else if path.is_file().not() { - return self.show_message("Shellcode path is not a file.".into()); + show_message("Shellcode path is not a file.".into(), MessageLevel::Error); + return Task::none(); } } @@ -547,10 +563,14 @@ impl Application for Pumpbin { Task::perform(generate, Message::GenerateDone) } - Message::GenerateDone(x) => self.show_message(match x { - Ok(_) => "Saved generated binary.".into(), - Err(e) => e, - }), + Message::GenerateDone(x) => { + match x { + Ok(_) => show_message("Saved generated binary.".into(), MessageLevel::Info), + Err(e) => show_message(e, MessageLevel::Error), + }; + + Task::none() + } Message::BinaryTypeChanged(x) => { self.selected_binary_type = Some(x); Task::none() @@ -602,10 +622,16 @@ impl Application for Pumpbin { self.update(Message::PluginItemClicked(selected_plugin)); } self.plugins = plugins; - self.show_message(format!("Added {} plugins, {} failed.", success, failed)) + show_message( + format!("Added {} plugins, {} failed.", success, failed), + MessageLevel::Info, + ); + } + Err(e) => { + show_message(e, MessageLevel::Error); } - Err(e) => self.show_message(e), } + Task::none() } Message::RemovePlugin(x) => { let mut plugins = self.plugins().clone(); @@ -623,28 +649,36 @@ impl Application for Pumpbin { }; Task::perform(remove_plugin, Message::RemovePluginDone) } - Message::RemovePluginDone(x) => match x { - Ok((plugin_name, plugins)) => { - self.plugins = plugins; + Message::RemovePluginDone(x) => { + match x { + Ok((plugin_name, plugins)) => { + self.plugins = plugins; - let mut names: Vec = - self.plugins().0.keys().map(|x| x.to_owned()).collect(); - names.sort(); + let mut names: Vec = + self.plugins().0.keys().map(|x| x.to_owned()).collect(); + names.sort(); - if let Some(name) = names.first() { - _ = self.update(Message::PluginItemClicked(name.to_owned())); - } else { - self.supported_binary_types = Default::default(); - self.selected_binary_type = None; - self.supported_platforms = Default::default(); - self.selected_platform = None; - self.selected_plugin = None; - self.shellcode_save_type = ShellcodeSaveType::Local; + if let Some(name) = names.first() { + _ = self.update(Message::PluginItemClicked(name.to_owned())); + } else { + self.supported_binary_types = Default::default(); + self.selected_binary_type = None; + self.supported_platforms = Default::default(); + self.selected_platform = None; + self.selected_plugin = None; + self.shellcode_save_type = ShellcodeSaveType::Local; + } + show_message( + format!("Removed plugin {}", plugin_name), + MessageLevel::Info, + ); } - self.show_message(format!("Removed plugin {}", plugin_name)) - } - Err(e) => self.show_message(e), - }, + Err(e) => { + show_message(e, MessageLevel::Error); + } + }; + Task::none() + } Message::PluginItemClicked(x) => { // unwrap is safe. // UI implemented strict restrictions. @@ -654,9 +688,11 @@ impl Application for Pumpbin { if plugin.plugin_name() == selected_plugin { // random encryption pass self.random_encrypt_pass(); - return self.show_message( - "Generated new random encryption passwords.".to_string(), + show_message( + "Generated new random encryption passwords.".into(), + MessageLevel::Info, ); + return Task::none(); } } @@ -704,13 +740,13 @@ impl Application for Pumpbin { } Message::B1nClicked => { if open::that(env!("CARGO_PKG_HOMEPAGE")).is_err() { - return self.show_message("Open home failed.".into()); + show_message("Open home failed.".into(), MessageLevel::Error); } Task::none() } Message::GithubClicked => { if open::that(env!("CARGO_PKG_REPOSITORY")).is_err() { - return self.show_message("Open repo failed.".into()); + show_message("Open repo failed.".into(), MessageLevel::Error); } Task::none() } @@ -718,10 +754,6 @@ impl Application for Pumpbin { self.selected_theme = x; Task::none() } - Message::ClearMessage(_) => { - self.message = "Welcome to PumpBin!".to_string(); - Task::none() - } } } @@ -739,7 +771,7 @@ impl Application for Pumpbin { ) .on_input(Message::ShellcodeSrcChanged) .icon(text_input::Icon { - font: FONT, + font: JETBRAINS_MONO_FONT, code_point: '󱓞', size: None, spacing: 12.0, @@ -759,14 +791,14 @@ impl Application for Pumpbin { let pick_list_handle = || pick_list::Handle::Dynamic { closed: pick_list::Icon { - font: FONT, + font: JETBRAINS_MONO_FONT, code_point: '', size: None, line_height: text::LineHeight::Relative(1.0), shaping: text::Shaping::Basic, }, open: pick_list::Icon { - font: FONT, + font: JETBRAINS_MONO_FONT, code_point: '', size: None, line_height: text::LineHeight::Relative(1.0), @@ -1115,13 +1147,8 @@ impl Application for Pumpbin { .width(Length::Fill) .height(Length::Fill); - let message = row![ - text(" ") - .color(self.theme().extended_palette().primary.base.color) - .size(25), - text(&self.message).color(self.theme().extended_palette().primary.base.color) - ] - .align_items(Alignment::Center); + let version = text(format!("PumpBin  v{}", self.version())) + .color(self.theme().extended_palette().primary.base.color); let b1n = button( Svg::new(Handle::from_memory(include_bytes!( @@ -1153,14 +1180,14 @@ impl Application for Pumpbin { let footer = column![ horizontal_rule(0), row![ - column![message] - .width(Length::FillPortion(1)) + column![version] + .width(Length::Fill) .align_items(Alignment::Start), column![row![b1n, github].align_items(Alignment::Center)] .width(Length::Shrink) .align_items(Alignment::Center), column![theme_list] - .width(Length::FillPortion(1)) + .width(Length::Fill) .align_items(Alignment::End) ] .padding([0, padding]) diff --git a/src/main.rs b/src/main.rs index a3189a7..800a66c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,9 +5,18 @@ use std::{fs, ops::Not}; use anyhow::anyhow; use dirs::data_dir; use iced::advanced::Application; -use pumpbin::{plugin::CONFIG_FILE_PATH, Pumpbin}; +use pumpbin::{error_dialog, plugin::CONFIG_FILE_PATH, settings, Pumpbin}; -fn main() -> anyhow::Result<()> { +fn main() { + match try_main() { + Ok(_) => (), + Err(e) => { + error_dialog(e); + } + } +} + +fn try_main() -> anyhow::Result<()> { let mut config_path = data_dir().ok_or(anyhow!("Get data_dir failed."))?; config_path.push("PumpBin"); config_path.push("plugins"); @@ -25,6 +34,6 @@ fn main() -> anyhow::Result<()> { .set(config_path) .map_err(|_| anyhow!("Set CONFIG_FILE_PATH failed."))?; - Pumpbin::run(Pumpbin::settings())?; + Pumpbin::run(settings())?; Ok(()) }