├── .gitattributes ├── fuzz ├── .gitignore ├── fuzz_targets │ └── parse.rs └── Cargo.toml ├── .gitignore ├── assets └── dotnet_executable_example.dll ├── examples ├── rdr.rs ├── automagic.rs ├── scroll.rs ├── ar.rs ├── lipo.rs ├── dotnet_pe_analysis.rs └── dyldinfo.rs ├── .travis.yml ├── LICENSE ├── Cargo.toml ├── src ├── error.rs ├── mach │ ├── bind_opcodes.rs │ ├── fat.rs │ ├── relocation.rs │ ├── exports.rs │ └── imports.rs ├── pe │ ├── utils.rs │ ├── data_directories.rs │ ├── characteristic.rs │ ├── debug.rs │ ├── relocation.rs │ ├── mod.rs │ ├── header.rs │ ├── section_table.rs │ ├── import.rs │ └── optional_header.rs ├── elf │ ├── gnu_hash.rs │ ├── compression_header.rs │ └── note.rs └── strtab.rs ├── tests ├── compare_dyldinfos.rs └── archive.rs ├── Makefile ├── etc ├── crt132.rs ├── crt1.rs └── crt1a.rs ├── README.md └── CHANGELOG.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rs linguist-language=rust 2 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *~ 4 | *# 5 | *.#* 6 | /cmake-build-debug/ 7 | /.idea/ 8 | **/*.rs.bk 9 | -------------------------------------------------------------------------------- /assets/dotnet_executable_example.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Agent00049/goblin/master/assets/dotnet_executable_example.dll -------------------------------------------------------------------------------- /fuzz/fuzz_targets/parse.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use libfuzzer_sys::fuzz_target; 3 | 4 | fuzz_target!(|data: &[u8]| { 5 | let _ = goblin::Object::parse(data); 6 | }); 7 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "goblin-fuzz" 3 | version = "0.0.1" 4 | authors = ["Seo Sanghyeon "] 5 | edition = "2018" 6 | publish = false 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies.goblin] 12 | path = ".." 13 | 14 | [dependencies.libfuzzer-sys] 15 | git = "https://github.com/rust-fuzz/libfuzzer-sys.git" 16 | 17 | # Prevent this from interfering with workspaces 18 | [workspace] 19 | members = ["."] 20 | 21 | [[bin]] 22 | name = "parse" 23 | path = "fuzz_targets/parse.rs" 24 | -------------------------------------------------------------------------------- /examples/rdr.rs: -------------------------------------------------------------------------------- 1 | use goblin::error; 2 | use std::path::Path; 3 | use std::env; 4 | use std::fs::File; 5 | use std::io::Read; 6 | 7 | fn run () -> error::Result<()> { 8 | for (i, arg) in env::args().enumerate() { 9 | if i == 1 { 10 | let path = Path::new(arg.as_str()); 11 | let mut fd = File::open(path)?; 12 | let buffer = { let mut v = Vec::new(); fd.read_to_end(&mut v).unwrap(); v}; 13 | let res = goblin::Object::parse(&buffer)?; 14 | println!("{:#?}", res); 15 | } 16 | } 17 | Ok(()) 18 | } 19 | 20 | pub fn main () { 21 | match run() { 22 | Ok(()) => (), 23 | Err(err) => println!("{:#}", err) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - 1.31.1 4 | - stable 5 | - beta 6 | - nightly 7 | os: 8 | - linux 9 | - osx 10 | matrix: 11 | fast_finish: true 12 | allow_failures: 13 | - rust: nightly 14 | 15 | script: 16 | - cargo build --verbose 17 | - cargo test --verbose 18 | - cargo build --release 19 | - cargo test --release 20 | - make api 21 | - if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then make nightly_api; fi 22 | before_deploy: 23 | - cargo doc --no-deps 24 | - echo '' > target/doc/index.html; 25 | deploy: 26 | - provider: pages 27 | skip-cleanup: true 28 | local-dir: target/doc 29 | github-token: "$GITHUB_OAUTH_TOKEN" 30 | keep-history: false 31 | on: 32 | branch: master 33 | condition: $TRAVIS_OS_NAME = "linux" && $TRAVIS_RUST_VERSION = "stable" 34 | -------------------------------------------------------------------------------- /examples/automagic.rs: -------------------------------------------------------------------------------- 1 | use std::default::Default; 2 | 3 | // demonstrates "automagical" elf32/64 switches via cfg on arch and pub use hacks. 4 | // SIZEOF_* will change depending on whether it's an x86_64 system or 32-bit x86, or really any cfg you can think of. 5 | // similarly the printers will be different, since they have different impls. #typepuns4life 6 | 7 | #[cfg(target_pointer_width = "64")] 8 | pub use goblin::elf64 as elf; 9 | 10 | #[cfg(target_pointer_width = "32")] 11 | pub use goblin::elf32 as elf; 12 | 13 | #[cfg(any(target_pointer_width = "64", target_pointer_width = "32"))] 14 | use crate::elf::{header, sym}; 15 | 16 | #[cfg(any(target_pointer_width = "64", target_pointer_width = "32"))] 17 | fn main() { 18 | let header: header::Header = Default::default(); 19 | let sym: sym::Sym = Default::default(); 20 | println!("header: {:?}, sym: {:?}", header, sym); 21 | println!("sizeof header: {}", header::SIZEOF_EHDR); 22 | println!("sizeof sym: {}", sym::SIZEOF_SYM); 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) m4b 2016-2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/scroll.rs: -------------------------------------------------------------------------------- 1 | /// Demonstrates the magical powers of scroll + goblin 2 | /// Goblin implements `TryFromCtx` for the header type 3 | /// which means downstream crates/clients can just "parse" headers out of 4 | /// arbitrary buffers, without learning new crate specific function names 5 | /// I.e., all you need are Types + Pread = Happiness 6 | 7 | use goblin::{error, elf64, elf}; 8 | use scroll::{Pwrite, Pread}; 9 | 10 | fn run () -> error::Result<()> { 11 | let crt1: Vec = include!("../etc/crt1.rs"); 12 | let header: elf64::header::Header = crt1.pread(0)?; 13 | assert_eq!(header.e_type, elf64::header::ET_REL); 14 | println!("header: {:?}", &header); 15 | // now lets write the header into some bytes 16 | let mut bytes = [0u8; elf64::header::SIZEOF_EHDR]; 17 | bytes.pwrite(header, 0)?; 18 | // read it back out 19 | let header2: elf64::header::Header = bytes.pread(0)?; 20 | // they're the same 21 | assert_eq!(header, header2); 22 | let elf: elf::Elf = crt1.pread(0)?; 23 | println!("elf: {:#?}", &elf); 24 | let elf = elf::Elf::parse(&crt1)?; 25 | println!("elf: {:#?}", &elf); 26 | Ok(()) 27 | } 28 | 29 | fn main() { 30 | run().unwrap(); 31 | } 32 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "goblin" 3 | version = "0.1.1" 4 | authors = ["m4b ", "seu ", "Will Glynn ", "Philip Craig "] 5 | edition = "2018" 6 | readme = "README.md" 7 | keywords = ["binary", "elf", "mach", "pe", "archive"] 8 | repository = "https://github.com/m4b/goblin" 9 | license = "MIT" 10 | description = "An impish, cross-platform, ELF, Mach-o, and PE binary parsing and loading crate" 11 | documentation = "https://docs.rs/goblin" 12 | categories = ["parsing", "development-tools::debugging"] 13 | include = ["src/**/*", "Cargo.toml", "CHANGELOG.md", "LICENSE", "README.md", "etc/*", "examples/*", "tests/*", "fuzz/**/*"] 14 | 15 | [dependencies] 16 | plain = "0.2.3" 17 | 18 | [dependencies.log] 19 | version = "0.4" 20 | default-features = false 21 | optional = true 22 | 23 | [dependencies.scroll] 24 | version = "0.10" 25 | default_features = false 26 | 27 | [features] 28 | default = ["std", "elf32", "elf64", "mach32", "mach64", "pe32", "pe64", "archive", "endian_fd"] 29 | std = ["alloc", "scroll/std"] 30 | alloc = ["scroll/derive", "log"] 31 | endian_fd = ["alloc"] 32 | elf32 = [] 33 | elf64 = [] 34 | # for now we will require mach and pe to be alloc + endian_fd 35 | mach32 = ["alloc", "endian_fd"] 36 | mach64 = ["alloc", "endian_fd"] 37 | pe32 = ["alloc", "endian_fd"] 38 | pe64 = ["alloc", "endian_fd"] 39 | archive = ["alloc"] 40 | 41 | [badges.travis-ci] 42 | branch = "master" 43 | repository = "m4b/goblin" 44 | -------------------------------------------------------------------------------- /examples/ar.rs: -------------------------------------------------------------------------------- 1 | //cargo run --example=ar -- crt1.a 2 | 3 | use goblin::elf; 4 | use goblin::archive; 5 | use std::env; 6 | use std::path::Path; 7 | use std::fs::File; 8 | use std::io::Read; 9 | 10 | pub fn main () { 11 | let len = env::args().len(); 12 | if len <= 2 { 13 | println!("usage: ar member") 14 | } else { 15 | let mut path = String::default(); 16 | let mut member = String::default(); 17 | for (i, arg) in env::args().enumerate() { 18 | if i == 1 { 19 | path = arg.as_str().to_owned(); 20 | } else if i == 2 { 21 | member = arg.as_str().to_owned(); 22 | } 23 | } 24 | let path = Path::new(&path); 25 | let buffer = { let mut v = Vec::new(); let mut f = File::open(&path).unwrap(); f.read_to_end(&mut v).unwrap(); v}; 26 | match archive::Archive::parse(&buffer) { 27 | Ok(archive) => { 28 | println!("{:#?}", &archive); 29 | println!("start: {:?}", archive.member_of_symbol("_start")); 30 | match archive.extract(&member, &buffer) { 31 | Ok(bytes) => { 32 | match elf::Elf::parse(&bytes) { 33 | Ok(elf) => { 34 | println!("got elf: {:#?}", elf); 35 | }, 36 | Err(err) => println!("Err: {:?}", err) 37 | } 38 | }, 39 | Err(err) => println!("Extraction Error: {:?}", err) 40 | } 41 | }, 42 | Err(err) => println!("Err: {:?}", err) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! A custom Goblin error 2 | //! 3 | 4 | use scroll; 5 | use core::result; 6 | use core::fmt; 7 | use crate::alloc::string::String; 8 | #[cfg(feature = "std")] 9 | use std::{error, io}; 10 | 11 | #[derive(Debug)] 12 | /// A custom Goblin error 13 | pub enum Error { 14 | /// The binary is malformed somehow 15 | Malformed(String), 16 | /// The binary's magic is unknown or bad 17 | BadMagic(u64), 18 | /// An error emanating from reading and interpreting bytes 19 | Scroll(scroll::Error), 20 | /// An IO based error 21 | #[cfg(feature = "std")] 22 | IO(io::Error), 23 | } 24 | 25 | #[cfg(feature = "std")] 26 | impl error::Error for Error { 27 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { 28 | match *self { 29 | Error::IO(ref io) => Some(io), 30 | Error::Scroll(ref scroll) => Some(scroll), 31 | Error::BadMagic(_) => None, 32 | Error::Malformed(_) => None, 33 | } 34 | } 35 | } 36 | 37 | #[cfg(feature = "std")] 38 | impl From for Error { 39 | fn from(err: io::Error) -> Error { 40 | Error::IO(err) 41 | } 42 | } 43 | 44 | impl From for Error { 45 | fn from(err: scroll::Error) -> Error { 46 | Error::Scroll(err) 47 | } 48 | } 49 | 50 | impl fmt::Display for Error { 51 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 52 | match *self { 53 | #[cfg(feature = "std")] 54 | Error::IO(ref err) => write!(fmt, "{}", err), 55 | Error::Scroll(ref err) => write!(fmt, "{}", err), 56 | Error::BadMagic(magic) => write!(fmt, "Invalid magic number: 0x{:x}", magic), 57 | Error::Malformed(ref msg) => write!(fmt, "Malformed entity: {}", msg), 58 | } 59 | } 60 | } 61 | 62 | /// An impish result 63 | pub type Result = result::Result; 64 | -------------------------------------------------------------------------------- /tests/compare_dyldinfos.rs: -------------------------------------------------------------------------------- 1 | use std::process; 2 | 3 | pub fn compare(args: Vec<&str>) { 4 | let apple = process::Command::new("/Library/Developer/CommandLineTools/usr/bin/dyldinfo") 5 | .args(&args) 6 | .output() 7 | .expect("run Apple dyldinfo"); 8 | 9 | let goblin = process::Command::new("cargo") 10 | .arg("run") 11 | .arg("--quiet") 12 | .arg("--example") 13 | .arg("dyldinfo") 14 | .arg("--") 15 | .args(&args) 16 | .output() 17 | .expect("run cargo dyldinfo"); 18 | 19 | if apple.stdout.as_slice() != goblin.stdout.as_slice() { 20 | println!("dyldinfo calls disagree!"); 21 | println!("Apple dyldinfo {:?} output:\n{}", &args, String::from_utf8_lossy(&apple.stdout)); 22 | println!("---"); 23 | println!("cargo dyldinfo {:?} output:\n{}", &args, String::from_utf8_lossy(&goblin.stdout)); 24 | panic!("Apple dyldinfo and cargo dyldinfo differed (args: {:?})", args); 25 | } 26 | } 27 | 28 | #[cfg(target_os="macos")] 29 | #[test] 30 | fn compare_binds() { 31 | compare(vec!["-bind", "/Library/Developer/CommandLineTools/usr/bin/dyldinfo"]); 32 | compare(vec!["-bind", "/Library/Developer/CommandLineTools/usr/bin/clang"]); 33 | compare(vec!["-bind", "/usr/bin/tmutil"]); 34 | } 35 | 36 | #[cfg(target_os="macos")] 37 | #[test] 38 | fn compare_lazy_binds() { 39 | compare(vec!["-lazy_bind", "/Library/Developer/CommandLineTools/usr/bin/dyldinfo"]); 40 | compare(vec!["-lazy_bind", "/Library/Developer/CommandLineTools/usr/bin/clang"]); 41 | compare(vec!["-lazy_bind", "/usr/bin/tmutil"]); 42 | } 43 | 44 | #[cfg(target_os="macos")] 45 | #[test] 46 | fn compare_combined_options() { 47 | compare(vec!["-lazy_bind", "-bind", "/Library/Developer/CommandLineTools/usr/bin/dyldinfo"]); 48 | } 49 | 50 | #[cfg(not(target_os="macos"))] 51 | #[test] 52 | fn skipped_on_this_platform() { 53 | // this test does nothing on other platforms 54 | } 55 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ELF32 = $(wildcard src/elf/_32/*.rs) 2 | ELF = $(wildcard src/elf/*.rs) 3 | ELF64 = $(wildcard src/elf/_64/*.rs) 4 | MACH = $(wildcard src/mach/*.rs) 5 | PE = $(wildcard src/pe/*.rs) 6 | SRC = $(wildcard src/*.rs) $(ELF) $(ELF64) $(ELF32) $(MACH) $(PE) 7 | 8 | ARTIFACTS = $(addprefix target/debug/, libgoblin.rlib libgoblin.so) 9 | 10 | $(ARTIFACTS): $(SRC) 11 | cargo rustc -- -Z incremental=target/ 12 | 13 | doc: 14 | cargo doc 15 | 16 | clean: 17 | cargo clean 18 | 19 | test: 20 | RUST_BACKTRACE=1 cargo test 21 | 22 | example: 23 | cargo run --example=rdr -- /bin/ls 24 | 25 | api: 26 | cargo build --no-default-features 27 | cargo build --no-default-features --features="std" 28 | cargo build --no-default-features --features="endian_fd std" 29 | cargo build --no-default-features --features="elf32" 30 | cargo build --no-default-features --features="elf32 elf64" 31 | cargo build --no-default-features --features="elf32 elf64 std" 32 | cargo build --no-default-features --features="elf32 elf64 endian_fd std" 33 | cargo build --no-default-features --features="archive std" 34 | cargo build --no-default-features --features="mach64 std" 35 | cargo build --no-default-features --features="mach32 std" 36 | cargo build --no-default-features --features="mach64 mach32 std" 37 | cargo build --no-default-features --features="pe32 std" 38 | cargo build --no-default-features --features="pe32 pe64 std" 39 | cargo build 40 | 41 | nightly_api: 42 | cargo build --no-default-features --features="alloc" 43 | cargo build --no-default-features --features="endian_fd" 44 | cargo build --no-default-features --features="elf32 elf64 endian_fd" 45 | cargo build --no-default-features --features="archive" 46 | cargo build --no-default-features --features="mach64" 47 | cargo build --no-default-features --features="mach32" 48 | cargo build --no-default-features --features="mach64 mach32" 49 | cargo build --no-default-features --features="pe32" 50 | cargo build --no-default-features --features="pe32 pe64" 51 | 52 | .PHONY: clean test example doc 53 | -------------------------------------------------------------------------------- /examples/lipo.rs: -------------------------------------------------------------------------------- 1 | use goblin::mach::{self, Mach}; 2 | use std::env; 3 | use std::process; 4 | use std::path::Path; 5 | use std::fs::File; 6 | use std::io::{Read, Write}; 7 | 8 | fn usage() -> ! { 9 | println!("usage: lipo "); 10 | println!(" -m64 Extracts and writes the 64-bit binary in this fat container, if any"); 11 | process::exit(1); 12 | } 13 | 14 | fn main () { 15 | let len = env::args().len(); 16 | 17 | if len <= 1 { 18 | usage(); 19 | } else { 20 | let mut m64 = false; 21 | { 22 | let mut flags = env::args().collect::>(); 23 | flags.pop(); 24 | flags.remove(0); 25 | for option in flags { 26 | match option.as_str() { 27 | "-m64" => { m64 = true } 28 | other => { 29 | println!("unknown flag: {}", other); 30 | println!(); 31 | usage(); 32 | } 33 | } 34 | } 35 | } 36 | 37 | let path_name = env::args_os().last().unwrap(); 38 | let path = Path::new(&path_name); 39 | let buffer = { let mut v = Vec::new(); let mut f = File::open(&path).unwrap(); f.read_to_end(&mut v).unwrap(); v}; 40 | match mach::Mach::parse(&buffer) { 41 | Ok(Mach::Binary(_macho)) => { 42 | println!("Already a single arch binary"); 43 | process::exit(2); 44 | }, 45 | Ok(Mach::Fat(fat)) => { 46 | for (i, arch) in fat.iter_arches().enumerate() { 47 | let arch = arch.unwrap(); 48 | let name = format!("{}.{}", &path_name.to_string_lossy(), i); 49 | let path = Path::new(&name); 50 | if arch.is_64() && m64 { 51 | let bytes = &buffer[arch.offset as usize..][..arch.size as usize]; 52 | let mut file = File::create(path).unwrap(); 53 | file.write_all(bytes).unwrap(); 54 | break; 55 | } else if !m64 { 56 | let bytes = &buffer[arch.offset as usize..][..arch.size as usize]; 57 | let mut file = File::create(path).unwrap(); 58 | file.write_all(bytes).unwrap(); 59 | } 60 | } 61 | }, 62 | Err(err) => { 63 | println!("err: {:?}", err); 64 | process::exit(2); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/dotnet_pe_analysis.rs: -------------------------------------------------------------------------------- 1 | /// Demonstrates how to read additional metadata (i.e. .Net runtime ones) from PE context 2 | 3 | use goblin::container::Endian; 4 | use goblin::pe::data_directories::DataDirectory; 5 | use goblin::pe::PE; 6 | use goblin::pe::utils::get_data; 7 | use scroll::ctx::TryFromCtx; 8 | use scroll::Pread; 9 | 10 | #[repr(C)] 11 | #[derive(Debug, Pread)] 12 | pub struct CliHeader { 13 | pub cb: u32, 14 | pub major_version: u16, 15 | pub minor_version: u16, 16 | pub metadata: DataDirectory, 17 | pub flags: u32, 18 | pub entry_point_token: u32, 19 | } 20 | 21 | #[repr(C)] 22 | #[derive(Debug)] 23 | struct MetadataRoot<'a> { 24 | pub signature: u32, 25 | pub major_version: u16, 26 | pub minor_version: u16, 27 | _reserved: u32, 28 | pub length: u32, 29 | pub version: &'a str, 30 | } 31 | 32 | impl<'a> TryFromCtx<'a, Endian> for MetadataRoot<'a> { 33 | type Error = scroll::Error; 34 | fn try_from_ctx(src: &'a [u8], endian: Endian) -> Result<(Self, usize), Self::Error> { 35 | let offset = &mut 0; 36 | let signature = src.gread_with(offset, endian)?; 37 | let major_version = src.gread_with(offset, endian)?; 38 | let minor_version = src.gread_with(offset, endian)?; 39 | let reserved = src.gread_with(offset, endian)?; 40 | let length = src.gread_with(offset, endian)?; 41 | let version = src.gread(offset)?; 42 | Ok(( 43 | Self { 44 | signature, 45 | major_version, 46 | minor_version, 47 | _reserved: reserved, 48 | length, 49 | version, 50 | }, 51 | *offset, 52 | )) 53 | } 54 | } 55 | 56 | fn main() { 57 | let file = include_bytes!("../assets/dotnet_executable_example.dll"); 58 | let file = &file[..]; 59 | let pe = PE::parse(file).unwrap(); 60 | if pe.header.coff_header.machine != 0x14c { 61 | panic!("Is not a .Net executable"); 62 | } 63 | let optional_header = pe.header.optional_header.expect("No optional header"); 64 | let file_alignment = optional_header.windows_fields.file_alignment; 65 | let cli_header = optional_header 66 | .data_directories 67 | .get_clr_runtime_header() 68 | .expect("No CLI header"); 69 | let sections = &pe.sections; 70 | 71 | let cli_header_value: CliHeader = get_data(file, sections, cli_header, file_alignment).unwrap(); 72 | println!("{:#?}", cli_header_value); 73 | let metadata_root: MetadataRoot = get_data(file, sections, cli_header_value.metadata, file_alignment).unwrap(); 74 | println!("{:#?}", metadata_root); 75 | } 76 | -------------------------------------------------------------------------------- /src/mach/bind_opcodes.rs: -------------------------------------------------------------------------------- 1 | //! Bind opcodes are interpreted by the dynamic linker to efficiently collect every symbol imported by this binary, and from which library using two-level namespacing 2 | //! 3 | //! Some uses of external symbols do not need to be bound immediately. 4 | //! Instead they can be lazily bound on first use. The lazy_bind 5 | //! are contains a stream of BIND opcodes to bind all lazy symbols. 6 | //! Normal use is that dyld ignores the lazy_bind section when 7 | //! loading an image. Instead the static linker arranged for a 8 | //! lazy pointer to initially point to a helper function which 9 | //! pushes the offset into the lazy_bind area for the symbol 10 | //! needing to be bound, then jumps to dyld which simply adds 11 | //! the offset to lazy_bind_off to get the information on what 12 | //! to bind. 13 | 14 | pub type Opcode = u8; 15 | 16 | // The following are used to encode binding information 17 | pub const BIND_TYPE_POINTER : u8 = 1; 18 | pub const BIND_TYPE_TEXT_ABSOLUTE32 : u8 = 2; 19 | pub const BIND_TYPE_TEXT_PCREL32 : u8 = 3; 20 | pub const BIND_SPECIAL_DYLIB_SELF : u8 = 0; 21 | pub const BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE : u8 = 0xf; // -1 22 | pub const BIND_SPECIAL_DYLIB_FLAT_LOOKUP : u8 = 0xe; // -2 23 | pub const BIND_SYMBOL_FLAGS_WEAK_IMPORT : u8 = 0x1; 24 | pub const BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION : u8 = 0x8; 25 | pub const BIND_OPCODE_MASK : u8 = 0xF0; 26 | pub const BIND_IMMEDIATE_MASK : u8 = 0x0F; 27 | pub const BIND_OPCODE_DONE : Opcode = 0x00; 28 | pub const BIND_OPCODE_SET_DYLIB_ORDINAL_IMM : Opcode = 0x10; 29 | pub const BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB : Opcode = 0x20; 30 | pub const BIND_OPCODE_SET_DYLIB_SPECIAL_IMM : Opcode = 0x30; 31 | pub const BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM : Opcode = 0x40; 32 | pub const BIND_OPCODE_SET_TYPE_IMM : Opcode = 0x50; 33 | pub const BIND_OPCODE_SET_ADDEND_SLEB : Opcode = 0x60; 34 | pub const BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB : Opcode = 0x70; 35 | pub const BIND_OPCODE_ADD_ADDR_ULEB : Opcode = 0x80; 36 | pub const BIND_OPCODE_DO_BIND : Opcode = 0x90; 37 | pub const BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB : Opcode = 0xA0; 38 | pub const BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED : Opcode = 0xB0; 39 | pub const BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB : Opcode = 0xC0; 40 | 41 | pub fn opcode_to_str(opcode: Opcode) -> &'static str { 42 | match opcode { 43 | BIND_OPCODE_DONE => "BIND_OPCODE_DONE", 44 | BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM", 45 | BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB", 46 | BIND_OPCODE_SET_DYLIB_SPECIAL_IMM => "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM", 47 | BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", 48 | BIND_OPCODE_SET_TYPE_IMM => "BIND_OPCODE_SET_TYPE_IMM", 49 | BIND_OPCODE_SET_ADDEND_SLEB => "BIND_OPCODE_SET_ADDEND_SLEB", 50 | BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", 51 | BIND_OPCODE_ADD_ADDR_ULEB => "BIND_OPCODE_ADD_ADDR_ULEB", 52 | BIND_OPCODE_DO_BIND => "BIND_OPCODE_DO_BIND", 53 | BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", 54 | BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", 55 | BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", 56 | _ => "UNKNOWN OPCODE" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/pe/utils.rs: -------------------------------------------------------------------------------- 1 | use scroll::Pread; 2 | use crate::alloc::string::ToString; 3 | use crate::error; 4 | 5 | use super::section_table; 6 | 7 | use core::cmp; 8 | use crate::pe::data_directories::DataDirectory; 9 | 10 | use log::debug; 11 | 12 | pub fn is_in_range (rva: usize, r1: usize, r2: usize) -> bool { 13 | r1 <= rva && rva < r2 14 | } 15 | 16 | // reference: Peter Ferrie. Reliable algorithm to extract overlay of a PE. https://bit.ly/2vBX2bR 17 | #[inline] 18 | fn aligned_pointer_to_raw_data(pointer_to_raw_data: usize) -> usize { 19 | const PHYSICAL_ALIGN: usize = 0x1ff; 20 | pointer_to_raw_data & !PHYSICAL_ALIGN 21 | } 22 | 23 | #[inline] 24 | fn section_read_size(section: §ion_table::SectionTable, file_alignment: u32) -> usize { 25 | fn round_size(size: usize) -> usize { 26 | const PAGE_MASK: usize = 0xfff; 27 | (size + PAGE_MASK) & !PAGE_MASK 28 | } 29 | 30 | let file_alignment = file_alignment as usize; 31 | let size_of_raw_data = section.size_of_raw_data as usize; 32 | let virtual_size = section.virtual_size as usize; 33 | let read_size = { 34 | let read_size = (section.pointer_to_raw_data as usize + size_of_raw_data + file_alignment - 1) & !(file_alignment - 1); 35 | cmp::min(read_size, round_size(size_of_raw_data)) 36 | }; 37 | 38 | if virtual_size == 0 { 39 | read_size 40 | } else { 41 | cmp::min(read_size, round_size(virtual_size)) 42 | } 43 | } 44 | 45 | fn rva2offset (rva: usize, section: §ion_table::SectionTable) -> usize { 46 | (rva - section.virtual_address as usize) + aligned_pointer_to_raw_data(section.pointer_to_raw_data as usize) 47 | } 48 | 49 | fn is_in_section (rva: usize, section: §ion_table::SectionTable, file_alignment: u32) -> bool { 50 | let section_rva = section.virtual_address as usize; 51 | is_in_range(rva, section_rva, section_rva + section_read_size(section, file_alignment)) 52 | } 53 | 54 | pub fn find_offset (rva: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> Option { 55 | for (i, section) in sections.iter().enumerate() { 56 | debug!("Checking {} for {:#x} ∈ {:#x}..{:#x}", section.name().unwrap_or(""), rva, section.virtual_address, section.virtual_address + section.virtual_size); 57 | if is_in_section(rva, §ion, file_alignment) { 58 | let offset = rva2offset(rva, §ion); 59 | debug!("Found in section {}({}), remapped into offset {:#x}", section.name().unwrap_or(""), i, offset); 60 | return Some(offset) 61 | } 62 | } 63 | None 64 | } 65 | 66 | pub fn find_offset_or (rva: usize, sections: &[section_table::SectionTable], file_alignment: u32, msg: &str) -> error::Result { 67 | find_offset(rva, sections, file_alignment).ok_or_else(|| error::Error::Malformed(msg.to_string())) 68 | } 69 | 70 | pub fn try_name<'a>(bytes: &'a [u8], rva: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<&'a str> { 71 | match find_offset(rva, sections, file_alignment) { 72 | Some(offset) => { 73 | Ok(bytes.pread::<&str>(offset)?) 74 | }, 75 | None => { 76 | Err(error::Error::Malformed(format!("Cannot find name from rva {:#x} in sections: {:?}", rva, sections))) 77 | } 78 | } 79 | } 80 | 81 | pub fn get_data<'a, T>(bytes: &'a [u8], sections: &[section_table::SectionTable], directory: DataDirectory, file_alignment: u32) -> error::Result 82 | where T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error> { 83 | let rva = directory.virtual_address as usize; 84 | let offset = find_offset(rva, sections, file_alignment) 85 | .ok_or_else(||error::Error::Malformed(directory.virtual_address.to_string()))?; 86 | let result: T = bytes.pread_with(offset, scroll::LE)?; 87 | Ok(result) 88 | } 89 | -------------------------------------------------------------------------------- /src/pe/data_directories.rs: -------------------------------------------------------------------------------- 1 | use crate::error; 2 | use scroll::{Pread, Pwrite, SizeWith}; 3 | 4 | #[repr(C)] 5 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 6 | #[derive(Pread, Pwrite, SizeWith)] 7 | pub struct DataDirectory { 8 | pub virtual_address: u32, 9 | pub size: u32, 10 | } 11 | 12 | pub const SIZEOF_DATA_DIRECTORY: usize = 8; 13 | const NUM_DATA_DIRECTORIES: usize = 16; 14 | 15 | impl DataDirectory { 16 | pub fn parse(bytes: &[u8], offset: &mut usize) -> error::Result { 17 | let dd = bytes.gread_with(offset, scroll::LE)?; 18 | Ok (dd) 19 | } 20 | } 21 | 22 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 23 | pub struct DataDirectories { 24 | pub data_directories: [Option; NUM_DATA_DIRECTORIES], 25 | } 26 | 27 | impl DataDirectories { 28 | pub fn parse(bytes: &[u8], count: usize, offset: &mut usize) -> error::Result { 29 | let mut data_directories = [None; NUM_DATA_DIRECTORIES]; 30 | if count > NUM_DATA_DIRECTORIES { return Err (error::Error::Malformed(format!("data directory count ({}) is greater than maximum number of data directories ({})", count, NUM_DATA_DIRECTORIES))) } 31 | for dir in data_directories.iter_mut().take(count) { 32 | let dd = DataDirectory::parse(bytes, offset)?; 33 | let dd = if dd.virtual_address == 0 && dd.size == 0 { None } else { Some (dd) }; 34 | *dir = dd; 35 | } 36 | Ok (DataDirectories { data_directories }) 37 | } 38 | pub fn get_export_table(&self) -> &Option { 39 | let idx = 0; 40 | unsafe { self.data_directories.get_unchecked(idx) } 41 | } 42 | pub fn get_import_table(&self) -> &Option { 43 | let idx = 1; 44 | unsafe { self.data_directories.get_unchecked(idx) } 45 | } 46 | pub fn get_resource_table(&self) -> &Option { 47 | let idx = 2; 48 | unsafe { self.data_directories.get_unchecked(idx) } 49 | } 50 | pub fn get_exception_table(&self) -> &Option { 51 | let idx = 3; 52 | unsafe { self.data_directories.get_unchecked(idx) } 53 | } 54 | pub fn get_certificate_table(&self) -> &Option { 55 | let idx = 4; 56 | unsafe { self.data_directories.get_unchecked(idx) } 57 | } 58 | pub fn get_base_relocation_table(&self) -> &Option { 59 | let idx = 5; 60 | unsafe { self.data_directories.get_unchecked(idx) } 61 | } 62 | pub fn get_debug_table(&self) -> &Option { 63 | let idx = 6; 64 | unsafe { self.data_directories.get_unchecked(idx) } 65 | } 66 | pub fn get_architecture(&self) -> &Option { 67 | let idx = 7; 68 | unsafe { self.data_directories.get_unchecked(idx) } 69 | } 70 | pub fn get_global_ptr(&self) -> &Option { 71 | let idx = 8; 72 | unsafe { self.data_directories.get_unchecked(idx) } 73 | } 74 | pub fn get_tls_table(&self) -> &Option { 75 | let idx = 9; 76 | unsafe { self.data_directories.get_unchecked(idx) } 77 | } 78 | pub fn get_load_config_table(&self) -> &Option { 79 | let idx = 10; 80 | unsafe { self.data_directories.get_unchecked(idx) } 81 | } 82 | pub fn get_bound_import_table(&self) -> &Option { 83 | let idx = 11; 84 | unsafe { self.data_directories.get_unchecked(idx) } 85 | } 86 | pub fn get_import_address_table(&self) -> &Option { 87 | let idx = 12; 88 | unsafe { self.data_directories.get_unchecked(idx) } 89 | } 90 | pub fn get_delay_import_descriptor(&self) -> &Option { 91 | let idx = 13; 92 | unsafe { self.data_directories.get_unchecked(idx) } 93 | } 94 | pub fn get_clr_runtime_header(&self) -> &Option { 95 | let idx = 14; 96 | unsafe { self.data_directories.get_unchecked(idx) } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/mach/fat.rs: -------------------------------------------------------------------------------- 1 | //! A Mach-o fat binary is a multi-architecture binary container 2 | 3 | use core::fmt; 4 | 5 | if_std! { 6 | use std::fs::File; 7 | use std::io::{self, Read}; 8 | } 9 | 10 | use scroll::{Pread, Pwrite, SizeWith}; 11 | use crate::mach::constants::cputype::{CpuType, CpuSubType, CPU_SUBTYPE_MASK, CPU_ARCH_ABI64}; 12 | use crate::error; 13 | 14 | pub const FAT_MAGIC: u32 = 0xcafe_babe; 15 | pub const FAT_CIGAM: u32 = 0xbeba_feca; 16 | 17 | #[repr(C)] 18 | #[derive(Clone, Copy, Default, Pread, Pwrite, SizeWith)] 19 | /// The Mach-o `FatHeader` always has its data bigendian 20 | pub struct FatHeader { 21 | /// The magic number, `cafebabe` 22 | pub magic: u32, 23 | /// How many fat architecture headers there are 24 | pub nfat_arch: u32, 25 | } 26 | 27 | pub const SIZEOF_FAT_HEADER: usize = 8; 28 | 29 | impl fmt::Debug for FatHeader { 30 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 31 | f.debug_struct("FatHeader") 32 | .field("magic", &format_args!("0x{:x}", self.magic)) 33 | .field("nfat_arch", &self.nfat_arch) 34 | .finish() 35 | } 36 | } 37 | 38 | impl FatHeader { 39 | /// Reinterpret a `FatHeader` from `bytes` 40 | pub fn from_bytes(bytes: [u8; SIZEOF_FAT_HEADER]) -> FatHeader { 41 | let mut offset = 0; 42 | let magic = bytes.gread_with(&mut offset, scroll::BE).unwrap(); 43 | let nfat_arch = bytes.gread_with(&mut offset, scroll::BE).unwrap(); 44 | FatHeader { 45 | magic, 46 | nfat_arch, 47 | } 48 | } 49 | 50 | /// Reads a `FatHeader` from a `File` on disk 51 | #[cfg(feature = "std")] 52 | pub fn from_fd(fd: &mut File) -> io::Result { 53 | let mut header = [0; SIZEOF_FAT_HEADER]; 54 | fd.read_exact(&mut header)?; 55 | Ok(FatHeader::from_bytes(header)) 56 | } 57 | 58 | /// Parse a mach-o fat header from the `bytes` 59 | pub fn parse(bytes: &[u8]) -> error::Result { 60 | Ok(bytes.pread_with::(0, scroll::BE)?) 61 | } 62 | 63 | } 64 | 65 | #[repr(C)] 66 | #[derive(Clone, Copy, Default, Pread, Pwrite, SizeWith)] 67 | /// The Mach-o `FatArch` always has its data bigendian 68 | pub struct FatArch { 69 | /// What kind of CPU this binary is 70 | pub cputype: u32, 71 | pub cpusubtype: u32, 72 | /// Where in the fat binary it starts 73 | pub offset: u32, 74 | /// How big the binary is 75 | pub size: u32, 76 | pub align: u32, 77 | } 78 | 79 | pub const SIZEOF_FAT_ARCH: usize = 20; 80 | 81 | impl fmt::Debug for FatArch { 82 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 83 | fmt.debug_struct("FatArch") 84 | .field("cputype", &self.cputype()) 85 | .field("cmdsize", &self.cpusubtype()) 86 | .field("offset", &format_args!("{:#x}", &self.offset)) 87 | .field("size", &self.size) 88 | .field("align", &self.align) 89 | .finish() 90 | } 91 | } 92 | 93 | impl FatArch { 94 | /// Get the slice of bytes this header describes from `bytes` 95 | pub fn slice<'a>(&self, bytes: &'a [u8]) -> &'a [u8] { 96 | let start = self.offset as usize; 97 | let end = (self.offset + self.size) as usize; 98 | &bytes[start..end] 99 | } 100 | 101 | /// Returns the cpu type 102 | pub fn cputype(&self) -> CpuType { 103 | self.cputype 104 | } 105 | 106 | /// Returns the cpu subtype with the capabilities removed 107 | pub fn cpusubtype(&self) -> CpuSubType { 108 | self.cpusubtype & !CPU_SUBTYPE_MASK 109 | } 110 | 111 | /// Returns the capabilities of the CPU 112 | pub fn cpu_caps(&self) -> u32 { 113 | (self.cpusubtype & CPU_SUBTYPE_MASK) >> 24 114 | } 115 | 116 | /// Whether this fat architecture header describes a 64-bit binary 117 | pub fn is_64(&self) -> bool { 118 | (self.cputype & CPU_ARCH_ABI64) == CPU_ARCH_ABI64 119 | } 120 | 121 | /// Parse a `FatArch` header from `bytes` at `offset` 122 | pub fn parse(bytes: &[u8], offset: usize) -> error::Result { 123 | let arch = bytes.pread_with::(offset, scroll::BE)?; 124 | Ok(arch) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/pe/characteristic.rs: -------------------------------------------------------------------------------- 1 | /* 2 | type characteristic = 3 | | IMAGE_FILE_RELOCS_STRIPPED 4 | | IMAGE_FILE_EXECUTABLE_IMAGE 5 | | IMAGE_FILE_LINE_NUMS_STRIPPED 6 | | IMAGE_FILE_LOCAL_SYMS_STRIPPED 7 | | IMAGE_FILE_AGGRESSIVE_WS_TRIM 8 | | IMAGE_FILE_LARGE_ADDRESS_AWARE 9 | | RESERVED 10 | | IMAGE_FILE_BYTES_REVERSED_LO 11 | | IMAGE_FILE_32BIT_MACHINE 12 | | IMAGE_FILE_DEBUG_STRIPPED 13 | | IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 14 | | IMAGE_FILE_NET_RUN_FROM_SWAP 15 | | IMAGE_FILE_SYSTEM 16 | | IMAGE_FILE_DLL 17 | | IMAGE_FILE_UP_SYSTEM_ONLY 18 | | IMAGE_FILE_BYTES_REVERSED_HI 19 | | UNKNOWN of int 20 | 21 | let get_characteristic = 22 | function 23 | | 0x0001 -> IMAGE_FILE_RELOCS_STRIPPED 24 | | 0x0002 -> IMAGE_FILE_EXECUTABLE_IMAGE 25 | | 0x0004 -> IMAGE_FILE_LINE_NUMS_STRIPPED 26 | | 0x0008 -> IMAGE_FILE_LOCAL_SYMS_STRIPPED 27 | | 0x0010 -> IMAGE_FILE_AGGRESSIVE_WS_TRIM 28 | | 0x0020 -> IMAGE_FILE_LARGE_ADDRESS_AWARE 29 | | 0x0040 -> RESERVED 30 | | 0x0080 -> IMAGE_FILE_BYTES_REVERSED_LO 31 | | 0x0100 -> IMAGE_FILE_32BIT_MACHINE 32 | | 0x0200 -> IMAGE_FILE_DEBUG_STRIPPED 33 | | 0x0400 -> IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 34 | | 0x0800 -> IMAGE_FILE_NET_RUN_FROM_SWAP 35 | | 0x1000 -> IMAGE_FILE_SYSTEM 36 | | 0x2000 -> IMAGE_FILE_DLL 37 | | 0x4000 -> IMAGE_FILE_UP_SYSTEM_ONLY 38 | | 0x8000 -> IMAGE_FILE_BYTES_REVERSED_HI 39 | | x -> UNKNOWN x 40 | 41 | let characteristic_to_string = 42 | function 43 | | IMAGE_FILE_RELOCS_STRIPPED -> "IMAGE_FILE_RELOCS_STRIPPED" 44 | | IMAGE_FILE_EXECUTABLE_IMAGE -> "IMAGE_FILE_EXECUTABLE_IMAGE" 45 | | IMAGE_FILE_LINE_NUMS_STRIPPED -> "IMAGE_FILE_LINE_NUMS_STRIPPED" 46 | | IMAGE_FILE_LOCAL_SYMS_STRIPPED -> "IMAGE_FILE_LOCAL_SYMS_STRIPPED" 47 | | IMAGE_FILE_AGGRESSIVE_WS_TRIM -> "IMAGE_FILE_AGGRESSIVE_WS_TRIM" 48 | | IMAGE_FILE_LARGE_ADDRESS_AWARE -> "IMAGE_FILE_LARGE_ADDRESS_AWARE" 49 | | RESERVED -> "RESERVED" 50 | | IMAGE_FILE_BYTES_REVERSED_LO -> "IMAGE_FILE_BYTES_REVERSED_LO" 51 | | IMAGE_FILE_32BIT_MACHINE -> "IMAGE_FILE_32BIT_MACHINE" 52 | | IMAGE_FILE_DEBUG_STRIPPED -> "IMAGE_FILE_DEBUG_STRIPPED" 53 | | IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP -> "IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP" 54 | | IMAGE_FILE_NET_RUN_FROM_SWAP -> "IMAGE_FILE_NET_RUN_FROM_SWAP" 55 | | IMAGE_FILE_SYSTEM -> "IMAGE_FILE_SYSTEM" 56 | | IMAGE_FILE_DLL -> "IMAGE_FILE_DLL" 57 | | IMAGE_FILE_UP_SYSTEM_ONLY -> "IMAGE_FILE_UP_SYSTEM_ONLY" 58 | | IMAGE_FILE_BYTES_REVERSED_HI -> "IMAGE_FILE_BYTES_REVERSED_HI" 59 | | UNKNOWN x -> Printf.sprintf "UNKNOWN_CHARACTERISTIC 0x%x" x 60 | 61 | let is_dll characteristics = 62 | let characteristic = characteristic_to_int IMAGE_FILE_DLL in 63 | characteristics land characteristic = characteristic 64 | 65 | let has characteristic characteristics = 66 | let characteristic = characteristic_to_int characteristic in 67 | characteristics land characteristic = characteristic 68 | 69 | (* TODO: this is a mad hack *) 70 | let show_type characteristics = 71 | if (has IMAGE_FILE_DLL characteristics) then "DLL" 72 | else if (has IMAGE_FILE_EXECUTABLE_IMAGE characteristics) then "EXE" 73 | else "MANY" (* print all *) 74 | */ 75 | 76 | pub const IMAGE_FILE_RELOCS_STRIPPED: u16 = 0x0001; 77 | pub const IMAGE_FILE_EXECUTABLE_IMAGE: u16 = 0x0002; 78 | pub const IMAGE_FILE_LINE_NUMS_STRIPPED: u16 = 0x0004; 79 | pub const IMAGE_FILE_LOCAL_SYMS_STRIPPED: u16 = 0x0008; 80 | pub const IMAGE_FILE_AGGRESSIVE_WS_TRIM: u16 = 0x0010; 81 | pub const IMAGE_FILE_LARGE_ADDRESS_AWARE: u16 = 0x0020; 82 | pub const RESERVED: u16 = 0x0040; 83 | pub const IMAGE_FILE_BYTES_REVERSED_LO: u16 = 0x0080; 84 | pub const IMAGE_FILE_32BIT_MACHINE: u16 = 0x0100; 85 | pub const IMAGE_FILE_DEBUG_STRIPPED: u16 = 0x0200; 86 | pub const IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP: u16 = 0x0400; 87 | pub const IMAGE_FILE_NET_RUN_FROM_SWAP: u16 = 0x0800; 88 | pub const IMAGE_FILE_SYSTEM: u16 = 0x1000; 89 | pub const IMAGE_FILE_DLL: u16 = 0x2000; 90 | pub const IMAGE_FILE_UP_SYSTEM_ONLY: u16 = 0x4000; 91 | pub const IMAGE_FILE_BYTES_REVERSED_HI: u16 = 0x8000; 92 | 93 | pub fn is_dll(characteristics: u16) -> bool { 94 | characteristics & IMAGE_FILE_DLL == IMAGE_FILE_DLL 95 | } 96 | 97 | pub fn is_exe(characteristics: u16) -> bool { 98 | characteristics & IMAGE_FILE_EXECUTABLE_IMAGE == IMAGE_FILE_EXECUTABLE_IMAGE 99 | } 100 | -------------------------------------------------------------------------------- /tests/archive.rs: -------------------------------------------------------------------------------- 1 | use goblin::archive::*; 2 | use scroll::Pread; 3 | use std::path::Path; 4 | use std::fs::File; 5 | 6 | #[test] 7 | fn parse_file_header() { 8 | let file_header: [u8; SIZEOF_HEADER] = [0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 9 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 10 | 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 11 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 12 | 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 13 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 14 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 15 | 0x32, 0x34, 0x34, 0x20, 0x20, 0x20, 0x20, 16 | 0x20, 0x20, 0x60, 0x0a]; 17 | let buffer = &file_header[..]; 18 | match buffer.pread::(0) { 19 | Err(e) => panic!("could not read the buffer: {:?}", e), 20 | Ok(file_header2) => { 21 | let file_header = MemberHeader { 22 | identifier: [0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,], 23 | timestamp: [48, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32], 24 | owner_id: [48, 32, 32, 32, 32, 32], 25 | group_id: [48, 32, 32, 32, 32, 32], 26 | mode: [48, 32, 32, 32, 32, 32, 32, 32], 27 | file_size: [56, 50, 52, 52, 32, 32, 32, 32, 32, 32], 28 | terminator: [96, 10] }; 29 | assert_eq!(file_header, file_header2) 30 | } 31 | } 32 | } 33 | 34 | #[test] 35 | fn parse_archive() { 36 | let crt1a: Vec = include!("../etc/crt1a.rs"); 37 | const START: &str = "_start"; 38 | match Archive::parse(&crt1a) { 39 | Ok(archive) => { 40 | assert_eq!(archive.member_of_symbol(START), Some("crt1.o")); 41 | if let Some(member) = archive.get("crt1.o") { 42 | assert_eq!(member.offset, 194); 43 | assert_eq!(member.size(), 1928) 44 | } else { 45 | panic!("could not get crt1.o"); 46 | } 47 | }, 48 | Err(err) => panic!("could not parse archive: {:?}", err), 49 | }; 50 | } 51 | 52 | #[test] 53 | fn parse_self() { 54 | use std::fs; 55 | use std::io::Read; 56 | let mut path = Path::new("target").join("debug").join("libgoblin.rlib"); 57 | // https://github.com/m4b/goblin/issues/63 58 | if fs::metadata(&path).is_err() { 59 | path = Path::new("target").join("release").join("libgoblin.rlib"); 60 | } 61 | let buffer = { 62 | let mut fd = File::open(path).expect("can open file; did you run cargo build first?"); 63 | let mut v = Vec::new(); 64 | fd.read_to_end(&mut v).expect("read file"); 65 | v 66 | }; 67 | 68 | let archive = Archive::parse(&buffer).expect("parse rlib"); 69 | 70 | // check that the archive has a useful symbol table by counting the total number of symbols 71 | let symbol_count: usize = archive.summarize().into_iter() 72 | .map(|(_member_name, _member_index, ref symbols)| symbols.len()) 73 | .sum(); 74 | assert!(symbol_count > 500); 75 | 76 | let goblin_object_name = archive.members() 77 | .into_iter() 78 | .find(|member| { 79 | println!("member: {:?}", member); 80 | member.ends_with("goblin-archive.o") // < 1.18 81 | || (member.starts_with("goblin") && member.ends_with("0.o")) // >= 1.18 && < 1.22 82 | || (member.starts_with("goblin") && member.ends_with("rust-cgu.o")) // = 1.22 83 | || (member.starts_with("goblin") && member.ends_with("rcgu.o")) // >= nightly 1.23 84 | }) 85 | .expect("goblin-.0.o not found"); 86 | 87 | let bytes = archive.extract(goblin_object_name, &buffer).expect("extract goblin object"); 88 | match goblin::Object::parse(&bytes).expect("parse object") { 89 | goblin::Object::Elf(elf) => { 90 | assert!(elf.entry == 0); 91 | } 92 | goblin::Object::Mach(goblin::mach::Mach::Binary(macho)) => { 93 | assert_eq!(macho.header.filetype, goblin::mach::header::MH_OBJECT); 94 | assert_eq!(macho.entry, 0); 95 | } 96 | other => { 97 | panic!("unexpected Object::parse result: {:?}", other); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /etc/crt132.rs: -------------------------------------------------------------------------------- 1 | vec![0x7F,0x45,0x4C,0x46,0x1,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x3,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xBC,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x0,0x0,0x28,0x0,0xD,0x0,0xA,0x0,0x4,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x4E,0x55,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x31,0xED,0x5E,0x89,0xE1,0x83,0xE4,0xF0,0x50,0x54,0x52,0x68,0x0,0x0,0x0,0x0,0x68,0x0,0x0,0x0,0x0,0x51,0x56,0x68,0x0,0x0,0x0,0x0,0xE8,0xFC,0xFF,0xFF,0xFF,0xF4,0x3,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x43,0x43,0x3A,0x20,0x28,0x47,0x4E,0x55,0x29,0x20,0x36,0x2E,0x31,0x2E,0x31,0x20,0x32,0x30,0x31,0x36,0x30,0x38,0x30,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x9,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0xF1,0xFF,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x11,0x0,0x4,0x0,0xF,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x0,0x2,0x0,0x1F,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x4B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x52,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x6,0x0,0x2F,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x11,0x0,0x5,0x0,0x3E,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x6,0x0,0x0,0x69,0x6E,0x69,0x74,0x2E,0x63,0x0,0x5F,0x66,0x70,0x5F,0x68,0x77,0x0,0x5F,0x5F,0x6C,0x69,0x62,0x63,0x5F,0x63,0x73,0x75,0x5F,0x66,0x69,0x6E,0x69,0x0,0x5F,0x5F,0x6C,0x69,0x62,0x63,0x5F,0x63,0x73,0x75,0x5F,0x69,0x6E,0x69,0x74,0x0,0x5F,0x49,0x4F,0x5F,0x73,0x74,0x64,0x69,0x6E,0x5F,0x75,0x73,0x65,0x64,0x0,0x5F,0x5F,0x6C,0x69,0x62,0x63,0x5F,0x73,0x74,0x61,0x72,0x74,0x5F,0x6D,0x61,0x69,0x6E,0x0,0x5F,0x5F,0x64,0x61,0x74,0x61,0x5F,0x73,0x74,0x61,0x72,0x74,0x0,0x0,0x0,0x0,0xC,0x0,0x0,0x0,0x1,0xB,0x0,0x0,0x11,0x0,0x0,0x0,0x1,0xD,0x0,0x0,0x18,0x0,0x0,0x0,0x1,0xE,0x0,0x0,0x1D,0x0,0x0,0x0,0x2,0x11,0x0,0x0,0x0,0x2E,0x73,0x79,0x6D,0x74,0x61,0x62,0x0,0x2E,0x73,0x74,0x72,0x74,0x61,0x62,0x0,0x2E,0x73,0x68,0x73,0x74,0x72,0x74,0x61,0x62,0x0,0x2E,0x6E,0x6F,0x74,0x65,0x2E,0x41,0x42,0x49,0x2D,0x74,0x61,0x67,0x0,0x2E,0x72,0x65,0x6C,0x2E,0x74,0x65,0x78,0x74,0x0,0x2E,0x72,0x6F,0x64,0x61,0x74,0x61,0x0,0x2E,0x72,0x6F,0x64,0x61,0x74,0x61,0x2E,0x63,0x73,0x74,0x34,0x0,0x2E,0x64,0x61,0x74,0x61,0x0,0x2E,0x62,0x73,0x73,0x0,0x2E,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x74,0x0,0x2E,0x6E,0x6F,0x74,0x65,0x2E,0x47,0x4E,0x55,0x2D,0x73,0x74,0x61,0x63,0x6B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1B,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2D,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x54,0x0,0x0,0x0,0x22,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x2,0x0,0x0,0x20,0x0,0x0,0x0,0xB,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x33,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x76,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3B,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7C,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x48,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4E,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x53,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x84,0x0,0x0,0x0,0x1B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5C,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9F,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x2,0x0,0x0,0x6C,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xA0,0x0,0x0,0x0,0x30,0x1,0x0,0x0,0xC,0x0,0x0,0x0,0xA,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xD0,0x1,0x0,0x0,0x5D,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,] -------------------------------------------------------------------------------- /src/pe/debug.rs: -------------------------------------------------------------------------------- 1 | use scroll::{Pread, Pwrite, SizeWith}; 2 | use crate::error; 3 | 4 | use crate::pe::section_table; 5 | use crate::pe::utils; 6 | use crate::pe::data_directories; 7 | 8 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 9 | pub struct DebugData<'a> { 10 | pub image_debug_directory: ImageDebugDirectory, 11 | pub codeview_pdb70_debug_info: Option>, 12 | } 13 | 14 | impl<'a> DebugData<'a> { 15 | pub fn parse(bytes: &'a [u8], dd: data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result { 16 | let image_debug_directory = ImageDebugDirectory::parse(bytes, dd, sections, file_alignment)?; 17 | let codeview_pdb70_debug_info = CodeviewPDB70DebugInfo::parse(bytes, &image_debug_directory)?; 18 | 19 | Ok(DebugData{ 20 | image_debug_directory, 21 | codeview_pdb70_debug_info 22 | }) 23 | } 24 | 25 | /// Return this executable's debugging GUID, suitable for matching against a PDB file. 26 | pub fn guid(&self) -> Option<[u8; 16]> { 27 | self.codeview_pdb70_debug_info 28 | .map(|pdb70| pdb70.signature) 29 | } 30 | } 31 | 32 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680307(v=vs.85).aspx 33 | #[repr(C)] 34 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 35 | #[derive(Pread, Pwrite, SizeWith)] 36 | pub struct ImageDebugDirectory { 37 | pub characteristics: u32, 38 | pub time_date_stamp: u32, 39 | pub major_version: u16, 40 | pub minor_version: u16, 41 | pub data_type: u32, 42 | pub size_of_data: u32, 43 | pub address_of_raw_data: u32, 44 | pub pointer_to_raw_data: u32, 45 | } 46 | 47 | pub const IMAGE_DEBUG_TYPE_UNKNOWN: u32 = 0; 48 | pub const IMAGE_DEBUG_TYPE_COFF: u32 = 1; 49 | pub const IMAGE_DEBUG_TYPE_CODEVIEW: u32 = 2; 50 | pub const IMAGE_DEBUG_TYPE_FPO: u32 = 3; 51 | pub const IMAGE_DEBUG_TYPE_MISC: u32 = 4; 52 | pub const IMAGE_DEBUG_TYPE_EXCEPTION: u32 = 5; 53 | pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6; 54 | pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9; 55 | 56 | impl ImageDebugDirectory { 57 | fn parse(bytes: &[u8], dd: data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result { 58 | let rva = dd.virtual_address as usize; 59 | let offset = utils::find_offset(rva, sections, file_alignment).ok_or_else(|| error::Error::Malformed(format!("Cannot map ImageDebugDirectory rva {:#x} into offset", rva)))?; 60 | let idd: Self = bytes.pread_with(offset, scroll::LE)?; 61 | Ok (idd) 62 | } 63 | } 64 | 65 | pub const CODEVIEW_PDB70_MAGIC: u32 = 0x5344_5352; 66 | pub const CODEVIEW_PDB20_MAGIC: u32 = 0x3031_424e; 67 | pub const CODEVIEW_CV50_MAGIC: u32 = 0x3131_424e; 68 | pub const CODEVIEW_CV41_MAGIC: u32 = 0x3930_424e; 69 | 70 | // http://llvm.org/doxygen/CVDebugRecord_8h_source.html 71 | #[repr(C)] 72 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 73 | pub struct CodeviewPDB70DebugInfo<'a> { 74 | pub codeview_signature: u32, 75 | pub signature: [u8; 16], 76 | pub age: u32, 77 | pub filename: &'a [u8], 78 | } 79 | 80 | impl<'a> CodeviewPDB70DebugInfo<'a> { 81 | pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result> { 82 | if idd.data_type != IMAGE_DEBUG_TYPE_CODEVIEW { 83 | // not a codeview debug directory 84 | // that's not an error, but it's not a CodeviewPDB70DebugInfo either 85 | return Ok(None); 86 | } 87 | 88 | // ImageDebugDirectory.pointer_to_raw_data stores a raw offset -- not a virtual offset -- which we can use directly 89 | let mut offset: usize = idd.pointer_to_raw_data as usize; 90 | 91 | // calculate how long the eventual filename will be, which doubles as a check of the record size 92 | let filename_length = idd.size_of_data as isize - 24; 93 | if filename_length < 0 || filename_length > 1024 { 94 | // the record is too short or too long to be plausible 95 | return Err(error::Error::Malformed(format!("ImageDebugDirectory size of data seems wrong: {:?}", idd.size_of_data))); 96 | } 97 | let filename_length = filename_length as usize; 98 | 99 | // check the codeview signature 100 | let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?; 101 | if codeview_signature != CODEVIEW_PDB70_MAGIC { 102 | return Ok(None); 103 | } 104 | 105 | // read the rest 106 | let mut signature: [u8; 16] = [0; 16]; 107 | signature.copy_from_slice(bytes.gread_with(&mut offset, 16)?); 108 | let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?; 109 | let filename = &bytes[offset..offset + filename_length]; 110 | 111 | Ok(Some(CodeviewPDB70DebugInfo{ 112 | codeview_signature, 113 | signature, 114 | age, 115 | filename, 116 | })) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /examples/dyldinfo.rs: -------------------------------------------------------------------------------- 1 | use goblin::mach; 2 | use std::env; 3 | use std::process; 4 | use std::path::Path; 5 | use std::fs::File; 6 | use std::io::Read; 7 | use std::borrow::Cow; 8 | 9 | fn usage() -> ! { 10 | println!("usage: dyldinfo "); 11 | println!(" -bind print binds as seen by macho::imports()"); 12 | println!(" -lazy_bind print lazy binds as seen by macho::imports()"); 13 | process::exit(1); 14 | } 15 | 16 | fn name_to_str(name: &[u8; 16]) -> Cow<'_, str> { 17 | for i in 0..16 { 18 | if name[i] == 0 { 19 | return String::from_utf8_lossy(&name[0..i]) 20 | } 21 | } 22 | String::from_utf8_lossy(&name[..]) 23 | } 24 | 25 | fn dylib_name(name: &str) -> &str { 26 | // observed behavior: 27 | // "/usr/lib/libc++.1.dylib" => "libc++" 28 | // "/usr/lib/libSystem.B.dylib" => "libSystem" 29 | // "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation" => "CoreFoundation" 30 | name 31 | .rsplit('/').next().unwrap() 32 | .split('.').next().unwrap() 33 | } 34 | 35 | fn print_binds(sections: &[mach::segment::Section], imports: &[mach::imports::Import]) { 36 | println!("bind information:"); 37 | 38 | println!( 39 | "{:7} {:16} {:14} {:7} {:6} {:16} symbol", 40 | "segment", 41 | "section", 42 | "address", 43 | "type", 44 | "addend", 45 | "dylib", 46 | ); 47 | 48 | for import in imports.iter().filter(|i| !i.is_lazy) { 49 | // find the section that imported this symbol 50 | let section = sections.iter() 51 | .find(|s| import.address >= s.addr && import.address < (s.addr + s.size)); 52 | 53 | // get &strs for its name 54 | let (segname, sectname) = section 55 | .map(|sect| (name_to_str(§.segname), name_to_str(§.sectname))) 56 | .unwrap_or((Cow::Borrowed("?"), Cow::Borrowed("?"))); 57 | 58 | println!( 59 | "{:7} {:16} 0x{:<12X} {:7} {:6} {:16} {}{}", 60 | segname, 61 | sectname, 62 | import.address, 63 | "pointer", 64 | import.addend, 65 | dylib_name(import.dylib), 66 | import.name, 67 | if import.is_weak { " (weak import)" } else { "" } 68 | ); 69 | } 70 | } 71 | 72 | fn print_lazy_binds(sections: &[mach::segment::Section], imports: &[mach::imports::Import]) { 73 | println!("lazy binding information (from lazy_bind part of dyld info):"); 74 | 75 | println!( 76 | "{:7} {:16} {:10} {:6} {:16} symbol", 77 | "segment", 78 | "section", 79 | "address", 80 | "index", 81 | "dylib", 82 | ); 83 | 84 | for import in imports.iter().filter(|i| i.is_lazy) { 85 | // find the section that imported this symbol 86 | let section = sections.iter() 87 | .find(|s| import.address >= s.addr && import.address < (s.addr + s.size)); 88 | 89 | // get &strs for its name 90 | let (segname, sectname) = section 91 | .map(|sect| (name_to_str(§.segname), name_to_str(§.sectname))) 92 | .unwrap_or((Cow::Borrowed("?"), Cow::Borrowed("?"))); 93 | 94 | println!( 95 | "{:7} {:16} 0x{:<8X} {:<06} {:16} {}", 96 | segname, 97 | sectname, 98 | import.address, 99 | format!("0x{:04X}", import.start_of_sequence_offset), 100 | dylib_name(import.dylib), 101 | import.name 102 | ); 103 | } 104 | } 105 | 106 | fn main () { 107 | let len = env::args().len(); 108 | 109 | let mut bind = false; 110 | let mut lazy_bind = false; 111 | 112 | if len <= 2 { 113 | usage(); 114 | } else { 115 | // parse flags 116 | { 117 | let mut flags = env::args().collect::>(); 118 | flags.pop(); 119 | flags.remove(0); 120 | for option in flags { 121 | match option.as_str() { 122 | "-bind" => { bind = true } 123 | "-lazy_bind" => { lazy_bind = true } 124 | other => { 125 | println!("unknown flag: {}", other); 126 | println!(); 127 | usage(); 128 | } 129 | } 130 | } 131 | } 132 | 133 | // open the file 134 | let path = env::args_os().last().unwrap(); 135 | let path = Path::new(&path); 136 | let buffer = { let mut v = Vec::new(); let mut f = File::open(&path).unwrap(); f.read_to_end(&mut v).unwrap(); v}; 137 | match mach::MachO::parse(&buffer, 0) { 138 | Ok(macho) => { 139 | // collect sections and sort by address 140 | let mut sections: Vec = Vec::new(); 141 | for sects in macho.segments.sections() { 142 | sections.extend(sects.map(|r| r.expect("section").0)); 143 | } 144 | sections.sort_by_key(|s| s.addr); 145 | 146 | // get the imports 147 | let imports = macho.imports().expect("imports"); 148 | 149 | if bind { 150 | print_binds(§ions, &imports); 151 | } 152 | if lazy_bind { 153 | print_lazy_binds(§ions, &imports); 154 | } 155 | }, 156 | Err(err) => { 157 | println!("err: {:?}", err); 158 | process::exit(2); 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/pe/relocation.rs: -------------------------------------------------------------------------------- 1 | use crate::error; 2 | use scroll::{IOread, IOwrite, Pread, Pwrite, SizeWith}; 3 | 4 | /// Size of a single COFF relocation. 5 | pub const COFF_RELOCATION_SIZE: usize = 10; 6 | 7 | // x86 relocations. 8 | 9 | /// The relocation is ignored. 10 | pub const IMAGE_REL_I386_ABSOLUTE: u16 = 0x0000; 11 | /// Not supported. 12 | pub const IMAGE_REL_I386_DIR16: u16 = 0x0001; 13 | /// Not supported. 14 | pub const IMAGE_REL_I386_REL16: u16 = 0x0002; 15 | /// The target's 32-bit VA. 16 | pub const IMAGE_REL_I386_DIR32: u16 = 0x0006; 17 | /// The target's 32-bit RVA. 18 | pub const IMAGE_REL_I386_DIR32NB: u16 = 0x0007; 19 | /// Not supported. 20 | pub const IMAGE_REL_I386_SEG12: u16 = 0x0009; 21 | /// The 16-bit section index of the section that contains the target. 22 | /// 23 | /// This is used to support debugging information. 24 | pub const IMAGE_REL_I386_SECTION: u16 = 0x000A; 25 | /// The 32-bit offset of the target from the beginning of its section. 26 | /// 27 | /// This is used to support debugging information and static thread local storage. 28 | pub const IMAGE_REL_I386_SECREL: u16 = 0x000B; 29 | /// The CLR token. 30 | pub const IMAGE_REL_I386_TOKEN: u16 = 0x000C; 31 | /// A 7-bit offset from the base of the section that contains the target. 32 | pub const IMAGE_REL_I386_SECREL7: u16 = 0x000D; 33 | /// The 32-bit relative displacement to the target. 34 | /// 35 | /// This supports the x86 relative branch and call instructions. 36 | pub const IMAGE_REL_I386_REL32: u16 = 0x0014; 37 | 38 | // x86-64 relocations. 39 | 40 | /// The relocation is ignored. 41 | pub const IMAGE_REL_AMD64_ABSOLUTE: u16 = 0x0000; 42 | /// The 64-bit VA of the relocation target. 43 | pub const IMAGE_REL_AMD64_ADDR64: u16 = 0x0001; 44 | /// The 32-bit VA of the relocation target. 45 | pub const IMAGE_REL_AMD64_ADDR32: u16 = 0x0002; 46 | /// The 32-bit address without an image base (RVA). 47 | pub const IMAGE_REL_AMD64_ADDR32NB: u16 = 0x0003; 48 | /// The 32-bit relative address from the byte following the relocation. 49 | pub const IMAGE_REL_AMD64_REL32: u16 = 0x0004; 50 | /// The 32-bit address relative to byte distance 1 from the relocation. 51 | pub const IMAGE_REL_AMD64_REL32_1: u16 = 0x0005; 52 | /// The 32-bit address relative to byte distance 2 from the relocation. 53 | pub const IMAGE_REL_AMD64_REL32_2: u16 = 0x0006; 54 | /// The 32-bit address relative to byte distance 3 from the relocation. 55 | pub const IMAGE_REL_AMD64_REL32_3: u16 = 0x0007; 56 | /// The 32-bit address relative to byte distance 4 from the relocation. 57 | pub const IMAGE_REL_AMD64_REL32_4: u16 = 0x0008; 58 | /// The 32-bit address relative to byte distance 5 from the relocation. 59 | pub const IMAGE_REL_AMD64_REL32_5: u16 = 0x0009; 60 | /// The 16-bit section index of the section that contains the target. 61 | /// 62 | /// This is used to support debugging information. 63 | pub const IMAGE_REL_AMD64_SECTION: u16 = 0x000A; 64 | /// The 32-bit offset of the target from the beginning of its section. 65 | /// 66 | /// This is used to support debugging information and static thread local storage. 67 | pub const IMAGE_REL_AMD64_SECREL: u16 = 0x000B; 68 | /// A 7-bit unsigned offset from the base of the section that contains the target. 69 | pub const IMAGE_REL_AMD64_SECREL7: u16 = 0x000C; 70 | /// CLR tokens. 71 | pub const IMAGE_REL_AMD64_TOKEN: u16 = 0x000D; 72 | /// A 32-bit signed span-dependent value emitted into the object. 73 | pub const IMAGE_REL_AMD64_SREL32: u16 = 0x000E; 74 | /// A pair that must immediately follow every span-dependent value. 75 | pub const IMAGE_REL_AMD64_PAIR: u16 = 0x000F; 76 | /// A 32-bit signed span-dependent value that is applied at link time. 77 | pub const IMAGE_REL_AMD64_SSPAN32: u16 = 0x0010; 78 | 79 | /// A COFF relocation. 80 | #[repr(C)] 81 | #[derive(Debug, Copy, Clone, PartialEq, Default, Pread, Pwrite, IOread, IOwrite, SizeWith)] 82 | pub struct Relocation { 83 | /// The address of the item to which relocation is applied. 84 | /// 85 | /// This is the offset from the beginning of the section, plus the 86 | /// value of the section's `virtual_address` field. 87 | pub virtual_address: u32, 88 | /// A zero-based index into the symbol table. 89 | /// 90 | /// This symbol gives the address that is to be used for the relocation. If the specified 91 | /// symbol has section storage class, then the symbol's address is the address with the 92 | /// first section of the same name. 93 | pub symbol_table_index: u32, 94 | /// A value that indicates the kind of relocation that should be performed. 95 | /// 96 | /// Valid relocation types depend on machine type. 97 | pub typ: u16, 98 | } 99 | 100 | /// An iterator for COFF relocations. 101 | #[derive(Default)] 102 | pub struct Relocations<'a> { 103 | offset: usize, 104 | relocations: &'a [u8], 105 | } 106 | 107 | impl<'a> Relocations<'a> { 108 | /// Parse a COFF relocation table at the given offset. 109 | /// 110 | /// The offset and number of relocations should be from the COFF section header. 111 | pub fn parse(bytes: &'a [u8], offset: usize, number: usize) -> error::Result> { 112 | let relocations = bytes.pread_with(offset, number * COFF_RELOCATION_SIZE)?; 113 | Ok(Relocations { 114 | offset: 0, 115 | relocations, 116 | }) 117 | } 118 | } 119 | 120 | impl<'a> Iterator for Relocations<'a> { 121 | type Item = Relocation; 122 | fn next(&mut self) -> Option { 123 | if self.offset >= self.relocations.len() { 124 | None 125 | } else { 126 | Some( 127 | self.relocations 128 | .gread_with(&mut self.offset, scroll::LE) 129 | .unwrap(), 130 | ) 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/elf/gnu_hash.rs: -------------------------------------------------------------------------------- 1 | //! A Gnu Hash table as 4 sections: 2 | //! 3 | //! 1. Header 4 | //! 2. Bloom Filter 5 | //! 3. Hash Buckets 6 | //! 4. Hash Values 7 | //! 8 | //! The header has is an array of four (4) u32s: 9 | //! 10 | //! 1. nbuckets 11 | //! 2. symndx 12 | //! 3. maskwords 13 | //! 4. shift2 14 | //! 15 | //! See: https://blogs.oracle.com/solaris/gnu-hash-elf-sections-v2 16 | 17 | /// GNU hash function: takes a string and returns the u32 hash of that string 18 | pub fn hash(symbol: &str) -> u32 { 19 | const HASH_SEED: u32 = 5381; 20 | let mut hash = HASH_SEED; 21 | for b in symbol.as_bytes() { 22 | hash = hash 23 | .wrapping_mul(33) 24 | .wrapping_add(u32::from(*b)); 25 | } 26 | hash 27 | } 28 | 29 | #[cfg(test)] 30 | mod tests { 31 | use super::hash; 32 | #[test] 33 | fn test_hash() { 34 | assert_eq!(hash("") , 0x0000_1505); 35 | assert_eq!(hash("printf") , 0x156b_2bb8); 36 | assert_eq!(hash("exit") , 0x7c96_7e3f); 37 | assert_eq!(hash("syscall") , 0xbac2_12a0); 38 | assert_eq!(hash("flapenguin.me"), 0x8ae9_f18e); 39 | } 40 | } 41 | 42 | macro_rules! elf_gnu_hash_impl { 43 | ($size:ty) => { 44 | 45 | use core::slice; 46 | use core::mem; 47 | use crate::strtab::Strtab; 48 | use super::sym; 49 | 50 | pub struct GnuHash<'process> { 51 | nbuckets: u32, 52 | symindex: usize, 53 | shift2: u32, 54 | maskbits: u32, 55 | bloomwords: &'process [$size], // either 32 or 64 bit masks, depending on platform 56 | maskwords_bitmask: u32, 57 | buckets: &'process [u32], 58 | hashvalues: &'process [u32], 59 | symtab: &'process [sym::Sym], 60 | } 61 | 62 | impl<'process> GnuHash<'process> { 63 | pub unsafe fn new(hashtab: *const u32, total_dynsyms: usize, symtab: &'process [sym::Sym]) -> GnuHash<'process> { 64 | let nbuckets = *hashtab; 65 | let symindex = *hashtab.add(1) as usize; 66 | let maskwords = *hashtab.add(2) as usize; // how many words our bloom filter mask has 67 | let shift2 = *hashtab.add(3); 68 | let bloomwords_ptr = hashtab.add(4) as *const $size; 69 | let buckets_ptr = bloomwords_ptr.add(maskwords) as *const u32; 70 | let buckets = slice::from_raw_parts(buckets_ptr, nbuckets as usize); 71 | let hashvalues_ptr = buckets_ptr.add(nbuckets as usize); 72 | let hashvalues = slice::from_raw_parts(hashvalues_ptr, total_dynsyms - symindex); 73 | let bloomwords = slice::from_raw_parts(bloomwords_ptr, maskwords); 74 | GnuHash { 75 | nbuckets, 76 | symindex, 77 | shift2, 78 | maskbits: mem::size_of::() as u32, 79 | bloomwords, 80 | hashvalues, 81 | buckets, 82 | maskwords_bitmask: ((maskwords as i32) - 1) as u32, 83 | symtab, 84 | } 85 | } 86 | 87 | #[inline(always)] 88 | fn lookup(&self, 89 | symbol: &str, 90 | hash: u32, 91 | strtab: &Strtab) 92 | -> Option<&sym::Sym> { 93 | let mut idx = self.buckets[(hash % self.nbuckets) as usize] as usize; 94 | // println!("lookup idx = buckets[hash % nbuckets] = {}", idx); 95 | if idx == 0 { 96 | return None; 97 | } 98 | let mut hash_idx = idx - self.symindex; 99 | let hash = hash & !1; 100 | // TODO: replace this with an iterator 101 | loop { 102 | let symbol_ = &self.symtab[idx]; 103 | let h2 = self.hashvalues[hash_idx]; 104 | idx += 1; 105 | hash_idx += 1; 106 | let name = &strtab[symbol_.st_name as usize]; 107 | // println!("{}: h2 0x{:x} resolves to: {}", i, h2, name); 108 | if hash == (h2 & !1) && name == symbol { 109 | // println!("lookup match for {} at: 0x{:x}", symbol, symbol_.st_value); 110 | return Some(symbol_); 111 | } 112 | if h2 & 1 == 1 { 113 | break; 114 | } // end of chain 115 | } 116 | None 117 | } 118 | 119 | #[inline(always)] 120 | fn filter(&self, hash: u32) -> bool { 121 | let bloom_idx = (hash / self.maskbits) & self.maskwords_bitmask; 122 | let h2 = hash >> self.shift2; 123 | let bitmask = (1u64 << (hash % self.maskbits)) | (1u64 << (h2 % self.maskbits)); 124 | // println!("lookup: maskwords: {} bloom_idx: {} bitmask: {} shift2: {}", self.maskwords, bloom_idx, bitmask, self.shift2); 125 | let filter = self.bloomwords[bloom_idx as usize] as usize; // FIXME: verify this is safe ;) 126 | filter & (bitmask as usize) != (bitmask as usize) // if true, def _don't have_ 127 | } 128 | 129 | /// Given a name, a hash of that name, a strtab to cross-reference names, maybe returns a Sym 130 | pub fn find(&self, 131 | name: &str, 132 | hash: u32, 133 | strtab: &Strtab) 134 | -> Option<&sym::Sym> { 135 | if self.filter(hash) { 136 | None 137 | } else { 138 | self.lookup(name, hash, strtab) 139 | } 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/strtab.rs: -------------------------------------------------------------------------------- 1 | //! A byte-offset based string table. 2 | //! Commonly used in ELF binaries, Unix archives, and even PE binaries. 3 | 4 | use core::ops::Index; 5 | use core::slice; 6 | use core::str; 7 | use core::fmt; 8 | use scroll::{ctx, Pread}; 9 | if_alloc! { 10 | use crate::error; 11 | use crate::alloc::vec::Vec; 12 | } 13 | 14 | /// A common string table format which is indexed by byte offsets (and not 15 | /// member index). Constructed using [`parse`](#method.parse) 16 | /// with your choice of delimiter. Please be careful. 17 | pub struct Strtab<'a> { 18 | bytes: &'a[u8], 19 | delim: ctx::StrCtx, 20 | } 21 | 22 | #[inline(always)] 23 | fn get_str(offset: usize, bytes: &[u8], delim: ctx::StrCtx) -> scroll::Result<&str> { 24 | bytes.pread_with::<&str>(offset, delim) 25 | } 26 | 27 | impl<'a> Strtab<'a> { 28 | /// Construct a new strtab with `bytes` as the backing string table, using `delim` as the delimiter between entries 29 | pub fn new (bytes: &'a [u8], delim: u8) -> Self { 30 | Strtab { delim: ctx::StrCtx::Delimiter(delim), bytes } 31 | } 32 | /// Construct a strtab from a `ptr`, and a `size`, using `delim` as the delimiter 33 | pub unsafe fn from_raw(ptr: *const u8, size: usize, delim: u8) -> Strtab<'a> { 34 | Strtab { delim: ctx::StrCtx::Delimiter(delim), bytes: slice::from_raw_parts(ptr, size) } 35 | } 36 | #[cfg(feature = "alloc")] 37 | /// Parses a strtab from `bytes` at `offset` with `len` size as the backing string table, using `delim` as the delimiter 38 | pub fn parse(bytes: &'a [u8], offset: usize, len: usize, delim: u8) -> error::Result> { 39 | let (end, overflow) = offset.overflowing_add(len); 40 | if overflow || end > bytes.len () { 41 | return Err(error::Error::Malformed(format!("Strtable size ({}) + offset ({}) is out of bounds for {} #bytes. Overflowed: {}", len, offset, bytes.len(), overflow))); 42 | } 43 | Ok(Strtab { bytes: &bytes[offset..end], delim: ctx::StrCtx::Delimiter(delim) }) 44 | } 45 | #[cfg(feature = "alloc")] 46 | /// Converts the string table to a vector, with the original `delim` used to separate the strings 47 | pub fn to_vec(&self) -> error::Result> { 48 | let len = self.bytes.len(); 49 | let mut strings = Vec::with_capacity(len); 50 | let mut i = 0; 51 | while i < len { 52 | let string = self.get(i).unwrap()?; 53 | i = i + string.len() + 1; 54 | strings.push(string); 55 | } 56 | Ok(strings) 57 | } 58 | /// Safely parses and gets a str reference from the backing bytes starting at byte `offset`. 59 | /// If the index is out of bounds, `None` is returned. 60 | /// Requires `feature = "alloc"` 61 | #[cfg(feature = "alloc")] 62 | pub fn get(&self, offset: usize) -> Option> { 63 | if offset >= self.bytes.len() { 64 | None 65 | } else { 66 | Some(get_str(offset, self.bytes, self.delim).map_err(core::convert::Into::into)) 67 | } 68 | } 69 | /// Gets a str reference from the backing bytes starting at byte `offset`. 70 | /// If the index is out of bounds, `None` is returned. Panics if bytes are invalid UTF-8. 71 | pub fn get_unsafe(&self, offset: usize) -> Option<&'a str> { 72 | if offset >= self.bytes.len() { 73 | None 74 | } else { 75 | Some(get_str(offset, self.bytes, self.delim).unwrap()) 76 | } 77 | } 78 | } 79 | 80 | impl<'a> fmt::Debug for Strtab<'a> { 81 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 82 | f.debug_struct("Strtab") 83 | .field("delim", &self.delim) 84 | .field("bytes", &str::from_utf8(self.bytes)) 85 | .finish() 86 | } 87 | } 88 | 89 | impl<'a> Default for Strtab<'a> { 90 | fn default() -> Strtab<'a> { 91 | Strtab { bytes: &[], delim: ctx::StrCtx::default() } 92 | } 93 | } 94 | 95 | impl<'a> Index for Strtab<'a> { 96 | type Output = str; 97 | /// Gets str reference at starting at byte `offset`. 98 | /// **NB**: this will panic if the underlying bytes are not valid utf8, or the offset is invalid 99 | #[inline(always)] 100 | fn index(&self, offset: usize) -> &Self::Output { 101 | // This can't delegate to get() because get() requires #[cfg(features = "alloc")] 102 | // It's also slightly less useful than get() because the lifetime -- specified by the Index 103 | // trait -- matches &self, even though we could return &'a instead 104 | get_str(offset, self.bytes, self.delim).unwrap() 105 | } 106 | } 107 | 108 | #[test] 109 | fn as_vec_no_final_null() { 110 | let bytes = b"\0printf\0memmove\0busta"; 111 | let strtab = unsafe { Strtab::from_raw(bytes.as_ptr(), bytes.len(), 0x0) }; 112 | let vec = strtab.to_vec().unwrap(); 113 | assert_eq!(vec.len(), 4); 114 | assert_eq!(vec, vec!["", "printf", "memmove", "busta"]); 115 | } 116 | 117 | #[test] 118 | fn as_vec_no_first_null_no_final_null() { 119 | let bytes = b"printf\0memmove\0busta"; 120 | let strtab = unsafe { Strtab::from_raw(bytes.as_ptr(), bytes.len(), 0x0) }; 121 | let vec = strtab.to_vec().unwrap(); 122 | assert_eq!(vec.len(), 3); 123 | assert_eq!(vec, vec!["printf", "memmove", "busta"]); 124 | } 125 | 126 | #[test] 127 | fn to_vec_final_null() { 128 | let bytes = b"\0printf\0memmove\0busta\0"; 129 | let strtab = unsafe { Strtab::from_raw(bytes.as_ptr(), bytes.len(), 0x0) }; 130 | let vec = strtab.to_vec().unwrap(); 131 | assert_eq!(vec.len(), 4); 132 | assert_eq!(vec, vec!["", "printf", "memmove", "busta"]); 133 | } 134 | 135 | #[test] 136 | fn to_vec_newline_delim() { 137 | let bytes = b"\nprintf\nmemmove\nbusta\n"; 138 | let strtab = unsafe { Strtab::from_raw(bytes.as_ptr(), bytes.len(), b'\n') }; 139 | let vec = strtab.to_vec().unwrap(); 140 | assert_eq!(vec.len(), 4); 141 | assert_eq!(vec, vec!["", "printf", "memmove", "busta"]); 142 | } 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libgoblin [![Build status][travis-badge]][travis-url] [![crates.io version][crates-goblin-badge]][crates-goblin] 2 | 3 | 4 | 5 | [travis-badge]: https://travis-ci.org/m4b/goblin.svg?branch=master 6 | [travis-url]: https://travis-ci.org/m4b/goblin 7 | [crates-goblin-badge]: https://img.shields.io/crates/v/goblin.svg 8 | [crates-goblin]: https://crates.io/crates/goblin 9 | 10 | ![say the right words](https://s-media-cache-ak0.pinimg.com/736x/1b/6a/aa/1b6aaa2bae005e2fed84b1a7c32ecb1b.jpg) 11 | 12 | ### Documentation 13 | 14 | https://docs.rs/goblin/ 15 | 16 | [changelog](CHANGELOG.md) 17 | 18 | ### Usage 19 | 20 | Goblin requires `rustc` 1.31.1. 21 | 22 | Add to your `Cargo.toml` 23 | 24 | ```toml 25 | [dependencies] 26 | goblin = "0.1" 27 | ``` 28 | 29 | ### Features 30 | 31 | * awesome crate name 32 | * zero-copy, cross-platform, endian-aware, ELF64/32 implementation - wow! 33 | * zero-copy, cross-platform, endian-aware, 32/64 bit Mach-o parser - zoiks! 34 | * PE 32/64-bit parser - bing! 35 | * a Unix _and_ BSD style archive parser (latter courtesy of [@willglynn]) - huzzah! 36 | * many cfg options - it will make your head spin, and make you angry when reading the source! 37 | * fuzzed - "I am happy to report that goblin withstood 100 million fuzzing runs, 1 million runs 38 | each for seed 1\~100." - [@sanxiyn] 39 | * tests 40 | 41 | `libgoblin` aims to be your one-stop shop for binary parsing, loading, and analysis. 42 | 43 | ### Use-cases 44 | 45 | Goblin primarily supports the following important use cases: 46 | 47 | 1. Core, std-free `#[repr(C)]` structs, tiny compile time, 32/64 (or both) at your leisure. 48 | 49 | 1. Type punning. Define a function once on a type, but have it work on 32 or 64-bit variants - 50 | without really changing anything, and no macros! See `examples/automagic.rs` for a basic example. 51 | 52 | 1. `std` mode. This throws in read and write impls via `Pread` and `Pwrite`, reading from file, 53 | convenience allocations, extra methods, etc. This is for clients who can allocate and want to 54 | read binaries off disk. 55 | 56 | 1. `Endian_fd`. A truly terrible name :laughing: this is for binary analysis like in [panopticon] 57 | or [falcon] which needs to read binaries of foreign endianness, _or_ as a basis for 58 | constructing cross platform foreign architecture binutils, e.g. [cargo-sym] and [bingrep] are 59 | simple examples of this, but the sky is the limit. 60 | 61 | Here are some things you could do with this crate (or help to implement so they could be done): 62 | 63 | 1. Write a compiler and use it to [generate binaries][faerie] (all the raw C structs have 64 | [`Pwrite`][scroll] derived). 65 | 1. Write a binary analysis tool which loads, parses, and analyzes various binary formats, e.g., 66 | [panopticon] or [falcon]. 67 | 1. Write a [semi-functioning dynamic linker][dryad]. 68 | 1. Write a [kernel][redox-os] and load binaries using `no_std` cfg. I.e., it is essentially just 69 | struct and const defs (like a C header) - no fd, no output, no std. 70 | 1. Write a [bin2json] tool, because why shouldn't binary formats be in JSON? 71 | 72 | 73 | 74 | [cargo-sym]: https://github.com/m4b/cargo-sym 75 | [bingrep]: https://github.com/m4b/bingrep 76 | [faerie]: https://github.com/m4b/faerie 77 | [dryad]: https://github.com/m4b/dryad 78 | [scroll]: https://github.com/m4b/scroll 79 | [redox-os]: https://github.com/redox-os/redox 80 | [bin2json]: https://github.com/m4b/bin2json 81 | [panopticon]: https://github.com/das-labor/panopticon 82 | [falcon]: https://github.com/endeav0r/falcon 83 | 84 | ### Cfgs 85 | 86 | `libgoblin` is designed to be massively configurable. The current flags are: 87 | 88 | * elf64 - 64-bit elf binaries, `repr(C)` struct defs 89 | * elf32 - 32-bit elf binaries, `repr(C)` struct defs 90 | * mach64 - 64-bit mach-o `repr(C)` struct defs 91 | * mach32 - 32-bit mach-o `repr(C)` struct defs 92 | * pe32 - 32-bit PE `repr(C)` struct defs 93 | * pe64 - 64-bit PE `repr(C)` struct defs 94 | * archive - a Unix Archive parser 95 | * endian_fd - parses according to the endianness in the binary 96 | * std - to allow `no_std` environments 97 | 98 | # Contributors 99 | 100 | Thank you all :heart: ! 101 | 102 | In lexicographic order: 103 | 104 | - [@amanieu] 105 | - [@burjui] 106 | - [@flanfly] 107 | - [@ibabushkin] 108 | - [@jan-auer] 109 | - [@jdub] 110 | - [@jrmuizel] 111 | - [@jsgf] 112 | - [@kjempelodott] 113 | - [@le-jzr] 114 | - [@lion128] 115 | - [@llogiq] 116 | - [@lzutao] 117 | - [@lzybkr] 118 | - [@m4b] 119 | - [@mitsuhiko] 120 | - [@mre] 121 | - [@pchickey] 122 | - [@philipc] 123 | - [@Pzixel] 124 | - [@raindev] 125 | - [@rocallahan] 126 | - [@sanxiyn] 127 | - [@tathanhdinh] 128 | - [@Techno-coder] 129 | - [@ticki] 130 | - [@wickerwacka] 131 | - [@willglynn] 132 | - [@wyxloading] 133 | - [@xcoldhandsx] 134 | 135 | 136 | 137 | [@amanieu]: https://github.com/amanieu 138 | [@burjui]: https://github.com/burjui 139 | [@flanfly]: https://github.com/flanfly 140 | [@ibabushkin]: https://github.com/ibabushkin 141 | [@jan-auer]: https://github.com/jan-auer 142 | [@jdub]: https://github.com/jdub 143 | [@jrmuizel]: https://github.com/jrmuizel 144 | [@jsgf]: https://github.com/jsgf 145 | [@kjempelodott]: https://github.com/kjempelodott 146 | [@le-jzr]: https://github.com/le-jzr 147 | [@lion128]: https://github.com/lion128 148 | [@llogiq]: https://github.com/llogiq 149 | [@lzutao]: https://github.com/lzutao 150 | [@lzybkr]: https://github.com/lzybkr 151 | [@m4b]: https://github.com/m4b 152 | [@mitsuhiko]: https://github.com/mitsuhiko 153 | [@mre]: https://github.com/mre 154 | [@pchickey]: https://github.com/pchickey 155 | [@philipc]: https://github.com/philipc 156 | [@Pzixel]: https://github.com/Pzixel 157 | [@raindev]: https://github.com/raindev 158 | [@rocallahan]: https://github.com/rocallahan 159 | [@sanxiyn]: https://github.com/sanxiyn 160 | [@tathanhdinh]: https://github.com/tathanhdinh 161 | [@Techno-coder]: https://github.com/Techno-coder 162 | [@ticki]: https://github.com/ticki 163 | [@wickerwacka]: https://github.com/wickerwaka 164 | [@willglynn]: https://github.com/willglynn 165 | [@wyxloading]: https://github.com/wyxloading 166 | [@xcoldhandsx]: https://github.com/xcoldhandsx 167 | 168 | ## Contributing 169 | 170 | 1. Please prefix commits with the affected binary component; the more specific the better, e.g., 171 | if you only modify relocations in the elf module, then do "elf.reloc: added new constants for Z80" 172 | 1. Commit messages must explain their change, no generic "changed", or "fix"; if you push commits 173 | like this on a PR, be aware [@m4b] or someone will most likely squash them. 174 | 1. If you are making a large change to a module, please raise an issue first and lets discuss; 175 | I don't want to waste your time if its not a good technical direction, or etc. 176 | 1. If your PR is not getting attention, please respond to all relevant comments raised on the PR, 177 | and if still no response, ping [@m4b], [@philipc], or [@willglynn] in github and also feel free 178 | to email [@m4b]. 179 | 1. Please add tests if you are adding a new feature. Feel free to add tests even if you are not, 180 | tests are awesome and easy in rust. 181 | 1. Once cargo format is officially released, please format your _patch_ using the default settings. 182 | -------------------------------------------------------------------------------- /src/pe/mod.rs: -------------------------------------------------------------------------------- 1 | //! A PE32 and PE32+ parser 2 | //! 3 | 4 | // TODO: panics with unwrap on None for apisetschema.dll, fhuxgraphics.dll and some others 5 | 6 | use crate::alloc::vec::Vec; 7 | 8 | pub mod header; 9 | pub mod optional_header; 10 | pub mod characteristic; 11 | pub mod section_table; 12 | pub mod data_directories; 13 | pub mod export; 14 | pub mod import; 15 | pub mod debug; 16 | pub mod exception; 17 | pub mod symbol; 18 | pub mod relocation; 19 | pub mod utils; 20 | 21 | use crate::error; 22 | use crate::container; 23 | use crate::strtab; 24 | 25 | use log::debug; 26 | 27 | #[derive(Debug)] 28 | /// An analyzed PE32/PE32+ binary 29 | pub struct PE<'a> { 30 | /// The PE header 31 | pub header: header::Header, 32 | /// A list of the sections in this PE binary 33 | pub sections: Vec, 34 | /// The size of the binary 35 | pub size: usize, 36 | /// The name of this `dll`, if it has one 37 | pub name: Option<&'a str>, 38 | /// Whether this is a `dll` or not 39 | pub is_lib: bool, 40 | /// Whether the binary is 64-bit (PE32+) 41 | pub is_64: bool, 42 | /// the entry point of the binary 43 | pub entry: usize, 44 | /// The binary's RVA, or image base - useful for computing virtual addreses 45 | pub image_base: usize, 46 | /// Data about any exported symbols in this binary (e.g., if it's a `dll`) 47 | pub export_data: Option>, 48 | /// Data for any imported symbols, and from which `dll`, etc., in this binary 49 | pub import_data: Option>, 50 | /// The list of exported symbols in this binary, contains synthetic information for easier analysis 51 | pub exports: Vec>, 52 | /// The list symbols imported by this binary from other `dll`s 53 | pub imports: Vec>, 54 | /// The list of libraries which this binary imports symbols from 55 | pub libraries: Vec<&'a str>, 56 | /// Debug information, if any, contained in the PE header 57 | pub debug_data: Option>, 58 | /// Exception handling and stack unwind information, if any, contained in the PE header 59 | pub exception_data: Option>, 60 | } 61 | 62 | impl<'a> PE<'a> { 63 | /// Reads a PE binary from the underlying `bytes` 64 | pub fn parse(bytes: &'a [u8]) -> error::Result { 65 | let header = header::Header::parse(bytes)?; 66 | debug!("{:#?}", header); 67 | let offset = &mut (header.dos_header.pe_pointer as usize + header::SIZEOF_PE_MAGIC + header::SIZEOF_COFF_HEADER + header.coff_header.size_of_optional_header as usize); 68 | let sections = header.coff_header.sections(bytes, offset)?; 69 | let is_lib = characteristic::is_dll(header.coff_header.characteristics); 70 | let mut entry = 0; 71 | let mut image_base = 0; 72 | let mut exports = vec![]; 73 | let mut export_data = None; 74 | let mut name = None; 75 | let mut imports = vec![]; 76 | let mut import_data = None; 77 | let mut libraries = vec![]; 78 | let mut debug_data = None; 79 | let mut exception_data = None; 80 | let mut is_64 = false; 81 | if let Some(optional_header) = header.optional_header { 82 | entry = optional_header.standard_fields.address_of_entry_point as usize; 83 | image_base = optional_header.windows_fields.image_base as usize; 84 | is_64 = optional_header.container()? == container::Container::Big; 85 | debug!("entry {:#x} image_base {:#x} is_64: {}", entry, image_base, is_64); 86 | let file_alignment = optional_header.windows_fields.file_alignment; 87 | if let Some(export_table) = *optional_header.data_directories.get_export_table() { 88 | if let Ok(ed) = export::ExportData::parse(bytes, export_table, §ions, file_alignment) { 89 | debug!("export data {:#?}", ed); 90 | exports = export::Export::parse(bytes, &ed, §ions, file_alignment)?; 91 | name = ed.name; 92 | debug!("name: {:#?}", name); 93 | export_data = Some(ed); 94 | } 95 | } 96 | debug!("exports: {:#?}", exports); 97 | if let Some(import_table) = *optional_header.data_directories.get_import_table() { 98 | let id = if is_64 { 99 | import::ImportData::parse::(bytes, import_table, §ions, file_alignment)? 100 | } else { 101 | import::ImportData::parse::(bytes, import_table, §ions, file_alignment)? 102 | }; 103 | debug!("import data {:#?}", id); 104 | if is_64 { 105 | imports = import::Import::parse::(bytes, &id, §ions)? 106 | } else { 107 | imports = import::Import::parse::(bytes, &id, §ions)? 108 | } 109 | libraries = id.import_data.iter().map( | data | { data.name }).collect::>(); 110 | libraries.sort(); 111 | libraries.dedup(); 112 | import_data = Some(id); 113 | } 114 | debug!("imports: {:#?}", imports); 115 | if let Some(debug_table) = *optional_header.data_directories.get_debug_table() { 116 | debug_data = Some(debug::DebugData::parse(bytes, debug_table, §ions, file_alignment)?); 117 | } 118 | 119 | if header.coff_header.machine == header::COFF_MACHINE_X86_64 { 120 | // currently only x86_64 is supported 121 | debug!("exception data: {:#?}", exception_data); 122 | if let Some(exception_table) = *optional_header.data_directories.get_exception_table() { 123 | exception_data = Some(exception::ExceptionData::parse(bytes, exception_table, §ions, file_alignment)?); 124 | } 125 | } 126 | } 127 | Ok( PE { 128 | header, 129 | sections, 130 | size: 0, 131 | name, 132 | is_lib, 133 | is_64, 134 | entry, 135 | image_base, 136 | export_data, 137 | import_data, 138 | exports, 139 | imports, 140 | libraries, 141 | debug_data, 142 | exception_data, 143 | }) 144 | } 145 | } 146 | 147 | /// An analyzed COFF object 148 | #[derive(Debug)] 149 | pub struct Coff<'a> { 150 | /// The COFF header 151 | pub header: header::CoffHeader, 152 | /// A list of the sections in this COFF binary 153 | pub sections: Vec, 154 | /// The COFF symbol table. 155 | pub symbols: symbol::SymbolTable<'a>, 156 | /// The string table. 157 | pub strings: strtab::Strtab<'a>, 158 | } 159 | 160 | impl<'a> Coff<'a> { 161 | /// Reads a COFF object from the underlying `bytes` 162 | pub fn parse(bytes: &'a [u8]) -> error::Result { 163 | let offset = &mut 0; 164 | let header = header::CoffHeader::parse(bytes, offset)?; 165 | debug!("{:#?}", header); 166 | // TODO: maybe parse optional header, but it isn't present for Windows. 167 | *offset += header.size_of_optional_header as usize; 168 | let sections = header.sections(bytes, offset)?; 169 | let symbols = header.symbols(bytes)?; 170 | let strings = header.strings(bytes)?; 171 | Ok(Coff { header, sections, symbols, strings }) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /etc/crt1.rs: -------------------------------------------------------------------------------- 1 | vec![0x7F,0x45,0x4C,0x46,0x2,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x3E,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0xE,0x0,0xB,0x0,0x4,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x4E,0x55,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x31,0xED,0x49,0x89,0xD1,0x5E,0x48,0x89,0xE2,0x48,0x83,0xE4,0xF0,0x50,0x54,0x49,0xC7,0xC0,0x0,0x0,0x0,0x0,0x48,0xC7,0xC1,0x0,0x0,0x0,0x0,0x48,0xC7,0xC7,0x0,0x0,0x0,0x0,0xFF,0x15,0x0,0x0,0x0,0x0,0xF4,0x0,0x1,0x0,0x2,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x7A,0x52,0x0,0x1,0x78,0x10,0x1,0x1B,0xC,0x7,0x8,0x90,0x1,0x7,0x10,0x14,0x0,0x0,0x0,0x1C,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x43,0x43,0x3A,0x20,0x28,0x47,0x4E,0x55,0x29,0x20,0x36,0x2E,0x31,0x2E,0x31,0x20,0x32,0x30,0x31,0x36,0x30,0x38,0x30,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4,0x0,0xF1,0xFF,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x12,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5A,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x0,0x0,0x0,0x20,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3E,0x0,0x0,0x0,0x11,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4D,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5F,0x0,0x0,0x0,0x10,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x6E,0x69,0x74,0x2E,0x63,0x0,0x5F,0x5F,0x6C,0x69,0x62,0x63,0x5F,0x63,0x73,0x75,0x5F,0x66,0x69,0x6E,0x69,0x0,0x5F,0x5F,0x6C,0x69,0x62,0x63,0x5F,0x63,0x73,0x75,0x5F,0x69,0x6E,0x69,0x74,0x0,0x5F,0x47,0x4C,0x4F,0x42,0x41,0x4C,0x5F,0x4F,0x46,0x46,0x53,0x45,0x54,0x5F,0x54,0x41,0x42,0x4C,0x45,0x5F,0x0,0x5F,0x49,0x4F,0x5F,0x73,0x74,0x64,0x69,0x6E,0x5F,0x75,0x73,0x65,0x64,0x0,0x5F,0x5F,0x6C,0x69,0x62,0x63,0x5F,0x73,0x74,0x61,0x72,0x74,0x5F,0x6D,0x61,0x69,0x6E,0x0,0x5F,0x5F,0x64,0x61,0x74,0x61,0x5F,0x73,0x74,0x61,0x72,0x74,0x0,0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xB,0x0,0x0,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xB,0x0,0x0,0x0,0xC,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xB,0x0,0x0,0x0,0xD,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2E,0x73,0x79,0x6D,0x74,0x61,0x62,0x0,0x2E,0x73,0x74,0x72,0x74,0x61,0x62,0x0,0x2E,0x73,0x68,0x73,0x74,0x72,0x74,0x61,0x62,0x0,0x2E,0x6E,0x6F,0x74,0x65,0x2E,0x41,0x42,0x49,0x2D,0x74,0x61,0x67,0x0,0x2E,0x72,0x65,0x6C,0x61,0x2E,0x74,0x65,0x78,0x74,0x0,0x2E,0x72,0x6F,0x64,0x61,0x74,0x61,0x2E,0x63,0x73,0x74,0x34,0x0,0x2E,0x72,0x65,0x6C,0x61,0x2E,0x65,0x68,0x5F,0x66,0x72,0x61,0x6D,0x65,0x0,0x2E,0x64,0x61,0x74,0x61,0x0,0x2E,0x62,0x73,0x73,0x0,0x2E,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x74,0x0,0x2E,0x6E,0x6F,0x74,0x65,0x2E,0x47,0x4E,0x55,0x2D,0x73,0x74,0x61,0x63,0x6B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1B,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2E,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8C,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5B,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xDF,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xE0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC8,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xD,0x0,0x0,0x0,0xA,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xA8,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x6C,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,] -------------------------------------------------------------------------------- /etc/crt1a.rs: -------------------------------------------------------------------------------- 1 | vec![0x21,0x3C,0x61,0x72,0x63,0x68,0x3E,0xA,0x2F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x36,0x36,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x60,0xA,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x86,0x0,0x0,0x0,0x86,0x5F,0x73,0x74,0x61,0x72,0x74,0x0,0x64,0x61,0x74,0x61,0x5F,0x73,0x74,0x61,0x72,0x74,0x0,0x5F,0x49,0x4F,0x5F,0x73,0x74,0x64,0x69,0x6E,0x5F,0x75,0x73,0x65,0x64,0x0,0x5F,0x5F,0x64,0x61,0x74,0x61,0x5F,0x73,0x74,0x61,0x72,0x74,0x0,0x63,0x72,0x74,0x31,0x2E,0x6F,0x2F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x36,0x34,0x34,0x20,0x20,0x20,0x20,0x20,0x31,0x39,0x32,0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x60,0xA,0x7F,0x45,0x4C,0x46,0x2,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x3E,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0xE,0x0,0xB,0x0,0x4,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x47,0x4E,0x55,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x31,0xED,0x49,0x89,0xD1,0x5E,0x48,0x89,0xE2,0x48,0x83,0xE4,0xF0,0x50,0x54,0x49,0xC7,0xC0,0x0,0x0,0x0,0x0,0x48,0xC7,0xC1,0x0,0x0,0x0,0x0,0x48,0xC7,0xC7,0x0,0x0,0x0,0x0,0xFF,0x15,0x0,0x0,0x0,0x0,0xF4,0x0,0x1,0x0,0x2,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x7A,0x52,0x0,0x1,0x78,0x10,0x1,0x1B,0xC,0x7,0x8,0x90,0x1,0x7,0x10,0x14,0x0,0x0,0x0,0x1C,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47,0x43,0x43,0x3A,0x20,0x28,0x47,0x4E,0x55,0x29,0x20,0x36,0x2E,0x31,0x2E,0x31,0x20,0x32,0x30,0x31,0x36,0x30,0x38,0x30,0x32,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x4,0x0,0xF1,0xFF,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x65,0x0,0x0,0x0,0x12,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5A,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x61,0x0,0x0,0x0,0x20,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3E,0x0,0x0,0x0,0x11,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4D,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5F,0x0,0x0,0x0,0x10,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x69,0x6E,0x69,0x74,0x2E,0x63,0x0,0x5F,0x5F,0x6C,0x69,0x62,0x63,0x5F,0x63,0x73,0x75,0x5F,0x66,0x69,0x6E,0x69,0x0,0x5F,0x5F,0x6C,0x69,0x62,0x63,0x5F,0x63,0x73,0x75,0x5F,0x69,0x6E,0x69,0x74,0x0,0x5F,0x47,0x4C,0x4F,0x42,0x41,0x4C,0x5F,0x4F,0x46,0x46,0x53,0x45,0x54,0x5F,0x54,0x41,0x42,0x4C,0x45,0x5F,0x0,0x5F,0x49,0x4F,0x5F,0x73,0x74,0x64,0x69,0x6E,0x5F,0x75,0x73,0x65,0x64,0x0,0x5F,0x5F,0x6C,0x69,0x62,0x63,0x5F,0x73,0x74,0x61,0x72,0x74,0x5F,0x6D,0x61,0x69,0x6E,0x0,0x5F,0x5F,0x64,0x61,0x74,0x61,0x5F,0x73,0x74,0x61,0x72,0x74,0x0,0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xB,0x0,0x0,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xB,0x0,0x0,0x0,0xC,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xB,0x0,0x0,0x0,0xD,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x26,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2E,0x73,0x79,0x6D,0x74,0x61,0x62,0x0,0x2E,0x73,0x74,0x72,0x74,0x61,0x62,0x0,0x2E,0x73,0x68,0x73,0x74,0x72,0x74,0x61,0x62,0x0,0x2E,0x6E,0x6F,0x74,0x65,0x2E,0x41,0x42,0x49,0x2D,0x74,0x61,0x67,0x0,0x2E,0x72,0x65,0x6C,0x61,0x2E,0x74,0x65,0x78,0x74,0x0,0x2E,0x72,0x6F,0x64,0x61,0x74,0x61,0x2E,0x63,0x73,0x74,0x34,0x0,0x2E,0x72,0x65,0x6C,0x61,0x2E,0x65,0x68,0x5F,0x66,0x72,0x61,0x6D,0x65,0x0,0x2E,0x64,0x61,0x74,0x61,0x0,0x2E,0x62,0x73,0x73,0x0,0x2E,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x74,0x0,0x2E,0x6E,0x6F,0x74,0x65,0x2E,0x47,0x4E,0x55,0x2D,0x73,0x74,0x61,0x63,0x6B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1B,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2E,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x34,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8C,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x41,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x50,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5B,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1B,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x64,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xDF,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x74,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xE0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC8,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0xD,0x0,0x0,0x0,0xA,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xA8,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x6C,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,] -------------------------------------------------------------------------------- /src/pe/header.rs: -------------------------------------------------------------------------------- 1 | use crate::alloc::vec::Vec; 2 | use crate::error; 3 | use crate::pe::{optional_header, section_table, symbol}; 4 | use crate::strtab; 5 | use log::debug; 6 | use scroll::{Pread, Pwrite, IOread, IOwrite, SizeWith}; 7 | 8 | /// DOS header present in all PE binaries 9 | #[repr(C)] 10 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 11 | pub struct DosHeader { 12 | /// Magic number: 5a4d 13 | pub signature: u16, 14 | /// Pointer to PE header, always at offset 0x3c 15 | pub pe_pointer: u32, 16 | } 17 | 18 | pub const DOS_MAGIC: u16 = 0x5a4d; 19 | pub const PE_POINTER_OFFSET: u32 = 0x3c; 20 | 21 | impl DosHeader { 22 | pub fn parse(bytes: &[u8]) -> error::Result { 23 | let signature = bytes.pread_with(0, scroll::LE) 24 | .map_err(|_| error::Error::Malformed(format!("cannot parse DOS signature (offset {:#x})", 0)))?; 25 | let pe_pointer = bytes.pread_with(PE_POINTER_OFFSET as usize, scroll::LE) 26 | .map_err(|_| error::Error::Malformed(format!("cannot parse PE header pointer (offset {:#x})", PE_POINTER_OFFSET)))?; 27 | Ok (DosHeader { signature, pe_pointer }) 28 | } 29 | } 30 | 31 | /// COFF Header 32 | #[repr(C)] 33 | #[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, IOread, IOwrite, SizeWith)] 34 | pub struct CoffHeader { 35 | /// The machine type 36 | pub machine: u16, 37 | pub number_of_sections: u16, 38 | pub time_date_stamp: u32, 39 | pub pointer_to_symbol_table: u32, 40 | pub number_of_symbol_table: u32, 41 | pub size_of_optional_header: u16, 42 | pub characteristics: u16, 43 | } 44 | 45 | pub const SIZEOF_COFF_HEADER: usize = 20; 46 | /// PE\0\0, little endian 47 | pub const PE_MAGIC: u32 = 0x0000_4550; 48 | pub const SIZEOF_PE_MAGIC: usize = 4; 49 | pub const COFF_MACHINE_X86: u16 = 0x14c; 50 | pub const COFF_MACHINE_X86_64: u16 = 0x8664; 51 | 52 | impl CoffHeader { 53 | pub fn parse(bytes: &[u8], offset: &mut usize) -> error::Result { 54 | Ok(bytes.gread_with(offset, scroll::LE)?) 55 | } 56 | 57 | /// Parse the COFF section headers. 58 | /// 59 | /// For COFF, these immediately follow the COFF header. For PE, these immediately follow the 60 | /// optional header. 61 | pub fn sections( 62 | &self, 63 | bytes: &[u8], 64 | offset: &mut usize, 65 | ) -> error::Result> { 66 | let nsections = self.number_of_sections as usize; 67 | let mut sections = Vec::with_capacity(nsections); 68 | // Note that if we are handling a BigCoff, the size of the symbol will be different! 69 | let string_table_offset = self.pointer_to_symbol_table as usize 70 | + symbol::SymbolTable::size(self.number_of_symbol_table as usize); 71 | for i in 0..nsections { 72 | let section = section_table::SectionTable::parse(bytes, offset, string_table_offset as usize)?; 73 | debug!("({}) {:#?}", i, section); 74 | sections.push(section); 75 | } 76 | Ok(sections) 77 | } 78 | 79 | /// Return the COFF symbol table. 80 | pub fn symbols<'a>( 81 | &self, 82 | bytes: &'a [u8], 83 | ) -> error::Result> { 84 | let offset = self.pointer_to_symbol_table as usize; 85 | let number = self.number_of_symbol_table as usize; 86 | symbol::SymbolTable::parse(bytes, offset, number) 87 | } 88 | 89 | /// Return the COFF string table. 90 | pub fn strings<'a>( 91 | &self, 92 | bytes: &'a [u8], 93 | ) -> error::Result> { 94 | let offset = self.pointer_to_symbol_table as usize 95 | + symbol::SymbolTable::size(self.number_of_symbol_table as usize); 96 | let length = bytes.pread_with::(offset, scroll::LE)? as usize; 97 | Ok(strtab::Strtab::parse(bytes, offset, length, 0).unwrap()) 98 | } 99 | } 100 | 101 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 102 | pub struct Header { 103 | pub dos_header: DosHeader, 104 | /// PE Magic: PE\0\0, little endian 105 | pub signature: u32, 106 | pub coff_header: CoffHeader, 107 | pub optional_header: Option, 108 | } 109 | 110 | impl Header { 111 | pub fn parse(bytes: &[u8]) -> error::Result { 112 | let dos_header = DosHeader::parse(&bytes)?; 113 | let mut offset = dos_header.pe_pointer as usize; 114 | let signature = bytes.gread_with(&mut offset, scroll::LE) 115 | .map_err(|_| error::Error::Malformed(format!("cannot parse PE signature (offset {:#x})", offset)))?; 116 | let coff_header = CoffHeader::parse(&bytes, &mut offset)?; 117 | let optional_header = 118 | if coff_header.size_of_optional_header > 0 { 119 | Some (bytes.pread::(offset)?) 120 | } 121 | else { None }; 122 | Ok( Header { dos_header, signature, coff_header, optional_header }) 123 | } 124 | } 125 | 126 | #[cfg(test)] 127 | mod tests { 128 | use super::{DOS_MAGIC, PE_MAGIC, COFF_MACHINE_X86, Header}; 129 | 130 | const CRSS_HEADER: [u8; 688] = 131 | [0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 132 | 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 133 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 134 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 135 | 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, 136 | 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 137 | 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, 138 | 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 139 | 0xaa, 0x4a, 0xc3, 0xeb, 0xee, 0x2b, 0xad, 0xb8, 0xee, 0x2b, 0xad, 0xb8, 0xee, 0x2b, 0xad, 0xb8, 140 | 0xee, 0x2b, 0xac, 0xb8, 0xfe, 0x2b, 0xad, 0xb8, 0x33, 0xd4, 0x66, 0xb8, 0xeb, 0x2b, 0xad, 0xb8, 141 | 0x33, 0xd4, 0x63, 0xb8, 0xea, 0x2b, 0xad, 0xb8, 0x33, 0xd4, 0x7a, 0xb8, 0xed, 0x2b, 0xad, 0xb8, 142 | 0x33, 0xd4, 0x64, 0xb8, 0xef, 0x2b, 0xad, 0xb8, 0x33, 0xd4, 0x61, 0xb8, 0xef, 0x2b, 0xad, 0xb8, 143 | 0x52, 0x69, 0x63, 0x68, 0xee, 0x2b, 0xad, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 144 | 0x50, 0x45, 0x00, 0x00, 0x4c, 0x01, 0x05, 0x00, 0xd9, 0x8f, 0x15, 0x52, 0x00, 0x00, 0x00, 0x00, 145 | 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x02, 0x01, 0x0b, 0x01, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 146 | 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 147 | 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 148 | 0x06, 0x00, 0x03, 0x00, 0x06, 0x00, 0x03, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 149 | 0x00, 0x60, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xe4, 0xab, 0x00, 0x00, 0x01, 0x00, 0x40, 0x05, 150 | 0x00, 0x00, 0x04, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 151 | 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 152 | 0x3c, 0x30, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 153 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0xb8, 0x22, 0x00, 0x00, 154 | 0x00, 0x50, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 155 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 156 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x10, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 157 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 158 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 159 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 160 | 0x24, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 161 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, 162 | 0x2e, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 163 | 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 164 | 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xc0, 0x2e, 0x69, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 165 | 0xf8, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 166 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 167 | 0x2e, 0x72, 0x73, 0x72, 0x63, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 168 | 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 169 | 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x42, 0x2e, 0x72, 0x65, 0x6c, 0x6f, 0x63, 0x00, 0x00, 170 | 0x86, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 171 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x42, 172 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 173 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; 174 | 175 | #[test] 176 | fn crss_header () { 177 | let header = Header::parse(&&CRSS_HEADER[..]).unwrap(); 178 | assert!(header.dos_header.signature == DOS_MAGIC); 179 | assert!(header.signature == PE_MAGIC); 180 | assert!(header.coff_header.machine == COFF_MACHINE_X86); 181 | println!("header: {:?}", &header); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/elf/compression_header.rs: -------------------------------------------------------------------------------- 1 | macro_rules! elf_compression_header { 2 | () => { 3 | use plain; 4 | // Declare that this is a plain type. 5 | unsafe impl plain::Plain for CompressionHeader {} 6 | 7 | impl ::core::fmt::Debug for CompressionHeader { 8 | fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 9 | f.debug_struct("CompressionHeader") 10 | .field("ch_type", &self.ch_type) 11 | .field("ch_size", &format_args!("0x{:x}", self.ch_size)) 12 | .field("ch_addralign", &format_args!("0x{:x}", self.ch_addralign)) 13 | .finish() 14 | } 15 | } 16 | } 17 | } 18 | 19 | /// ZLIB/DEFLATE algorithm. 20 | pub const ELFCOMPRESS_ZLIB: u32 = 1; 21 | /// Start of OS-specific. 22 | pub const ELFCOMPRESS_LOOS: u32 = 0x6000_0000; 23 | /// End of OS-specific. 24 | pub const ELFCOMPRESS_HIOS: u32 = 0x6fff_ffff; 25 | /// Start of processor-specific. 26 | pub const ELFCOMPRESS_LOPROC: u32 = 0x7000_0000; 27 | /// End of processor-specific. 28 | pub const ELFCOMPRESS_HIPROC: u32 = 0x7fff_ffff; 29 | 30 | macro_rules! elf_compression_header_std_impl { ($size:ty) => { 31 | 32 | #[cfg(test)] 33 | mod tests { 34 | use super::*; 35 | #[test] 36 | fn size_of() { 37 | assert_eq!(::std::mem::size_of::(), SIZEOF_CHDR); 38 | } 39 | } 40 | 41 | if_alloc! { 42 | use crate::elf::compression_header::CompressionHeader as ElfCompressionHeader; 43 | 44 | use plain::Plain; 45 | 46 | if_std! { 47 | use crate::error::Result; 48 | 49 | use std::fs::File; 50 | use std::io::{Read, Seek}; 51 | use std::io::SeekFrom::Start; 52 | } 53 | 54 | impl From for ElfCompressionHeader { 55 | fn from(ch: CompressionHeader) -> Self { 56 | ElfCompressionHeader { 57 | ch_type: ch.ch_type, 58 | ch_size: u64::from(ch.ch_size), 59 | ch_addralign: u64::from(ch.ch_addralign), 60 | } 61 | } 62 | } 63 | 64 | impl CompressionHeader { 65 | pub fn from_bytes(bytes: &[u8]) -> CompressionHeader { 66 | let mut chdr = CompressionHeader::default(); 67 | chdr.copy_from_bytes(bytes).expect("buffer is too short for header"); 68 | chdr 69 | } 70 | 71 | #[cfg(feature = "std")] 72 | pub fn from_fd(fd: &mut File, offset: u64) -> Result { 73 | let mut chdr = CompressionHeader::default(); 74 | fd.seek(Start(offset))?; 75 | unsafe { 76 | fd.read_exact(plain::as_mut_bytes(&mut chdr))?; 77 | } 78 | Ok(chdr) 79 | } 80 | } 81 | } // end if_alloc 82 | };} 83 | 84 | #[cfg(feature = "alloc")] 85 | use scroll::{Pread, Pwrite, SizeWith}; 86 | 87 | pub mod compression_header32 { 88 | pub use crate::elf::compression_header::*; 89 | 90 | #[repr(C)] 91 | #[derive(Copy, Clone, Eq, PartialEq, Default)] 92 | #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, SizeWith))] 93 | /// The compression header is used at the start of SHF_COMPRESSED sections 94 | pub struct CompressionHeader { 95 | /// Compression format 96 | pub ch_type: u32, 97 | /// Uncompressed data size 98 | pub ch_size: u32, 99 | /// Uncompressed data alignment 100 | pub ch_addralign: u32, 101 | } 102 | 103 | elf_compression_header!(); 104 | 105 | pub const SIZEOF_CHDR: usize = 12; 106 | 107 | elf_compression_header_std_impl!(u32); 108 | 109 | if_alloc! { 110 | impl From for CompressionHeader { 111 | fn from(ch: ElfCompressionHeader) -> Self { 112 | CompressionHeader { 113 | ch_type: ch.ch_type, 114 | ch_size: ch.ch_size as u32, 115 | ch_addralign: ch.ch_addralign as u32, 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | 123 | pub mod compression_header64 { 124 | pub use crate::elf::compression_header::*; 125 | 126 | #[repr(C)] 127 | #[derive(Copy, Clone, Eq, PartialEq, Default)] 128 | #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, SizeWith))] 129 | /// The compression header is used at the start of SHF_COMPRESSED sections 130 | pub struct CompressionHeader { 131 | /// Compression format 132 | pub ch_type: u32, 133 | pub ch_reserved: u32, 134 | /// Uncompressed data size 135 | pub ch_size: u64, 136 | /// Uncompressed data alignment 137 | pub ch_addralign: u64, 138 | } 139 | 140 | elf_compression_header!(); 141 | 142 | pub const SIZEOF_CHDR: usize = 24; 143 | 144 | elf_compression_header_std_impl!(u64); 145 | 146 | if_alloc! { 147 | impl From for CompressionHeader { 148 | fn from(ch: ElfCompressionHeader) -> Self { 149 | CompressionHeader { 150 | ch_type: ch.ch_type, 151 | ch_reserved: 0, 152 | ch_size: ch.ch_size as u64, 153 | ch_addralign: ch.ch_addralign as u64, 154 | } 155 | } 156 | } 157 | } 158 | } 159 | 160 | /////////////////////////////// 161 | // Std/analysis/Unified Structs 162 | /////////////////////////////// 163 | 164 | if_alloc! { 165 | #[cfg(feature = "endian_fd")] 166 | use crate::error; 167 | use core::fmt; 168 | use core::result; 169 | use scroll::ctx; 170 | use crate::container::{Container, Ctx}; 171 | 172 | #[derive(Default, PartialEq, Clone)] 173 | /// A unified CompressionHeader - convertable to and from 32-bit and 64-bit variants 174 | pub struct CompressionHeader { 175 | /// Compression format 176 | pub ch_type: u32, 177 | /// Uncompressed data size 178 | pub ch_size: u64, 179 | /// Uncompressed data alignment 180 | pub ch_addralign: u64, 181 | } 182 | 183 | impl CompressionHeader { 184 | /// Return the size of the underlying compression header, given a `container` 185 | #[inline] 186 | pub fn size(ctx: Ctx) -> usize { 187 | use scroll::ctx::SizeWith; 188 | Self::size_with(&ctx) 189 | } 190 | pub fn new() -> Self { 191 | CompressionHeader { 192 | ch_type: 0, 193 | ch_size: 0, 194 | ch_addralign: 2 << 8, 195 | } 196 | } 197 | /// Parse a compression header from `bytes` at `offset`, using the given `ctx` 198 | #[cfg(feature = "endian_fd")] 199 | pub fn parse(bytes: &[u8], mut offset: usize, ctx: Ctx) -> error::Result { 200 | use scroll::Pread; 201 | bytes.gread_with(&mut offset, ctx) 202 | } 203 | } 204 | 205 | impl fmt::Debug for CompressionHeader { 206 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 207 | f.debug_struct("CompressionHeader") 208 | .field("ch_type", &self.ch_type) 209 | .field("ch_size", &format_args!("0x{:x}", self.ch_size)) 210 | .field("ch_addralign", &format_args!("0x{:x}", self.ch_addralign)) 211 | .finish() 212 | } 213 | } 214 | 215 | impl ctx::SizeWith for CompressionHeader { 216 | fn size_with( &Ctx { container, .. }: &Ctx) -> usize { 217 | match container { 218 | Container::Little => { 219 | compression_header32::SIZEOF_CHDR 220 | }, 221 | Container::Big => { 222 | compression_header64::SIZEOF_CHDR 223 | }, 224 | } 225 | } 226 | } 227 | 228 | impl<'a> ctx::TryFromCtx<'a, Ctx> for CompressionHeader { 229 | type Error = crate::error::Error; 230 | fn try_from_ctx(bytes: &'a [u8], Ctx {container, le}: Ctx) -> result::Result<(Self, usize), Self::Error> { 231 | use scroll::Pread; 232 | let res = match container { 233 | Container::Little => { 234 | (bytes.pread_with::(0, le)?.into(), compression_header32::SIZEOF_CHDR) 235 | }, 236 | Container::Big => { 237 | (bytes.pread_with::(0, le)?.into(), compression_header64::SIZEOF_CHDR) 238 | } 239 | }; 240 | Ok(res) 241 | } 242 | } 243 | 244 | impl ctx::TryIntoCtx for CompressionHeader { 245 | type Error = crate::error::Error; 246 | fn try_into_ctx(self, bytes: &mut [u8], Ctx {container, le}: Ctx) -> result::Result { 247 | use scroll::Pwrite; 248 | match container { 249 | Container::Little => { 250 | let chdr: compression_header32::CompressionHeader = self.into(); 251 | Ok(bytes.pwrite_with(chdr, 0, le)?) 252 | }, 253 | Container::Big => { 254 | let chdr: compression_header64::CompressionHeader = self.into(); 255 | Ok(bytes.pwrite_with(chdr, 0, le)?) 256 | } 257 | } 258 | } 259 | } 260 | impl ctx::IntoCtx for CompressionHeader { 261 | fn into_ctx(self, bytes: &mut [u8], Ctx {container, le}: Ctx) { 262 | use scroll::Pwrite; 263 | match container { 264 | Container::Little => { 265 | let chdr: compression_header32::CompressionHeader = self.into(); 266 | bytes.pwrite_with(chdr, 0, le).unwrap(); 267 | }, 268 | Container::Big => { 269 | let chdr: compression_header64::CompressionHeader = self.into(); 270 | bytes.pwrite_with(chdr, 0, le).unwrap(); 271 | } 272 | } 273 | } 274 | } 275 | } // end if_alloc 276 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | Before 1.0, this project does not adhere to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 5 | 6 | Goblin is now 0.1, which means we will try our best to ease breaking changes. Tracking issue is here: https://github.com/m4b/goblin/issues/97 7 | 8 | ## [0.1.1] - 2019-11-10 9 | ### Fixed 10 | elf: Don't fail entire elf parse when interpreter is malformed string, @jsgf https://github.com/m4b/goblin/pull/192 11 | 12 | ## [0.1.0] - 2019-11-3 13 | ### Added 14 | - update to scroll 0.10 api 15 | ### Changed 16 | - BREAKING: rename export to lib in Reexport::DLLOrdinal from @lzybkr 17 | - pe: only parse ExceptionData for machine X86_64, thanks @wyxloading 18 | ### Fixed 19 | pe: Fix resolution of redirect unwind info, thanks @jan-auer https://github.com/m4b/goblin/pull/183 20 | pe: fix reexport dll and ordinal, thanks @lzybkr: d62889f469846af0cceb789b415f1e14f5f9e402 21 | 22 | ## [0.0.24] - 2019-7-13 23 | ### Added 24 | - archive: new public enum type to determine which kind of archive was parsed 25 | ### Fixed 26 | - archive: thanks @raindev 27 | * fix parsing of windows style archives: https://github.com/m4b/goblin/pull/174 28 | * stricter parsing of archives with multiple indexes: https://github.com/m4b/goblin/pull/175 29 | 30 | ## [0.0.23] - 2019-6-30 31 | ### Added 32 | - pe: add write support for COFF object files!!! This is huge; we now support at a basic level writing out all major binary object formats, thanks @philipc: https://github.com/m4b/goblin/pull/159 33 | - elf: add more e_ident constants 34 | - mach: add segment protection constants 35 | - elf: add risc-v relocation constants 36 | - elf: add constants for arm64_32 (ILP32 ABI on 64-bit arm) 37 | - pe: coff relocations and other auxiliary symbol records 38 | 39 | ### Fixed 40 | - mach: fix 0 length data sections in mach-o segments, seen in some object files, thanks @raindev: https://github.com/m4b/goblin/pull/172 41 | - build: alloc build was fixed: https://github.com/m4b/goblin/pull/170 42 | - pe: fix `set_name_offset` compilation for 32-bit: https://github.com/m4b/goblin/pull/163 43 | 44 | ## [0.0.22] - 2019-4-13 45 | ### Added 46 | - Beautify debugging by using `debug_struct` in `Debug` implementation of many structs. 47 | - PE: fix rva mask, thanks @wickawacka: https://github.com/m4b/goblin/pull/152 48 | - PE: add PE exception tables, thanks @jan-auer: https://github.com/m4b/goblin/pull/136 49 | 50 | ### Changed 51 | - Bump lowest Rust version to 1.31.1 and transition project to Rust 2018 edition. 52 | - BREAKING: Rename module `goblin::elf::dyn` to `goblin::elf::dynamic` due to `dyn` 53 | become a keyword in Rust 2018 edition. 54 | - BREAKING: Rename `mach::exports::SymbolKind::to_str(kind: SymbolKind)` -> `to_str(&self)`. 55 | - BREAKING: Rename `strtab::Strtab::to_vec(self)` -> `to_vec(&self).` 56 | 57 | ### Removed 58 | - BREAKING: `goblin::error::Error::description` would be removed. Use `to_string()` method instead. 59 | 60 | ### Fixed 61 | - elf: handle some invalid sizes, thanks @philipc: https://github.com/m4b/goblin/pull/121 62 | 63 | ## [0.0.21] - 2019-2-21 64 | ### Added 65 | - elf: add symbol visibility. thanks @pchickey: https://github.com/m4b/goblin/pull/119 66 | 67 | ## [0.0.20] - 2019-2-10 68 | ### Added 69 | - elf: parse section header relocs even when not an object file. thanks @Techno-Coder: https://github.com/m4b/goblin/pull/118 70 | - pe: make utils public, add better examples for data directory usage. thanks @Pzixel: https://github.com/m4b/goblin/pull/116 71 | 72 | ## [0.0.19] - 2018-10-23 73 | ### Added 74 | - elf: fix regression when parsing dynamic symbols from some binaries, thanks @philipc: https://github.com/m4b/goblin/issues/111 75 | 76 | ## [0.0.18] - 2018-10-14 77 | ### Changed 78 | - BREAKING: updated required compiler to 1.20 (due to scroll 1.20 requirement) 79 | - BREAKING: elf: removed bias field, as it was misleading/useless/incorrect 80 | - BREAKING: elf: add lazy relocation iterators: Thanks @ibabushkin https://github.com/m4b/goblin/pull/102 81 | - BREAKING: mach: remove repr(packed) from dylib and fvmlib (this should not affect anyone): https://github.com/m4b/goblin/issues/105 82 | ### Added 83 | - elf: use gnu/sysv hash table to compute sizeof dynsyms more accurately: again _huge_ thanks to @philipc https://github.com/m4b/goblin/pull/109 84 | - elf: handle multiple load biases: _huge_ thanks @philipc: https://github.com/m4b/goblin/pull/107 85 | - mach: add arm64e constants: Thanks @mitsuhiko https://github.com/m4b/goblin/pull/103 86 | - PE: calculate read bytes using alignment: Thanks @tathanhdinh https://github.com/m4b/goblin/pull/101 87 | - PE: get proper names for PE sections: Thanks @roblabla https://github.com/m4b/goblin/pull/100 88 | 89 | ## [0.0.17] - 2018-7-16 90 | ### Changed 91 | - BREAKING: updated required compiler to 1.19 (technically only required for tests, but assume this is required for building as well) 92 | - fixed nightly alloc api issues: https://github.com/m4b/goblin/issues/94 93 | 94 | ## [0.0.16] - 2018-7-14 95 | ### Changed 96 | - BREAKING: pe.export: name is now optional to reflect realities of PE parsing, and add more robustness to parser. many thanks to @tathanhdinh! https://github.com/m4b/goblin/pull/88 97 | - elf.note: treat alignment similar to other tools, e.g., readelf. Thanks @xcoldhandsx: https://github.com/m4b/goblin/pull/91 98 | ### Added 99 | - elf: more inline annotations on various methods, thanks@amanieu: https://github.com/m4b/goblin/pull/87 100 | 101 | ## [0.0.15] - 2018-4-22 102 | ### Changed 103 | - BREAKING: elf.reloc: u64/i64 used for r_offset/r_addend, and addend is now proper optional, thanks @amanieu! https://github.com/m4b/goblin/pull/86/ 104 | - update to scroll 0.9 105 | - pe32+: parse better, thanks @kjempelodott, https://github.com/m4b/goblin/pull/82 106 | ### Added 107 | - mach: add constants for `n_types` when `N_STAB` field is being used, thanks @jrmuizel! https://github.com/m4b/goblin/pull/85 108 | - elf: implement support for compressed headers, thanks @rocallahan! https://github.com/m4b/goblin/pull/83 109 | - new nightly "alloc" feature: allows compiling the goblin parser on nightly with extern crate + no_std, thanks @philipc! https://github.com/m4b/goblin/pull/77 110 | - mach.segments: do not panic on bad internal data bounds: https://github.com/m4b/goblin/issues/74 111 | - mach: correctly add weak dylibs to import libs: https://github.com/m4b/goblin/issues/73 112 | 113 | ## [0.0.14] - 2018-1-15 114 | ### Changed 115 | - BREAKING: elf: `iter_notes` renamed to `iter_note_headers` 116 | - BREAKING: mach: remove `is_little_endian()`, `ctx()`, and `container()` methods from header, as they were completely invalid for big-endian architectures since the header was parsed according to the endianness of the binary correctly into memory, and hence would always report `MH_MAGIC` or `MH_MAGIC64` as the magic value. 117 | - elf: courtesy of @jan-auer, note iterator now properly iterates over multiple PH_NOTEs 118 | ### Added 119 | - mach: added hotly requested feature - goblin now has new functionality to parse big-endian, powerpc 32-bit mach-o binaries correctly 120 | - mach: new function to correctly extract the parsing context for a mach-o binary, `parse_magic_and_ctx` 121 | - elf: note iterator has new `iter_note_sections` method 122 | 123 | ## [0.0.13] - 2017-12-10 124 | ### Changed 125 | - BREAKING: remove deprecated goblin::parse method 126 | - BREAKING: ELF `to_range` removed on program and section headers; use `vm_range` and `file_range` for respective ranges 127 | - Technically BREAKING: @philipc added Symtab and symbol iterator to ELF, but is basically the same, unless you were explicitly relying on the backing vector 128 | - use scroll 0.8.0 and us scroll_derive via scroll 129 | - fix notes including \0 terminator (causes breakage because tools like grep treat resulting output as a binary output...) 130 | ### Added 131 | - pe: add PE characteristics constants courtesy @philipc 132 | - mach: SizeWith for RelocationInfo 133 | - mach: IOWrite and Pwrite impls for Nlist 134 | 135 | ## [0.0.12] - 2017-10-29 136 | ### Changed 137 | - fix proper std feature flag to log; this was an oversight in last version 138 | - proper cputype and cpusubtype constants to mach, along with mappings, courtesy of @mitsuhiko 139 | - new osx and ios version constants 140 | - all mach load commands now implement IOread and IOwrite from scroll 141 | - add new elf::note module and associated structs + constants, and `iter_notes` method to Elf object 142 | - remove all unused muts; this will make nightly and future stables no longer warn 143 | 144 | ### Added 145 | - fix macho nstab treatment, thanks @philipc ! 146 | - mach header cpusubtype bug fixed, thanks @mitsuhiko ! 147 | 148 | ## [0.0.11] - 2017-08-24 149 | ### Added 150 | - goblin::Object::parse; add deprecation to goblin::parse 151 | - MAJOR archive now parses bsd style archives AND is zero-copy by @willglynn 152 | - MAJOR macho import parser bug fixed by @willglynn 153 | - added writer impls for Section and Segment 154 | - add get_unsafe to strtab for Option<&str> returns 155 | - relocations method on mach 156 | - more elf relocations 157 | - mach relocations 158 | - convenience functions for many elf structures that elf writer will appreciate 159 | - mach relocation iteration 160 | - update to scroll 0.7 161 | - add cread/ioread impls for various structs 162 | 163 | ### Changed 164 | - BREAKING: sections() and section iterator now return (Section, &[u8]) 165 | - Segment, Section, RelocationIterator are now in segment module 166 | - removed lifetime from section, removed data and raw data, and embedded ctx 167 | - all scroll::Error have been removed from public API ref #33 168 | - better mach symbol iteration 169 | - better mach section iteration 170 | - remove wow_so_meta_doge due to linker issues 171 | - Strtab.get now returns a Option, when index is bad 172 | - elf.soname is &str 173 | - elf.libraries is now Vec<&str> 174 | 175 | ## [0.0.10] - 2017-05-09 176 | ### Added 177 | - New goblin::Object for enum containing the parsed binary container, or convenience goblin::parse(&[u8) for parsing bytes into respective container format 178 | ### Changed 179 | - All binaries formats now have lifetimes 180 | - Elf has a lifetime 181 | - Strtab.new now requires a &'a[u8] 182 | - Strtab.get now returns a scroll::Result<&'a str> (use strtab[index] if you want old behavior and don't care about panics); returning scroll::Error is a bug, fixed in next release 183 | 184 | ## [0.0.9] - 2017-04-05 185 | ### Changed 186 | - Archive has a lifetime 187 | - Mach has a lifetime 188 | -------------------------------------------------------------------------------- /src/elf/note.rs: -------------------------------------------------------------------------------- 1 | // Defined note types for GNU systems. 2 | 3 | #[cfg(feature = "log")] 4 | use log::debug; 5 | #[cfg(feature = "alloc")] 6 | use scroll::{Pread, Pwrite, IOread, IOwrite, SizeWith}; 7 | 8 | /// ABI information. 9 | /// 10 | /// The descriptor consists of words: 11 | /// * word 0: OS descriptor 12 | /// * word 1: major version of the ABI 13 | /// * word 2: minor version of the ABI 14 | /// * word 3: subminor version of the ABI 15 | pub const NT_GNU_ABI_TAG: u32 = 1; 16 | 17 | /// Old name 18 | pub const ELF_NOTE_ABI: u32 = NT_GNU_ABI_TAG; 19 | // Known OSes. These values can appear in word 0 of an 20 | // `NT_GNU_ABI_TAG` note section entry. 21 | pub const ELF_NOTE_OS_LINUX: u32 = 0; 22 | pub const ELF_NOTE_OS_GNU: u32 = 1; 23 | pub const ELF_NOTE_OS_SOLARIS2: u32 = 2; 24 | pub const ELF_NOTE_OS_FREEBSD: u32 = 3; 25 | 26 | /// Synthetic `hwcap` information. 27 | /// 28 | /// The descriptor begins with two words: 29 | /// * word 0: number of entries 30 | /// * word 1: bitmask of enabled entries 31 | /// 32 | /// Then follow variable-length entries, one byte followed by a '\0'-terminated 33 | /// `hwcap` name string. The byte gives the bit number to test if enabled, 34 | /// `(1U << bit) & bitmask`. 35 | pub const NT_GNU_HWCAP: u32 = 2; 36 | 37 | /// Build ID bits as generated by ld --build-id. 38 | /// 39 | /// The descriptor consists of any nonzero number of bytes. 40 | pub const NT_GNU_BUILD_ID: u32 = 3; 41 | 42 | /// Version note generated by GNU gold containing a version string. 43 | pub const NT_GNU_GOLD_VERSION: u32 = 4; 44 | 45 | #[derive(Clone, Copy, Debug)] 46 | #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, IOread, IOwrite, SizeWith))] 47 | #[repr(C)] 48 | /// Note section contents. Each entry in the note section begins with a header 49 | /// of a fixed form. 50 | pub struct Nhdr32 { 51 | /// Length of the note's name (includes the terminator) 52 | pub n_namesz: u32, 53 | /// Length of the note's descriptor 54 | pub n_descsz: u32, 55 | /// Type of the note 56 | pub n_type: u32, 57 | } 58 | 59 | #[derive(Clone, Copy, Debug)] 60 | #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, IOread, IOwrite, SizeWith))] 61 | #[repr(C)] 62 | /// Note section contents. Each entry in the note section begins with a header 63 | /// of a fixed form. 64 | pub struct Nhdr64 { 65 | /// Length of the note's name (includes the terminator) 66 | pub n_namesz: u64, 67 | /// Length of the note's descriptor. 68 | pub n_descsz: u64, 69 | /// Type of the note. 70 | pub n_type: u64, 71 | } 72 | 73 | if_alloc! { 74 | use crate::error; 75 | use crate::container; 76 | use scroll::ctx; 77 | use crate::alloc::vec::Vec; 78 | 79 | /// An iterator over ELF binary notes in a note section or segment 80 | pub struct NoteDataIterator<'a> { 81 | pub data: &'a [u8], 82 | pub size: usize, 83 | pub offset: usize, 84 | pub ctx: (usize, container::Ctx), // (alignment, ctx) 85 | } 86 | 87 | impl<'a> Iterator for NoteDataIterator<'a> { 88 | type Item = error::Result>; 89 | fn next(&mut self) -> Option { 90 | if self.offset >= self.size { 91 | None 92 | } else { 93 | debug!("NoteIterator - {:#x}", self.offset); 94 | match self.data.gread_with(&mut self.offset, self.ctx) { 95 | Ok(res) => Some(Ok(res)), 96 | Err(e) => Some(Err(e)) 97 | } 98 | } 99 | } 100 | } 101 | 102 | /// An iterator over ELF binary notes 103 | pub struct NoteIterator<'a> { 104 | pub iters: Vec>, 105 | pub index: usize, 106 | } 107 | 108 | impl<'a> Iterator for NoteIterator<'a> { 109 | type Item = error::Result>; 110 | fn next(&mut self) -> Option { 111 | while self.index < self.iters.len() { 112 | if let Some(note_result) = self.iters[self.index].next() { 113 | return Some(note_result); 114 | } 115 | 116 | self.index += 1; 117 | } 118 | 119 | None 120 | } 121 | } 122 | 123 | #[derive(Debug)] 124 | struct NoteHeader { 125 | n_namesz: usize, 126 | n_descsz: usize, 127 | n_type: u32, 128 | } 129 | 130 | impl From for NoteHeader { 131 | fn from(header: Nhdr32) -> Self { 132 | NoteHeader { 133 | n_namesz: header.n_namesz as usize, 134 | n_descsz: header.n_descsz as usize, 135 | n_type: header.n_type, 136 | } 137 | } 138 | } 139 | 140 | impl From for NoteHeader { 141 | fn from(header: Nhdr64) -> Self { 142 | NoteHeader { 143 | n_namesz: header.n_namesz as usize, 144 | n_descsz: header.n_descsz as usize, 145 | n_type: header.n_type as u32, 146 | } 147 | } 148 | } 149 | 150 | fn align(alignment: usize, offset: &mut usize) { 151 | let diff = *offset % alignment; 152 | if diff != 0 { 153 | *offset += alignment - diff; 154 | } 155 | } 156 | 157 | /// A 32/64 bit Note struct, with the name and desc pre-parsed 158 | #[derive(Debug)] 159 | pub struct Note<'a> { 160 | /// The type of this note 161 | pub n_type: u32, 162 | /// NUL terminated string, where `namesz` includes the terminator 163 | pub name: &'a str, // needs padding such that namesz + padding % {wordsize} == 0 164 | /// arbitrary data of length `descsz` 165 | pub desc: &'a [u8], // needs padding such that descsz + padding % {wordsize} == 0 166 | } 167 | 168 | impl<'a> Note<'a> { 169 | pub fn type_to_str(&self) -> &'static str { 170 | match self.n_type { 171 | NT_GNU_ABI_TAG => "NT_GNU_ABI_TAG", 172 | NT_GNU_HWCAP => "NT_GNU_HWCAP", 173 | NT_GNU_BUILD_ID => "NT_GNU_BUILD_ID", 174 | NT_GNU_GOLD_VERSION => "NT_GNU_GOLD_VERSION", 175 | _ => "NT_UNKNOWN" 176 | } 177 | } 178 | } 179 | 180 | impl<'a> ctx::TryFromCtx<'a, (usize, container::Ctx)> for Note<'a> { 181 | type Error = error::Error; 182 | fn try_from_ctx(bytes: &'a [u8], (alignment, ctx): (usize, container::Ctx)) -> Result<(Self, usize), Self::Error> { 183 | let offset = &mut 0; 184 | let mut alignment = alignment; 185 | if alignment < 4 { 186 | alignment = 4; 187 | } 188 | let header: NoteHeader = { 189 | match alignment { 190 | 4|8 => bytes.gread_with::(offset, ctx.le)?.into(), 191 | _ => return Err(error::Error::Malformed(format!("Notes has unimplemented alignment requirement: {:#x}", alignment))) 192 | } 193 | }; 194 | debug!("{:?} - {:#x}", header, *offset); 195 | // -1 because includes \0 terminator 196 | let name = bytes.gread_with::<&'a str>(offset, ctx::StrCtx::Length(header.n_namesz - 1))?; 197 | *offset += 1; 198 | align(alignment, offset); 199 | debug!("note name {} - {:#x}", name, *offset); 200 | let desc = bytes.gread_with::<&'a [u8]>(offset, header.n_descsz)?; 201 | align(alignment, offset); 202 | debug!("desc {:?} - {:#x}", desc, *offset); 203 | Ok((Note { 204 | name, 205 | desc, 206 | n_type: header.n_type, 207 | }, *offset)) 208 | } 209 | } 210 | 211 | #[cfg(test)] 212 | mod tests { 213 | use super::*; 214 | 215 | static NOTE_DATA: [u8; 68] = [0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 216 | 0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 217 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 218 | 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 219 | 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 220 | 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 221 | 0xbc, 0xfc, 0x66, 0xcd, 0xc7, 0xd5, 0x14, 0x7b, 222 | 0x53, 0xb1, 0x10, 0x11, 0x94, 0x86, 0x8e, 0xf9, 223 | 0x4f, 0xe8, 0xdd, 0xdb]; 224 | 225 | static CONTEXT: (usize, container::Ctx) = (4, container::Ctx { 226 | container: container::Container::Big, 227 | le: ::scroll::Endian::Little, 228 | }); 229 | 230 | fn make_note_iter(start: usize, end: usize) -> NoteDataIterator<'static> { 231 | NoteDataIterator { 232 | data: &NOTE_DATA, 233 | size: end, 234 | offset: start, 235 | ctx: CONTEXT, 236 | } 237 | } 238 | 239 | #[test] 240 | fn iter_single_section() { 241 | let mut notes = NoteIterator { 242 | iters: vec![make_note_iter(0, 68)], 243 | index: 0, 244 | }; 245 | 246 | assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG); 247 | assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID); 248 | assert!(notes.next().is_none()); 249 | } 250 | 251 | #[test] 252 | fn iter_multiple_sections() { 253 | let mut notes = NoteIterator { 254 | iters: vec![make_note_iter(0, 32), make_note_iter(32, 68)], 255 | index: 0, 256 | }; 257 | 258 | assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG); 259 | assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID); 260 | assert!(notes.next().is_none()); 261 | } 262 | 263 | #[test] 264 | fn skip_empty_sections() { 265 | let mut notes = NoteIterator { 266 | iters: vec![ 267 | make_note_iter(0, 32), 268 | make_note_iter(0, 0), 269 | make_note_iter(32, 68), 270 | ], 271 | index: 0, 272 | }; 273 | 274 | assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG); 275 | assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID); 276 | assert!(notes.next().is_none()); 277 | } 278 | 279 | #[test] 280 | fn ignore_no_sections() { 281 | let mut notes = NoteIterator { iters: vec![], index: 0 }; 282 | assert!(notes.next().is_none()); 283 | } 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /src/mach/relocation.rs: -------------------------------------------------------------------------------- 1 | // Format of a relocation entry of a Mach-O file. Modified from the 4.3BSD 2 | // format. The modifications from the original format were changing the value 3 | // of the r_symbolnum field for "local" (r_extern == 0) relocation entries. 4 | // This modification is required to support symbols in an arbitrary number of 5 | // sections not just the three sections (text, data and bss) in a 4.3BSD file. 6 | // Also the last 4 bits have had the r_type tag added to them. 7 | 8 | // The r_address is not really the address as it's name indicates but an offset. 9 | // In 4.3BSD a.out objects this offset is from the start of the "segment" for 10 | // which relocation entry is for (text or data). For Mach-O object files it is 11 | // also an offset but from the start of the "section" for which the relocation 12 | // entry is for. See comments in about the r_address feild 13 | // in images for used with the dynamic linker. 14 | 15 | // In 4.3BSD a.out objects if r_extern is zero then r_symbolnum is an ordinal 16 | // for the segment the symbol being relocated is in. These ordinals are the 17 | // symbol types N_TEXT, N_DATA, N_BSS or N_ABS. In Mach-O object files these 18 | // ordinals refer to the sections in the object file in the order their section 19 | // structures appear in the headers of the object file they are in. The first 20 | // section has the ordinal 1, the second 2, and so on. This means that the 21 | // same ordinal in two different object files could refer to two different 22 | // sections. And further could have still different ordinals when combined 23 | // by the link-editor. The value R_ABS is used for relocation entries for 24 | // absolute symbols which need no further relocation. 25 | use core::fmt; 26 | use crate::mach; 27 | use scroll::{Pread, Pwrite, IOwrite, SizeWith, IOread}; 28 | 29 | // TODO: armv7 relocations are scattered, must and r_address with 0x8000_0000 to check if its scattered or not 30 | #[derive(Copy, Clone, Pread, Pwrite, IOwrite, SizeWith, IOread)] 31 | #[repr(C)] 32 | pub struct RelocationInfo { 33 | /// Offset in the section to what is being relocated 34 | pub r_address: i32, 35 | /// Contains all of the relocation info as a bitfield. 36 | /// r_symbolnum, 24 bits, r_pcrel 1 bit, r_length 2 bits, r_extern 1 bit, r_type 4 bits 37 | pub r_info: u32, 38 | } 39 | 40 | pub const SIZEOF_RELOCATION_INFO: usize = 8; 41 | 42 | impl RelocationInfo { 43 | /// Symbol index if `r_extern` == 1 or section ordinal if `r_extern` == 0. In bits :24 44 | #[inline] 45 | pub fn r_symbolnum(self) -> usize { 46 | (self.r_info & 0x00ff_ffffu32) as usize 47 | } 48 | /// Was relocated pc relative already, 1 bit 49 | #[inline] 50 | pub fn r_pcrel(self) -> u8 { 51 | ((self.r_info & 0x0100_0000u32) >> 24) as u8 52 | } 53 | /// The length of the relocation, 0=byte, 1=word, 2=long, 3=quad, 2 bits 54 | #[inline] 55 | pub fn r_length(self) -> u8 { 56 | ((self.r_info & 0x0600_0000u32) >> 25) as u8 57 | } 58 | /// Does not include value of sym referenced, 1 bit 59 | #[inline] 60 | pub fn r_extern(self) -> u8 { 61 | ((self.r_info & 0x0800_0000) >> 27) as u8 62 | } 63 | /// Ff not 0, machine specific relocation type, in bits :4 64 | #[inline] 65 | pub fn r_type(self) -> u8 { 66 | ((self.r_info & 0xf000_0000) >> 28) as u8 67 | } 68 | /// If true, this relocation is for a symbol; if false, or a section ordinal otherwise 69 | #[inline] 70 | pub fn is_extern(self) -> bool { 71 | self.r_extern() == 1 72 | } 73 | /// If true, this is a PIC relocation 74 | #[inline] 75 | pub fn is_pic(self) -> bool { 76 | self.r_pcrel() > 0 77 | } 78 | /// Returns a string representation of this relocation, given the machine `cputype` 79 | pub fn to_str(self, cputype: mach::cputype::CpuType) -> &'static str { 80 | reloc_to_str(self.r_type(), cputype) 81 | } 82 | } 83 | 84 | /// Absolute relocation type for Mach-O files 85 | pub const R_ABS: u8 = 0; 86 | 87 | impl fmt::Debug for RelocationInfo { 88 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 89 | f.debug_struct("RelocationInfo") 90 | .field("r_address", &format_args!("{:#x}", &self.r_address)) 91 | .field("r_info", &format_args!("{:#x}", &self.r_info)) 92 | .field("r_symbolnum", &format_args!("{:#x}", &self.r_symbolnum())) 93 | .field("r_pcrel", &(self.r_pcrel())) 94 | .field("r_length", &self.r_length()) 95 | .field("r_extern", &self.r_extern()) 96 | .field("r_type", &self.r_type()) 97 | .finish() 98 | } 99 | } 100 | 101 | pub type RelocType = u8; 102 | 103 | /// Absolute address 104 | pub const X86_64_RELOC_UNSIGNED: RelocType = 0; 105 | /// Signed 32-bit displacement 106 | pub const X86_64_RELOC_SIGNED: RelocType = 1; 107 | /// A CALL/JMP instruction with 32-bit displacement 108 | pub const X86_64_RELOC_BRANCH: RelocType = 2; 109 | /// A MOVQ load of a GOT entry 110 | pub const X86_64_RELOC_GOT_LOAD: RelocType = 3; 111 | /// Other GOT references 112 | pub const X86_64_RELOC_GOT: RelocType = 4; 113 | /// Must be followed by a X86_64_RELOC_UNSIGNED relocation 114 | pub const X86_64_RELOC_SUBTRACTOR: RelocType = 5; 115 | /// for signed 32-bit displacement with a -1 addend 116 | pub const X86_64_RELOC_SIGNED_1: RelocType = 6; 117 | /// for signed 32-bit displacement with a -2 addend 118 | pub const X86_64_RELOC_SIGNED_2: RelocType = 7; 119 | /// for signed 32-bit displacement with a -4 addend 120 | pub const X86_64_RELOC_SIGNED_4: RelocType = 8; 121 | /// for thread local variables 122 | pub const X86_64_RELOC_TLV: RelocType = 9; 123 | 124 | // x86 relocations 125 | pub const GENERIC_RELOC_VANILLA: RelocType = 0; 126 | pub const GENERIC_RELOC_PAIR: RelocType = 1; 127 | pub const GENERIC_RELOC_SECTDIFF: RelocType = 2; 128 | pub const GENERIC_RELOC_LOCAL_SECTDIFF: RelocType = 3; 129 | pub const GENERIC_RELOC_PB_LA_P: RelocType = 4; 130 | 131 | // arm relocations 132 | pub const ARM_RELOC_VANILLA: RelocType = GENERIC_RELOC_VANILLA; 133 | pub const ARM_RELOC_PAIR: RelocType = GENERIC_RELOC_PAIR; 134 | pub const ARM_RELOC_SECTDIFF: RelocType = GENERIC_RELOC_SECTDIFF; 135 | pub const ARM_RELOC_LOCAL_SECTDIFF: RelocType = 3; 136 | pub const ARM_RELOC_PB_LA_PTR: RelocType = 4; 137 | pub const ARM_RELOC_BR24: RelocType = 5; 138 | pub const ARM_THUMB_RELOC_BR22: RelocType = 6; 139 | /// Obsolete 140 | pub const ARM_THUMB_32BIT_BRANCH: RelocType = 7; 141 | pub const ARM_RELOC_HALF: RelocType = 8; 142 | pub const ARM_RELOC_HALF_SECTDIFF: RelocType = 9; 143 | 144 | /// For pointers. 145 | pub const ARM64_RELOC_UNSIGNED: RelocType = 0; 146 | /// Must be followed by an ARM64_RELOC_UNSIGNED 147 | pub const ARM64_RELOC_SUBTRACTOR: RelocType = 1; 148 | /// A B/BL instruction with 26-bit displacement. 149 | pub const ARM64_RELOC_BRANCH26: RelocType = 2; 150 | /// PC-rel distance to page of target. 151 | pub const ARM64_RELOC_PAGE21: RelocType = 3; 152 | /// Offset within page, scaled by r_length. 153 | pub const ARM64_RELOC_PAGEOFF12: RelocType = 4; 154 | /// PC-rel distance to page of GOT slot. 155 | pub const ARM64_RELOC_GOT_LOAD_PAGE21: RelocType = 5; 156 | /// Offset within page of GOT slot, scaled by r_length. 157 | pub const ARM64_RELOC_GOT_LOAD_PAGEOFF12: RelocType = 6; 158 | /// For pointers to GOT slots. 159 | pub const ARM64_RELOC_POINTER_TO_GOT: RelocType = 7; 160 | /// PC-rel distance to page of TLVP slot. 161 | pub const ARM64_RELOC_TLVP_LOAD_PAGE21: RelocType = 8; 162 | /// Offset within page of TLVP slot, scaled by r_length. 163 | pub const ARM64_RELOC_TLVP_LOAD_PAGEOFF12: RelocType = 9; 164 | /// Must be followed by ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12. 165 | pub const ARM64_RELOC_ADDEND: RelocType = 10; 166 | 167 | pub fn reloc_to_str(reloc: RelocType, cputype: mach::cputype::CpuType) -> &'static str { 168 | use crate::mach::constants::cputype::*; 169 | match cputype { 170 | CPU_TYPE_ARM64 | CPU_TYPE_ARM64_32 => { 171 | match reloc { 172 | ARM64_RELOC_UNSIGNED => "ARM64_RELOC_UNSIGNED", 173 | ARM64_RELOC_SUBTRACTOR => "ARM64_RELOC_SUBTRACTOR", 174 | ARM64_RELOC_BRANCH26 => "ARM64_RELOC_BRANCH26", 175 | ARM64_RELOC_PAGE21 => "ARM64_RELOC_PAGE21", 176 | ARM64_RELOC_PAGEOFF12 => "ARM64_RELOC_PAGEOFF12", 177 | ARM64_RELOC_GOT_LOAD_PAGE21 => "ARM64_RELOC_GOT_LOAD_PAGE21", 178 | ARM64_RELOC_GOT_LOAD_PAGEOFF12 => "ARM64_RELOC_GOT_LOAD_PAGEOFF12", 179 | ARM64_RELOC_POINTER_TO_GOT => "ARM64_RELOC_POINTER_TO_GOT", 180 | ARM64_RELOC_TLVP_LOAD_PAGE21 => "ARM64_RELOC_TLVP_LOAD_PAGE21", 181 | ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", 182 | ARM64_RELOC_ADDEND => "ARM64_RELOC_ADDEND", 183 | _ => "UNKNOWN", 184 | } 185 | }, 186 | CPU_TYPE_X86_64 => { 187 | match reloc { 188 | X86_64_RELOC_UNSIGNED => "X86_64_RELOC_UNSIGNED", 189 | X86_64_RELOC_SIGNED => "X86_64_RELOC_SIGNED", 190 | X86_64_RELOC_BRANCH => "X86_64_RELOC_BRANCH", 191 | X86_64_RELOC_GOT_LOAD => "X86_64_RELOC_GOT_LOAD", 192 | X86_64_RELOC_GOT => "X86_64_RELOC_GOT", 193 | X86_64_RELOC_SUBTRACTOR => "X86_64_RELOC_SUBTRACTOR", 194 | X86_64_RELOC_SIGNED_1 => "X86_64_RELOC_SIGNED_1", 195 | X86_64_RELOC_SIGNED_2 => "X86_64_RELOC_SIGNED_2", 196 | X86_64_RELOC_SIGNED_4 => "X86_64_RELOC_SIGNED_4", 197 | X86_64_RELOC_TLV => "X86_64_RELOC_TLV", 198 | _ => "UNKNOWN", 199 | } 200 | }, 201 | CPU_TYPE_ARM => { 202 | match reloc { 203 | ARM_RELOC_VANILLA => "ARM_RELOC_VANILLA", 204 | ARM_RELOC_PAIR => "ARM_RELOC_PAIR", 205 | ARM_RELOC_SECTDIFF => "ARM_RELOC_SECTDIFF", 206 | ARM_RELOC_LOCAL_SECTDIFF => "ARM_RELOC_LOCAL_SECTDIFF", 207 | ARM_RELOC_PB_LA_PTR => "ARM_RELOC_PB_LA_PTR", 208 | ARM_RELOC_BR24 => "ARM_RELOC_BR24", 209 | ARM_THUMB_RELOC_BR22 => "ARM_THUMB_RELOC_BR22", 210 | ARM_THUMB_32BIT_BRANCH => "ARM_THUMB_32BIT_BRANCH", 211 | ARM_RELOC_HALF => "ARM_RELOC_HALF", 212 | ARM_RELOC_HALF_SECTDIFF => "ARM_RELOC_HALF_SECTDIFF", 213 | _ => "UNKNOWN", 214 | } 215 | }, 216 | CPU_TYPE_X86 => { 217 | match reloc { 218 | GENERIC_RELOC_VANILLA => "GENERIC_RELOC_VANILLA", 219 | GENERIC_RELOC_PAIR => "GENERIC_RELOC_PAIR", 220 | GENERIC_RELOC_SECTDIFF => "GENERIC_RELOC_SECTDIFF", 221 | GENERIC_RELOC_LOCAL_SECTDIFF => "GENERIC_RELOC_LOCAL_SECTDIFF", 222 | GENERIC_RELOC_PB_LA_P => "GENERIC_RELOC_PB_LA_P", 223 | _ => "UNKNOWN", 224 | } 225 | }, 226 | _ => "BAD_CPUTYPE" 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /src/pe/section_table.rs: -------------------------------------------------------------------------------- 1 | use crate::alloc::string::{String, ToString}; 2 | use scroll::{ctx, Pread, Pwrite}; 3 | use crate::error::{self, Error}; 4 | use crate::pe::relocation; 5 | 6 | #[repr(C)] 7 | #[derive(Debug, PartialEq, Clone, Default)] 8 | pub struct SectionTable { 9 | pub name: [u8; 8], 10 | pub real_name: Option, 11 | pub virtual_size: u32, 12 | pub virtual_address: u32, 13 | pub size_of_raw_data: u32, 14 | pub pointer_to_raw_data: u32, 15 | pub pointer_to_relocations: u32, 16 | pub pointer_to_linenumbers: u32, 17 | pub number_of_relocations: u16, 18 | pub number_of_linenumbers: u16, 19 | pub characteristics: u32, 20 | } 21 | 22 | pub const SIZEOF_SECTION_TABLE: usize = 8 * 5; 23 | 24 | // Based on https://github.com/llvm-mirror/llvm/blob/af7b1832a03ab6486c42a40d21695b2c03b2d8a3/lib/Object/COFFObjectFile.cpp#L70 25 | // Decodes a string table entry in base 64 (//AAAAAA). Expects string without 26 | // prefixed slashes. 27 | fn base64_decode_string_entry(s: &str) -> Result { 28 | assert!(s.len() <= 6, "String too long, possible overflow."); 29 | 30 | let mut val = 0; 31 | for c in s.bytes() { 32 | let v = if b'A' <= c && c <= b'Z' { // 00..=25 33 | c - b'A' 34 | } else if b'a' <= c && c <= b'z' { // 26..=51 35 | c - b'a' + 26 36 | } else if b'0' <= c && c <= b'9' { // 52..=61 37 | c - b'0' + 52 38 | } else if c == b'+' { // 62 39 | 62 40 | } else if c == b'/' { // 63 41 | 63 42 | } else { 43 | return Err(()) 44 | }; 45 | val = val * 64 + v as usize; 46 | } 47 | Ok(val) 48 | } 49 | 50 | impl SectionTable { 51 | pub fn parse(bytes: &[u8], offset: &mut usize, string_table_offset: usize) -> error::Result { 52 | let mut table = SectionTable::default(); 53 | let mut name = [0u8; 8]; 54 | name.copy_from_slice(bytes.gread_with(offset, 8)?); 55 | 56 | table.name = name; 57 | table.virtual_size = bytes.gread_with(offset, scroll::LE)?; 58 | table.virtual_address = bytes.gread_with(offset, scroll::LE)?; 59 | table.size_of_raw_data = bytes.gread_with(offset, scroll::LE)?; 60 | table.pointer_to_raw_data = bytes.gread_with(offset, scroll::LE)?; 61 | table.pointer_to_relocations = bytes.gread_with(offset, scroll::LE)?; 62 | table.pointer_to_linenumbers = bytes.gread_with(offset, scroll::LE)?; 63 | table.number_of_relocations = bytes.gread_with(offset, scroll::LE)?; 64 | table.number_of_linenumbers = bytes.gread_with(offset, scroll::LE)?; 65 | table.characteristics = bytes.gread_with(offset, scroll::LE)?; 66 | 67 | if let Some(idx) = table.name_offset()? { 68 | table.real_name = Some(bytes.pread::<&str>(string_table_offset + idx)?.to_string()); 69 | } 70 | Ok(table) 71 | } 72 | 73 | pub fn name_offset(&self) -> error::Result> { 74 | // Based on https://github.com/llvm-mirror/llvm/blob/af7b1832a03ab6486c42a40d21695b2c03b2d8a3/lib/Object/COFFObjectFile.cpp#L1054 75 | if self.name[0] == b'/' { 76 | let idx: usize = if self.name[1] == b'/' { 77 | let b64idx = self.name.pread::<&str>(2)?; 78 | base64_decode_string_entry(b64idx).map_err(|_| 79 | Error::Malformed(format!("Invalid indirect section name //{}: base64 decoding failed", b64idx)))? 80 | } else { 81 | let name = self.name.pread::<&str>(1)?; 82 | name.parse().map_err(|err| 83 | Error::Malformed(format!("Invalid indirect section name /{}: {}", name, err)))? 84 | }; 85 | Ok(Some(idx)) 86 | } else { 87 | Ok(None) 88 | } 89 | } 90 | 91 | #[allow(clippy::useless_let_if_seq)] 92 | pub fn set_name_offset(&mut self, mut idx: usize) -> error::Result<()> { 93 | if idx <= 9_999_999 { // 10^7 - 1 94 | // write!(&mut self.name[1..], "{}", idx) without using io::Write. 95 | // We write into a temporary since we calculate digits starting at the right. 96 | let mut name = [0; 7]; 97 | let mut len = 0; 98 | if idx == 0 { 99 | name[6] = b'0'; 100 | len = 1; 101 | } else { 102 | while idx != 0 { 103 | let rem = (idx % 10) as u8; 104 | idx /= 10; 105 | name[6 - len] = b'0' + rem; 106 | len += 1; 107 | } 108 | } 109 | self.name = [0; 8]; 110 | self.name[0] = b'/'; 111 | self.name[1..][..len].copy_from_slice(&name[7 - len..]); 112 | Ok(()) 113 | } else if idx as u64 <= 0xfff_fff_fff { // 64^6 - 1 114 | self.name[0] = b'/'; 115 | self.name[1] = b'/'; 116 | for i in 0..6 { 117 | let rem = (idx % 64) as u8; 118 | idx /= 64; 119 | let c = match rem { 120 | 0..=25 => b'A' + rem, 121 | 26..=51 => b'a' + rem - 26, 122 | 52..=61 => b'0' + rem - 52, 123 | 62 => b'+', 124 | 63 => b'/', 125 | _ => unreachable!(), 126 | }; 127 | self.name[7 - i] = c; 128 | } 129 | Ok(()) 130 | } else { 131 | Err(Error::Malformed(format!("Invalid section name offset: {}", idx))) 132 | } 133 | } 134 | 135 | pub fn name(&self) -> error::Result<&str> { 136 | match self.real_name.as_ref() { 137 | Some(s) => Ok(s), 138 | None => Ok(self.name.pread(0)?) 139 | } 140 | } 141 | 142 | pub fn relocations<'a>(&self, bytes: &'a[u8]) -> error::Result> { 143 | let offset = self.pointer_to_relocations as usize; 144 | let number = self.number_of_relocations as usize; 145 | relocation::Relocations::parse(bytes, offset, number) 146 | } 147 | } 148 | 149 | impl ctx::SizeWith for SectionTable { 150 | fn size_with(_ctx: &scroll::Endian) -> usize { 151 | SIZEOF_SECTION_TABLE 152 | } 153 | } 154 | 155 | impl ctx::TryIntoCtx for SectionTable { 156 | type Error = error::Error; 157 | fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result { 158 | let offset = &mut 0; 159 | bytes.gwrite(&self.name[..], offset)?; 160 | bytes.gwrite_with(self.virtual_size, offset, ctx)?; 161 | bytes.gwrite_with(self.virtual_address, offset, ctx)?; 162 | bytes.gwrite_with(self.size_of_raw_data, offset, ctx)?; 163 | bytes.gwrite_with(self.pointer_to_raw_data, offset, ctx)?; 164 | bytes.gwrite_with(self.pointer_to_relocations, offset, ctx)?; 165 | bytes.gwrite_with(self.pointer_to_linenumbers, offset, ctx)?; 166 | bytes.gwrite_with(self.number_of_relocations, offset, ctx)?; 167 | bytes.gwrite_with(self.number_of_linenumbers, offset, ctx)?; 168 | bytes.gwrite_with(self.characteristics, offset, ctx)?; 169 | Ok(SIZEOF_SECTION_TABLE) 170 | } 171 | } 172 | 173 | impl ctx::IntoCtx for SectionTable { 174 | fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) { 175 | bytes.pwrite_with(self, 0, ctx).unwrap(); 176 | } 177 | } 178 | 179 | /// The section should not be padded to the next boundary. This flag is obsolete and is replaced 180 | /// by `IMAGE_SCN_ALIGN_1BYTES`. This is valid only for object files. 181 | pub const IMAGE_SCN_TYPE_NO_PAD: u32 = 0x0000_0008; 182 | /// The section contains executable code. 183 | pub const IMAGE_SCN_CNT_CODE: u32 = 0x0000_0020; 184 | /// The section contains initialized data. 185 | pub const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x0000_0040; 186 | /// The section contains uninitialized data. 187 | pub const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x0000_0080; 188 | pub const IMAGE_SCN_LNK_OTHER: u32 = 0x0000_0100; 189 | /// The section contains comments or other information. The .drectve section has this type. 190 | /// This is valid for object files only. 191 | pub const IMAGE_SCN_LNK_INFO: u32 = 0x0000_0200; 192 | /// The section will not become part of the image. This is valid only for object files. 193 | pub const IMAGE_SCN_LNK_REMOVE: u32 = 0x0000_0800; 194 | /// The section contains COMDAT data. This is valid only for object files. 195 | pub const IMAGE_SCN_LNK_COMDAT: u32 = 0x0000_1000; 196 | /// The section contains data referenced through the global pointer (GP). 197 | pub const IMAGE_SCN_GPREL: u32 = 0x0000_8000; 198 | pub const IMAGE_SCN_MEM_PURGEABLE: u32 = 0x0002_0000; 199 | pub const IMAGE_SCN_MEM_16BIT: u32 = 0x0002_0000; 200 | pub const IMAGE_SCN_MEM_LOCKED: u32 = 0x0004_0000; 201 | pub const IMAGE_SCN_MEM_PRELOAD: u32 = 0x0008_0000; 202 | 203 | pub const IMAGE_SCN_ALIGN_1BYTES: u32 = 0x0010_0000; 204 | pub const IMAGE_SCN_ALIGN_2BYTES: u32 = 0x0020_0000; 205 | pub const IMAGE_SCN_ALIGN_4BYTES: u32 = 0x0030_0000; 206 | pub const IMAGE_SCN_ALIGN_8BYTES: u32 = 0x0040_0000; 207 | pub const IMAGE_SCN_ALIGN_16BYTES: u32 = 0x0050_0000; 208 | pub const IMAGE_SCN_ALIGN_32BYTES: u32 = 0x0060_0000; 209 | pub const IMAGE_SCN_ALIGN_64BYTES: u32 = 0x0070_0000; 210 | pub const IMAGE_SCN_ALIGN_128BYTES: u32 = 0x0080_0000; 211 | pub const IMAGE_SCN_ALIGN_256BYTES: u32 = 0x0090_0000; 212 | pub const IMAGE_SCN_ALIGN_512BYTES: u32 = 0x00A0_0000; 213 | pub const IMAGE_SCN_ALIGN_1024BYTES: u32 = 0x00B0_0000; 214 | pub const IMAGE_SCN_ALIGN_2048BYTES: u32 = 0x00C0_0000; 215 | pub const IMAGE_SCN_ALIGN_4096BYTES: u32 = 0x00D0_0000; 216 | pub const IMAGE_SCN_ALIGN_8192BYTES: u32 = 0x00E0_0000; 217 | pub const IMAGE_SCN_ALIGN_MASK: u32 = 0x00F0_0000; 218 | 219 | /// The section contains extended relocations. 220 | pub const IMAGE_SCN_LNK_NRELOC_OVFL: u32 = 0x0100_0000; 221 | /// The section can be discarded as needed. 222 | pub const IMAGE_SCN_MEM_DISCARDABLE: u32 = 0x0200_0000; 223 | /// The section cannot be cached. 224 | pub const IMAGE_SCN_MEM_NOT_CACHED: u32 = 0x0400_0000; 225 | /// The section is not pageable. 226 | pub const IMAGE_SCN_MEM_NOT_PAGED: u32 = 0x0800_0000; 227 | /// The section can be shared in memory. 228 | pub const IMAGE_SCN_MEM_SHARED: u32 = 0x1000_0000; 229 | /// The section can be executed as code. 230 | pub const IMAGE_SCN_MEM_EXECUTE: u32 = 0x2000_0000; 231 | /// The section can be read. 232 | pub const IMAGE_SCN_MEM_READ: u32 = 0x4000_0000; 233 | /// The section can be written to. 234 | pub const IMAGE_SCN_MEM_WRITE: u32 = 0x8000_0000; 235 | 236 | #[cfg(test)] 237 | mod tests { 238 | use super::*; 239 | 240 | #[test] 241 | fn set_name_offset() { 242 | let mut section = SectionTable::default(); 243 | for &(offset, name) in [ 244 | (0usize, b"/0\0\0\0\0\0\0"), 245 | (1, b"/1\0\0\0\0\0\0"), 246 | (9_999_999, b"/9999999"), 247 | (10_000_000, b"//AAmJaA"), 248 | #[cfg(target_pointer_width = "64")] 249 | (0xfff_fff_fff, b"////////"), 250 | ].iter() { 251 | section.set_name_offset(offset).unwrap(); 252 | assert_eq!(§ion.name, name); 253 | assert_eq!(section.name_offset().unwrap(), Some(offset)); 254 | } 255 | #[cfg(target_pointer_width = "64")] 256 | assert!(section.set_name_offset(0x1_000_000_000).is_err()); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/pe/import.rs: -------------------------------------------------------------------------------- 1 | use crate::alloc::borrow::Cow; 2 | use crate::alloc::vec::Vec; 3 | use core::fmt::{LowerHex, Debug}; 4 | 5 | use scroll::{Pread, Pwrite, SizeWith}; 6 | use scroll::ctx::TryFromCtx; 7 | use crate::error; 8 | 9 | use crate::pe::section_table; 10 | use crate::pe::utils; 11 | use crate::pe::data_directories; 12 | 13 | use log::{debug, warn}; 14 | 15 | pub const IMPORT_BY_ORDINAL_32: u32 = 0x8000_0000; 16 | pub const IMPORT_BY_ORDINAL_64: u64 = 0x8000_0000_0000_0000; 17 | pub const IMPORT_RVA_MASK_32: u32 = 0x7fff_ffff; 18 | pub const IMPORT_RVA_MASK_64: u64 = 0x0000_0000_7fff_ffff; 19 | 20 | pub trait Bitfield<'a>: Into + PartialEq + Eq + LowerHex + Debug + TryFromCtx<'a, scroll::Endian, Error=scroll::Error> { 21 | fn is_ordinal(&self) -> bool; 22 | fn to_ordinal(&self) -> u16; 23 | fn to_rva(&self) -> u32; 24 | fn size_of() -> usize; 25 | fn is_zero(&self) -> bool; 26 | } 27 | 28 | impl<'a> Bitfield<'a> for u64 { 29 | fn is_ordinal(&self) -> bool { self & IMPORT_BY_ORDINAL_64 == IMPORT_BY_ORDINAL_64 } 30 | fn to_ordinal(&self) -> u16 { (0xffff & self) as u16 } 31 | fn to_rva(&self) -> u32 { (self & IMPORT_RVA_MASK_64) as u32 } 32 | fn size_of() -> usize { 8 } 33 | fn is_zero(&self) -> bool { *self == 0 } 34 | } 35 | 36 | impl<'a> Bitfield<'a> for u32 { 37 | fn is_ordinal(&self) -> bool { self & IMPORT_BY_ORDINAL_32 == IMPORT_BY_ORDINAL_32 } 38 | fn to_ordinal(&self) -> u16 { (0xffff & self) as u16 } 39 | fn to_rva(&self) -> u32 { (self & IMPORT_RVA_MASK_32) as u32 } 40 | fn size_of() -> usize { 4 } 41 | fn is_zero(&self) -> bool { *self == 0 } 42 | } 43 | 44 | #[derive(Debug, Clone)] 45 | pub struct HintNameTableEntry<'a> { 46 | pub hint: u16, 47 | pub name: &'a str, 48 | } 49 | 50 | impl<'a> HintNameTableEntry<'a> { 51 | fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result { 52 | let offset = &mut offset; 53 | let hint = bytes.gread_with(offset, scroll::LE)?; 54 | let name = bytes.pread::<&'a str>(*offset)?; 55 | Ok(HintNameTableEntry { hint, name }) 56 | } 57 | } 58 | 59 | #[derive(Debug, Clone)] 60 | pub enum SyntheticImportLookupTableEntry<'a> { 61 | OrdinalNumber(u16), 62 | HintNameTableRVA ((u32, HintNameTableEntry<'a>)), // [u8; 31] bitfield :/ 63 | } 64 | 65 | pub type ImportLookupTable<'a> = Vec>; 66 | 67 | impl<'a> SyntheticImportLookupTableEntry<'a> { 68 | pub fn parse>(bytes: &'a [u8], mut offset: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result> { 69 | let le = scroll::LE; 70 | let offset = &mut offset; 71 | let mut table = Vec::new(); 72 | loop { 73 | let bitfield: T = bytes.gread_with(offset, le)?; 74 | if bitfield.is_zero() { 75 | debug!("imports done"); 76 | break; 77 | } else { 78 | let entry = { 79 | debug!("bitfield {:#x}", bitfield); 80 | use self::SyntheticImportLookupTableEntry::*; 81 | if bitfield.is_ordinal() { 82 | let ordinal = bitfield.to_ordinal(); 83 | debug!("importing by ordinal {:#x}", ordinal); 84 | OrdinalNumber(ordinal) 85 | } else { 86 | let rva = bitfield.to_rva(); 87 | let hentry = { 88 | debug!("searching for RVA {:#x}", rva); 89 | if let Some(offset) = utils::find_offset(rva as usize, sections, file_alignment) { 90 | debug!("offset {:#x}", offset); 91 | HintNameTableEntry::parse(bytes, offset)? 92 | } else { 93 | warn!("Entry {} has bad RVA: {:#x}", table.len(), rva); 94 | continue 95 | } 96 | }; 97 | HintNameTableRVA ((rva, hentry)) 98 | } 99 | }; 100 | table.push(entry); 101 | } 102 | } 103 | Ok(table) 104 | } 105 | } 106 | 107 | // get until entry is 0 108 | pub type ImportAddressTable = Vec; 109 | 110 | #[repr(C)] 111 | #[derive(Debug)] 112 | #[derive(Pread, Pwrite, SizeWith)] 113 | pub struct ImportDirectoryEntry { 114 | pub import_lookup_table_rva: u32, 115 | pub time_date_stamp: u32, 116 | pub forwarder_chain: u32, 117 | pub name_rva: u32, 118 | pub import_address_table_rva: u32, 119 | } 120 | 121 | pub const SIZEOF_IMPORT_DIRECTORY_ENTRY: usize = 20; 122 | 123 | impl ImportDirectoryEntry { 124 | pub fn is_null (&self) -> bool { 125 | (self.import_lookup_table_rva == 0) && 126 | (self.time_date_stamp == 0) && 127 | (self.forwarder_chain == 0) && 128 | (self.name_rva == 0) && 129 | (self.import_address_table_rva == 0) 130 | } 131 | } 132 | 133 | #[derive(Debug)] 134 | pub struct SyntheticImportDirectoryEntry<'a> { 135 | pub import_directory_entry: ImportDirectoryEntry, 136 | /// Computed 137 | pub name: &'a str, 138 | /// The import lookup table is a vector of either ordinals, or RVAs + import names 139 | pub import_lookup_table: Option>, 140 | /// Computed 141 | pub import_address_table: ImportAddressTable, 142 | } 143 | 144 | impl<'a> SyntheticImportDirectoryEntry<'a> { 145 | pub fn parse>(bytes: &'a [u8], import_directory_entry: ImportDirectoryEntry, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result> { 146 | const LE: scroll::Endian = scroll::LE; 147 | let name_rva = import_directory_entry.name_rva; 148 | let name = utils::try_name(bytes, name_rva as usize, sections, file_alignment)?; 149 | let import_lookup_table = { 150 | let import_lookup_table_rva = import_directory_entry.import_lookup_table_rva; 151 | let import_address_table_rva = import_directory_entry.import_address_table_rva; 152 | if let Some(import_lookup_table_offset) = utils::find_offset(import_lookup_table_rva as usize, sections, file_alignment) { 153 | debug!("Synthesizing lookup table imports for {} lib, with import lookup table rva: {:#x}", name, import_lookup_table_rva); 154 | let import_lookup_table = SyntheticImportLookupTableEntry::parse::(bytes, import_lookup_table_offset, sections, file_alignment)?; 155 | debug!("Successfully synthesized import lookup table entry from lookup table: {:#?}", import_lookup_table); 156 | Some(import_lookup_table) 157 | } else if let Some(import_address_table_offset) = utils::find_offset(import_address_table_rva as usize, sections, file_alignment) { 158 | debug!("Synthesizing lookup table imports for {} lib, with import address table rva: {:#x}", name, import_lookup_table_rva); 159 | let import_address_table = SyntheticImportLookupTableEntry::parse::(bytes, import_address_table_offset, sections, file_alignment)?; 160 | debug!("Successfully synthesized import lookup table entry from IAT: {:#?}", import_address_table); 161 | Some(import_address_table) 162 | } else { 163 | None 164 | } 165 | }; 166 | 167 | let import_address_table_offset = &mut utils::find_offset(import_directory_entry.import_address_table_rva as usize, sections, file_alignment).ok_or_else(|| error::Error::Malformed(format!("Cannot map import_address_table_rva {:#x} into offset for {}", import_directory_entry.import_address_table_rva, name)))?; 168 | let mut import_address_table = Vec::new(); 169 | loop { 170 | let import_address = bytes.gread_with::(import_address_table_offset, LE)?.into(); 171 | if import_address == 0 { break } else { import_address_table.push(import_address); } 172 | } 173 | Ok(SyntheticImportDirectoryEntry { 174 | import_directory_entry, 175 | name, 176 | import_lookup_table, 177 | import_address_table 178 | }) 179 | } 180 | } 181 | 182 | #[derive(Debug)] 183 | /// Contains a list of synthesized import data for this binary, e.g., which symbols from which libraries it is importing from 184 | pub struct ImportData<'a> { 185 | pub import_data: Vec>, 186 | } 187 | 188 | impl<'a> ImportData<'a> { 189 | pub fn parse>(bytes: &'a[u8], dd: data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result> { 190 | let import_directory_table_rva = dd.virtual_address as usize; 191 | debug!("import_directory_table_rva {:#x}", import_directory_table_rva); 192 | let offset = &mut utils::find_offset(import_directory_table_rva, sections, file_alignment).ok_or_else(|| error::Error::Malformed(format!("Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset", import_directory_table_rva)))?; 193 | debug!("import data offset {:#x}", offset); 194 | let mut import_data = Vec::new(); 195 | loop { 196 | let import_directory_entry: ImportDirectoryEntry = bytes.gread_with(offset, scroll::LE)?; 197 | debug!("{:#?}", import_directory_entry); 198 | if import_directory_entry.is_null() { 199 | break; 200 | } else { 201 | let entry = SyntheticImportDirectoryEntry::parse::(bytes, import_directory_entry, sections, file_alignment)?; 202 | debug!("entry {:#?}", entry); 203 | import_data.push(entry); 204 | } 205 | } 206 | debug!("finished ImportData"); 207 | Ok(ImportData { import_data}) 208 | } 209 | } 210 | 211 | #[derive(Debug)] 212 | /// A synthesized symbol import, the name is pre-indexed, and the binary offset is computed, as well as which dll it belongs to 213 | pub struct Import<'a> { 214 | pub name: Cow<'a, str>, 215 | pub dll: &'a str, 216 | pub ordinal: u16, 217 | pub offset: usize, 218 | pub rva: usize, 219 | pub size: usize, 220 | } 221 | 222 | impl<'a> Import<'a> { 223 | pub fn parse>(_bytes: &'a [u8], import_data: &ImportData<'a>, _sections: &[section_table::SectionTable]) -> error::Result>> { 224 | let mut imports = Vec::new(); 225 | for data in &import_data.import_data { 226 | if let Some(ref import_lookup_table) = data.import_lookup_table { 227 | let dll = data.name; 228 | let import_base = data.import_directory_entry.import_address_table_rva as usize; 229 | debug!("Getting imports from {}", &dll); 230 | for (i, entry) in import_lookup_table.iter().enumerate() { 231 | let offset = import_base + (i * T::size_of()); 232 | use self::SyntheticImportLookupTableEntry::*; 233 | let (rva, name, ordinal) = 234 | match *entry { 235 | HintNameTableRVA ((rva, ref hint_entry)) => { 236 | // if hint_entry.name = "" && hint_entry.hint = 0 { 237 | // println!(" warning hint/name table rva from {} without hint {:#x}", dll, rva); 238 | // } 239 | (rva, Cow::Borrowed(hint_entry.name), hint_entry.hint) 240 | }, 241 | OrdinalNumber(ordinal) => { 242 | let name = format!("ORDINAL {}", ordinal); 243 | (0x0, Cow::Owned(name), ordinal) 244 | }, 245 | }; 246 | let import = 247 | Import { 248 | name, 249 | ordinal, dll, 250 | size: T::size_of(), offset, rva: rva as usize 251 | }; 252 | imports.push(import); 253 | } 254 | } 255 | } 256 | Ok (imports) 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/pe/optional_header.rs: -------------------------------------------------------------------------------- 1 | use crate::container; 2 | use crate::error; 3 | 4 | use crate::pe::data_directories; 5 | 6 | use scroll::{ctx, Endian, LE}; 7 | use scroll::{Pread, Pwrite, SizeWith}; 8 | 9 | /// standard COFF fields 10 | #[repr(C)] 11 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 12 | #[derive(Pread, Pwrite, SizeWith)] 13 | pub struct StandardFields32 { 14 | pub magic: u16, 15 | pub major_linker_version: u8, 16 | pub minor_linker_version: u8, 17 | pub size_of_code: u32, 18 | pub size_of_initialized_data: u32, 19 | pub size_of_uninitialized_data: u32, 20 | pub address_of_entry_point: u32, 21 | pub base_of_code: u32, 22 | /// absent in 64-bit PE32+ 23 | pub base_of_data: u32, 24 | } 25 | 26 | pub const SIZEOF_STANDARD_FIELDS_32: usize = 28; 27 | 28 | /// standard 64-bit COFF fields 29 | #[repr(C)] 30 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 31 | #[derive(Pread, Pwrite, SizeWith)] 32 | pub struct StandardFields64 { 33 | pub magic: u16, 34 | pub major_linker_version: u8, 35 | pub minor_linker_version: u8, 36 | pub size_of_code: u32, 37 | pub size_of_initialized_data: u32, 38 | pub size_of_uninitialized_data: u32, 39 | pub address_of_entry_point: u32, 40 | pub base_of_code: u32, 41 | } 42 | 43 | pub const SIZEOF_STANDARD_FIELDS_64: usize = 24; 44 | 45 | /// Unified 32/64-bit COFF fields 46 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 47 | pub struct StandardFields { 48 | pub magic: u16, 49 | pub major_linker_version: u8, 50 | pub minor_linker_version: u8, 51 | pub size_of_code: u64, 52 | pub size_of_initialized_data: u64, 53 | pub size_of_uninitialized_data: u64, 54 | pub address_of_entry_point: u64, 55 | pub base_of_code: u64, 56 | /// absent in 64-bit PE32+ 57 | pub base_of_data: u32, 58 | } 59 | 60 | impl From for StandardFields { 61 | fn from(fields: StandardFields32) -> Self { 62 | StandardFields { 63 | magic: fields.magic, 64 | major_linker_version: fields.major_linker_version, 65 | minor_linker_version: fields.minor_linker_version, 66 | size_of_code: u64::from(fields.size_of_code), 67 | size_of_initialized_data: u64::from(fields.size_of_initialized_data), 68 | size_of_uninitialized_data: u64::from(fields.size_of_uninitialized_data), 69 | address_of_entry_point: u64::from(fields.address_of_entry_point), 70 | base_of_code: u64::from(fields.base_of_code), 71 | base_of_data: fields.base_of_data, 72 | } 73 | } 74 | } 75 | 76 | impl From for StandardFields { 77 | fn from(fields: StandardFields64) -> Self { 78 | StandardFields { 79 | magic: fields.magic, 80 | major_linker_version: fields.major_linker_version, 81 | minor_linker_version: fields.minor_linker_version, 82 | size_of_code: u64::from(fields.size_of_code), 83 | size_of_initialized_data: u64::from(fields.size_of_initialized_data), 84 | size_of_uninitialized_data: u64::from(fields.size_of_uninitialized_data), 85 | address_of_entry_point: u64::from(fields.address_of_entry_point), 86 | base_of_code: u64::from(fields.base_of_code), 87 | base_of_data: 0, 88 | } 89 | } 90 | } 91 | 92 | /// Standard fields magic number for 32-bit binary 93 | pub const MAGIC_32: u16 = 0x10b; 94 | /// Standard fields magic number for 64-bit binary 95 | pub const MAGIC_64: u16 = 0x20b; 96 | 97 | /// Windows specific fields 98 | #[repr(C)] 99 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 100 | #[derive(Pread, Pwrite, SizeWith)] 101 | pub struct WindowsFields32 { 102 | pub image_base: u32, 103 | pub section_alignment: u32, 104 | pub file_alignment: u32, 105 | pub major_operating_system_version: u16, 106 | pub minor_operating_system_version: u16, 107 | pub major_image_version: u16, 108 | pub minor_image_version: u16, 109 | pub major_subsystem_version: u16, 110 | pub minor_subsystem_version: u16, 111 | pub win32_version_value: u32, 112 | pub size_of_image: u32, 113 | pub size_of_headers: u32, 114 | pub check_sum: u32, 115 | pub subsystem: u16, 116 | pub dll_characteristics: u16, 117 | pub size_of_stack_reserve: u32, 118 | pub size_of_stack_commit: u32, 119 | pub size_of_heap_reserve: u32, 120 | pub size_of_heap_commit: u32, 121 | pub loader_flags: u32, 122 | pub number_of_rva_and_sizes: u32, 123 | } 124 | 125 | pub const SIZEOF_WINDOWS_FIELDS_32: usize = 68; 126 | 127 | /// 64-bit Windows specific fields 128 | #[repr(C)] 129 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 130 | #[derive(Pread, Pwrite, SizeWith)] 131 | pub struct WindowsFields64 { 132 | pub image_base: u64, 133 | pub section_alignment: u32, 134 | pub file_alignment: u32, 135 | pub major_operating_system_version: u16, 136 | pub minor_operating_system_version: u16, 137 | pub major_image_version: u16, 138 | pub minor_image_version: u16, 139 | pub major_subsystem_version: u16, 140 | pub minor_subsystem_version: u16, 141 | pub win32_version_value: u32, 142 | pub size_of_image: u32, 143 | pub size_of_headers: u32, 144 | pub check_sum: u32, 145 | pub subsystem: u16, 146 | pub dll_characteristics: u16, 147 | pub size_of_stack_reserve: u64, 148 | pub size_of_stack_commit: u64, 149 | pub size_of_heap_reserve: u64, 150 | pub size_of_heap_commit: u64, 151 | pub loader_flags: u32, 152 | pub number_of_rva_and_sizes: u32, 153 | } 154 | 155 | pub const SIZEOF_WINDOWS_FIELDS_64: usize = 88; 156 | 157 | // /// Generic 32/64-bit Windows specific fields 158 | // #[derive(Debug, PartialEq, Copy, Clone, Default)] 159 | // pub struct WindowsFields { 160 | // pub image_base: u64, 161 | // pub section_alignment: u32, 162 | // pub file_alignment: u32, 163 | // pub major_operating_system_version: u16, 164 | // pub minor_operating_system_version: u16, 165 | // pub major_image_version: u16, 166 | // pub minor_image_version: u16, 167 | // pub major_subsystem_version: u16, 168 | // pub minor_subsystem_version: u16, 169 | // pub win32_version_value: u32, 170 | // pub size_of_image: u32, 171 | // pub size_of_headers: u32, 172 | // pub check_sum: u32, 173 | // pub subsystem: u16, 174 | // pub dll_characteristics: u16, 175 | // pub size_of_stack_reserve: u64, 176 | // pub size_of_stack_commit: u64, 177 | // pub size_of_heap_reserve: u64, 178 | // pub size_of_heap_commit: u64, 179 | // pub loader_flags: u32, 180 | // pub number_of_rva_and_sizes: u32, 181 | // } 182 | 183 | impl From for WindowsFields { 184 | fn from(windows: WindowsFields32) -> Self { 185 | WindowsFields { 186 | image_base: u64::from(windows.image_base), 187 | section_alignment: windows.section_alignment, 188 | file_alignment: windows.file_alignment, 189 | major_operating_system_version: windows.major_operating_system_version, 190 | minor_operating_system_version: windows.minor_operating_system_version, 191 | major_image_version: windows.major_image_version, 192 | minor_image_version: windows.minor_image_version, 193 | major_subsystem_version: windows.major_subsystem_version, 194 | minor_subsystem_version: windows.minor_subsystem_version, 195 | win32_version_value: windows.win32_version_value, 196 | size_of_image: windows.size_of_image, 197 | size_of_headers: windows.size_of_headers, 198 | check_sum: windows.check_sum, 199 | subsystem: windows.subsystem, 200 | dll_characteristics: windows.dll_characteristics, 201 | size_of_stack_reserve: u64::from(windows.size_of_stack_reserve), 202 | size_of_stack_commit: u64::from(windows.size_of_stack_commit), 203 | size_of_heap_reserve: u64::from(windows.size_of_heap_reserve), 204 | size_of_heap_commit: u64::from(windows.size_of_heap_commit), 205 | loader_flags: windows.loader_flags, 206 | number_of_rva_and_sizes: windows.number_of_rva_and_sizes, 207 | } 208 | } 209 | } 210 | 211 | // impl From for WindowsFields { 212 | // fn from(windows: WindowsFields32) -> Self { 213 | // WindowsFields { 214 | // image_base: windows.image_base, 215 | // section_alignment: windows.section_alignment, 216 | // file_alignment: windows.file_alignment, 217 | // major_operating_system_version: windows.major_operating_system_version, 218 | // minor_operating_system_version: windows.minor_operating_system_version, 219 | // major_image_version: windows.major_image_version, 220 | // minor_image_version: windows.minor_image_version, 221 | // major_subsystem_version: windows.major_subsystem_version, 222 | // minor_subsystem_version: windows.minor_subsystem_version, 223 | // win32_version_value: windows.win32_version_value, 224 | // size_of_image: windows.size_of_image, 225 | // size_of_headers: windows.size_of_headers, 226 | // check_sum: windows.check_sum, 227 | // subsystem: windows.subsystem, 228 | // dll_characteristics: windows.dll_characteristics, 229 | // size_of_stack_reserve: windows.size_of_stack_reserve, 230 | // size_of_stack_commit: windows.size_of_stack_commit, 231 | // size_of_heap_reserve: windows.size_of_heap_reserve, 232 | // size_of_heap_commit: windows.size_of_heap_commit, 233 | // loader_flags: windows.loader_flags, 234 | // number_of_rva_and_sizes: windows.number_of_rva_and_sizes, 235 | // } 236 | // } 237 | // } 238 | 239 | pub type WindowsFields = WindowsFields64; 240 | 241 | #[derive(Debug, PartialEq, Copy, Clone)] 242 | pub struct OptionalHeader { 243 | pub standard_fields: StandardFields, 244 | pub windows_fields: WindowsFields, 245 | pub data_directories: data_directories::DataDirectories 246 | } 247 | 248 | impl OptionalHeader { 249 | pub fn container(&self) -> error::Result { 250 | match self.standard_fields.magic { 251 | MAGIC_32 => { 252 | Ok(container::Container::Little) 253 | }, 254 | MAGIC_64 => { 255 | Ok(container::Container::Big) 256 | }, 257 | magic => { 258 | Err(error::Error::BadMagic(u64::from(magic))) 259 | } 260 | } 261 | } 262 | } 263 | 264 | impl<'a> ctx::TryFromCtx<'a, Endian> for OptionalHeader { 265 | type Error = crate::error::Error; 266 | fn try_from_ctx(bytes: &'a [u8], _: Endian) -> error::Result<(Self, usize)> { 267 | let magic = bytes.pread_with::(0, LE)?; 268 | let offset = &mut 0; 269 | let (standard_fields, windows_fields): (StandardFields, WindowsFields) = match magic { 270 | MAGIC_32 => { 271 | let standard_fields = bytes.gread_with::(offset, LE)?.into(); 272 | let windows_fields = bytes.gread_with::(offset, LE)?.into(); 273 | (standard_fields, windows_fields) 274 | }, 275 | MAGIC_64 => { 276 | let standard_fields = bytes.gread_with::(offset, LE)?.into(); 277 | let windows_fields = bytes.gread_with::(offset, LE)?; 278 | (standard_fields, windows_fields) 279 | }, 280 | _ => return Err(error::Error::BadMagic(u64::from(magic))) 281 | }; 282 | let data_directories = data_directories::DataDirectories::parse(&bytes, windows_fields.number_of_rva_and_sizes as usize, offset)?; 283 | Ok ((OptionalHeader { 284 | standard_fields, 285 | windows_fields, 286 | data_directories, 287 | }, 0)) // TODO: FIXME 288 | } 289 | } 290 | 291 | #[cfg(test)] 292 | mod tests { 293 | use super::*; 294 | #[test] 295 | fn sizeof_standards32() { 296 | assert_eq!(::std::mem::size_of::(), SIZEOF_STANDARD_FIELDS_32); 297 | } 298 | #[test] 299 | fn sizeof_windows32() { 300 | assert_eq!(::std::mem::size_of::(), SIZEOF_WINDOWS_FIELDS_32); 301 | } 302 | #[test] 303 | fn sizeof_standards64() { 304 | assert_eq!(::std::mem::size_of::(), SIZEOF_STANDARD_FIELDS_64); 305 | } 306 | #[test] 307 | fn sizeof_windows64() { 308 | assert_eq!(::std::mem::size_of::(), SIZEOF_WINDOWS_FIELDS_64); 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /src/mach/exports.rs: -------------------------------------------------------------------------------- 1 | //! Symbols exported by this binary and available for dynamic linking are encoded in mach-o binaries using a special trie 2 | //! 3 | //! **Note**: the trie is constructed lazily in case it won't be used, and since computing exports will require allocation, to compute the exports, you need call the export trie's [exports()](struct.ExportTrie.html#method.exports) method. 4 | 5 | // TODO: 6 | // (1) Weak of regular_symbol_info type probably needs to be added ? 7 | // (3) /usr/lib/libstdc++.6.0.9.dylib has flag 0xc at many offsets... they're weak 8 | 9 | use core::ops::Range; 10 | use scroll::{Pread, Uleb128}; 11 | use crate::error; 12 | use core::fmt::{self, Debug}; 13 | use crate::mach::load_command; 14 | use crate::alloc::vec::Vec; 15 | use crate::alloc::string::String; 16 | 17 | type Flag = u64; 18 | 19 | // "The following are used on the flags byte of a terminal node 20 | // in the export information." 21 | pub const EXPORT_SYMBOL_FLAGS_KIND_MASK : Flag = 0x03; 22 | pub const EXPORT_SYMBOL_FLAGS_KIND_REGULAR : Flag = 0x00; 23 | pub const EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE : Flag = 0x02; // this is a symbol not present in the loader.h but only in the dyld compressed image loader source code, and only available with a #def macro for export flags but libobjc. def has this 24 | pub const EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL : Flag = 0x01; 25 | pub const EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION : Flag = 0x04; 26 | pub const EXPORT_SYMBOL_FLAGS_REEXPORT : Flag = 0x08; 27 | pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER : Flag = 0x10; 28 | 29 | #[derive(Debug)] 30 | pub enum SymbolKind { 31 | Regular, 32 | Absolute, 33 | ThreadLocal, 34 | UnknownSymbolKind(Flag), 35 | } 36 | 37 | impl SymbolKind { 38 | pub fn new(kind: Flag) -> SymbolKind { 39 | match kind & EXPORT_SYMBOL_FLAGS_KIND_MASK { 40 | 0x00 => SymbolKind::Regular, 41 | 0x01 => SymbolKind::ThreadLocal, 42 | 0x02 => SymbolKind::Absolute, 43 | _ => SymbolKind::UnknownSymbolKind(kind), 44 | } 45 | } 46 | pub fn to_str(&self) -> &'static str { 47 | match self { 48 | SymbolKind::Regular => "Regular", 49 | SymbolKind::Absolute => "Absolute", 50 | SymbolKind::ThreadLocal => "Thread_LOCAL", 51 | SymbolKind::UnknownSymbolKind(_k) => "Unknown", 52 | } 53 | } 54 | } 55 | 56 | #[derive(Debug)] 57 | /// An export can be a regular export, a re-export, or a stub 58 | pub enum ExportInfo<'a> { 59 | /// A regular exported symbol, which is an address where it is found, and the flags associated with it 60 | Regular { 61 | address: u64, 62 | flags: Flag, 63 | }, 64 | /// if lib_symbol_name None then same symbol name, otherwise reexport of lib_symbol_name with name in the trie 65 | /// "If the string is zero length, then the symbol is re-export from the specified dylib with the same name" 66 | Reexport { 67 | lib: &'a str, 68 | lib_symbol_name: Option<&'a str>, 69 | flags: Flag, 70 | }, 71 | /// If the flags is `EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER`, then following the flags are two `Uleb128`s: the stub offset and the resolver offset. The stub is used by non-lazy pointers. The resolver is used by lazy pointers and must be called to get the actual address to use 72 | Stub { 73 | stub_offset: scroll::Uleb128, 74 | resolver_offset: scroll::Uleb128, 75 | flags: Flag, 76 | }, 77 | } 78 | 79 | impl<'a> ExportInfo<'a> { 80 | /// Parse out the export info from `bytes`, at `offset` 81 | pub fn parse(bytes: &'a [u8], libs: &[&'a str], flags: Flag, mut offset: usize) -> error::Result> { 82 | use self::ExportInfo::*; 83 | let regular = |offset| -> error::Result { 84 | let address = bytes.pread::(offset)?; 85 | Ok(Regular { 86 | address: address.into(), 87 | flags 88 | }) 89 | }; 90 | let reexport = |mut offset| -> error::Result> { 91 | let lib_ordinal: u64 = { 92 | let tmp = bytes.pread::(offset)?; 93 | offset += tmp.size(); 94 | tmp.into() 95 | }; 96 | let lib_symbol_name = bytes.pread::<&str>(offset)?; 97 | let lib = libs[lib_ordinal as usize]; 98 | let lib_symbol_name = if lib_symbol_name == "" { None } else { Some (lib_symbol_name)}; 99 | Ok(Reexport { 100 | lib, 101 | lib_symbol_name, 102 | flags 103 | }) 104 | }; 105 | match SymbolKind::new(flags) { 106 | SymbolKind::Regular => { 107 | if flags & EXPORT_SYMBOL_FLAGS_REEXPORT != 0 { 108 | reexport(offset) 109 | } else if flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER != 0 { // 0x10 110 | let stub_offset = bytes.pread::(offset)?; 111 | offset += stub_offset.size(); 112 | let resolver_offset = bytes.pread::(offset)?; 113 | Ok(Stub { 114 | stub_offset, 115 | resolver_offset, 116 | flags 117 | }) 118 | // else if (flags = kEXPORT_SYMBOL_FLAGS_WEAK_DEFINITION) then (*0x40 unused*) 119 | } else { 120 | regular(offset) 121 | } 122 | }, 123 | SymbolKind::ThreadLocal | SymbolKind::Absolute => { 124 | if flags & EXPORT_SYMBOL_FLAGS_REEXPORT != 0 { 125 | reexport(offset) 126 | } else { 127 | regular(offset) 128 | } 129 | }, 130 | SymbolKind::UnknownSymbolKind(_kind) => { 131 | // 0x5f causes errors, but parsing as regular symbol resolves... 132 | //Err(error::Error::Malformed(format!("Unknown kind {:#x} from flags {:#x} in get_symbol_type at offset {}", kind, flags, offset))) 133 | regular(offset) 134 | } 135 | } 136 | } 137 | } 138 | 139 | #[derive(Debug)] 140 | /// A finalized symbolic export reconstructed from the export trie 141 | pub struct Export<'a> { 142 | /// The reconsituted export name which dyld matches against 143 | pub name: String, 144 | /// The export info in the node data 145 | pub info: ExportInfo<'a>, 146 | /// How large this export is 147 | pub size: usize, 148 | /// The offset this symbol export is found in the binary 149 | pub offset: u64, 150 | } 151 | 152 | impl<'a> Export<'a> { 153 | /// Create a new export from `name` and `info` 154 | pub fn new(name: String, info: ExportInfo<'a>) -> Export<'a> { 155 | let offset = match info { 156 | ExportInfo::Regular { address, .. } => address, 157 | _ => 0x0, 158 | }; 159 | Export { name, info, size: 0, offset } 160 | } 161 | } 162 | 163 | /// An export trie efficiently encodes all of the symbols exported by this binary for dynamic linking 164 | pub struct ExportTrie<'a> { 165 | data: &'a [u8], 166 | location: Range, 167 | } 168 | 169 | impl<'a> ExportTrie<'a> { 170 | 171 | #[inline] 172 | fn walk_nodes(&self, libs: &[&'a str], branches: Vec<(String, usize)>, acc: &mut Vec>) -> error::Result<()> { 173 | for (symbol, next_node) in branches { 174 | self.walk_trie(libs, symbol, next_node, acc)?; 175 | } 176 | Ok(()) 177 | } 178 | 179 | // current_symbol can be a str iiuc 180 | fn walk_branches(&self, nbranches: usize, current_symbol: String, mut offset: usize) -> error::Result> { 181 | let mut branches = Vec::with_capacity(nbranches); 182 | //println!("\t@{:#x}", *offset); 183 | for _i in 0..nbranches { 184 | // additional offset calculations are relative to the base we received 185 | let offset = &mut offset; 186 | let string = self.data.pread::<&str>(*offset)?; 187 | let mut key = current_symbol.clone(); 188 | key.push_str(string); 189 | // +1 for null terminator 190 | *offset = *offset + string.len() + 1; 191 | //println!("\t({}) string_len: {} offset: {:#x}", i, string.len(), *offset); 192 | // value is relative to export trie base 193 | let next_node = Uleb128::read(&self.data, offset)? as usize + self.location.start; 194 | //println!("\t({}) string: {} next_node: {:#x}", _i, key, next_node); 195 | branches.push((key, next_node)); 196 | } 197 | Ok(branches) 198 | } 199 | 200 | fn walk_trie(&self, libs: &[&'a str], current_symbol: String, start: usize, exports: &mut Vec>) -> error::Result<()> { 201 | if start < self.location.end { 202 | let mut offset = start; 203 | let terminal_size = Uleb128::read(&self.data, &mut offset)?; 204 | // let mut input = String::new(); 205 | // ::std::io::stdin().read_line(&mut input).unwrap(); 206 | // println!("@ {:#x} node: {:#x} current_symbol: {}", start, terminal_size, current_symbol); 207 | if terminal_size == 0 { 208 | let nbranches = Uleb128::read(&self.data, &mut offset)? as usize; 209 | //println!("\t@ {:#x} BRAN {}", *offset, nbranches); 210 | let branches = self.walk_branches(nbranches, current_symbol, offset)?; 211 | self.walk_nodes(libs, branches, exports) 212 | } else { // terminal node, but the tricky part is that they can have children... 213 | let pos = offset; 214 | let children_start = &mut (pos + terminal_size as usize); 215 | let nchildren = Uleb128::read(&self.data, children_start)? as usize; 216 | let flags = Uleb128::read(&self.data, &mut offset)?; 217 | //println!("\t@ {:#x} TERM {} flags: {:#x}", offset, nchildren, flags); 218 | let info = ExportInfo::parse(&self.data, libs, flags, offset)?; 219 | let export = Export::new(current_symbol.clone(), info); 220 | //println!("\t{:?}", &export); 221 | exports.push(export); 222 | if nchildren == 0 { 223 | // this branch is done 224 | Ok(()) 225 | } else { 226 | // more branches to walk 227 | let branches = self.walk_branches(nchildren, current_symbol, *children_start)?; 228 | self.walk_nodes(libs, branches, exports) 229 | } 230 | } 231 | } else { Ok(()) } 232 | } 233 | 234 | /// Walk the export trie for symbols exported by this binary, using the provided `libs` to resolve re-exports 235 | pub fn exports(&self, libs: &[&'a str]) -> error::Result>> { 236 | let offset = self.location.start; 237 | let current_symbol = String::new(); 238 | let mut exports = Vec::new(); 239 | self.walk_trie(libs, current_symbol, offset, &mut exports)?; 240 | Ok(exports) 241 | } 242 | 243 | /// Create a new, lazy, zero-copy export trie from the `DyldInfo` `command` 244 | pub fn new(bytes: &'a [u8], command: &load_command::DyldInfoCommand) -> Self { 245 | let start = command.export_off as usize; 246 | let end = (command.export_size + command.export_off) as usize; 247 | ExportTrie { 248 | data: bytes, 249 | location: start..end, 250 | } 251 | } 252 | } 253 | 254 | impl<'a> Debug for ExportTrie<'a> { 255 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 256 | fmt.debug_struct("ExportTrie") 257 | .field("data", &"<... redacted ...>") 258 | .field("location", &format_args!("{:#x}..{:#x}", self.location.start, self.location.end)) 259 | .finish() 260 | } 261 | } 262 | 263 | #[cfg(test)] 264 | mod tests { 265 | use super::*; 266 | #[test] 267 | fn export_trie () { 268 | const EXPORTS: [u8; 64] = [0x00,0x01,0x5f,0x00,0x05,0x00,0x02,0x5f,0x6d,0x68,0x5f,0x65,0x78,0x65,0x63,0x75,0x74,0x65,0x5f,0x68,0x65,0x61,0x64,0x65,0x72,0x00,0x1f,0x6d,0x61,0x00,0x23,0x02,0x00,0x00,0x00,0x00,0x02,0x78,0x69,0x6d,0x75,0x6d,0x00,0x30,0x69,0x6e,0x00,0x35,0x03,0x00,0xc0,0x1e,0x00,0x03,0x00,0xd0,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00]; 269 | let exports = &EXPORTS[..]; 270 | let libs = vec!["/usr/lib/libderp.so", "/usr/lib/libthuglife.so"]; 271 | let mut command = load_command::DyldInfoCommand::default(); 272 | command.export_size = exports.len() as u32; 273 | let trie = ExportTrie::new(&exports, &command); 274 | println!("trie: {:#?}", &trie); 275 | let exports = trie.exports(&libs).unwrap(); 276 | println!("len: {} exports: {:#?}", exports.len(), &exports); 277 | assert_eq!(exports.len() as usize, 3usize) 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/mach/imports.rs: -------------------------------------------------------------------------------- 1 | //! Dynamically linked symbolic imports 2 | 3 | // table of tuples: 4 | // 5 | // symbol flags are undocumented 6 | 7 | use core::ops::Range; 8 | use core::fmt::{self, Debug}; 9 | use scroll::{Sleb128, Uleb128, Pread}; 10 | use crate::alloc::vec::Vec; 11 | 12 | use crate::container; 13 | use crate::error; 14 | use crate::mach::load_command; 15 | use crate::mach::bind_opcodes; 16 | use crate::mach::segment; 17 | 18 | #[derive(Debug)] 19 | /// Import binding information generated by running the Finite State Automaton programmed via `bind_opcodes` 20 | struct BindInformation<'a> { 21 | seg_index: u8, 22 | seg_offset: u64, 23 | bind_type: u8, 24 | symbol_library_ordinal: u8, 25 | symbol_name: &'a str, 26 | symbol_flags: u8, 27 | addend: i64, 28 | special_dylib: u8, // seeing self = 0 assuming this means the symbol is imported from itself, because its... libSystem.B.dylib? 29 | is_lazy: bool, 30 | } 31 | 32 | impl<'a> BindInformation<'a> { 33 | pub fn new (is_lazy: bool) -> Self { 34 | let mut bind_info = BindInformation::default(); 35 | if is_lazy { 36 | bind_info.is_lazy = true; 37 | bind_info.bind_type = bind_opcodes::BIND_TYPE_POINTER; 38 | } 39 | bind_info 40 | } 41 | pub fn is_weak(&self) -> bool { 42 | self.symbol_flags & bind_opcodes::BIND_SYMBOL_FLAGS_WEAK_IMPORT != 0 43 | } 44 | } 45 | 46 | impl<'a> Default for BindInformation<'a> { 47 | fn default() -> Self { 48 | BindInformation { 49 | seg_index: 0, 50 | seg_offset: 0x0, 51 | bind_type: 0x0, 52 | special_dylib: 1, 53 | symbol_library_ordinal: 0, 54 | symbol_name: "", 55 | symbol_flags: 0, 56 | addend: 0, 57 | is_lazy: false 58 | } 59 | } 60 | } 61 | 62 | #[derive(Debug)] 63 | /// An dynamically linked symbolic import 64 | pub struct Import<'a> { 65 | /// The symbol name dyld uses to resolve this import 66 | pub name: &'a str, 67 | /// The library this symbol belongs to (thanks to two-level namespaces) 68 | pub dylib: &'a str, 69 | /// Whether the symbol is lazily resolved or not 70 | pub is_lazy: bool, 71 | /// The offset in the binary this import is found 72 | pub offset: u64, 73 | /// The size of this import 74 | pub size: usize, 75 | /// The virtual memory address at which this import is found 76 | pub address: u64, 77 | /// The addend of this import 78 | pub addend: i64, 79 | /// Whether this import is weak 80 | pub is_weak: bool, 81 | /// The offset in the stream of bind opcodes that caused this import 82 | pub start_of_sequence_offset: u64 83 | } 84 | 85 | impl<'a> Import<'a> { 86 | /// Create a new import from the import binding information in `bi` 87 | fn new(bi: &BindInformation<'a>, libs: &[&'a str], segments: &[segment::Segment], start_of_sequence_offset: usize) -> Import<'a> { 88 | let (offset, address) = { 89 | let segment = &segments[bi.seg_index as usize]; 90 | ( 91 | segment.fileoff + bi.seg_offset, 92 | segment.vmaddr + bi.seg_offset 93 | ) 94 | }; 95 | let size = if bi.is_lazy { 8 } else { 0 }; 96 | Import { 97 | name: bi.symbol_name, 98 | dylib: libs[bi.symbol_library_ordinal as usize], 99 | is_lazy: bi.is_lazy, 100 | offset, 101 | size, 102 | address, 103 | addend: bi.addend, 104 | is_weak: bi.is_weak(), 105 | start_of_sequence_offset: start_of_sequence_offset as u64 106 | } 107 | } 108 | } 109 | 110 | /// An interpreter for mach BIND opcodes. 111 | /// Runs on prebound (non lazy) symbols (usually dylib extern consts and extern variables), 112 | /// and lazy symbols (usually dylib functions) 113 | pub struct BindInterpreter<'a> { 114 | data: &'a [u8], 115 | location: Range, 116 | lazy_location: Range, 117 | } 118 | 119 | impl<'a> Debug for BindInterpreter<'a> { 120 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 121 | fmt.debug_struct("BindInterpreter") 122 | .field("data", &"<... redacted ...>") 123 | .field("location", &format_args!("{:#x}..{:#x}", self.location.start, self.location.end)) 124 | .field("lazy_location", &format_args!("{:#x}..{:#x}", self.lazy_location.start, self.lazy_location.end)) 125 | .finish() 126 | } 127 | } 128 | 129 | 130 | impl<'a> BindInterpreter<'a> { 131 | /// Construct a new import binding interpreter from `bytes` and the load `command` 132 | pub fn new(bytes: &'a [u8], command: &load_command::DyldInfoCommand) -> Self { 133 | let get_pos = |off: u32, size: u32| -> Range { 134 | off as usize..(off + size) as usize 135 | }; 136 | let location = get_pos(command.bind_off, command.bind_size); 137 | let lazy_location = get_pos(command.lazy_bind_off, command.lazy_bind_size); 138 | BindInterpreter { 139 | data: bytes, 140 | location, 141 | lazy_location, 142 | } 143 | } 144 | /// Return the imports in this binary 145 | pub fn imports(&self, libs: &[&'a str], segments: &[segment::Segment], ctx: container::Ctx) -> error::Result>>{ 146 | let mut imports = Vec::new(); 147 | self.run(false, libs, segments, ctx, &mut imports)?; 148 | self.run( true, libs, segments, ctx, &mut imports)?; 149 | Ok(imports) 150 | } 151 | fn run(&self, is_lazy: bool, libs: &[&'a str], segments: &[segment::Segment], ctx: container::Ctx, imports: &mut Vec>) -> error::Result<()>{ 152 | use crate::mach::bind_opcodes::*; 153 | let location = if is_lazy { 154 | &self.lazy_location 155 | } else { 156 | &self.location 157 | }; 158 | let mut bind_info = BindInformation::new(is_lazy); 159 | let mut offset = location.start; 160 | let mut start_of_sequence: usize = 0; 161 | while offset < location.end { 162 | let opcode = self.data.gread::(&mut offset)? as bind_opcodes::Opcode; 163 | // let mut input = String::new(); 164 | // ::std::io::stdin().read_line(&mut input).unwrap(); 165 | // println!("opcode: {} ({:#x}) offset: {:#x}\n {:?}", opcode_to_str(opcode & BIND_OPCODE_MASK), opcode, offset - location.start - 1, &bind_info); 166 | match opcode & BIND_OPCODE_MASK { 167 | // we do nothing, don't update our records, and add a new, fresh record 168 | BIND_OPCODE_DONE => { 169 | bind_info = BindInformation::new(is_lazy); 170 | start_of_sequence = offset - location.start; 171 | }, 172 | BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => { 173 | let symbol_library_ordinal = opcode & BIND_IMMEDIATE_MASK; 174 | bind_info.symbol_library_ordinal = symbol_library_ordinal; 175 | }, 176 | BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => { 177 | let symbol_library_ordinal = Uleb128::read(&self.data, &mut offset)?; 178 | bind_info.symbol_library_ordinal = symbol_library_ordinal as u8; 179 | }, 180 | BIND_OPCODE_SET_DYLIB_SPECIAL_IMM => { 181 | // dyld puts the immediate into the symbol_library_ordinal field... 182 | let special_dylib = opcode & BIND_IMMEDIATE_MASK; 183 | // Printf.printf "special_dylib: 0x%x\n" special_dylib 184 | bind_info.special_dylib = special_dylib; 185 | }, 186 | BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { 187 | let symbol_flags = opcode & BIND_IMMEDIATE_MASK; 188 | let symbol_name = self.data.pread::<&str>(offset)?; 189 | offset += symbol_name.len() + 1; // second time this \0 caused debug woes 190 | bind_info.symbol_name = symbol_name; 191 | bind_info.symbol_flags = symbol_flags; 192 | }, 193 | BIND_OPCODE_SET_TYPE_IMM => { 194 | let bind_type = opcode & BIND_IMMEDIATE_MASK; 195 | bind_info.bind_type = bind_type; 196 | }, 197 | BIND_OPCODE_SET_ADDEND_SLEB => { 198 | let addend = Sleb128::read(&self.data, &mut offset)?; 199 | bind_info.addend = addend; 200 | }, 201 | BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { 202 | let seg_index = opcode & BIND_IMMEDIATE_MASK; 203 | // dyld sets the address to the segActualLoadAddress(segIndex) + uleb128 204 | // address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); 205 | let seg_offset = Uleb128::read(&self.data, &mut offset)?; 206 | bind_info.seg_index = seg_index; 207 | bind_info.seg_offset = seg_offset; 208 | }, 209 | BIND_OPCODE_ADD_ADDR_ULEB => { 210 | let addr = Uleb128::read(&self.data, &mut offset)?; 211 | let seg_offset = bind_info.seg_offset.wrapping_add(addr); 212 | bind_info.seg_offset = seg_offset; 213 | }, 214 | // record the record by placing its value into our list 215 | BIND_OPCODE_DO_BIND => { 216 | // from dyld: 217 | // if ( address >= segmentEndAddress ) 218 | // throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); 219 | // (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last); 220 | // address += sizeof(intptr_t); 221 | imports.push(Import::new(&bind_info, libs, segments, start_of_sequence)); 222 | let seg_offset = bind_info.seg_offset.wrapping_add(ctx.size() as u64); 223 | bind_info.seg_offset = seg_offset; 224 | }, 225 | BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => { 226 | // dyld: 227 | // if ( address >= segmentEndAddress ) 228 | // throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); 229 | // (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last); 230 | // address += read_uleb128(p, end) + sizeof(intptr_t); 231 | // we bind the old record, then increment bind info address for the next guy, plus the ptr offset *) 232 | imports.push(Import::new(&bind_info, libs, segments, start_of_sequence)); 233 | let addr = Uleb128::read(&self.data, &mut offset)?; 234 | let seg_offset = bind_info.seg_offset.wrapping_add(addr).wrapping_add(ctx.size() as u64); 235 | bind_info.seg_offset = seg_offset; 236 | }, 237 | BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => { 238 | // dyld: 239 | // if ( address >= segmentEndAddress ) 240 | // throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); 241 | // (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last); 242 | // address += immediate*sizeof(intptr_t) + sizeof(intptr_t); 243 | // break; 244 | // similarly, we bind the old record, then perform address manipulation for the next record 245 | imports.push(Import::new(&bind_info, libs, segments, start_of_sequence)); 246 | let scale = opcode & BIND_IMMEDIATE_MASK; 247 | let size = ctx.size() as u64; 248 | let seg_offset = bind_info.seg_offset.wrapping_add(u64::from(scale) * size).wrapping_add(size); 249 | bind_info.seg_offset = seg_offset; 250 | }, 251 | BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => { 252 | // dyld: 253 | // count = read_uleb128(p, end); 254 | // skip = read_uleb128(p, end); 255 | // for (uint32_t i=0; i < count; ++i) { 256 | // if ( address >= segmentEndAddress ) 257 | // throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); 258 | // (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last); 259 | // address += skip + sizeof(intptr_t); 260 | // } 261 | // break; 262 | let count = Uleb128::read(&self.data, &mut offset)?; 263 | let skip = Uleb128::read(&self.data, &mut offset)?; 264 | let skip_plus_size = skip + ctx.size() as u64; 265 | for _i in 0..count { 266 | imports.push(Import::new(&bind_info, libs, segments, start_of_sequence)); 267 | let seg_offset = bind_info.seg_offset.wrapping_add(skip_plus_size); 268 | bind_info.seg_offset = seg_offset; 269 | } 270 | }, 271 | _ => { 272 | } 273 | } 274 | } 275 | Ok(()) 276 | } 277 | } 278 | --------------------------------------------------------------------------------