mirror of
https://codeberg.org/unspeaker/vestal.git
synced 2025-05-02 07:00:14 +02:00
report load addr overlaps
This commit is contained in:
parent
5490fef835
commit
f5a4ce9116
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
target
|
||||
*.dll
|
||||
*.exe
|
||||
bin/
|
||||
|
|
44
Cargo.lock
generated
44
Cargo.lock
generated
|
@ -88,6 +88,17 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "binary-layout"
|
||||
version = "4.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66c7e8da2156abef3421f6226ef339ade8c0d157ec50932d5e624f1c6a5127b4"
|
||||
dependencies = [
|
||||
"doc-comment",
|
||||
"paste",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -216,6 +227,12 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
|
@ -477,6 +494,12 @@ version = "0.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pkbuffer"
|
||||
version = "0.4.2"
|
||||
|
@ -658,6 +681,26 @@ dependencies = [
|
|||
"jack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "1.6.3"
|
||||
|
@ -696,6 +739,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|||
name = "vestal"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"binary-layout",
|
||||
"clap",
|
||||
"exe",
|
||||
"hexy",
|
||||
|
|
|
@ -12,6 +12,7 @@ pretty-hex = "0.4.1"
|
|||
exe = "0.5.6"
|
||||
iced-x86 = "1.21.0"
|
||||
itertools = "0.14.0"
|
||||
binary-layout = "4.0.2"
|
||||
#elf = "0.7.4"
|
||||
#goblin = "0.9.3"
|
||||
#lancelot = "0.9.7"
|
||||
|
|
15
crates/vestal/src/bang.rs
Normal file
15
crates/vestal/src/bang.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use crate::*;
|
||||
|
||||
/// You can manually patch DLLs by prepending
|
||||
/// a `#!/usr/bin/env vestal` line to them.
|
||||
pub fn slice_shebang (buffer: &[u8]) -> (Arc<[u8]>, Arc<[u8]>) {
|
||||
if buffer.get(0) == Some(&b'#') && buffer.get(1) == Some(&b'!') {
|
||||
if let Some((bang, data)) = buffer.split_once(|x|*x==0x0a) {
|
||||
(bang.to_vec().into(), data.to_vec().into())
|
||||
} else {
|
||||
(buffer.to_vec().into(), vec![].into())
|
||||
}
|
||||
} else {
|
||||
(vec![].into(), buffer.to_vec().into())
|
||||
}
|
||||
}
|
165
crates/vestal/src/link.rs
Normal file
165
crates/vestal/src/link.rs
Normal file
|
@ -0,0 +1,165 @@
|
|||
//! Create a minimal ELF file manually.
|
||||
//!
|
||||
//! Based on reading the [ELF-64
|
||||
//! standard](https://uclibc.org/docs/elf-64-gen.pdf) and the [x86-64
|
||||
//! architecture supplement](https://uclibc.org/docs/psABI-x86_64.pdf) (for the
|
||||
//! value `EM_X86_64`, specific to x86-64).
|
||||
//!
|
||||
//! Also learned from the classic blog post "[A Whirlwind Tutorial on Creating
|
||||
//! Really Teensy Elf Executables for
|
||||
//! Linux](https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html)" and
|
||||
//! the code in <https://github.com/AjayBrahmakshatriya/minimal-elf/>.
|
||||
|
||||
// from https://github.com/tchajed/minimal-elf/blob/main/src/lib.rs
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use binary_layout::prelude::*;
|
||||
use std::io::prelude::*;
|
||||
use std::path::Path;
|
||||
use std::{fs::OpenOptions, os::unix::prelude::OpenOptionsExt};
|
||||
|
||||
type Elf64_Addr = u64;
|
||||
type Elf64_Off = u64;
|
||||
type Elf64_Half = u16;
|
||||
type Elf64_Word = u32;
|
||||
// type Elf64_Sword = i32;
|
||||
type Elf64_Xword = u64;
|
||||
// type Elf64_Sxword = i64;
|
||||
|
||||
pub const VADDR: u64 = 0x400000;
|
||||
const PROGRAM_OFFSET: u64 = {
|
||||
// XXX: manually implement unwrap since it isn't stable as a const fn
|
||||
let sz1 = match elf64_hdr::SIZE {
|
||||
Some(s) => s,
|
||||
None => panic!("unsized"),
|
||||
};
|
||||
let sz2 = match elf64_phdr::SIZE {
|
||||
Some(s) => s,
|
||||
None => panic!("unsized"),
|
||||
};
|
||||
(sz1 + sz2) as u64
|
||||
};
|
||||
|
||||
define_layout!(elf64_file, LittleEndian, {
|
||||
hdr: elf64_hdr::NestedView,
|
||||
phdr: elf64_phdr::NestedView,
|
||||
program: [u8],
|
||||
});
|
||||
define_layout!(elf64_hdr, LittleEndian, {
|
||||
ident: elf64_ident::NestedView,
|
||||
_type: Elf64_Half,
|
||||
machine: Elf64_Half,
|
||||
version: Elf64_Word,
|
||||
entry: Elf64_Addr, // virtual address of entry point
|
||||
phoff: Elf64_Off, // program header
|
||||
shoff: Elf64_Off, // section header
|
||||
flags: Elf64_Word, // processor-specific
|
||||
ehsize: Elf64_Half,
|
||||
phentsize: Elf64_Half,
|
||||
phnum: Elf64_Half, // number of program header entries
|
||||
shentsize: Elf64_Half, // size of section header entry
|
||||
shnum: Elf64_Half, // number of section header entries
|
||||
shstrndx: Elf64_Half, // section name string table index
|
||||
});
|
||||
define_layout!(elf64_phdr, LittleEndian, {
|
||||
_type: Elf64_Word,
|
||||
flags: Elf64_Word,
|
||||
offset: Elf64_Off,
|
||||
vaddr: Elf64_Addr,
|
||||
paddr: Elf64_Addr,
|
||||
filesz: Elf64_Xword,
|
||||
memsz: Elf64_Xword,
|
||||
align: Elf64_Xword,
|
||||
});
|
||||
define_layout!(elf64_ident, LittleEndian, {
|
||||
mag: [u8; 4],
|
||||
class: u8,
|
||||
data: u8,
|
||||
version: u8,
|
||||
os_abi: u8,
|
||||
abi_version: u8,
|
||||
pad: [u8; 7],
|
||||
});
|
||||
fn set_ident<S: AsRef<[u8]> + AsMut<[u8]>>(mut view: elf64_ident::View<S>) {
|
||||
view.mag_mut()
|
||||
.copy_from_slice(&[0x7f, 'E' as u8, 'L' as u8, 'F' as u8]);
|
||||
view.class_mut().write(2); // class: ELFCLASS64
|
||||
view.data_mut().write(1); // data encoding: ELFDATA2LSB
|
||||
view.version_mut().write(1); // file version: EV_CURRENT
|
||||
view.os_abi_mut().write(0); // OS/ABI identification: System V
|
||||
view.abi_version_mut().write(0); // ABI version: System V third edition
|
||||
view.pad_mut().copy_from_slice(&[0u8; 7]);
|
||||
}
|
||||
fn set_elf64_hdr<S: AsRef<[u8]> + AsMut<[u8]>>(mut view: elf64_hdr::View<S>) {
|
||||
set_ident(view.ident_mut());
|
||||
view._type_mut().write(2); // ET_EXEC
|
||||
view.machine_mut().write(62); // EM_X86_64
|
||||
view.version_mut().write(1); // EV_CURRENT
|
||||
view.entry_mut().write(VADDR + PROGRAM_OFFSET);
|
||||
view.phoff_mut().write(elf64_hdr::SIZE.unwrap() as u64);
|
||||
view.flags_mut().write(0); // no processor-specific flags
|
||||
view.ehsize_mut().write(elf64_hdr::SIZE.unwrap() as u16);
|
||||
view.phentsize_mut().write(elf64_phdr::SIZE.unwrap() as u16);
|
||||
view.phnum_mut().write(1);
|
||||
}
|
||||
fn set_elf64_phdr<S>(mut view: elf64_phdr::View<S>, program_size: u64)
|
||||
where
|
||||
S: AsRef<[u8]> + AsMut<[u8]>,
|
||||
{
|
||||
view._type_mut().write(1); // PT_LOAD
|
||||
view.flags_mut().write(0x1 | 0x2 | 0x4); // PF_X | PF_W | PF_R
|
||||
|
||||
// location of segment in file
|
||||
let offset = (elf64_hdr::SIZE.unwrap() + elf64_phdr::SIZE.unwrap()) as u64;
|
||||
view.offset_mut().write(offset);
|
||||
// virtual address of segment
|
||||
view.vaddr_mut().write(VADDR + PROGRAM_OFFSET);
|
||||
|
||||
view.filesz_mut().write(program_size);
|
||||
view.memsz_mut().write(program_size);
|
||||
view.align_mut().write(4096);
|
||||
}
|
||||
pub fn create_elf(program: &[u8]) -> Vec<u8> {
|
||||
let hdr_sz = elf64_hdr::SIZE.unwrap();
|
||||
let phdr_sz = elf64_phdr::SIZE.unwrap();
|
||||
let mut buf = vec![0u8; hdr_sz + phdr_sz + program.len()];
|
||||
let mut file = elf64_file::View::new(&mut buf);
|
||||
set_elf64_hdr(file.hdr_mut());
|
||||
set_elf64_phdr(file.phdr_mut(), program.len() as u64);
|
||||
file.program_mut().copy_from_slice(program);
|
||||
buf
|
||||
}
|
||||
//fn create_program() -> Vec<u8> {
|
||||
//use iced_x86::code_asm::*;
|
||||
//let f = || -> Result<_, IcedError> {
|
||||
//let mut a = CodeAssembler::new(64)?;
|
||||
//// push + pop is 2+1 bytes, which is slightly shorter than even mov(eax, 60)
|
||||
//a.push(60)?;
|
||||
//a.pop(rax)?;
|
||||
//// a.mov(eax, 60)?;
|
||||
//// zero edi in two bytes
|
||||
//a.xor(edi, edi)?;
|
||||
//a.syscall()?;
|
||||
//let bytes = a.assemble(VADDR)?;
|
||||
//Ok(bytes)
|
||||
//};
|
||||
//f().unwrap()
|
||||
//}
|
||||
#[cfg(test)] #[test] fn test_ident_size_ok() {
|
||||
// XXX: could be a static assertion but Option<>::unwrap() is not a
|
||||
// const_fn
|
||||
assert_eq!(16, elf64_ident::SIZE.unwrap());
|
||||
}
|
||||
//#[cfg(test)] #[test] fn test_create_program() {
|
||||
//let program = create_program();
|
||||
//assert_eq!(7, program.len());
|
||||
//}
|
||||
//pub fn write_elf<P: AsRef<Path>>(path: P) -> std::io::Result<()> {
|
||||
//let buf = create_elf(&create_program());
|
||||
//let mut options = OpenOptions::new();
|
||||
//options.write(true).create(true).mode(0o755);
|
||||
//let mut file = options.open(path)?;
|
||||
//file.write_all(&buf)?;
|
||||
//Ok(())
|
||||
//}
|
|
@ -67,8 +67,12 @@ impl Vestal {
|
|||
},
|
||||
};
|
||||
imports.push((thunk, orig, import));
|
||||
if self.addr_to_import.contains_key(&call_via) {
|
||||
panic!("addr space overlap");
|
||||
if let Some(existing) = self.addr_to_import.get(&call_via) {
|
||||
panic!("addr space overlap at 0x{call_via:x}: {}::{} vs {}::{}",
|
||||
existing.0,
|
||||
existing.1,
|
||||
dep.to_string(),
|
||||
name);
|
||||
}
|
||||
self.addr_to_import.insert(call_via, (dep.to_string(), name));
|
||||
}
|
||||
|
@ -84,7 +88,7 @@ impl Vestal {
|
|||
Ok(())
|
||||
}
|
||||
pub fn load_bang_data (&mut self, path: &Arc<PathBuf>) -> Usually<Arc<[u8]>> {
|
||||
let (bang, data) = slice_shebang(read(path.as_path())?.as_slice());
|
||||
let (bang, data) = crate::bang::slice_shebang(read(path.as_path())?.as_slice());
|
||||
self.path_to_bang.insert(path.clone(), bang.clone());
|
||||
if bang.len() > 0 {
|
||||
println!(" (bang {path:?} {:x})", bang.len())
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
mod util;
|
||||
mod load;
|
||||
mod show;
|
||||
mod link;
|
||||
mod bang;
|
||||
pub(crate) use self::util::*;
|
||||
use clap::{arg, command, value_parser, ArgAction, Command};
|
||||
fn main () -> Usually<()> {
|
||||
|
@ -49,6 +51,24 @@ impl Vestal {
|
|||
let len = dll_data.len();
|
||||
println!(" (bytes {len} 0x{len:x})");
|
||||
self.show_calls(dll_path, dll_path.as_ref() == path)?;
|
||||
if dll_path.as_ref() == path {
|
||||
println!("{:?}", dll_data.hex_dump());
|
||||
let text_section = dll.get_section_by_name(".text")?;
|
||||
println!("\n{:#?}", text_section);
|
||||
let start = text_section.pointer_to_raw_data.0 as usize;
|
||||
let size = text_section.size_of_raw_data as usize;
|
||||
let vsize = text_section.virtual_size as usize;
|
||||
println!("\n{:?}", &dll_data[start..start+size].hex_dump());
|
||||
println!("\n{:?}", &dll_data[start..start+vsize].hex_dump());
|
||||
let elf = crate::link::create_elf(&dll_data[start..start+vsize]);
|
||||
println!("\n{:?}", &elf.hex_dump());
|
||||
println!("\n{:?}", &elf.len());
|
||||
let mut options = std::fs::OpenOptions::new();
|
||||
options.write(true).create(true).mode(0o755);
|
||||
let mut file = options.open("output")?;
|
||||
file.write_all(&elf)?;
|
||||
println!("\nDone.");
|
||||
}
|
||||
total += len;
|
||||
//println!("{:?}", dll_data.hex_dump());
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ impl Vestal {
|
|||
self.addr_to_import.get(&call_target).unwrap_or(&unknown).1);
|
||||
let dependent = path.file_name().unwrap();
|
||||
if verbose {
|
||||
println!(" ({BOLD}{external:30}{RESET} O=0x{:08x} {BOLD}R=0x{:08x}{RESET} {:25} {:40} 0x{:08x}",
|
||||
println!(" ({BOLD}{external}{RESET}\n Offset(0x{:08x}) RVA(R=0x{:08x})\n {:25} {:40} 0x{:08x}",
|
||||
offset,
|
||||
offset_rva,
|
||||
opcodes.iter().map(|x|format!("{x:>02x}")).collect::<Vec<_>>().join(" "),
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
pub(crate) use std::collections::{BTreeMap, BTreeSet};
|
||||
pub(crate) use std::error::Error;
|
||||
pub(crate) use std::fs::{read, canonicalize};
|
||||
pub(crate) use std::io::Write;
|
||||
pub(crate) use std::os::unix::fs::OpenOptionsExt;
|
||||
pub(crate) use std::path::{Path, PathBuf};
|
||||
pub(crate) use std::pin::Pin;
|
||||
pub(crate) use std::sync::Arc;
|
||||
pub(crate) use std::error::Error;
|
||||
pub(crate) use std::path::{Path, PathBuf};
|
||||
pub(crate) use std::collections::{BTreeMap, BTreeSet};
|
||||
pub(crate) use std::fs::{read, canonicalize};
|
||||
pub(crate) use itertools::izip;
|
||||
//pub(crate) use ::lancelot::loader::pe::{PE, reloc::apply_relocations};
|
||||
//pub(crate) use ::goblin::{error, Object, pe::{import::Import, export::Export}};
|
||||
|
@ -16,16 +18,3 @@ pub(crate) use ::exe::{Buffer, PE, VecPE, PtrPE, types::*, headers::*};
|
|||
pub(crate) type Usually<T> = Result<T, Box<dyn std::error::Error>>;
|
||||
pub const RESET: &str = "\u{001b}[0m";
|
||||
pub const BOLD: &str = "\u{001b}[1m";
|
||||
/// You can manually patch DLLs by prepending
|
||||
/// a `#!/usr/bin/env vestal` line to them.
|
||||
pub fn slice_shebang (buffer: &[u8]) -> (Arc<[u8]>, Arc<[u8]>) {
|
||||
if buffer.get(0) == Some(&b'#') && buffer.get(1) == Some(&b'!') {
|
||||
if let Some((bang, data)) = buffer.split_once(|x|*x==0x0a) {
|
||||
(bang.to_vec().into(), data.to_vec().into())
|
||||
} else {
|
||||
(buffer.to_vec().into(), vec![].into())
|
||||
}
|
||||
} else {
|
||||
(vec![].into(), buffer.to_vec().into())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue