#![allow(warnings)]
use lief;
use lief::runtime::memory::{MmapFlags, Perm};
use lief::runtime::{Memory, Module, Process};
fn test_process() {
println!(
"{} {} {} {:?} {:?}",
Process::pid(),
Process::tid(),
Process::page_size(),
Process::arch(),
Process::platform()
);
}
fn test_modules() {
for m in lief::runtime::modules() {
println!(
"{} {} {:#x} {:#x} {:#x} {:?}",
m.name(),
m.path(),
m.imagebase(),
m.size(),
m.end(),
m.contains(0)
);
assert!(lief::runtime::module_from_addr(m.imagebase()).is_some());
assert!(lief::runtime::module_from_path(m.path()).is_some());
assert!(lief::runtime::module_from_name(&m.name()).is_some());
match &m {
lief::runtime::Modules::Linux(linux) => {}
lief::runtime::Modules::Android(android) => {}
lief::runtime::Modules::Windows(win) => {}
lief::runtime::Modules::Osx(osx) => {}
}
}
}
fn test_osx_host() {
use lief::runtime::osx::Host;
let version = Host::os_version_name();
println!("os_version_name: {}", version);
assert!(!version.is_empty());
}
fn test_windows_host() {
use lief::runtime::windows::{Host, Version};
let version = Host::version().expect("missing version");
println!("windows version: {}", version);
let v = Version::new(10, 0, 26200);
assert_eq!(v.major, 10);
assert_eq!(v.minor, 0);
assert_eq!(v.build_number, 26200);
assert_eq!(format!("{}", v), "10.0.26200");
assert!(Version::new(10, 0, 10240) < Version::new(10, 0, 26100));
assert_eq!(Version::new(6, 1, 7600), Version::new(6, 1, 7600));
assert!(version >= Version::new(6, 1, 0));
}
fn test_windows_process() {
use lief::runtime::windows::Process;
let peb = Process::peb().expect("missing PEB");
println!(
"being_debugged={} ldr={:#x} process_parameters={:#x} session_id={}",
peb.being_debugged(),
peb.ldr(),
peb.process_parameters(),
peb.session_id()
);
assert_ne!(peb.ldr(), 0);
assert_ne!(peb.process_parameters(), 0);
let _ = peb.atl_thunk_slist_ptr();
let _ = peb.atl_thunk_slist_ptr32();
let _ = peb.post_process_init_routine();
let entries: Vec<_> = peb.entries().collect();
assert!(entries.len() > 1);
let first = &entries[0];
assert_ne!(first.dll_base(), 0);
assert!(first.size_of_image() > 0);
assert!(!first.base_dll_name().is_empty());
assert!(
first
.full_dll_name()
.to_lowercase()
.ends_with(&first.base_dll_name().to_lowercase())
);
assert!(
entries
.iter()
.any(|e| e.base_dll_name().eq_ignore_ascii_case("ntdll.dll"))
);
}
fn test_linux_process() {
use lief::runtime::linux::Process;
let version = Process::glibc_version();
println!("glibc_version: {:?}", version);
assert!(version.is_some());
let cmdline = Process::cmdline();
println!("cmdline: {:?}", cmdline);
assert!(cmdline.is_some());
}
fn test_linux_module() {
let libc = lief::runtime::modules()
.find_map(|m| match m {
lief::runtime::Modules::Linux(linux) if linux.name().starts_with("libc") => Some(linux),
_ => None,
})
.expect("expected libc to be loaded in the current process");
assert!(!libc.handle().is_null());
assert!(!libc.dlsym("malloc".to_string()).is_null());
assert!(libc.dlsym("__lief_does_not_exist__".to_string()).is_null());
let from_hdl = lief::runtime::linux::Module::from_handle(libc.handle());
assert!(from_hdl.is_some());
let parsed = libc.parse_from_path().expect("parse_from_path failed");
assert!(parsed.header().entrypoint() > 0);
let config = lief::elf::ParserConfig::default();
let parsed = libc
.parse_from_path_with_config(&config)
.expect("parse_from_path_with_config failed");
assert!(parsed.header().entrypoint() > 0);
let _ = libc.parse_from_memory();
let _ = libc.parse_from_memory_with_config(&config);
let data = libc.dump();
assert_eq!(data.len() as u64, libc.size());
assert_eq!(&data[..4], b"\x7fELF");
let out = std::env::temp_dir().join("lief_runtime_libc.dump");
let written = libc.dump_to_file(&out);
assert_eq!(written.len() as u64, libc.size());
assert_eq!(&written[..4], b"\x7fELF");
assert_eq!(std::fs::read(&out).expect("dump file not written"), written);
let _ = std::fs::remove_file(&out);
}
fn test_android() {
use lief::runtime::android::{Host, Process};
let sdk = Host::sdk_version();
println!("android sdk_version: {:?}", sdk);
let prop = Process::get_system_property("ro.build.version.sdk");
println!("android ro.build.version.sdk: {:?}", prop);
let cmdline = Process::cmdline();
println!("android cmdline: {:?}", cmdline);
let props: Vec<_> = Process::properties().collect();
println!("android #properties: {}", props.len());
for prop in &props {
assert!(!prop.name().is_empty());
}
if cfg!(target_os = "android") {
assert!(sdk.is_some_and(|v| v > 0));
let prop = prop.expect("missing ro.build.version.sdk");
assert_eq!(prop.name(), "ro.build.version.sdk");
assert!(!prop.value().is_empty());
println!(
"property: {} = {} (serial: {})",
prop.name(),
prop.value(),
prop.serial()
);
assert!(cmdline.is_some());
assert!(Process::get_system_property("lief.does.not.exist").is_none());
assert!(!props.is_empty());
assert!(props.iter().any(|p| p.name() == "ro.build.version.sdk"));
}
}
fn test_memory() {
let mut chunk = Memory::mmap(
0x4000,
MmapFlags::ANONYMOUS | MmapFlags::PRIVATE,
Perm::READ,
)
.unwrap();
println!(
"{:#x} {:#x} {:?} {:#x} {:#x}",
chunk.addr(),
chunk.size(),
chunk.permissions(),
chunk.page_start(),
chunk.page_end()
);
assert_eq!(chunk.permissions(), Perm::READ);
{
chunk.change_permissions(Perm::READ | Perm::WRITE);
assert_eq!(chunk.permissions(), Perm::READ | Perm::WRITE);
unsafe {
Memory::write::<u64>(chunk.addr(), 1);
assert_eq!(Memory::read::<u64>(chunk.addr()), 1);
}
chunk.change_permissions(Perm::READ);
assert_eq!(chunk.permissions(), Perm::READ);
}
chunk.change_permissions(Perm::READ);
chunk.make_x();
chunk.make_rw();
chunk.make_rx();
chunk.make_rwx();
chunk.make_ro();
chunk.cache_flush();
assert!(chunk.is_valid());
Memory::munmap(&mut chunk);
}
#[test]
fn test_runtime() {
if !lief::runtime::enabled() {
return;
}
test_process();
test_modules();
test_memory();
test_android();
if cfg!(target_os = "macos") {
test_osx_host();
}
if cfg!(target_os = "windows") {
test_windows_host();
test_windows_process();
}
if cfg!(target_os = "linux") {
test_linux_module();
test_linux_process();
}
}