replace hardcoded parameters with u32 bitflag

This commit is contained in:
17ms 2024-02-16 23:18:59 +02:00
parent 2a16a674b3
commit 030bf89a92
4 changed files with 67 additions and 22 deletions

View File

@ -4,7 +4,7 @@ use std::{
}; };
use airborne_utils::calc_hash; use airborne_utils::calc_hash;
use clap::Parser; use clap::{ArgAction, Parser};
use windows_sys::Win32::{ use windows_sys::Win32::{
System::Diagnostics::Debug::IMAGE_NT_HEADERS64, System::Diagnostics::Debug::IMAGE_NT_HEADERS64,
System::{ System::{
@ -31,9 +31,15 @@ struct Args {
/// Path to the output file /// Path to the output file
#[arg(short, long = "output")] #[arg(short, long = "output")]
output_path: PathBuf, output_path: PathBuf,
/// Flag to pass to the loader (by default DllMain is called) /// Disable randomized delays during IAT patching
#[arg(long, default_value_t = 0)] #[arg(short, long, action = ArgAction::SetFalse, default_value_t = true)]
flag: u32, // preferably set type as u32 here instead of casting it when generating bootstrap no_delay: bool,
/// Disable IAT import descriptor shuffling
#[arg(short, long, action = ArgAction::SetFalse, default_value_t = true)]
no_shuffle: bool,
/// Call payload's user defined function instead of DllMain
#[arg(short, long, action = ArgAction::SetTrue, default_value_t = false)]
ufn: bool,
} }
// NOTE: must be updated accordingly if the loader name or the bootstrap code is modified // NOTE: must be updated accordingly if the loader name or the bootstrap code is modified
@ -43,6 +49,9 @@ const BOOTSTRAP_TOTAL_LENGTH: u32 = 79;
fn main() { fn main() {
let args = Args::parse(); let args = Args::parse();
// (bool, bool, bool) -(OR)-> u32
let combined_flag = airborne_utils::create_u32_flag(args.no_delay, args.no_shuffle, args.ufn);
// preserve the path from being dropped // preserve the path from being dropped
let output_path = args.output_path.clone(); let output_path = args.output_path.clone();
@ -77,7 +86,7 @@ fn main() {
&mut payload_b, &mut payload_b,
function_hash, function_hash,
args.parameter, args.parameter,
args.flag, combined_flag,
) { ) {
Ok(sc) => sc, Ok(sc) => sc,
Err(e) => { Err(e) => {

View File

@ -10,6 +10,7 @@ use core::{
slice::from_raw_parts, slice::from_raw_parts,
}; };
use airborne_utils::Flags;
use windows_sys::{ use windows_sys::{
core::PWSTR, core::PWSTR,
Win32::{ Win32::{
@ -41,10 +42,6 @@ use windows_sys::{
use crate::memory::*; use crate::memory::*;
// TODO: replace with parameters from the shellcode generator
const SHUFFLE_IMPORTS: bool = true;
const DELAY_IMPORTS: bool = true;
const MAX_IMPORT_DELAY_MS: u64 = 2000; const MAX_IMPORT_DELAY_MS: u64 = 2000;
#[cfg(not(test))] #[cfg(not(test))]
@ -70,6 +67,8 @@ pub unsafe extern "system" fn loader(
_shellcode_bin: *mut c_void, _shellcode_bin: *mut c_void,
flags: u32, flags: u32,
) { ) {
let flags = airborne_utils::parse_u32_flag(flags);
/* /*
1.) locate the required functions and modules from exports with their hashed names 1.) locate the required functions and modules from exports with their hashed names
*/ */
@ -139,7 +138,7 @@ pub unsafe extern "system" fn loader(
return; return;
} }
patch_iat(base_addr_ptr, import_descriptor_ptr, &far_procs); patch_iat(base_addr_ptr, import_descriptor_ptr, &far_procs, &flags);
/* /*
5.) finalize the sections by setting protective permissions after mapping the image 5.) finalize the sections by setting protective permissions after mapping the image
@ -151,15 +150,7 @@ pub unsafe extern "system" fn loader(
6.) execute DllMain or user defined function depending on the flag passed into the shellcode by the generator 6.) execute DllMain or user defined function depending on the flag passed into the shellcode by the generator
*/ */
if flags == 0 { if flags.ufn {
let dll_main_addr = base_addr_ptr as usize
+ (*module_nt_headers_ptr).OptionalHeader.AddressOfEntryPoint as usize;
#[allow(non_snake_case)]
let DllMain = transmute::<_, DllMain>(dll_main_addr);
DllMain(base_addr_ptr as _, DLL_PROCESS_ATTACH, module_base_ptr as _);
} else {
// UserFunction address = base address + RVA of user function // UserFunction address = base address + RVA of user function
let user_fn_addr = get_export_addr(base_addr_ptr as _, function_hash).unwrap(); let user_fn_addr = get_export_addr(base_addr_ptr as _, function_hash).unwrap();
@ -168,6 +159,14 @@ pub unsafe extern "system" fn loader(
// execution with user data passed into the shellcode by the generator // execution with user data passed into the shellcode by the generator
UserFunction(user_data, user_data_len); UserFunction(user_data, user_data_len);
} else {
let dll_main_addr = base_addr_ptr as usize
+ (*module_nt_headers_ptr).OptionalHeader.AddressOfEntryPoint as usize;
#[allow(non_snake_case)]
let DllMain = transmute::<_, DllMain>(dll_main_addr);
DllMain(base_addr_ptr as _, DLL_PROCESS_ATTACH, module_base_ptr as _);
} }
} }
@ -418,6 +417,7 @@ unsafe fn patch_iat(
base_addr_ptr: *mut c_void, base_addr_ptr: *mut c_void,
mut import_descriptor_ptr: *mut IMAGE_IMPORT_DESCRIPTOR, mut import_descriptor_ptr: *mut IMAGE_IMPORT_DESCRIPTOR,
far_procs: &FarProcs, far_procs: &FarProcs,
flags: &Flags,
) -> BOOL { ) -> BOOL {
/* /*
1.) shuffle Import Directory Table entries (image import descriptors) 1.) shuffle Import Directory Table entries (image import descriptors)
@ -436,7 +436,7 @@ unsafe fn patch_iat(
let id_ptr = import_descriptor_ptr; let id_ptr = import_descriptor_ptr;
if import_count > 1 && SHUFFLE_IMPORTS { if import_count > 1 && flags.shuffle {
// Fisher-Yates shuffle // Fisher-Yates shuffle
for i in 0..import_count - 1 { for i in 0..import_count - 1 {
let rn = match get_random(far_procs) { let rn = match get_random(far_procs) {
@ -465,7 +465,7 @@ unsafe fn patch_iat(
return 0; return 0;
} }
if DELAY_IMPORTS { if flags.delay {
// skip delay if winapi call fails // skip delay if winapi call fails
let rn = get_random(far_procs).unwrap_or(0); let rn = get_random(far_procs).unwrap_or(0);
let delay = rn % MAX_IMPORT_DELAY_MS; let delay = rn % MAX_IMPORT_DELAY_MS;

View File

@ -2,8 +2,44 @@
// gen_xor_key isn't required to be a shared module, as it's only used in the shellcode generator // gen_xor_key isn't required to be a shared module, as it's only used in the shellcode generator
const DELAY_FLAG: u32 = 0b0001;
const SHUFFLE_FLAG: u32 = 0b0010;
const UFN_FLAG: u32 = 0b0100;
const HASH_KEY: usize = 5381; const HASH_KEY: usize = 5381;
pub struct Flags {
pub delay: bool,
pub shuffle: bool,
pub ufn: bool,
}
pub fn parse_u32_flag(flag: u32) -> Flags {
Flags {
delay: flag & DELAY_FLAG != 0,
shuffle: flag & SHUFFLE_FLAG != 0,
ufn: flag & UFN_FLAG != 0,
}
}
pub fn create_u32_flag(delay: bool, shuffle: bool, ufn: bool) -> u32 {
let mut flags = 0;
if delay {
flags |= DELAY_FLAG;
}
if shuffle {
flags |= SHUFFLE_FLAG;
}
if ufn {
flags |= UFN_FLAG;
}
flags
}
pub fn xor_cipher(data: &mut [u8], key: &[u8]) { pub fn xor_cipher(data: &mut [u8], key: &[u8]) {
for (i, byte) in data.iter_mut().enumerate() { for (i, byte) in data.iter_mut().enumerate() {
*byte ^= key[i % key.len()]; *byte ^= key[i % key.len()];