Making my own tun library that will actually work

modified:   Cargo.lock
	modified:   Cargo.toml
	renamed:    frida_core/src/config/mod.rs -> frida_cli/src/config/mod.rs
	modified:   frida_cli/src/main.rs
	renamed:    frida_core/src/obfs.rs -> frida_cli/src/obfs.rs
	renamed:    frida_core/src/udp.rs -> frida_cli/src/udp.rs
	renamed:    frida_core/src/client.rs -> frida_client/src/client.rs
	modified:   frida_core/Cargo.toml
	new file:   frida_core/src/device.rs
	modified:   frida_core/src/main.rs
	new file:   frida_core/src/tun.rs
	new file:   frida_core/src/win_tun.rs
	modified:   frida_gui/Cargo.toml
	renamed:    frida_core/src/gui/mod.rs -> frida_gui/src/gui/mod.rs
	renamed:    frida_core/src/gui/tab/mod.rs -> frida_gui/src/gui/tab/mod.rs
	renamed:    frida_core/src/gui/tab_button.rs -> frida_gui/src/gui/tab_button.rs
	renamed:    frida_core/src/gui/tab_panel.rs -> frida_gui/src/gui/tab_panel.rs
	modified:   frida_lib/Cargo.toml
	renamed:    frida_core/src/android.rs -> frida_lib/src/android.rs
	renamed:    frida_core/src/server.rs -> frida_server/src/server.rs
	new file:   wintun.dll
This commit is contained in:
Michael Wain 2024-12-08 03:56:38 +03:00
parent ac30109672
commit 0edc0fef1d
21 changed files with 943 additions and 274 deletions

758
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,3 @@
[workspace]
resolver = "2"
members = ["frida_core","frida_client","frida_server","frida_cli","frida_gui","frida_lib"]

View File

