mirror of
https://github.com/Astatin3/unshell-nodes-rs.git
synced 2026-06-08 16:18:08 -06:00
Start rewrite, get layers working
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
use std::{error::Error, io::Write, net::SocketAddr};
|
||||
|
||||
use unshell_rs_lib::{
|
||||
layers::{LayerConfig, build_client},
|
||||
networkers::{ClientTrait, Connection, TCPClient},
|
||||
};
|
||||
|
||||
use crate::client;
|
||||
|
||||
pub struct Cli;
|
||||
|
||||
impl Cli {
|
||||
pub fn connect(addr: SocketAddr) -> Result<(), Box<dyn Error>> {
|
||||
let mut client = build_client(
|
||||
TCPClient::connect(&addr)?,
|
||||
vec![LayerConfig::Handshake, LayerConfig::Base64],
|
||||
)?;
|
||||
|
||||
let stdin = std::io::stdin();
|
||||
let mut stdout = std::io::stdout();
|
||||
|
||||
loop {
|
||||
print!("> ");
|
||||
stdout.flush()?;
|
||||
|
||||
let mut input = String::new();
|
||||
stdin.read_line(&mut input)?;
|
||||
let input = input.trim();
|
||||
|
||||
client.write(input)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
use std::{
|
||||
error::Error,
|
||||
net::SocketAddr,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use unshell_rs_lib::{
|
||||
connection::{C2Packet, Parameter, Parameters},
|
||||
networkers::{AsyncConnection, ClientTrait, TCPClient, TCPConnection},
|
||||
};
|
||||
|
||||
pub struct UnshellClient {
|
||||
#[allow(dead_code)]
|
||||
addr: SocketAddr,
|
||||
tx: Sender<C2Packet>,
|
||||
pub rx: Receiver<C2Packet>,
|
||||
parameters: Arc<Mutex<Parameters>>,
|
||||
}
|
||||
|
||||
impl UnshellClient {
|
||||
pub fn new(addr: SocketAddr) -> Result<Self, Box<dyn Error>> {
|
||||
let client = TCPClient::connect(&addr)?;
|
||||
|
||||
let (tx, rx) = TCPConnection::as_async(client);
|
||||
|
||||
let parameters = Arc::new(Mutex::new(Parameters::new()));
|
||||
|
||||
Ok(Self {
|
||||
addr,
|
||||
tx,
|
||||
rx,
|
||||
parameters,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_parameter(&mut self, key: String, param: Parameter) {
|
||||
let mut params_lock = self.parameters.lock().unwrap();
|
||||
params_lock.insert(key.clone(), param.clone());
|
||||
self.tx.send(C2Packet::SetParameter(key, param)).unwrap();
|
||||
}
|
||||
|
||||
pub fn get_parameter(&self, key: &str) -> Option<Parameter> {
|
||||
self.parameters.lock().unwrap().get(key).cloned()
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
use slint::{ComponentHandle, Weak};
|
||||
use std::{
|
||||
error::Error,
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
};
|
||||
use unshell_rs_lib::connection::{C2Packet, Parameter};
|
||||
|
||||
use crate::client::UnshellClient;
|
||||
|
||||
pub struct UnshellGui {}
|
||||
|
||||
slint::include_modules!();
|
||||
impl UnshellGui {
|
||||
pub fn start(client: UnshellClient) -> Result<(), Box<dyn Error>> {
|
||||
let ui = AppWindow::new()?;
|
||||
let rx = client.rx.clone();
|
||||
let client = Arc::new(Mutex::new(client));
|
||||
|
||||
let ui_handle = ui.as_weak();
|
||||
let client_clone = Arc::clone(&client);
|
||||
ui.on_tab_clicked(move |index| {
|
||||
let ui = ui_handle.unwrap();
|
||||
ui.set_current_tab(index);
|
||||
let mut client_lock = client_clone.lock().unwrap();
|
||||
client_lock.set_parameter("Current Tab".to_string(), Parameter::CurrentTab(index));
|
||||
trace!("Tab {} selected", index);
|
||||
});
|
||||
|
||||
ui.set_app_info({
|
||||
(String::new()
|
||||
+ "Unshell\n"
|
||||
+ "Version "
|
||||
+ env!("CARGO_PKG_VERSION")
|
||||
+ "\n\n View the source code at:\n https://github.com/astatin3/unshell-rs")
|
||||
.into()
|
||||
});
|
||||
|
||||
let ui_handle = ui.as_weak();
|
||||
thread::spawn(move || {
|
||||
fn on_param_update(ui_handle: Weak<AppWindow>, parameter: &Parameter) {
|
||||
// info!("{}", name);
|
||||
match parameter {
|
||||
Parameter::Test1 => todo!(),
|
||||
Parameter::CurrentTab(i) => {
|
||||
let i = i.clone();
|
||||
slint::invoke_from_event_loop(move || {
|
||||
ui_handle.unwrap().set_current_tab(i)
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
if let Ok(data) = rx.recv() {
|
||||
match data {
|
||||
C2Packet::SetAllParameters(parameters) => {
|
||||
for key in parameters.keys() {
|
||||
on_param_update(ui_handle.clone(), parameters.get(key).unwrap());
|
||||
}
|
||||
}
|
||||
C2Packet::ParameterUpate(name, parameter) => {
|
||||
on_param_update(ui_handle.clone(), ¶meter);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ui.run()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+2
-5
@@ -1,6 +1,3 @@
|
||||
mod gui;
|
||||
mod cli;
|
||||
|
||||
mod client;
|
||||
|
||||
pub use client::UnshellClient;
|
||||
pub use gui::UnshellGui;
|
||||
pub use cli::Cli;
|
||||
|
||||
+6
-4
@@ -2,8 +2,10 @@
|
||||
extern crate log;
|
||||
|
||||
mod client;
|
||||
mod server;
|
||||
// mod server;
|
||||
|
||||
pub use client::UnshellClient;
|
||||
pub use client::UnshellGui;
|
||||
pub use server::UnshellServer;
|
||||
pub use client::Cli;
|
||||
|
||||
// pub use client::UnshellClient;
|
||||
// pub use client::UnshellGui;
|
||||
// pub use server::UnshellServer;
|
||||
|
||||
+22
-36
@@ -7,7 +7,9 @@ use std::{
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use log::error;
|
||||
use unshell_rs::{UnshellClient, UnshellGui, UnshellServer};
|
||||
use unshell_rs::Cli;
|
||||
use unshell_rs_lib::connection::Node;
|
||||
// use unshell_rs::{UnshellClient, UnshellGui, UnshellServer};
|
||||
// use unshell_rs
|
||||
|
||||
pub static DEFAULT_CONFIG_FILEPATH: &'static str = "server_config.json";
|
||||
@@ -32,7 +34,7 @@ struct Args {
|
||||
enum Commands {
|
||||
/// Run as a service, and potentially hosting a website
|
||||
#[command(arg_required_else_help = true)]
|
||||
Server {
|
||||
Relay {
|
||||
/// IPv4 to listen for clients on.
|
||||
host: String,
|
||||
|
||||
@@ -47,8 +49,8 @@ enum Commands {
|
||||
// #[arg(short, long, default_value_t = DEFAULT_SERVICE_PORT)]
|
||||
// web_port: u16,
|
||||
},
|
||||
/// Run GUI and connect to remote server
|
||||
Remote {
|
||||
/// Connect to remote server
|
||||
Connect {
|
||||
/// Remote server to connect to
|
||||
host: String,
|
||||
|
||||
@@ -56,12 +58,6 @@ enum Commands {
|
||||
#[arg(short, long, default_value_t = DEFAULT_SERVICE_PORT)]
|
||||
port: u16,
|
||||
},
|
||||
/// Run both server and GUI on local machine.
|
||||
Local {
|
||||
/// Json file to store config
|
||||
#[arg(short, long, default_value_t = DEFAULT_CONFIG_FILEPATH.to_string())]
|
||||
config_filepath: String,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
@@ -73,41 +69,31 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args = Args::parse();
|
||||
|
||||
match args.command {
|
||||
Commands::Local { config_filepath } => {
|
||||
let mut server = UnshellServer::from_filepath(config_filepath.as_str());
|
||||
server.run(LOCAL_SOCKET)?;
|
||||
|
||||
let client = UnshellClient::new(LOCAL_SOCKET)?;
|
||||
|
||||
UnshellGui::start(client)?;
|
||||
}
|
||||
Commands::Remote { host, port } => {
|
||||
let addr = SocketAddr::from_str(format!("{}:{}", host, port).as_str());
|
||||
let client = UnshellClient::new(if let Ok(addr) = addr {
|
||||
addr
|
||||
} else {
|
||||
error!("Could not parse address!");
|
||||
return Ok(());
|
||||
})?;
|
||||
|
||||
UnshellGui::start(client)?;
|
||||
}
|
||||
Commands::Server {
|
||||
Commands::Relay {
|
||||
host,
|
||||
port,
|
||||
config_filepath,
|
||||
} => {
|
||||
let mut unshell_server = UnshellServer::from_filepath(config_filepath.as_str());
|
||||
|
||||
let addr = SocketAddr::from_str(format!("{}:{}", host, port).as_str());
|
||||
if let Ok(addr) = addr {
|
||||
unshell_server.run(addr)?;
|
||||
if let Err(e) = Node::run(if let Ok(addr) = addr {
|
||||
addr
|
||||
} else {
|
||||
error!("Could not parse address!");
|
||||
return Ok(());
|
||||
}) {
|
||||
error!("{}", e);
|
||||
}
|
||||
}
|
||||
Commands::Connect { host, port } => {
|
||||
let addr = SocketAddr::from_str(format!("{}:{}", host, port).as_str());
|
||||
if let Err(e) = Cli::connect(if let Ok(addr) = addr {
|
||||
addr
|
||||
} else {
|
||||
error!("Could not parse address!");
|
||||
return Ok(());
|
||||
}) {
|
||||
error!("{}", e);
|
||||
}
|
||||
|
||||
loop {}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use unshell_rs_lib::config::campaign::CampaignConfig;
|
||||
|
||||
use unshell_rs_lib::connection::Parameters;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref DEFAULT_CAMPAIGN: CampaignConfig = CampaignConfig {
|
||||
name: "Default Campaign".to_string(),
|
||||
listeners: Vec::new(),
|
||||
};
|
||||
pub static ref DEFAULT_USERS: Vec<User> = vec![User {
|
||||
name: "User".into(),
|
||||
key: "CHANGEME".to_string(),
|
||||
}];
|
||||
pub static ref DEFAULT_PARAMETERS: Parameters = {
|
||||
let p = Parameters::new();
|
||||
|
||||
p
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct User {
|
||||
pub name: String,
|
||||
pub key: String,
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
mod config;
|
||||
mod server;
|
||||
|
||||
pub use crate::server::config::{DEFAULT_CAMPAIGN, DEFAULT_USERS, User};
|
||||
|
||||
pub use server::UnshellServer;
|
||||
@@ -1,155 +0,0 @@
|
||||
use std::{
|
||||
error::Error,
|
||||
fs::File,
|
||||
io::Read,
|
||||
net::SocketAddr,
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
};
|
||||
|
||||
use crossbeam_channel::Sender;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use unshell_rs_lib::{
|
||||
config::campaign::CampaignConfig,
|
||||
connection::{C2Packet, ErrorPacket, Parameters},
|
||||
networkers::{AsyncConnection, ServerTrait, TCPConnection, TCPServer, run_listener_state},
|
||||
};
|
||||
|
||||
use crate::server::{DEFAULT_CAMPAIGN, DEFAULT_USERS, User, config::DEFAULT_PARAMETERS};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct UnshellServerConfig {
|
||||
campaign: CampaignConfig,
|
||||
parameters: Parameters,
|
||||
users: Vec<User>,
|
||||
|
||||
#[serde(skip)]
|
||||
clients: Vec<Client>,
|
||||
}
|
||||
|
||||
impl UnshellServerConfig {
|
||||
pub fn broadcast_update_param(&self, name: String) {
|
||||
for i in 0..self.clients.len() {
|
||||
let _ = self.clients.get(i).unwrap().broadcast_tx.send(name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnshellServer {
|
||||
config: Arc<Mutex<UnshellServerConfig>>,
|
||||
}
|
||||
|
||||
impl UnshellServer {
|
||||
pub fn from_filepath(filepath: &str) -> Self {
|
||||
let s = (|| -> Result<Self, Box<dyn Error>> {
|
||||
let mut file = File::open(filepath.to_string())?;
|
||||
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
|
||||
let config = serde_json::from_str::<UnshellServerConfig>(contents.as_str())?;
|
||||
|
||||
info!("Loaded server config from {}", filepath);
|
||||
|
||||
Ok(Self {
|
||||
config: Arc::new(Mutex::new(config)),
|
||||
})
|
||||
})()
|
||||
.unwrap_or({
|
||||
warn!("Loaded default server config");
|
||||
Self {
|
||||
config: Arc::new(Mutex::new(UnshellServerConfig {
|
||||
campaign: DEFAULT_CAMPAIGN.clone(),
|
||||
users: DEFAULT_USERS.clone(),
|
||||
parameters: DEFAULT_PARAMETERS.clone(),
|
||||
|
||||
clients: Vec::new(),
|
||||
})),
|
||||
}
|
||||
});
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
pub fn run(&mut self, addr: SocketAddr) -> Result<(), Box<dyn Error>> {
|
||||
let config_clone = Arc::clone(&self.config);
|
||||
run_listener_state(TCPServer::bind(&addr)?, Client::run, config_clone);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Remote client type for unshell parameters
|
||||
struct Client {
|
||||
pub broadcast_tx: Sender<String>,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn run(connection: TCPConnection, config: Arc<Mutex<UnshellServerConfig>>) {
|
||||
let (tx, rx) = TCPConnection::as_async::<C2Packet>(connection);
|
||||
|
||||
let (broadcast_tx, broadcast_rx) = crossbeam_channel::unbounded::<String>();
|
||||
|
||||
let s = Self { broadcast_tx };
|
||||
|
||||
let mut config_lock = config.lock().unwrap();
|
||||
config_lock.clients.push(s);
|
||||
let config_clone = Arc::clone(&config);
|
||||
let tx_clone = tx.clone();
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
if let Ok(key) = broadcast_rx.recv() {
|
||||
let config_lock = config_clone.lock().unwrap();
|
||||
if let Err(e) = tx_clone.send(C2Packet::ParameterUpate(
|
||||
key.clone(),
|
||||
config_lock.parameters.get(&key).unwrap().clone(),
|
||||
)) {
|
||||
error!("Failed to send packet: {}", e);
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if let Err(e) = tx.send(C2Packet::SetAllParameters(config_lock.parameters.clone())) {
|
||||
error!("Failed to send packet: {}", e);
|
||||
};
|
||||
std::mem::drop(config_lock);
|
||||
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
// if !connection.is_alive() {
|
||||
// warn!("Client {} disconnected!", connection.get_info());
|
||||
// break;
|
||||
// }
|
||||
if let Ok(packet) = rx.recv() {
|
||||
if let Err(e) = match packet {
|
||||
C2Packet::GetParameter(param) => {
|
||||
tx.send(C2Packet::AckGetParameter(param.clone(), {
|
||||
let config_lock = config.lock().unwrap();
|
||||
let result = config_lock.parameters.get(¶m);
|
||||
result.cloned()
|
||||
}))
|
||||
}
|
||||
C2Packet::SetParameter(name, param) => {
|
||||
tx.send(C2Packet::AckSetParameter({
|
||||
let mut config_lock = config.lock().unwrap();
|
||||
config_lock.parameters.insert(name.clone(), param);
|
||||
config_lock.broadcast_update_param(name);
|
||||
true
|
||||
}))
|
||||
}
|
||||
|
||||
C2Packet::Error(error) => {
|
||||
warn!("Got error: {:?}", error);
|
||||
Ok(())
|
||||
}
|
||||
_ => tx.send(C2Packet::Error(ErrorPacket::UnsupportedRequestError)),
|
||||
} {
|
||||
error!("Failed to send packet: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user