Files
pumpbin/src/plugin.rs

591 lines
18 KiB
Rust
Raw Normal View History

use std::{
collections::HashMap,
fs, iter,
ops::Not,
path::{Path, PathBuf},
sync::OnceLock,
};
2024-06-20 17:54:19 +08:00
use anyhow::{anyhow, bail};
use bincode::{decode_from_slice, encode_to_vec, Decode, Encode};
use capnp::{
2024-07-09 15:47:39 +08:00
io::Write,
message::{self, ReaderOptions},
serialize_packed,
};
2024-07-09 15:47:39 +08:00
use flate2::Compression;
2024-06-20 17:54:19 +08:00
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<PathBuf> = OnceLock::new();
#[derive(Debug, Default, Clone)]
pub struct PluginInfo {
pub plugin_name: String,
pub author: String,
pub version: String,
pub desc: String,
2024-06-20 17:54:19 +08:00
}
impl PluginInfo {
pub fn plugin_name(&self) -> &str {
&self.plugin_name
2024-06-20 17:54:19 +08:00
}
pub fn author(&self) -> &str {
&self.author
2024-06-20 17:54:19 +08:00
}
pub fn version(&self) -> &str {
&self.version
2024-06-20 17:54:19 +08:00
}
pub fn desc(&self) -> &str {
&self.desc
2024-06-20 17:54:19 +08:00
}
}
#[derive(Debug, Default, Clone)]
pub struct PluginReplace {
pub src_prefix: Vec<u8>,
pub size_holder: Option<Vec<u8>>,
pub max_len: u64,
2024-06-20 17:54:19 +08:00
}
impl PluginReplace {
pub fn src_prefix(&self) -> &[u8] {
&self.src_prefix
2024-06-20 17:54:19 +08:00
}
pub fn size_holder(&self) -> Option<&Vec<u8>> {
self.size_holder.as_ref()
}
pub fn max_len(&self) -> usize {
self.max_len as usize
2024-06-20 17:54:19 +08:00
}
}
#[derive(Debug, Default, Clone)]
2024-06-20 17:54:19 +08:00
pub struct Bins {
2024-06-21 20:04:21 +08:00
pub executable: Option<Vec<u8>>,
pub dynamic_library: Option<Vec<u8>>,
2024-06-20 17:54:19 +08:00
}
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
}
}
2024-06-20 17:54:19 +08:00
impl Bins {
pub fn executable(&self) -> Option<&Vec<u8>> {
self.executable.as_ref()
}
pub fn dynamic_library(&self) -> Option<&Vec<u8>> {
self.dynamic_library.as_ref()
}
pub fn executable_mut(&mut self) -> &mut Option<Vec<u8>> {
&mut self.executable
}
pub fn dynamic_library_mut(&mut self) -> &mut Option<Vec<u8>> {
&mut self.dynamic_library
}
2024-06-20 17:54:19 +08:00
}
#[derive(Debug, Default, Clone)]
pub struct PluginBins {
pub windows: Bins,
pub linux: Bins,
pub darwin: Bins,
2024-06-20 17:54:19 +08:00
}
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
2024-06-20 17:54:19 +08:00
}
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(),
}
2024-06-20 17:54:19 +08:00
}
}
2024-06-20 17:54:19 +08:00
impl PluginBins {
pub fn windows(&self) -> &Bins {
&self.windows
}
pub fn linux(&self) -> &Bins {
&self.linux
}
pub fn darwin(&self) -> &Bins {
&self.darwin
2024-06-20 17:54:19 +08:00
}
}
#[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>>,
2024-06-20 17:54:19 +08:00
}
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()
}
})
2024-06-20 17:54:19 +08:00
}
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,
}
})
2024-06-20 17:54:19 +08:00
}
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 }
})
2024-06-20 17:54:19 +08:00
}
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()
})
2024-06-20 17:54:19 +08:00
}
}
2024-06-20 17:54:19 +08:00
impl PluginPlugins {
pub fn encrypt_shellcode(&self) -> Option<&Vec<u8>> {
self.encrypt_shellcode.as_ref()
2024-06-20 17:54:19 +08:00
}
pub fn format_encrypted_shellcode(&self) -> Option<&Vec<u8>> {
self.format_encrypted_shellcode.as_ref()
2024-06-20 17:54:19 +08:00
}
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
2024-06-20 17:54:19 +08:00
}
pub fn format_url_remote_mut(&mut self) -> &mut Option<Vec<u8>> {
&mut self.format_url_remote
2024-06-20 17:54:19 +08:00
}
pub fn upload_final_shellcode_remote_mut(&mut self) -> &mut Option<Vec<u8>> {
&mut self.upload_final_shellcode_remote
2024-06-20 17:54:19 +08:00
}
}
#[derive(Debug, Default, Clone)]
pub struct Plugin {
pub version: String,
pub info: PluginInfo,
pub replace: PluginReplace,
pub bins: PluginBins,
pub plugins: PluginPlugins,
}
2024-06-20 17:54:19 +08:00
impl Plugin {
pub fn decode_from_slice(data: &[u8]) -> anyhow::Result<Self> {
2024-07-09 15:47:39 +08:00
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::<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 encode_to_vec(&self) -> anyhow::Result<Vec<u8>> {
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)?;
2024-07-09 15:47:39 +08:00
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<Pass>,
) -> 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<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());
}
2024-06-20 17:54:19 +08:00
Ok(())
2024-06-20 17:54:19 +08:00
}
}
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
}
}
2024-06-20 17:54:19 +08:00
#[derive(Debug, Clone, Default, Encode, Decode, PartialEq, Eq)]
pub struct Plugins(HashMap<String, Vec<u8>>);
2024-06-20 17:54:19 +08:00
impl Plugins {
pub fn reade_plugins() -> anyhow::Result<Plugins> {
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)
2024-06-20 17:54:19 +08:00
}
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."))?;
2024-06-20 17:54:19 +08:00
if plugins_path.is_dir() {
fs::remove_dir(plugins_path)?;
2024-06-20 17:54:19 +08:00
}
2024-06-20 17:54:19 +08:00
fs::write(plugins_path, buf)?;
Ok(())
}
pub fn get(&self, name: &str) -> anyhow::Result<Plugin> {
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<String> {
let mut names: Vec<String> = 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<u8>) {
self.0.insert(name, plugin);
}
pub fn remove(&mut self, name: &str) {
self.0.remove(name);
}
}