@ -1,3 +1,171 @@
fn main() {
println!("Hello, world!");
use std::{fs, net::{Ipv4Addr}, str};
use clap::{App, Arg, ArgMatches};
use env_logger::Builder;
use log::{error, LevelFilter};
use crate::config::{ ServerConfiguration, ClientConfiguration, ObfsProtocol, ServerPeer };
use crate::client::{desktop::DesktopClient, general::VpnClient};
mod obfs;
mod server;
mod client;
mod udp;
mod config;
fn generate_server_config(matches: &ArgMatches, config_path: &str) {
let bind_address = matches.value_of("bind-address").expect("No bind address specified");
let internal_address = matches.value_of("internal-address").expect("No internal address specified");
let broadcast_mode = matches.value_of("broadcast-mode").is_some();
let keepalive: u8 = matches.value_of("keepalive").unwrap().parse().expect("Keepalive argument should be a number");
let obfs_type = match matches.value_of("obfs-type").expect("Obfs type should be specified") {
"dns" => ObfsProtocol::FakeDNS,
"veil" => ObfsProtocol::VEIL,
"xor" => ObfsProtocol::XOR,
_ => ObfsProtocol::NONE
};
let _ = fs::write(config_path, serde_yaml::to_string(&ServerConfiguration::default(bind_address, internal_address, broadcast_mode, keepalive, obfs_type)).unwrap());
}
fn generate_peer_config(matches: &ArgMatches, config_path: &str, cfg_raw: &String) {
let keepalive: u8 = matches.value_of("keepalive").unwrap().parse().expect("Keepalive argument should be a number");
let grab_endpoint = matches.value_of("grab-endpoint").is_some();
let endpoint = matches.value_of("endpoint").or(Some("0.0.0.0:0")).unwrap();
let peer_cfg = matches.value_of("peer-cfg").expect("No peer cfg path specified");
let mut config: ServerConfiguration = serde_yaml::from_str(cfg_raw).expect("Bad server config file structure");
let prs = &mut config.peers[..];
prs.sort_by(|a, b| a.ip.octets()[3].cmp(&b.ip.octets()[3]));
let mut internal_address = prs.iter()
.map(|p| p.ip)
.collect::<Vec<Ipv4Addr>>()
.first()
.or(Some(&config.interface.internal_address.parse::<Ipv4Addr>().unwrap()))
.unwrap()
.clone();
internal_address = Ipv4Addr::new(internal_address.octets()[0], internal_address.octets()[1], internal_address.octets()[2], internal_address.octets()[3]+1);
let cl_cfg = &ClientConfiguration::default(if grab_endpoint { &config.interface.bind_address } else { endpoint },
keepalive,
&config.interface.public_key,
&internal_address.to_string());
config.peers.push(ServerPeer { public_key: cl_cfg.client.public_key.clone(), ip: internal_address.clone() });
let _ = fs::write(peer_cfg, serde_yaml::to_string(cl_cfg).unwrap());
let _ = fs::write(config_path, serde_yaml::to_string(&config).unwrap());
}
async fn init_server(cfg_raw: &str, s_interface: Option<&str>) {
let config: ServerConfiguration = serde_yaml::from_str(cfg_raw).expect("Bad server config file structure");
server::server_mode(config, s_interface).await;
}
async fn init_client(cfg_raw: &str, s_interface: Option<String>) {
let config: ClientConfiguration = serde_yaml::from_str(cfg_raw).expect("Bad client config file structure");
//client::client_mode(config, s_interface).await;
let client = DesktopClient{client_config: config, s_interface};
client.start().await;
}
#[tokio::main]
async fn main() {
//console_subscriber::init();
// Initialize the logger with 'info' as the default level
Builder::new()
.filter(None, LevelFilter::Info)
.init();
let matches = App::new("Frida")
.version("0.1.2")
.author("alterwain")
.about("VPN software")
.arg(Arg::with_name("mode")
.required(true)
.index(1)
.possible_values(&["server", "client", "gen_cfg", "new_peer"])
.help("Runs the program in certain mode"))
.arg(Arg::with_name("config")
.long("config")
.required(true)
.value_name("FILE")
.help("The path to VPN configuration file")
.takes_value(true))
.arg(Arg::with_name("peer-cfg")
.long("peer-cfg")
.value_name("FILE")
.help("The path to VPN peer configuration file")
.takes_value(true))
.arg(Arg::with_name("bind-address")
.long("bind-address")
.value_name("IP:PORT")
.help("The ip:port that would be used to bind server (config)")
.takes_value(true))
.arg(Arg::with_name("endpoint")
.long("endpoint")
.value_name("IP:PORT")
.help("The ip:port that would be used by client to connect (config)")
.takes_value(true))
.arg(Arg::with_name("internal-address")
.long("internal-address")
.value_name("IP")
.help("The address of VPN server in it's subnet (config)")
.takes_value(true))
.arg(Arg::with_name("broadcast-mode")
.long("broadcast-mode")
.help("If set to true, then all incoming traffic with an unknown destination address will be forwarded to all peers (config)")
.takes_value(false))
.arg(Arg::with_name("grab-endpoint")
.long("grab-endpoint")
.help("If set to true, the endpoint address for peers will be grabbed from server config (config)")
.takes_value(false))
.arg(Arg::with_name("keepalive")
.long("keepalive")
.required(false)
.value_name("SECONDS")
.default_value("0")
.help("Keepalive packets interval (config)")
.takes_value(true))
.arg(Arg::with_name("obfs-type")
.long("obfs-type")
.possible_values(&["dns", "veil", "xor"])
.takes_value(true)
.value_name("OBFS")
.help("Obfuscation protocol (config)"))
.arg(Arg::with_name("interface")
.long("interface")
.required(false)
.takes_value(true)
.value_name("NAME")
.help("Explicitly set network interface name for routing"))
.get_matches();
let mode = matches.value_of("mode").unwrap();
if let Some(config_path) = matches.value_of("config") {
let data = fs::read(config_path);
if data.is_err() {
match mode {
"gen_cfg" => generate_server_config(&matches, config_path),
_ => error!("There is no config file.")
}
return;
}
let cfg_raw = &String::from_utf8(data.unwrap()).unwrap();
match mode {
"server" => init_server(cfg_raw, matches.value_of("interface")).await,
"client" => init_client(cfg_raw, matches.value_of("interface").map_or(None, |x| Some(String::from(x)))).await,
"new_peer" => generate_peer_config(&matches, config_path, cfg_raw),
_ => error!("There is config file already")
}
}
}

View File

@ -9,18 +9,10 @@ categories = ["network-programming", "asynchronous"]
readme = "../README.md"
workspace = "../"
[lib]
crate-type = ["cdylib"]
path = "src/android.rs"
[[bin]]
name = "frida-cli"
name = "frida-core"
path = "src/main.rs"
[[bin]]
name = "frida-gui"
path = "src/gui/mod.rs"
[dependencies]
clap = "2.33"
aes-gcm = "0.10.3"
@ -50,10 +42,17 @@ tun = { version = "0.7.5", features = ["async"] }
iced = { version = "0.13.1", features = ["tokio"] }
dirs = "5.0.1"
tray-item = "0.10.0"
wintun = "0.5.0"
[target.'cfg(target_os="windows")'.build-dependencies]
embed-resource = "2.3"
[target.'cfg(target_os="darwin")'.dependencies]
tun-tap = "0.1.4"
[target.'cfg(target_os="linux")'.dependencies]
tokio-tun = "0.12.1"
[target.'cfg(target_os="android")'.dependencies]
jni = "^0.20"
robusta_jni = "0.2.2"

31
frida_core/src/device.rs Normal file
View File

@ -0,0 +1,31 @@
#[derive(Default)]
pub struct AbstractDevice {
address: String,
netmask: String,
destination: String,
mtu: u16,
tun_name: String
}
impl AbstractDevice {
fn address(&mut self, address: String) {
self.address = address;
}
fn netmask(&mut self, netmask: String) {
self.netmask = netmask;
}
fn destination(&mut self, destination: String) {
self.destination = destination;
}
fn mtu(&mut self, mtu: u16) {
self.mtu = mtu;
}
fn tun_name(&mut self, tun_name: String) {
self.tun_name = tun_name;
}
}

View File

@ -1,171 +1,30 @@
use std::{fs, net::{Ipv4Addr}, str};
use clap::{App, Arg, ArgMatches};
use env_logger::Builder;
use log::{error, LevelFilter};
use crate::config::{ ServerConfiguration, ClientConfiguration, ObfsProtocol, ServerPeer };
use crate::client::{desktop::DesktopClient, general::VpnClient};
use log::{info, error, LevelFilter};
mod obfs;
mod server;
mod client;
mod udp;
mod config;
fn generate_server_config(matches: &ArgMatches, config_path: &str) {
let bind_address = matches.value_of("bind-address").expect("No bind address specified");
let internal_address = matches.value_of("internal-address").expect("No internal address specified");
let broadcast_mode = matches.value_of("broadcast-mode").is_some();
let keepalive: u8 = matches.value_of("keepalive").unwrap().parse().expect("Keepalive argument should be a number");
let obfs_type = match matches.value_of("obfs-type").expect("Obfs type should be specified") {
"dns" => ObfsProtocol::FakeDNS,
"veil" => ObfsProtocol::VEIL,
"xor" => ObfsProtocol::XOR,
_ => ObfsProtocol::NONE
};
let _ = fs::write(config_path, serde_yaml::to_string(&ServerConfiguration::default(bind_address, internal_address, broadcast_mode, keepalive, obfs_type)).unwrap());
}
fn generate_peer_config(matches: &ArgMatches, config_path: &str, cfg_raw: &String) {
let keepalive: u8 = matches.value_of("keepalive").unwrap().parse().expect("Keepalive argument should be a number");
let grab_endpoint = matches.value_of("grab-endpoint").is_some();
let endpoint = matches.value_of("endpoint").or(Some("0.0.0.0:0")).unwrap();
let peer_cfg = matches.value_of("peer-cfg").expect("No peer cfg path specified");
let mut config: ServerConfiguration = serde_yaml::from_str(cfg_raw).expect("Bad server config file structure");
let prs = &mut config.peers[..];
prs.sort_by(|a, b| a.ip.octets()[3].cmp(&b.ip.octets()[3]));
let mut internal_address = prs.iter()
.map(|p| p.ip)
.collect::<Vec<Ipv4Addr>>()
.first()
.or(Some(&config.interface.internal_address.parse::<Ipv4Addr>().unwrap()))
.unwrap()
.clone();
internal_address = Ipv4Addr::new(internal_address.octets()[0], internal_address.octets()[1], internal_address.octets()[2], internal_address.octets()[3]+1);
let cl_cfg = &ClientConfiguration::default(if grab_endpoint { &config.interface.bind_address } else { endpoint },
keepalive,
&config.interface.public_key,
&internal_address.to_string());
config.peers.push(ServerPeer { public_key: cl_cfg.client.public_key.clone(), ip: internal_address.clone() });
let _ = fs::write(peer_cfg, serde_yaml::to_string(cl_cfg).unwrap());
let _ = fs::write(config_path, serde_yaml::to_string(&config).unwrap());
}
async fn init_server(cfg_raw: &str, s_interface: Option<&str>) {
let config: ServerConfiguration = serde_yaml::from_str(cfg_raw).expect("Bad server config file structure");
server::server_mode(config, s_interface).await;
}
async fn init_client(cfg_raw: &str, s_interface: Option<String>) {
let config: ClientConfiguration = serde_yaml::from_str(cfg_raw).expect("Bad client config file structure");
//client::client_mode(config, s_interface).await;
let client = DesktopClient{client_config: config, s_interface};
client.start().await;
}
mod device;
mod tun;
mod win_tun;
#[tokio::main]
async fn main() {
//console_subscriber::init();
// Initialize the logger with 'info' as the default level
Builder::new()
.filter(None, LevelFilter::Info)
.init();
let matches = App::new("Frida")
.version("0.1.2")
.author("alterwain")
.about("VPN software")
.arg(Arg::with_name("mode")
.required(true)
.index(1)
.possible_values(&["server", "client", "gen_cfg", "new_peer"])
.help("Runs the program in certain mode"))
.arg(Arg::with_name("config")
.long("config")
.required(true)
.value_name("FILE")
.help("The path to VPN configuration file")
.takes_value(true))
.arg(Arg::with_name("peer-cfg")
.long("peer-cfg")
.value_name("FILE")
.help("The path to VPN peer configuration file")
.takes_value(true))
.arg(Arg::with_name("bind-address")
.long("bind-address")
.value_name("IP:PORT")
.help("The ip:port that would be used to bind server (config)")
.takes_value(true))
.arg(Arg::with_name("endpoint")
.long("endpoint")
.value_name("IP:PORT")
.help("The ip:port that would be used by client to connect (config)")
.takes_value(true))
.arg(Arg::with_name("internal-address")
.long("internal-address")
.value_name("IP")
.help("The address of VPN server in it's subnet (config)")
.takes_value(true))
.arg(Arg::with_name("broadcast-mode")
.long("broadcast-mode")
.help("If set to true, then all incoming traffic with an unknown destination address will be forwarded to all peers (config)")
.takes_value(false))
.arg(Arg::with_name("grab-endpoint")
.long("grab-endpoint")
.help("If set to true, the endpoint address for peers will be grabbed from server config (config)")
.takes_value(false))
.arg(Arg::with_name("keepalive")
.long("keepalive")
.required(false)
.value_name("SECONDS")
.default_value("0")
.help("Keepalive packets interval (config)")
.takes_value(true))
.arg(Arg::with_name("obfs-type")
.long("obfs-type")
.possible_values(&["dns", "veil", "xor"])
.takes_value(true)
.value_name("OBFS")
.help("Obfuscation protocol (config)"))
.arg(Arg::with_name("interface")
.long("interface")
.required(false)
.takes_value(true)
.value_name("NAME")
.help("Explicitly set network interface name for routing"))
.get_matches();
let (reader, writer) = tun::create_tun();
let mode = matches.value_of("mode").unwrap();
if let Some(config_path) = matches.value_of("config") {
let data = fs::read(config_path);
if data.is_err() {
match mode {
"gen_cfg" => generate_server_config(&matches, config_path),
_ => error!("There is no config file.")
let a = tokio::spawn(async move {
let mut buf = Vec::new();
info!("Started!");
loop {
// info!("We've got {} bytes of data!", c)
let r = reader.read(&mut buf).await;
match r {
Ok(c) => {},
Err(e) => error!("We've got a nasty error message!")
}
return;
}
});
let cfg_raw = &String::from_utf8(data.unwrap()).unwrap();
match mode {
"server" => init_server(cfg_raw, matches.value_of("interface")).await,
"client" => init_client(cfg_raw, matches.value_of("interface").map_or(None, |x| Some(String::from(x)))).await,
"new_peer" => generate_peer_config(&matches, config_path, cfg_raw),
_ => error!("There is config file already")
}
}
a.await;
}

7
frida_core/src/tun.rs Normal file
View File

@ -0,0 +1,7 @@
#[cfg(target_os = "windows")]
use crate::win_tun::{DeviceReader, DeviceWriter, create};
pub(crate) fn create_tun() -> (DeviceReader, DeviceWriter) {
#[cfg(target_os = "windows")]
create()
}

50
frida_core/src/win_tun.rs Normal file
View File

@ -0,0 +1,50 @@
use wintun::Session;
use std::sync::Arc;
use std::error::Error;
pub fn create() -> (DeviceReader, DeviceWriter) {
//Unsafe because we are loading an arbitrary dll file
let wintun = unsafe { wintun::load_from_path("wintun.dll") }
.expect("Failed to load wintun dll");
//Try to open an adapter with the name "Demo"
let adapter = match wintun::Adapter::open(&wintun, "Demo") {
Ok(a) => a,
Err(_) => {
wintun::Adapter::create(&wintun, "Demo", "Example", None)
.expect("Failed to create wintun adapter!")
}
};
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
let reader_session = session.clone();
let writer_session = session.clone();
(DeviceReader{ session: reader_session }, DeviceWriter{ session: writer_session })
}
pub struct DeviceWriter {
session: Arc<Session>
}
pub struct DeviceReader {
session: Arc<Session>
}
impl DeviceWriter {
pub async fn write(&self, buf: &Vec<u8>) -> Result<usize, Box<dyn Error>> {
let mut write_pack = self.session.allocate_send_packet(buf.len() as u16)?;
write_pack.bytes_mut().copy_from_slice(buf);
self.session.send_packet(write_pack);
Ok(buf.len())
}
}
impl DeviceReader {
pub async fn read(&self, buf: &mut Vec<u8>) -> Result<usize, Box<dyn Error>> {
let packet = self.session.receive_blocking()?;
*buf = packet.bytes().to_vec();
Ok(buf.len())
}
}

View File

@ -9,4 +9,8 @@ categories = ["network-programming", "asynchronous"]
readme = "../README.md"
workspace = "../"
[[bin]]
name = "frida-gui"
path = "src/gui/mod.rs"
[dependencies]

View File

@ -9,4 +9,8 @@ categories = ["network-programming", "asynchronous"]
readme = "../README.md"
workspace = "../"
[lib]
crate-type = ["cdylib"]
path = "src/android.rs"
[dependencies]

BIN
wintun.dll Normal file

Binary file not shown.