diff --git a/.gitignore b/.gitignore index d8ba7bb..1d6d67b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ *.exe *.pdb + +!/test/*.exe diff --git a/README.md b/README.md index 18fd8a7..f928617 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,5 @@ a PE loader for educational purposes. - https://learn.microsoft.com/en-us/windows/win32/debug/pe-format - https://learn.microsoft.com/en-us/archive/msdn-magazine/2002/february/inside-windows-win32-portable-executable-file-format-in-detail -- https://learn.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2 \ No newline at end of file +- https://learn.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2 +- https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order diff --git a/src/lib.rs b/src/lib.rs index ff49b95..a8a1761 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,11 @@ mod sys; -use std::{ffi::CStr, fmt::Debug, fs::File}; +use std::{ + ffi::CStr, + fmt::Debug, + fs::File, + path::{Path, PathBuf}, +}; #[derive(Clone, Copy, Debug, bytemuck::Zeroable, bytemuck::Pod)] #[repr(C)] @@ -250,8 +255,8 @@ pub fn execute(pe: &[u8]) { ); dbg!(section_table); - // just some arbitrary offset that probably won't collide with anything - let base = optional_header.image_base as usize + 0xFFFFFF0000; + // let's always load it at the image base for now... + let base = optional_header.image_base as usize; let allocation_granularity = crate::sys::allocation_granularity(); @@ -312,8 +317,18 @@ pub fn execute(pe: &[u8]) { dbg!(import_directory); let name = CStr::from_bytes_until_nul(&a[import_directory.name_rva as usize..]).unwrap(); + if name.is_empty() { + // Trailing null import directory. + break; + } dbg!(name); + let dll = find_dll(name); + match dll { + Some(path) => eprintln!(" found {name:?} at {path:?}"), + None => eprintln!(" COULD NOT FIND {name:?}"), + } + let import_lookups = bytemuck::cast_slice::( &a[import_directory.import_lookup_table_rva as usize..], ); @@ -335,6 +350,15 @@ pub fn execute(pe: &[u8]) { } } } + + eprintln!("YOLO"); + + unsafe { + let entrypoint = std::mem::transmute:: !>( + optional_header.address_of_entry_point as usize, + ); + entrypoint(); + }; } fn parse_header(pe: &[u8]) -> (&CoffHeader, usize) { @@ -357,3 +381,30 @@ fn parse_header(pe: &[u8]) -> (&CoffHeader, usize) { (signature_pointer as usize) + 4 + std::mem::size_of::(), ) } + +fn find_dll(name: &CStr) -> Option { + // https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order + let name = name.to_str().unwrap(); + if name.starts_with("api-") { + // This is an API set, essentially a virtual alias + // https://learn.microsoft.com/en-us/windows/win32/apiindex/windows-apisets + return None; + } + + let system = sys::system_directory().unwrap(); + eprintln!(" searching {system:?} for {name}"); + let from_system = std::fs::read_dir(system).unwrap().find(|child| { + child + .as_ref() + .unwrap() + .file_name() + .to_str() + .unwrap() + .eq_ignore_ascii_case(name) + }); + if let Some(from_system) = from_system { + return Some(from_system.unwrap().path()); + } + + None +} diff --git a/src/main.rs b/src/main.rs index f06f452..647eada 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ fn main() { .open( std::env::args() .nth(1) - .unwrap_or_else(|| "example_exe.exe".into()), + .unwrap_or_else(|| "test/example_exe.exe".into()), ) .unwrap(); let map = unsafe { memmap2::Mmap::map(&file).unwrap() }; diff --git a/src/sys.rs b/src/sys.rs index 679cd4c..e24afd1 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -8,13 +8,16 @@ pub(crate) enum Mode { #[cfg(windows)] mod imp { - use std::{ffi::c_void, io, u32}; + use std::{ffi::c_void, io, path::PathBuf, u32}; use windows::Win32::{ Foundation::INVALID_HANDLE_VALUE, System::{ - Memory::{FILE_MAP_EXECUTE, FILE_MAP_WRITE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_READONLY, PAGE_READWRITE}, - SystemInformation::SYSTEM_INFO, + Memory::{ + FILE_MAP_EXECUTE, FILE_MAP_WRITE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, + PAGE_READONLY, PAGE_READWRITE, + }, + SystemInformation::{GetSystemDirectoryW, SYSTEM_INFO}, }, }; @@ -36,7 +39,10 @@ mod imp { info.dwPageSize as usize } - pub(crate) unsafe fn anon_write_map<'a>(size: usize, address: *const ()) -> io::Result<&'a mut [u8]> { + pub(crate) unsafe fn anon_write_map<'a>( + size: usize, + address: *const (), + ) -> io::Result<&'a mut [u8]> { let map = windows::Win32::System::Memory::CreateFileMappingA( INVALID_HANDLE_VALUE, None, @@ -88,6 +94,20 @@ mod imp { .map_err(Into::into) } } + + pub(crate) fn system_directory() -> io::Result { + let mut buf = vec![0; 1024]; + let ret = unsafe { GetSystemDirectoryW(Some(&mut buf)) }; + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(std::char::decode_utf16(buf) + .map(Result::unwrap) + .take_while(|c| *c != '\0') + .collect::() + .into()) + } + } } #[cfg(unix)] mod imp { diff --git a/test/example_exe.exe b/test/example_exe.exe new file mode 100644 index 0000000..903f3c5 Binary files /dev/null and b/test/example_exe.exe differ