feat!: v1.0.0

Implementing a Plug-in System with Extism.
Serialize the Plugin struct with Cap'n Proto for backward compatibility.
Refactor the project code.
This commit is contained in:
b1n
2024-07-09 09:59:56 +08:00
parent c5cfb7e082
commit ed890a4186
14 changed files with 4919 additions and 1407 deletions

1763
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "pumpbin" name = "pumpbin"
version = "0.3.0" version = "1.0.0"
authors = ["b1n <b1n@b1n.io>"] authors = ["b1n <b1n@b1n.io>"]
edition = "2021" edition = "2021"
description = "PumpBin is an Implant Generation Platform." description = "PumpBin is an Implant Generation Platform."
@@ -19,19 +19,20 @@ path = "src/bin/maker.rs"
[dependencies] [dependencies]
rfd = { version = "0.14.1", default-features = false , features = ["gtk3"]} rfd = { version = "0.14.1", default-features = false , features = ["gtk3"]}
dirs = "5.0.1" dirs = "5.0.1"
flatbuffers = "24.3.25"
open = "5.1.4" open = "5.1.4"
bincode = { version = "2.0.0-rc.3", default-features = false , features = ["alloc", "derive", "std"]} bincode = { version = "2.0.0-rc.3", default-features = false , features = ["alloc", "derive", "std"]}
anyhow = "1.0.86" anyhow = "1.0.86"
rand = "0.8.5" rand = "0.8.5"
memchr = "2.7.4" memchr = "2.7.4"
aes-gcm = "0.10.3" capnp = "0.19"
tokio = { version = "1.38.0", default-features = false , features = ["time"]} extism = "1.4.1"
serde = { version = "1.0.203", features = ["derive"] }
serde_json = "1.0.120"
[dependencies.iced] [dependencies.iced]
version = "0.13.0-dev" version = "0.13.0-dev"
git = "https://github.com/iced-rs/iced" git = "https://github.com/iced-rs/iced"
rev = "6c1027af8d54ad21e282337b53097eb196d62c00" rev = "978327f9e7f68d3e5bc280faa0617487d8eabc57"
default-features = false default-features = false
features = ["advanced", "svg", "tokio", "wgpu", "image"] features = ["advanced", "svg", "tokio", "wgpu", "image"]
@@ -44,6 +45,9 @@ winresource = "0.1.17"
[dev-dependencies] [dev-dependencies]
tempfile = "3.10.1" tempfile = "3.10.1"
[build-dependencies]
capnpc = "0.19"
[profile.release] [profile.release]
strip = true strip = true
opt-level = 3 opt-level = 3

View File

@@ -1,4 +1,16 @@
fn build_capnp() {
capnpc::CompilerCommand::new()
.src_prefix("capnp")
.file("capnp/plugin.capnp")
.output_path("capnp")
.run()
.expect("schema compiler command");
}
fn main() { fn main() {
#[cfg(debug_assertions)]
build_capnp();
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
let mut res = winresource::WindowsResource::new(); let mut res = winresource::WindowsResource::new();

40
capnp/plugin.capnp Normal file
View File

@@ -0,0 +1,40 @@
@0x96d5aac4519892f3;
struct Plugin {
version @0 : Text;
info @1 :PluginInfo;
replace @2 :PluginReplace;
bins @3 :PluginBins;
plugins @4 :PluginPlugins;
struct PluginInfo{
pluginName @0 :Text;
author @1 :Text;
version @2 :Text;
desc @3 :Text;
}
struct PluginReplace {
srcPrefix @0 :Data;
sizeHolder @1 :Data;
maxLen @2 :UInt64;
}
struct PluginBins {
windows @0 :Bins;
linux @1 :Bins;
darwin @2 :Bins;
struct Bins {
executable @0 :Data;
dynamicLibrary @1 :Data;
}
}
struct PluginPlugins {
encryptShellcode @0 :Data;
formatEncryptedShellcode @1 :Data;
formatUrlRemote @2 :Data;
uploadFinalShellcodeRemote @3 :Data;
}
}

1890
capnp/plugin_capnp.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,66 +1,163 @@
#![windows_subsystem = "windows"] #![windows_subsystem = "windows"]
use std::{fs, path::PathBuf, usize}; use std::{fs, ops::Not, path::PathBuf};
use anyhow::{anyhow, bail};
use dirs::{desktop_dir, home_dir}; use dirs::{desktop_dir, home_dir};
use iced::{ use iced::{
advanced::Application, application,
executor, futures::TryFutureExt,
widget::{ widget::{
button, column, horizontal_rule, pick_list, radio, row, svg::Handle, text, text_editor, button, column, horizontal_rule, pick_list, radio, row, svg::Handle, text, text_editor,
text_input, Svg, text_input, Column, Svg,
}, },
Alignment, Length, Renderer, Task, Theme, Alignment, Length, Size, Task, Theme,
}; };
use pumpbin::{ use pumpbin::{
error_dialog, plugin::{Plugin, PluginInfo, PluginReplace},
plugin::{Bins, Plugin}, utils::{self, error_dialog, message_dialog},
settings, svg_style, ShellcodeSaveType, JETBRAINS_MONO_FONT,
};
use pumpbin::{
plugin::{EncryptType, Platforms},
show_message,
}; };
use pumpbin::{style, ShellcodeSaveType};
use rfd::{AsyncFileDialog, MessageLevel}; use rfd::{AsyncFileDialog, MessageLevel};
fn main() {
if let Err(e) = try_main() {
error_dialog(e);
}
}
fn try_main() -> anyhow::Result<()> {
let size = Size::new(1200.0, 800.0);
let mut window_settings = utils::window_settings();
window_settings.size = size;
window_settings.min_size = Some(size);
application("PumpBin Maker", Maker::update, Maker::view)
.settings(utils::settings())
.window(window_settings)
.theme(Maker::theme)
.run()?;
Ok(())
}
#[derive(Debug, Clone, Copy)]
enum ChooseFileType {
WindowsExe,
WindowsLib,
LinuxExe,
LinuxLib,
DarwinExe,
DarwinLib,
EncryptShellcodePlugin,
FormatEncryptedShellcodePlugin,
FormatUrlRemote,
UploadFinalShellcodeRemote,
}
#[derive(Debug, Clone)]
enum MakerMessage {
PluginNameChanged(String),
AuthorChanged(String),
VersionChanged(String),
SrcPrefixChanged(String),
MaxLenChanged(String),
ShellcodeSaveTypeChanged(ShellcodeSaveType),
SizeHolderChanged(String),
WindowsExeChanged(String),
WindowsLibChanged(String),
LinuxExeChanged(String),
LinuxLibChanged(String),
DarwinExeChanged(String),
DarwinLibChanged(String),
EncryptShllcodePluginChanged(String),
FormatEncryptedShellcodePluginChanged(String),
FormatUrlRemotePluginChanged(String),
UploadFinalShellcodeRemotePluginChanged(String),
DescAction(text_editor::Action),
GenerateClicked,
GenerateDone(Result<(), String>),
ChooseFileClicked(ChooseFileType),
ChooseFileDone((Option<String>, ChooseFileType)),
B1nClicked,
GithubClicked,
ThemeChanged(Theme),
}
#[derive(Debug)] #[derive(Debug)]
struct Maker { struct Maker {
plugin_name: String, plugin_name: String,
author: String, author: String,
version: String, version: String,
prefix: String, src_prefix: String,
max_len: String, max_len: String,
shellcode_save_type: ShellcodeSaveType, shellcode_save_type: ShellcodeSaveType,
size_holder: String, size_holder: String,
encrypt_type: EncryptType,
windows_exe: String, windows_exe: String,
windows_lib: String, windows_lib: String,
linux_exe: String, linux_exe: String,
linux_lib: String, linux_lib: String,
darwin_exe: String, darwin_exe: String,
darwin_lib: String, darwin_lib: String,
encrypt_shellcode_plugin: String,
format_encrypted_shellcode_plugin: String,
format_url_remote_plugin: String,
upload_final_shellcode_remote_plugin: String,
desc: text_editor::Content, desc: text_editor::Content,
pumpbin_version: String, pumpbin_version: String,
selected_theme: Theme, selected_theme: Theme,
} }
impl Maker {
fn check_generate(&self) -> anyhow::Result<()> {
if self.plugin_name.is_empty() {
bail!("Plugin Name is empty.");
}
if self.src_prefix.is_empty() {
bail!("Prefix is empty.");
}
let max_len = self.max_len();
if max_len.is_empty() {
bail!("Max Len is empty.");
}
if max_len.parse::<usize>().is_err() {
bail!("Max Len numeric only.");
};
if let ShellcodeSaveType::Local = self.shellcode_save_type() {
if self.size_holder().is_empty() {
bail!("Size Holder is empty.");
}
};
anyhow::Ok(())
}
}
impl Default for Maker { impl Default for Maker {
fn default() -> Self { fn default() -> Self {
Self { Self {
plugin_name: Default::default(), plugin_name: Default::default(),
author: Default::default(), author: Default::default(),
version: Default::default(), version: Default::default(),
prefix: Default::default(), src_prefix: Default::default(),
max_len: Default::default(), max_len: Default::default(),
shellcode_save_type: ShellcodeSaveType::Local, shellcode_save_type: Default::default(),
size_holder: Default::default(), size_holder: Default::default(),
encrypt_type: EncryptType::None,
windows_exe: Default::default(), windows_exe: Default::default(),
windows_lib: Default::default(), windows_lib: Default::default(),
linux_exe: Default::default(), linux_exe: Default::default(),
linux_lib: Default::default(), linux_lib: Default::default(),
darwin_exe: Default::default(), darwin_exe: Default::default(),
darwin_lib: Default::default(), darwin_lib: Default::default(),
encrypt_shellcode_plugin: Default::default(),
format_encrypted_shellcode_plugin: Default::default(),
format_url_remote_plugin: Default::default(),
upload_final_shellcode_remote_plugin: Default::default(),
desc: text_editor::Content::new(), desc: text_editor::Content::new(),
pumpbin_version: env!("CARGO_PKG_VERSION").into(), pumpbin_version: env!("CARGO_PKG_VERSION").into(),
selected_theme: Theme::CatppuccinMacchiato, selected_theme: Theme::CatppuccinMacchiato,
@@ -81,8 +178,8 @@ impl Maker {
&self.version &self.version
} }
fn prefix(&self) -> &str { fn src_prefix(&self) -> &str {
&self.prefix &self.src_prefix
} }
fn max_len(&self) -> &str { fn max_len(&self) -> &str {
@@ -97,14 +194,6 @@ impl Maker {
&self.size_holder &self.size_holder
} }
fn encrypt_type(&self) -> &EncryptType {
&self.encrypt_type
}
fn encrypt_type_mut(&mut self) -> &mut EncryptType {
&mut self.encrypt_type
}
fn windows_exe(&self) -> &str { fn windows_exe(&self) -> &str {
&self.windows_exe &self.windows_exe
} }
@@ -129,6 +218,22 @@ impl Maker {
&self.darwin_lib &self.darwin_lib
} }
fn encrypt_shellcode_plugin(&self) -> &str {
&self.encrypt_shellcode_plugin
}
fn format_encrypted_shellcode_plugin(&self) -> &str {
&self.format_encrypted_shellcode_plugin
}
fn format_url_remote_plugin(&self) -> &str {
&self.format_url_remote_plugin
}
fn upload_final_shellcode_remote_plugin(&self) -> &str {
&self.upload_final_shellcode_remote_plugin
}
fn desc(&self) -> &text_editor::Content { fn desc(&self) -> &text_editor::Content {
&self.desc &self.desc
} }
@@ -146,399 +251,216 @@ impl Maker {
} }
} }
#[derive(Debug, Clone, Copy)] impl Maker {
enum ChooseFileType { pub fn update(&mut self, message: MakerMessage) -> iced::Task<MakerMessage> {
WindowsExe,
WindowsLib,
LinuxExe,
LinuxLib,
DarwinExe,
DarwinLib,
}
#[derive(Debug, Clone)]
enum MakerMessage {
PluginNameChanged(String),
AuthorChanged(String),
VersionChanged(String),
PrefixChanged(String),
MaxLenChanged(String),
ShellcodeSaveTypeChanged(ShellcodeSaveType),
SizeHolderChanged(String),
EncryptTypeChanged(EncryptType),
XorPassChanged(String),
AesKeyChanged(String),
AesNonceChanged(String),
WindowsExeChanged(String),
WindowsLibChanged(String),
LinuxExeChanged(String),
LinuxLibChanged(String),
DarwinExeChanged(String),
DarwinLibChanged(String),
DescAction(text_editor::Action),
GenerateClicked,
GenerateDone(Result<(), String>),
ChooseFileClicked(ChooseFileType),
WindowsExeChooseDone(Result<String, String>),
WindowsLibChooseDone(Result<String, String>),
LinuxExeChooseDone(Result<String, String>),
LinuxLibChooseDone(Result<String, String>),
DarwinExeChooseDone(Result<String, String>),
DarwinLibChooseDone(Result<String, String>),
B1nClicked,
GithubClicked,
ThemeChanged(Theme),
}
impl Application for Maker {
type Executor = executor::Default;
type Flags = ();
type Message = MakerMessage;
type Theme = Theme;
type Renderer = Renderer;
fn new(_flags: Self::Flags) -> (Self, iced::Task<Self::Message>) {
(Self::default(), Task::none())
}
fn title(&self) -> String {
"PumpBin Maker".to_string()
}
fn update(&mut self, message: Self::Message) -> iced::Task<Self::Message> {
match message { match message {
MakerMessage::PluginNameChanged(x) => { MakerMessage::PluginNameChanged(x) => self.plugin_name = x,
self.plugin_name = x; MakerMessage::AuthorChanged(x) => self.author = x,
Task::none() MakerMessage::VersionChanged(x) => self.version = x,
MakerMessage::SrcPrefixChanged(x) => self.src_prefix = x,
MakerMessage::MaxLenChanged(x) => self.max_len = x,
MakerMessage::ShellcodeSaveTypeChanged(x) => self.shellcode_save_type = x,
MakerMessage::SizeHolderChanged(x) => self.size_holder = x,
MakerMessage::WindowsExeChanged(x) => self.windows_exe = x,
MakerMessage::WindowsLibChanged(x) => self.windows_lib = x,
MakerMessage::LinuxExeChanged(x) => self.linux_exe = x,
MakerMessage::LinuxLibChanged(x) => self.linux_lib = x,
MakerMessage::DarwinExeChanged(x) => self.darwin_exe = x,
MakerMessage::DarwinLibChanged(x) => self.darwin_lib = x,
MakerMessage::EncryptShllcodePluginChanged(x) => self.encrypt_shellcode_plugin = x,
MakerMessage::FormatEncryptedShellcodePluginChanged(x) => {
self.format_encrypted_shellcode_plugin = x
} }
MakerMessage::AuthorChanged(x) => { MakerMessage::FormatUrlRemotePluginChanged(x) => self.format_url_remote_plugin = x,
self.author = x; MakerMessage::UploadFinalShellcodeRemotePluginChanged(x) => {
Task::none() self.upload_final_shellcode_remote_plugin = x
}
MakerMessage::VersionChanged(x) => {
self.version = x;
Task::none()
}
MakerMessage::PrefixChanged(x) => {
self.prefix = x;
Task::none()
}
MakerMessage::MaxLenChanged(x) => {
self.max_len = x;
Task::none()
}
MakerMessage::ShellcodeSaveTypeChanged(x) => {
self.shellcode_save_type = x;
Task::none()
}
MakerMessage::SizeHolderChanged(x) => {
self.size_holder = x;
Task::none()
}
MakerMessage::EncryptTypeChanged(x) => {
self.encrypt_type = x;
Task::none()
}
MakerMessage::XorPassChanged(x) => {
if let EncryptType::Xor(xor) = self.encrypt_type_mut() {
*xor = x.as_bytes().to_vec();
}
Task::none()
}
MakerMessage::AesKeyChanged(x) => {
if let EncryptType::AesGcm(aes_gcm) = self.encrypt_type_mut() {
*aes_gcm.key_holder_mut() = x.as_bytes().to_vec();
}
Task::none()
}
MakerMessage::AesNonceChanged(x) => {
if let EncryptType::AesGcm(aes_gcm) = self.encrypt_type_mut() {
*aes_gcm.nonce_holder_mut() = x.as_bytes().to_vec();
}
Task::none()
}
MakerMessage::WindowsExeChanged(x) => {
self.windows_exe = x;
Task::none()
}
MakerMessage::WindowsLibChanged(x) => {
self.windows_lib = x;
Task::none()
}
MakerMessage::LinuxExeChanged(x) => {
self.linux_exe = x;
Task::none()
}
MakerMessage::LinuxLibChanged(x) => {
self.linux_lib = x;
Task::none()
}
MakerMessage::DarwinExeChanged(x) => {
self.darwin_exe = x;
Task::none()
}
MakerMessage::DarwinLibChanged(x) => {
self.darwin_lib = x;
Task::none()
}
MakerMessage::DescAction(x) => {
self.desc_mut().perform(x);
Task::none()
} }
MakerMessage::DescAction(x) => self.desc_mut().perform(x),
MakerMessage::GenerateClicked => { MakerMessage::GenerateClicked => {
if self.plugin_name().is_empty() { if let Err(e) = self.check_generate() {
show_message("Plugin Name is empty.".to_string(), MessageLevel::Error); message_dialog(e.to_string(), MessageLevel::Error);
return Task::none(); return Task::none();
} }
if self.prefix().is_empty() { let mut plugin = Plugin {
show_message("Prefix is empty.".to_string(), MessageLevel::Error); version: self.pumpbin_version().to_string(),
return Task::none(); info: PluginInfo {
} plugin_name: self.plugin_name().to_string(),
author: {
if self.max_len().is_empty() { let author = self.author().to_string();
show_message("MaxLen is empty.".to_string(), MessageLevel::Error); if author.is_empty() {
return Task::none(); "None".to_string()
} } else {
author
let max_len; }
if let Ok(max) = self.max_len().parse::<usize>() {
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() {
show_message("Size Holder is empty.".to_string(), MessageLevel::Error);
return Task::none();
}
}
match self.encrypt_type() {
EncryptType::None => (),
EncryptType::Xor(x) => {
if x.is_empty() {
show_message("Xor Pass is empty.".to_string(), MessageLevel::Error);
return Task::none();
}
}
EncryptType::AesGcm(x) => {
if x.key_holder().is_empty() {
show_message("AesGcm Key is empty.".to_string(), MessageLevel::Error);
return Task::none();
} else if x.nonce_holder().is_empty() {
show_message("AesGcm Nonce is empty.".to_string(), MessageLevel::Error);
return Task::none();
}
}
}
let windows_exe_path = PathBuf::from(self.windows_exe());
let windows_dll_path = PathBuf::from(self.windows_lib());
let windows = match (
windows_exe_path.exists() && windows_exe_path.is_file(),
windows_dll_path.exists() && windows_dll_path.is_file(),
) {
(false, false) => None,
_ => Some(Bins {
executable: if let Ok(bin) = fs::read(&windows_exe_path) {
Some(bin)
} else {
None
}, },
dynamic_library: if let Ok(bin) = fs::read(&windows_dll_path) { version: {
Some(bin) let version = self.version().to_string();
} else { if version.is_empty() {
None "None".to_string()
} else {
version
}
}, },
}), desc: {
let desc = self.desc().text();
if desc.is_empty() {
"None".to_string()
} else {
desc
}
},
},
replace: PluginReplace {
src_prefix: self.src_prefix().as_bytes().to_vec(),
size_holder: match self.shellcode_save_type() {
ShellcodeSaveType::Local => {
Some(self.size_holder().as_bytes().to_vec())
}
ShellcodeSaveType::Remote => None,
},
max_len: self.max_len().parse().unwrap(),
},
..Default::default()
}; };
let linux_exe_path = PathBuf::from(self.linux_exe()); let paths: Vec<(String, ChooseFileType)> = vec![
let linux_dll_path = PathBuf::from(self.linux_lib()); (self.windows_exe(), ChooseFileType::WindowsExe),
let linux = match ( (self.windows_lib(), ChooseFileType::WindowsLib),
linux_exe_path.exists() && linux_exe_path.is_file(), (self.linux_exe(), ChooseFileType::LinuxExe),
linux_dll_path.exists() && linux_dll_path.is_file(), (self.linux_lib(), ChooseFileType::LinuxLib),
) { (self.darwin_exe(), ChooseFileType::DarwinExe),
(false, false) => None, (self.darwin_lib(), ChooseFileType::DarwinLib),
_ => Some(Bins { (
executable: if let Ok(bin) = fs::read(&linux_exe_path) { self.encrypt_shellcode_plugin(),
Some(bin) ChooseFileType::EncryptShellcodePlugin,
} else { ),
None (
}, self.format_encrypted_shellcode_plugin(),
dynamic_library: if let Ok(bin) = fs::read(&linux_dll_path) { ChooseFileType::FormatEncryptedShellcodePlugin,
Some(bin) ),
} else { (
None self.format_url_remote_plugin(),
}, ChooseFileType::FormatUrlRemote,
}), ),
}; (
self.upload_final_shellcode_remote_plugin(),
ChooseFileType::UploadFinalShellcodeRemote,
),
]
.into_iter()
.map(|(x, y)| (x.to_string(), y))
.collect();
let darwin_exe_path = PathBuf::from(self.darwin_exe());
let darwin_dll_path = PathBuf::from(self.darwin_lib());
let darwin = match (
darwin_exe_path.exists() && darwin_exe_path.is_file(),
darwin_dll_path.exists() && darwin_dll_path.is_file(),
) {
(false, false) => None,
_ => Some(Bins {
executable: if let Ok(bin) = fs::read(&darwin_exe_path) {
Some(bin)
} else {
None
},
dynamic_library: if let Ok(bin) = fs::read(&darwin_dll_path) {
Some(bin)
} else {
None
},
}),
};
let plugin = Plugin {
plugin_name: self.plugin_name().to_string(),
author: match self.author().is_empty() {
true => None,
false => Some(self.author().to_string()),
},
version: match self.version().is_empty() {
true => None,
false => Some(self.version().to_string()),
},
desc: match self.desc().text().is_empty() {
true => None,
false => Some(self.desc().text()),
},
prefix: self.prefix().as_bytes().to_vec(),
size_holder: match self.shellcode_save_type() {
ShellcodeSaveType::Local => Some(self.size_holder().as_bytes().to_vec()),
ShellcodeSaveType::Remote => None,
},
max_len,
encrypt_type: self.encrypt_type().to_owned(),
platforms: Platforms {
windows,
linux,
darwin,
},
};
let plugin_name = self.plugin_name().to_owned();
let make_plugin = async move { let make_plugin = async move {
for (path_str, file_type) in paths {
if path_str.is_empty().not() {
let path = PathBuf::from(path_str);
let data = fs::read(path)?;
let bin = match file_type {
ChooseFileType::WindowsExe => plugin.bins.windows.executable_mut(),
ChooseFileType::WindowsLib => {
plugin.bins.windows.dynamic_library_mut()
}
ChooseFileType::LinuxExe => plugin.bins.linux.executable_mut(),
ChooseFileType::LinuxLib => plugin.bins.linux.dynamic_library_mut(),
ChooseFileType::DarwinExe => plugin.bins.darwin.executable_mut(),
ChooseFileType::DarwinLib => {
plugin.bins.darwin.dynamic_library_mut()
}
ChooseFileType::EncryptShellcodePlugin => {
plugin.plugins.encrypt_shellcode_mut()
}
ChooseFileType::FormatEncryptedShellcodePlugin => {
plugin.plugins.format_encrypted_shellcode_mut()
}
ChooseFileType::FormatUrlRemote => {
plugin.plugins.format_url_remote_mut()
}
ChooseFileType::UploadFinalShellcodeRemote => {
plugin.plugins.upload_final_shellcode_remote_mut()
}
};
*bin = Some(data);
}
}
let file = AsyncFileDialog::new() let file = AsyncFileDialog::new()
.set_directory(desktop_dir().unwrap_or(".".into())) .set_directory(desktop_dir().unwrap_or(".".into()))
.set_file_name(format!("{}.b1n", plugin_name)) .set_file_name(format!("{}.b1n", plugin.info().plugin_name()))
.set_can_create_directories(true) .set_can_create_directories(true)
.set_title("save plugin") .set_title("Save generated plugin")
.save_file() .save_file()
.await .await
.ok_or("Canceled plugin saving.".to_string())?; .ok_or(anyhow!("Canceled the saving of the generated plugin."))?;
plugin fs::write(file.path(), plugin.encode_to_vec()?)?;
.write_plugin(file.path())
.map_err(|_| "Write plugin failed.".to_string())?;
Ok(()) anyhow::Ok(())
}; }
.map_err(|e| e.to_string());
Task::perform(make_plugin, MakerMessage::GenerateDone) return Task::perform(make_plugin, MakerMessage::GenerateDone);
} }
MakerMessage::GenerateDone(x) => { MakerMessage::GenerateDone(x) => {
match x { match x {
Ok(_) => show_message("Generate done.".to_string(), MessageLevel::Info), Ok(_) => message_dialog("Generate done.".to_string(), MessageLevel::Info),
Err(e) => show_message(e, MessageLevel::Error), Err(e) => message_dialog(e, MessageLevel::Error),
}; };
Task::none()
} }
MakerMessage::ChooseFileClicked(x) => { MakerMessage::ChooseFileClicked(x) => {
let choose_file = async move { let choose_file = async move {
AsyncFileDialog::new() let file = AsyncFileDialog::new()
.set_directory(home_dir().unwrap_or(".".into())) .set_directory(home_dir().unwrap_or(".".into()))
.set_title("choose file") .set_title("Choose file")
.pick_file() .pick_file()
.await .await
.map(|x| x.path().to_string_lossy().to_string()) .map(|x| x.path().to_string_lossy().to_string());
.ok_or("Canceled file selection.".to_string())
(file, x)
}; };
Task::perform( return Task::perform(choose_file, MakerMessage::ChooseFileDone);
choose_file,
match x {
ChooseFileType::WindowsExe => MakerMessage::WindowsExeChooseDone,
ChooseFileType::WindowsLib => MakerMessage::WindowsLibChooseDone,
ChooseFileType::LinuxExe => MakerMessage::LinuxExeChooseDone,
ChooseFileType::LinuxLib => MakerMessage::LinuxLibChooseDone,
ChooseFileType::DarwinExe => MakerMessage::DarwinExeChooseDone,
ChooseFileType::DarwinLib => MakerMessage::DarwinLibChooseDone,
},
)
} }
MakerMessage::WindowsExeChooseDone(x) => { MakerMessage::ChooseFileDone((path, choose_type)) => {
if let Ok(path) = x { if let Some(path) = path {
self.windows_exe = path; match choose_type {
ChooseFileType::WindowsExe => self.windows_exe = path,
ChooseFileType::WindowsLib => self.windows_lib = path,
ChooseFileType::LinuxExe => self.linux_exe = path,
ChooseFileType::LinuxLib => self.linux_lib = path,
ChooseFileType::DarwinExe => self.darwin_exe = path,
ChooseFileType::DarwinLib => self.darwin_lib = path,
ChooseFileType::EncryptShellcodePlugin => {
self.encrypt_shellcode_plugin = path
}
ChooseFileType::FormatEncryptedShellcodePlugin => {
self.format_encrypted_shellcode_plugin = path
}
ChooseFileType::FormatUrlRemote => self.format_url_remote_plugin = path,
ChooseFileType::UploadFinalShellcodeRemote => {
self.upload_final_shellcode_remote_plugin = path
}
}
} }
Task::none()
}
MakerMessage::WindowsLibChooseDone(x) => {
if let Ok(path) = x {
self.windows_lib = path;
}
Task::none()
}
MakerMessage::LinuxExeChooseDone(x) => {
if let Ok(path) = x {
self.linux_exe = path;
}
Task::none()
}
MakerMessage::LinuxLibChooseDone(x) => {
if let Ok(path) = x {
self.linux_lib = path;
}
Task::none()
}
MakerMessage::DarwinExeChooseDone(x) => {
if let Ok(path) = x {
self.darwin_exe = path;
}
Task::none()
}
MakerMessage::DarwinLibChooseDone(x) => {
if let Ok(path) = x {
self.darwin_lib = path;
}
Task::none()
} }
MakerMessage::B1nClicked => { MakerMessage::B1nClicked => {
if open::that(env!("CARGO_PKG_HOMEPAGE")).is_err() { if open::that(env!("CARGO_PKG_HOMEPAGE")).is_err() {
show_message("Open home failed.".into(), MessageLevel::Error); message_dialog("Open home failed.".into(), MessageLevel::Error);
} }
Task::none()
} }
MakerMessage::GithubClicked => { MakerMessage::GithubClicked => {
if open::that(env!("CARGO_PKG_REPOSITORY")).is_err() { if open::that(env!("CARGO_PKG_REPOSITORY")).is_err() {
show_message("Open repo failed.".into(), MessageLevel::Error); message_dialog("Open repo failed.".into(), MessageLevel::Error);
} }
Task::none()
}
MakerMessage::ThemeChanged(x) => {
self.selected_theme = x;
Task::none()
} }
MakerMessage::ThemeChanged(x) => self.selected_theme = x,
} }
Task::none()
} }
fn view(&self) -> iced::Element<'_, Self::Message, Self::Theme, Self::Renderer> { pub fn view(&self) -> Column<MakerMessage> {
let choose_button = || { let choose_button = || {
button( button(
Svg::new(Handle::from_memory(include_bytes!( Svg::new(Handle::from_memory(include_bytes!(
@@ -548,23 +470,6 @@ impl Application for Maker {
) )
}; };
let pick_list_handle = || pick_list::Handle::Dynamic {
closed: pick_list::Icon {
font: JETBRAINS_MONO_FONT,
code_point: '',
size: None,
line_height: text::LineHeight::Relative(1.0),
shaping: text::Shaping::Basic,
},
open: pick_list::Icon {
font: JETBRAINS_MONO_FONT,
code_point: '',
size: None,
line_height: text::LineHeight::Relative(1.0),
shaping: text::Shaping::Basic,
},
};
let maker = column![ let maker = column![
row![ row![
column![ column![
@@ -584,11 +489,11 @@ impl Application for Maker {
.align_items(Alignment::Start), .align_items(Alignment::Start),
column![ column![
text("Prefix"), text("Prefix"),
text_input("", self.prefix()).on_input(MakerMessage::PrefixChanged), text_input("", self.src_prefix()).on_input(MakerMessage::SrcPrefixChanged),
] ]
.align_items(Alignment::Start), .align_items(Alignment::Start),
column![ column![
text("MaxLen"), text("Max Len"),
text_input("", self.max_len()).on_input(MakerMessage::MaxLenChanged), text_input("", self.max_len()).on_input(MakerMessage::MaxLenChanged),
] ]
.align_items(Alignment::Start), .align_items(Alignment::Start),
@@ -627,42 +532,6 @@ impl Application for Maker {
.spacing(20) .spacing(20)
] ]
.align_items(Alignment::Start), .align_items(Alignment::Start),
column![
text("Encrypt Type"),
row![pick_list(
EncryptType::all(),
Some(self.encrypt_type()),
MakerMessage::EncryptTypeChanged
)
.handle(pick_list_handle())]
.push_maybe(match self.encrypt_type() {
EncryptType::None => None,
EncryptType::Xor(x) => Some(
row![
text("Pass:"),
text_input("", &String::from_utf8_lossy(x))
.on_input(MakerMessage::XorPassChanged)
]
.spacing(10)
.align_items(Alignment::Center)
),
EncryptType::AesGcm(x) => Some(
row![
text("Key:"),
text_input("", &String::from_utf8_lossy(x.key_holder()))
.on_input(MakerMessage::AesKeyChanged),
text("Nonce:"),
text_input("", &String::from_utf8_lossy(x.nonce_holder()))
.on_input(MakerMessage::AesNonceChanged)
]
.spacing(10)
.align_items(Alignment::Center)
),
})
.spacing(20)
.align_items(Alignment::Center)
]
.align_items(Alignment::Start),
column![ column![
text("Windows"), text("Windows"),
row![ row![
@@ -713,6 +582,75 @@ impl Application for Maker {
.spacing(10) .spacing(10)
] ]
.align_items(Alignment::Start), .align_items(Alignment::Start),
row![
column![column![
text("Encrypt Shellcode Plug-in"),
row![
text_input("", self.encrypt_shellcode_plugin())
.on_input(MakerMessage::EncryptShllcodePluginChanged),
choose_button().on_press(MakerMessage::ChooseFileClicked(
ChooseFileType::EncryptShellcodePlugin
))
]
.align_items(Alignment::Center)
.spacing(10),
]
.align_items(Alignment::Start)]
.push_maybe(match self.shellcode_save_type() {
ShellcodeSaveType::Local => None,
ShellcodeSaveType::Remote => Some(column![
text("Format Url Remote Plug-in"),
row![
text_input("", self.format_url_remote_plugin())
.on_input(MakerMessage::FormatUrlRemotePluginChanged),
choose_button().on_press(MakerMessage::ChooseFileClicked(
ChooseFileType::FormatUrlRemote
))
]
.align_items(Alignment::Center)
.spacing(10)
]),
})
.width(Length::FillPortion(1))
.align_items(Alignment::Center),
column![column![
text("Format Encrypted Shellcode Plug-in"),
row![
text_input("", self.format_encrypted_shellcode_plugin())
.on_input(MakerMessage::FormatEncryptedShellcodePluginChanged),
choose_button().on_press(MakerMessage::ChooseFileClicked(
ChooseFileType::FormatEncryptedShellcodePlugin
))
]
.align_items(Alignment::Center)
.spacing(10),
]
.align_items(Alignment::Start)]
.push_maybe(match self.shellcode_save_type() {
ShellcodeSaveType::Local => None,
ShellcodeSaveType::Remote => Some(
column![
text("Upload Final Shellcode Remote Plug-in"),
row![
text_input("", self.upload_final_shellcode_remote_plugin())
.on_input(
MakerMessage::UploadFinalShellcodeRemotePluginChanged
),
choose_button().on_press(MakerMessage::ChooseFileClicked(
ChooseFileType::UploadFinalShellcodeRemote
))
]
.align_items(Alignment::Center)
.spacing(10)
]
.align_items(Alignment::Start)
),
})
.width(Length::FillPortion(1))
.align_items(Alignment::Center)
]
.align_items(Alignment::Center)
.spacing(10),
column![ column![
text("Description"), text("Description"),
text_editor(self.desc()) text_editor(self.desc())
@@ -739,7 +677,7 @@ impl Application for Maker {
))) )))
.width(30) .width(30)
.height(30) .height(30)
.style(svg_style::svg_primary_base), .style(style::svg::svg_primary_base),
) )
.style(button::text) .style(button::text)
.on_press(MakerMessage::B1nClicked); .on_press(MakerMessage::B1nClicked);
@@ -749,7 +687,7 @@ impl Application for Maker {
))) )))
.width(30) .width(30)
.height(30) .height(30)
.style(svg_style::svg_primary_base), .style(style::svg::svg_primary_base),
) )
.style(button::text) .style(button::text)
.on_press(MakerMessage::GithubClicked); .on_press(MakerMessage::GithubClicked);
@@ -778,24 +716,10 @@ impl Application for Maker {
] ]
.align_items(Alignment::Center); .align_items(Alignment::Center);
column![maker, footer].align_items(Alignment::Center).into() column![maker, footer].align_items(Alignment::Center)
} }
fn theme(&self) -> Self::Theme { pub fn theme(&self) -> Theme {
self.selected_theme() self.selected_theme()
} }
} }
fn main() {
match try_main() {
Ok(_) => (),
Err(e) => {
error_dialog(e);
}
}
}
fn try_main() -> anyhow::Result<()> {
Maker::run(settings())?;
Ok(())
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,15 +4,16 @@ use std::{fs, ops::Not};
use anyhow::anyhow; use anyhow::anyhow;
use dirs::data_dir; use dirs::data_dir;
use iced::advanced::Application; use iced::application;
use pumpbin::{error_dialog, plugin::CONFIG_FILE_PATH, settings, Pumpbin}; use pumpbin::{
plugin::CONFIG_FILE_PATH,
utils::{self, error_dialog},
Pumpbin,
};
fn main() { fn main() {
match try_main() { if let Err(e) = try_main() {
Ok(_) => (), error_dialog(e);
Err(e) => {
error_dialog(e);
}
} }
} }
@@ -34,6 +35,10 @@ fn try_main() -> anyhow::Result<()> {
.set(config_path) .set(config_path)
.map_err(|_| anyhow!("Set CONFIG_FILE_PATH failed."))?; .map_err(|_| anyhow!("Set CONFIG_FILE_PATH failed."))?;
Pumpbin::run(settings())?; application("PumpBin", Pumpbin::update, Pumpbin::view)
.settings(utils::settings())
.window(utils::window_settings())
.theme(Pumpbin::theme)
.run()?;
Ok(()) Ok(())
} }

View File

@@ -1,104 +1,102 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
fmt::Display, fs, iter,
fs, ops::Not,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::OnceLock, sync::OnceLock,
}; };
use aes_gcm::{aead::Aead, Aes256Gcm, Key, KeyInit, Nonce}; use anyhow::{anyhow, bail};
use anyhow::anyhow;
use bincode::{decode_from_slice, encode_to_vec, Decode, Encode}; use bincode::{decode_from_slice, encode_to_vec, Decode, Encode};
use capnp::{
message::{self, ReaderOptions},
serialize_packed,
};
use crate::{
plugin_capnp,
plugin_system::{
run_plugin, EncryptShellcodeInput, EncryptShellcodeOutput, FormatEncryptedShellcodeInput,
FormatEncryptedShellcodeOutput, FormatUrlRemoteInput, FormatUrlRemoteOutput, Pass,
UploadFinalShellcodeRemoteInput, UploadFinalShellcodeRemoteOutput,
},
utils, BinaryType, Platform, ShellcodeSaveType,
};
// 500 MiB
const LIMIT: usize = 1024 * 1024 * 500;
pub const BINCODE_PLUGIN_CONFIG: bincode::config::Configuration<
bincode::config::LittleEndian,
bincode::config::Varint,
bincode::config::Limit<LIMIT>,
> = bincode::config::standard().with_limit();
const BINCODE_PLUGINS_CONFIG: bincode::config::Configuration = bincode::config::standard(); const BINCODE_PLUGINS_CONFIG: bincode::config::Configuration = bincode::config::standard();
pub static CONFIG_FILE_PATH: OnceLock<PathBuf> = OnceLock::new(); pub static CONFIG_FILE_PATH: OnceLock<PathBuf> = OnceLock::new();
#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] #[derive(Debug, Default, Clone)]
pub struct AesGcmPass { pub struct PluginInfo {
key_holder: Vec<u8>, pub plugin_name: String,
nonce_holder: Vec<u8>, pub author: String,
pub version: String,
pub desc: String,
} }
impl AesGcmPass { impl PluginInfo {
pub fn key_holder(&self) -> &[u8] { pub fn plugin_name(&self) -> &str {
&self.key_holder &self.plugin_name
} }
pub fn key_holder_mut(&mut self) -> &mut Vec<u8> { pub fn author(&self) -> &str {
&mut self.key_holder &self.author
} }
pub fn nonce_holder(&self) -> &[u8] { pub fn version(&self) -> &str {
&self.nonce_holder &self.version
} }
pub fn nonce_holder_mut(&mut self) -> &mut Vec<u8> { pub fn desc(&self) -> &str {
&mut self.nonce_holder &self.desc
} }
} }
#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] #[derive(Debug, Default, Clone)]
pub enum EncryptType { pub struct PluginReplace {
None, pub src_prefix: Vec<u8>,
Xor(Vec<u8>), pub size_holder: Option<Vec<u8>>,
AesGcm(AesGcmPass), pub max_len: u64,
} }
impl Display for EncryptType { impl PluginReplace {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { pub fn src_prefix(&self) -> &[u8] {
match self { &self.src_prefix
EncryptType::None => write!(f, "None"), }
EncryptType::Xor(_) => write!(f, "Xor"),
EncryptType::AesGcm(_) => write!(f, "AesGcm"), pub fn size_holder(&self) -> Option<&Vec<u8>> {
} self.size_holder.as_ref()
}
pub fn max_len(&self) -> usize {
self.max_len as usize
} }
} }
impl EncryptType { #[derive(Debug, Default, Clone)]
pub const fn all() -> [EncryptType; 3] {
[
EncryptType::None,
EncryptType::Xor(vec![]),
EncryptType::AesGcm(AesGcmPass {
key_holder: vec![],
nonce_holder: vec![],
}),
]
}
pub fn encrypt(&self, path: &Path) -> anyhow::Result<Vec<u8>> {
let data = fs::read(path)?;
match self {
EncryptType::None => Ok(data),
EncryptType::Xor(x) => Ok(data
.iter()
.enumerate()
.map(|(i, byte)| byte ^ x[i % x.len()])
.collect()),
EncryptType::AesGcm(x) => {
let key = Key::<Aes256Gcm>::from_slice(x.key_holder());
let aes = Aes256Gcm::new(key);
let nonce = Nonce::from_slice(x.nonce_holder());
aes.encrypt(nonce, data.as_slice()).map_err(|e| anyhow!(e))
}
}
}
}
#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
pub struct Bins { pub struct Bins {
pub executable: Option<Vec<u8>>, pub executable: Option<Vec<u8>>,
pub dynamic_library: Option<Vec<u8>>, pub dynamic_library: Option<Vec<u8>>,
} }
impl Bins {
pub fn is_platform_supported(&self) -> bool {
matches!((self.executable(), self.dynamic_library()), (None, None)).not()
}
pub fn supported_binary_types(&self) -> Vec<BinaryType> {
let mut bin_types = Vec::default();
if self.executable().is_some() {
bin_types.push(BinaryType::Executable);
}
if self.dynamic_library().is_some() {
bin_types.push(BinaryType::DynamicLibrary);
}
bin_types
}
}
impl Bins { impl Bins {
pub fn executable(&self) -> Option<&Vec<u8>> { pub fn executable(&self) -> Option<&Vec<u8>> {
self.executable.as_ref() self.executable.as_ref()
@@ -107,97 +105,424 @@ impl Bins {
pub fn dynamic_library(&self) -> Option<&Vec<u8>> { pub fn dynamic_library(&self) -> Option<&Vec<u8>> {
self.dynamic_library.as_ref() self.dynamic_library.as_ref()
} }
}
#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] pub fn executable_mut(&mut self) -> &mut Option<Vec<u8>> {
pub struct Platforms { &mut self.executable
pub windows: Option<Bins>,
pub linux: Option<Bins>,
pub darwin: Option<Bins>,
}
impl Platforms {
pub fn windows(&self) -> Option<&Bins> {
self.windows.as_ref()
} }
pub fn linux(&self) -> Option<&Bins> { pub fn dynamic_library_mut(&mut self) -> &mut Option<Vec<u8>> {
self.linux.as_ref() &mut self.dynamic_library
}
pub fn darwin(&self) -> Option<&Bins> {
self.darwin.as_ref()
} }
} }
#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] #[derive(Debug, Default, Clone)]
pub struct PluginBins {
pub windows: Bins,
pub linux: Bins,
pub darwin: Bins,
}
impl PluginBins {
pub fn supported_plaforms(&self) -> Vec<Platform> {
let mut platforms = Vec::default();
if self.windows().is_platform_supported() {
platforms.push(Platform::Windows);
}
if self.linux().is_platform_supported() {
platforms.push(Platform::Linux);
}
if self.darwin().is_platform_supported() {
platforms.push(Platform::Darwin);
}
platforms
}
pub fn get_that_binary(&self, platform: Platform, bin_type: BinaryType) -> Vec<u8> {
let platform = match platform {
Platform::Windows => self.windows(),
Platform::Linux => self.linux(),
Platform::Darwin => self.darwin(),
};
match bin_type {
BinaryType::Executable => platform.executable().unwrap().to_vec(),
BinaryType::DynamicLibrary => platform.dynamic_library().unwrap().to_vec(),
}
}
}
impl PluginBins {
pub fn windows(&self) -> &Bins {
&self.windows
}
pub fn linux(&self) -> &Bins {
&self.linux
}
pub fn darwin(&self) -> &Bins {
&self.darwin
}
}
#[derive(Debug, Default, Clone)]
pub struct PluginPlugins {
pub encrypt_shellcode: Option<Vec<u8>>,
pub format_encrypted_shellcode: Option<Vec<u8>>,
pub format_url_remote: Option<Vec<u8>>,
pub upload_final_shellcode_remote: Option<Vec<u8>>,
}
impl PluginPlugins {
pub fn run_encrypt_shellcode(&self, path: &Path) -> anyhow::Result<EncryptShellcodeOutput> {
let shellcode = fs::read(path)?;
Ok(if let Some(wasm) = self.encrypt_shellcode() {
let input = EncryptShellcodeInput { shellcode };
let res = run_plugin(wasm, "encrypt_shellcode", &input)?;
serde_json::from_slice(res.as_slice())?
} else {
EncryptShellcodeOutput {
encrypted: shellcode,
..Default::default()
}
})
}
pub fn run_format_encrypted_shellcode(
&self,
shellcode: &[u8],
) -> anyhow::Result<FormatEncryptedShellcodeOutput> {
let shellcode = shellcode.to_owned();
Ok(if let Some(wasm) = self.format_encrypted_shellcode() {
let input = FormatEncryptedShellcodeInput { shellcode };
let res = run_plugin(wasm, "format_encrypted_shellcode", &input)?;
serde_json::from_slice(res.as_slice())?
} else {
FormatEncryptedShellcodeOutput {
formated_shellcode: shellcode,
}
})
}
pub fn run_format_url_remote(&self, url: &str) -> anyhow::Result<FormatUrlRemoteOutput> {
let url = url.to_owned();
Ok(if let Some(wasm) = self.format_url_remote() {
let input = FormatUrlRemoteInput { url };
let res = run_plugin(wasm, "format_url_remote", &input)?;
serde_json::from_slice(res.as_slice())?
} else {
FormatUrlRemoteOutput { formated_url: url }
})
}
pub fn run_upload_final_shellcode_remote(
&self,
final_shellcode: &[u8],
) -> anyhow::Result<UploadFinalShellcodeRemoteOutput> {
let final_shellcode = final_shellcode.to_owned();
Ok(if let Some(wasm) = self.upload_final_shellcode_remote() {
let input = UploadFinalShellcodeRemoteInput { final_shellcode };
let res = run_plugin(wasm, "upload_final_shellcode_remote", &input)?;
serde_json::from_slice(res.as_slice())?
} else {
UploadFinalShellcodeRemoteOutput::default()
})
}
}
impl PluginPlugins {
pub fn encrypt_shellcode(&self) -> Option<&Vec<u8>> {
self.encrypt_shellcode.as_ref()
}
pub fn format_encrypted_shellcode(&self) -> Option<&Vec<u8>> {
self.format_encrypted_shellcode.as_ref()
}
pub fn format_url_remote(&self) -> Option<&Vec<u8>> {
self.format_url_remote.as_ref()
}
pub fn upload_final_shellcode_remote(&self) -> Option<&Vec<u8>> {
self.upload_final_shellcode_remote.as_ref()
}
pub fn encrypt_shellcode_mut(&mut self) -> &mut Option<Vec<u8>> {
&mut self.encrypt_shellcode
}
pub fn format_encrypted_shellcode_mut(&mut self) -> &mut Option<Vec<u8>> {
&mut self.format_encrypted_shellcode
}
pub fn format_url_remote_mut(&mut self) -> &mut Option<Vec<u8>> {
&mut self.format_url_remote
}
pub fn upload_final_shellcode_remote_mut(&mut self) -> &mut Option<Vec<u8>> {
&mut self.upload_final_shellcode_remote
}
}
#[derive(Debug, Default, Clone)]
pub struct Plugin { pub struct Plugin {
pub plugin_name: String, pub version: String,
pub author: Option<String>, pub info: PluginInfo,
pub version: Option<String>, pub replace: PluginReplace,
pub desc: Option<String>, pub bins: PluginBins,
pub prefix: Vec<u8>, pub plugins: PluginPlugins,
pub size_holder: Option<Vec<u8>>,
pub max_len: usize,
pub encrypt_type: EncryptType,
pub platforms: Platforms,
} }
impl Plugin { impl Plugin {
pub fn plugin_name(&self) -> &str { pub fn decode_from_slice(data: &[u8]) -> anyhow::Result<Self> {
&self.plugin_name let message = serialize_packed::read_message(data, ReaderOptions::new())?;
let plugin = message.get_root::<plugin_capnp::plugin::Reader>()?;
let info = plugin.get_info()?;
let replace = plugin.get_replace()?;
let bins = plugin.get_bins()?;
let plugins = plugin.get_plugins()?;
let check_empty = |bin: &[u8]| {
if bin.is_empty() {
None
} else {
Some(bin.to_vec())
}
};
Ok(Self {
version: plugin.get_version()?.to_string()?,
info: PluginInfo {
plugin_name: info.get_plugin_name()?.to_string()?,
author: info.get_author()?.to_string()?,
version: info.get_version()?.to_string()?,
desc: info.get_desc()?.to_string()?,
},
replace: PluginReplace {
src_prefix: replace.get_src_prefix()?.to_vec(),
size_holder: check_empty(replace.get_size_holder()?),
max_len: replace.get_max_len(),
},
bins: PluginBins {
windows: {
let platform_bins = bins.get_windows()?;
Bins {
executable: check_empty(platform_bins.get_executable()?),
dynamic_library: check_empty(platform_bins.get_dynamic_library()?),
}
},
linux: {
let platform_bins = bins.get_linux()?;
Bins {
executable: check_empty(platform_bins.get_executable()?),
dynamic_library: check_empty(platform_bins.get_dynamic_library()?),
}
},
darwin: {
let platform_bins = bins.get_darwin()?;
Bins {
executable: check_empty(platform_bins.get_executable()?),
dynamic_library: check_empty(platform_bins.get_dynamic_library()?),
}
},
},
plugins: PluginPlugins {
encrypt_shellcode: check_empty(plugins.get_encrypt_shellcode()?),
format_encrypted_shellcode: check_empty(plugins.get_format_encrypted_shellcode()?),
format_url_remote: check_empty(plugins.get_format_url_remote()?),
upload_final_shellcode_remote: check_empty(
plugins.get_upload_final_shellcode_remote()?,
),
},
})
} }
pub fn author(&self) -> Option<&String> { pub fn encode_to_vec(&self) -> anyhow::Result<Vec<u8>> {
self.author.as_ref() let mut message = message::Builder::new_default();
let mut plugin = message.init_root::<plugin_capnp::plugin::Builder>();
plugin.set_version(self.version());
let mut info = plugin.reborrow().init_info();
let plugin_info = self.info();
info.set_plugin_name(plugin_info.plugin_name());
info.set_author(plugin_info.author());
info.set_version(plugin_info.version());
info.set_desc(plugin_info.desc());
let mut replace = plugin.reborrow().init_replace();
let plugin_replace = self.replace();
replace.set_src_prefix(plugin_replace.src_prefix());
if let Some(size_holder) = plugin_replace.size_holder() {
replace.set_size_holder(size_holder);
}
replace.set_max_len(plugin_replace.max_len() as u64);
let mut bins = plugin.reborrow().init_bins();
if self.bins().windows().is_platform_supported() {
let mut builder = bins.reborrow().init_windows();
let platform_bins = self.bins().windows();
if let Some(bin) = platform_bins.executable() {
builder.set_executable(bin);
}
if let Some(bin) = platform_bins.dynamic_library() {
builder.set_dynamic_library(bin);
}
}
if self.bins().linux().is_platform_supported() {
let mut builder = bins.reborrow().init_linux();
let platform_bins = self.bins().linux();
if let Some(bin) = platform_bins.executable() {
builder.set_executable(bin);
}
if let Some(bin) = platform_bins.dynamic_library() {
builder.set_dynamic_library(bin);
}
}
if self.bins().darwin().is_platform_supported() {
let mut builder = bins.reborrow().init_darwin();
let platform_bins = self.bins().darwin();
if let Some(bin) = platform_bins.executable() {
builder.set_executable(bin);
}
if let Some(bin) = platform_bins.dynamic_library() {
builder.set_dynamic_library(bin);
}
}
let mut plugins = plugin.reborrow().init_plugins();
let plugin_plugins = self.plugins();
if let Some(plugin) = plugin_plugins.encrypt_shellcode() {
plugins.set_encrypt_shellcode(plugin);
}
if let Some(plugin) = plugin_plugins.format_encrypted_shellcode() {
plugins.set_format_encrypted_shellcode(plugin);
}
if let Some(plugin) = plugin_plugins.format_url_remote() {
plugins.set_format_url_remote(plugin);
}
if let Some(plugin) = plugin_plugins.upload_final_shellcode_remote() {
plugins.set_upload_final_shellcode_remote(plugin);
}
let mut buf = Vec::new();
serialize_packed::write_message(&mut buf, &message)?;
anyhow::Ok(buf)
} }
pub fn version(&self) -> Option<&String> { pub fn replace_binary(
self.version.as_ref() &self,
} bin: &mut [u8],
shellcode_src: String,
mut pass: Vec<Pass>,
) -> anyhow::Result<()> {
let save_type = if self.replace().size_holder().is_some() {
ShellcodeSaveType::Local
} else {
ShellcodeSaveType::Remote
};
pub fn desc(&self) -> Option<&String> { // replace shellcode src
self.desc.as_ref() let shellcode_src = match save_type {
} ShellcodeSaveType::Local => {
let path = Path::new(&shellcode_src);
let output = self.plugins().run_encrypt_shellcode(path)?;
pass = output.pass().to_vec();
pub fn prefix(&self) -> &[u8] { let final_shellcode = self
&self.prefix .plugins()
} .run_format_encrypted_shellcode(output.encrypted())?;
pub fn size_holder(&self) -> Option<&Vec<u8>> { final_shellcode.formated_shellcode().to_vec()
self.size_holder.as_ref() }
} ShellcodeSaveType::Remote => {
let mut shellcode_src = self
.plugins()
.run_format_url_remote(&shellcode_src)?
.formated_url()
.as_bytes()
.to_vec();
shellcode_src.push(b'\0');
pub fn max_len(&self) -> usize { shellcode_src
self.max_len }
} };
pub fn encrypt_type(&self) -> &EncryptType { if shellcode_src.len() > self.replace().max_len() {
&self.encrypt_type bail!(
} "{} too long.",
match save_type {
ShellcodeSaveType::Local => "Shellcode",
ShellcodeSaveType::Remote => "Shellcode Url",
}
);
}
pub fn platforms(&self) -> &Platforms { utils::replace(
&self.platforms bin,
} self.replace().src_prefix(),
} shellcode_src.as_slice(),
self.replace().max_len(),
);
impl Plugin { // replace pass
pub fn reade_plugin(path: &Path) -> anyhow::Result<Plugin> { for pass in pass {
let buf = fs::read(path)?; let holder = pass.holder();
let (plugin, _) = decode_from_slice(buf.as_slice(), BINCODE_PLUGIN_CONFIG)?; let replace_by = pass.replace_by();
Ok(plugin)
}
pub fn write_plugin(&self, path: &Path) -> anyhow::Result<()> { utils::replace(bin, holder, replace_by, holder.len());
let buf = encode_to_vec(self, BINCODE_PLUGIN_CONFIG)?; }
fs::write(path, buf.as_slice())?;
// replace size_holder
if save_type == ShellcodeSaveType::Local {
let size_holder = self.replace().size_holder().unwrap();
let shellcode_len_bytes = shellcode_src.len().to_string().as_bytes().to_vec();
if shellcode_len_bytes.len() > size_holder.len() {
bail!("Shellcode size bytes too long.");
}
let mut size_bytes: Vec<u8> = iter::repeat(b'0')
.take(size_holder.len() - shellcode_len_bytes.len())
.collect();
size_bytes.extend_from_slice(shellcode_len_bytes.as_slice());
utils::replace(bin, size_holder, size_bytes.as_slice(), size_holder.len());
}
Ok(()) Ok(())
} }
} }
impl Plugin {
pub fn version(&self) -> &str {
&self.version
}
pub fn info(&self) -> &PluginInfo {
&self.info
}
pub fn replace(&self) -> &PluginReplace {
&self.replace
}
pub fn bins(&self) -> &PluginBins {
&self.bins
}
pub fn plugins(&self) -> &PluginPlugins {
&self.plugins
}
}
#[derive(Debug, Clone, Default, Encode, Decode, PartialEq, Eq)] #[derive(Debug, Clone, Default, Encode, Decode, PartialEq, Eq)]
pub struct Plugins(pub HashMap<String, Plugin>); pub struct Plugins(HashMap<String, Vec<u8>>);
impl Plugins { impl Plugins {
pub fn reade_plugins() -> anyhow::Result<Plugins> { pub fn reade_plugins() -> anyhow::Result<Plugins> {
@@ -224,77 +549,31 @@ impl Plugins {
Ok(()) Ok(())
} }
}
#[cfg(test)] pub fn get(&self, name: &str) -> anyhow::Result<Plugin> {
mod test { let buf = self
use std::io::Write; .0
.get(name)
.ok_or(anyhow!("Get plugin by name failed."))?;
use tempfile::NamedTempFile; Plugin::decode_from_slice(buf)
use super::*;
#[test]
fn test_plugin() {
let plugin = Plugin {
plugin_name: "test_plugin".into(),
author: Some("b1n".into()),
version: Some("0.1.0".into()),
desc: Some("test desc".into()),
prefix: b"$$SHELLCODE$$".to_vec(),
size_holder: Some(b"$$99999$$".to_vec()),
max_len: 1024 * 1024,
encrypt_type: EncryptType::AesGcm(AesGcmPass {
key_holder: b"key".to_vec(),
nonce_holder: b"nonce".to_vec(),
}),
platforms: Platforms {
windows: None,
linux: None,
darwin: None,
},
};
let plugin_file = NamedTempFile::new().unwrap();
plugin.write_plugin(plugin_file.path()).unwrap();
let decode_plugin = Plugin::reade_plugin(plugin_file.path()).unwrap();
assert_eq!(decode_plugin, plugin);
} }
#[test] pub fn get_sorted_names(&self) -> Vec<String> {
fn test_xor_encrypt() { let mut names: Vec<String> = self.0.keys().map(|x| x.to_owned()).collect();
let mut plain_text = NamedTempFile::new().unwrap(); names.sort();
plain_text.as_file_mut().write_all(b"test").unwrap(); names
let pass = b"pass";
let xor = EncryptType::Xor(pass.to_vec());
let encrypted = xor.encrypt(plain_text.path()).unwrap();
let decrypted: Vec<u8> = encrypted
.iter()
.enumerate()
.map(|(i, byte)| byte ^ pass[i % pass.len()])
.collect();
assert_eq!(decrypted.as_slice(), b"test");
} }
#[test] pub fn is_empty(&self) -> bool {
fn test_aes_gcm_encrypt() { self.0.is_empty()
let mut plain_text = NamedTempFile::new().unwrap(); }
plain_text.as_file_mut().write_all(b"test").unwrap();
let pass = AesGcmPass { pub fn insert(&mut self, name: String, plugin: Vec<u8>) {
key_holder: b"kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk".to_vec(), self.0.insert(name, plugin);
nonce_holder: b"nnnnnnnnnnnn".to_vec(), }
};
let aes_gcm = EncryptType::AesGcm(pass.clone());
let encrypted = aes_gcm.encrypt(plain_text.path()).unwrap();
let aes = Aes256Gcm::new_from_slice(pass.key_holder()).unwrap(); pub fn remove(&mut self, name: &str) {
let nonce = Nonce::from_slice(pass.nonce_holder()); self.0.remove(name);
let decrypted = aes.decrypt(nonce, encrypted.as_slice()).unwrap();
assert_eq!(decrypted.as_slice(), b"test");
} }
} }

99
src/plugin_system.rs Normal file
View File

@@ -0,0 +1,99 @@
use std::time::Duration;
use extism::{Manifest, Wasm};
use serde::{Deserialize, Serialize};
pub fn run_plugin<T: Serialize>(wasm: &[u8], func: &str, input: &T) -> anyhow::Result<Vec<u8>> {
let data = Wasm::data(wasm.to_vec());
let manifest = Manifest::new([data])
.with_timeout(Duration::from_secs(5))
.with_allowed_host("*");
let mut plugin = extism::Plugin::new(manifest, [], true)?;
plugin.call::<Vec<u8>, Vec<u8>>(func, serde_json::to_vec(input)?)
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct EncryptShellcodeInput {
pub shellcode: Vec<u8>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Pass {
pub holder: Vec<u8>,
pub replace_by: Vec<u8>,
}
impl Pass {
pub fn holder(&self) -> &[u8] {
&self.holder
}
pub fn replace_by(&self) -> &[u8] {
&self.replace_by
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct EncryptShellcodeOutput {
pub encrypted: Vec<u8>,
pub pass: Vec<Pass>,
}
impl EncryptShellcodeOutput {
pub fn encrypted(&self) -> &[u8] {
&self.encrypted
}
pub fn pass(&self) -> &[Pass] {
&self.pass
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct FormatEncryptedShellcodeInput {
pub shellcode: Vec<u8>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct FormatEncryptedShellcodeOutput {
pub formated_shellcode: Vec<u8>,
}
impl FormatEncryptedShellcodeOutput {
pub fn formated_shellcode(&self) -> &[u8] {
&self.formated_shellcode
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct FormatUrlRemoteInput {
pub url: String,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct FormatUrlRemoteOutput {
pub formated_url: String,
}
impl FormatUrlRemoteOutput {
pub fn formated_url(&self) -> &str {
&self.formated_url
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct UploadFinalShellcodeRemoteInput {
pub final_shellcode: Vec<u8>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct UploadFinalShellcodeRemoteOutput {
pub url: String,
}
impl UploadFinalShellcodeRemoteOutput {
pub fn url(&self) -> &str {
&self.url
}
}

2
src/style/mod.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod button;
pub mod svg;

78
src/utils.rs Normal file
View File

@@ -0,0 +1,78 @@
use std::iter;
use iced::{
advanced::graphics::image::image_rs::ImageFormat,
window::{self, Level, Position},
Font, Pixels, Settings, Size, Task,
};
use memchr::memmem;
use rand::RngCore;
use rfd::{AsyncMessageDialog, MessageButtons, MessageDialog, MessageDialogResult, MessageLevel};
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 message_dialog(message: String, level: MessageLevel) -> Task<MessageDialogResult> {
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 {
Settings {
fonts: vec![include_bytes!("../assets/JetBrainsMonoNerdFont-Regular.ttf").into()],
default_font: JETBRAINS_MONO_FONT,
default_text_size: Pixels(13.0),
antialiasing: true,
..Default::default()
}
}
pub fn window_settings() -> window::Settings {
let size = Size::new(1000.0, 600.0);
window::Settings {
size,
position: Position::Centered,
min_size: Some(size),
visible: true,
resizable: true,
decorations: true,
transparent: false,
level: Level::Normal,
icon: window::icon::from_file_data(
include_bytes!("../logo/icon.png"),
Some(ImageFormat::Png),
)
.ok(),
exit_on_close_request: true,
..Default::default()
}
}
pub fn replace(bin: &mut [u8], holder: &[u8], replace_by: &[u8], max_len: usize) {
let mut replace_by = replace_by.to_owned();
let find = memmem::find_iter(bin, holder).next();
if let Some(position) = find {
let mut random: Vec<u8> = iter::repeat(b'0')
.take(max_len - replace_by.len())
.collect();
rand::thread_rng().fill_bytes(&mut random);
replace_by.extend_from_slice(random.as_slice());
bin[position..(position + max_len)].copy_from_slice(replace_by.as_slice());
}
}