Created
November 7, 2020 07:09
-
-
Save iankronquist/45805ac40e93e3236f537a16010b1dad to your computer and use it in GitHub Desktop.
dumb elf loader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(asm)] | |
#![allow(unused)] | |
const EI_CLASS: u8 = 4; | |
const EI_NIDENT: usize = 16; | |
#[derive(Default,Debug, Copy, Clone)] | |
#[repr(C,packed)] | |
struct Elf64Header { | |
ident: [u8;EI_NIDENT], | |
type_: u16, | |
machine: u16, | |
version: u32, | |
entry: u64, | |
phoff: u64, | |
shoff: u64, | |
flags: u32, | |
ehsize: u16, | |
phentsize: u16, | |
phnum: u16, | |
shentsize: u16, | |
shnum: u16, | |
shstrndx: u16, | |
} | |
const ET_NONE: u16 = 0; | |
const ET_REL: u16 = 1; | |
const ET_EXEC: u16 = 2; | |
const ET_DYN: u16 = 3; | |
const ET_CORE: u16 = 4; | |
const EM_X86_64: u16 = 62; | |
const EV_CURRENT: u32 = 1; | |
const ELFMAG: &[u8;4] = b"\x7fELF"; | |
const ELFCLASS32: u8 = 1; | |
const ELFCLASS64: u8 = 2; | |
const PT_NULL: u32 = 0; | |
const PT_LOAD: u32 = 1; | |
#[derive(Debug, Copy, Clone)] | |
#[repr(C,packed)] | |
struct ElfSectionHeader { | |
name: u32, | |
type_: u32, | |
flags: u64, | |
addr: u64, | |
offset: u64, | |
size: u64, | |
link: u32, | |
info: u32, | |
addralign: u64, | |
entsize: u64, | |
} | |
#[derive(Debug, Copy, Clone)] | |
#[repr(C,packed)] | |
struct ElfSymbol { | |
name: u32, | |
info: u8, | |
other: u8, | |
index: u16, | |
value: u64, | |
size: u64, | |
} | |
struct ElfRel { | |
offset: u64, | |
info: u64, | |
} | |
struct ElfRela { | |
offset: u64, | |
info: u64, | |
addend: i64, | |
} | |
#[derive(Default, Debug, Copy, Clone)] | |
#[repr(C,packed)] | |
struct ElfProgramHeader { | |
type_: u32, | |
flags: u32, | |
offset: u64, | |
vaddr: u64, | |
paddr: u64, | |
filesz: u64, | |
memsz: u64, | |
align: u64, | |
} | |
struct ElfDyn { | |
tag: i64, | |
val_or_ptr: u64, | |
} | |
#[derive(Debug)] | |
pub enum ByteBufferParseError { | |
MisAligned, | |
TooSmall, | |
} | |
pub fn check_buf_to_struct<'a, T: Copy + Sized>(buf: &'a [u8]) -> Result<(), ByteBufferParseError> { | |
if buf.as_ptr().align_offset(core::mem::align_of::<T>()) != 0 { | |
return Err(ByteBufferParseError::MisAligned); | |
} | |
let desired_size: usize = core::mem::size_of::<T>(); | |
if buf.len() < desired_size { | |
return Err(ByteBufferParseError::TooSmall); | |
} | |
Ok(()) | |
} | |
pub fn check_buf_to_slice<'a, T: Copy + Sized>( | |
buf: &'a [u8], | |
count: usize, | |
) -> Result<(), ByteBufferParseError> { | |
if buf.as_ptr().align_offset(core::mem::align_of::<T>()) != 0 { | |
return Err(ByteBufferParseError::MisAligned); | |
} | |
let desired_size: usize = core::mem::size_of::<T>() * count; | |
if buf.len() < desired_size { | |
return Err(ByteBufferParseError::TooSmall); | |
} | |
Ok(()) | |
} | |
pub fn buf_to_struct_mut<'a, T: Copy + Sized>( | |
buf: &'a [u8], | |
) -> Result<&'a mut T, ByteBufferParseError> { | |
check_buf_to_struct::<T>(buf)?; | |
unsafe { Ok(core::mem::transmute(buf.as_ptr())) } | |
} | |
pub fn buf_to_struct<'a, T: Copy + Sized>(buf: &'a [u8]) -> Result<&'a T, ByteBufferParseError> { | |
check_buf_to_struct::<T>(buf)?; | |
unsafe { Ok(core::mem::transmute(buf.as_ptr())) } | |
} | |
pub fn buf_to_slice<'a, T: Copy + Sized>( | |
buf: &'a [u8], | |
count: usize, | |
) -> Result<&'a [T], ByteBufferParseError> { | |
check_buf_to_slice::<T>(buf, count)?; | |
unsafe { | |
Ok(&*core::ptr::slice_from_raw_parts( | |
buf.as_ptr() as *const T, | |
count, | |
)) | |
} | |
} | |
pub fn buf_to_slice_mut<'a, T: Copy + Sized>( | |
buf: &'a mut [u8], | |
count: usize, | |
) -> Result<&'a mut [T], ByteBufferParseError> { | |
check_buf_to_slice::<T>(buf, count)?; | |
unsafe { | |
Ok(&mut *core::ptr::slice_from_raw_parts_mut( | |
buf.as_mut_ptr() as *mut T, | |
count, | |
)) | |
} | |
} | |
use std::io::Read; | |
use std::io::Seek; | |
use std::env; | |
use std::fs; | |
use std::ffi::CStr; | |
use std::str; | |
fn index_of(slice: &[u8], val: u8) -> Option<usize> { | |
for (i, byte) in slice.iter().enumerate() { | |
if *byte == val { | |
return Some(i); | |
} | |
} | |
None | |
} | |
extern "C" { | |
pub fn mmap(addr: *mut u8, len: usize, prot: i32, flags: i32, fd: i32, offset: u32) -> *mut u8; | |
static mut errno: u32; | |
} | |
fn map(addr: *mut u8, len: usize, prot: i32, flags: i32, fd: i32, offset: u32) -> &'static mut [u8] { | |
println!("mmap(addr {:x?}, len {:x?}, prot {:x?}, flags {:x?}, fd {:x?}, offset {:x?})", addr, len, prot, flags, fd, offset); | |
unsafe { | |
let ptr = mmap(addr, len, prot, flags, fd, offset); | |
println!("ptr {:x?} {:x?}", ptr, unsafe { errno }); | |
assert!(ptr as usize != !0); | |
std::slice::from_raw_parts_mut(ptr, len) | |
} | |
} | |
fn rand_addr() -> *mut u8 { | |
let mut n: u64; | |
unsafe { | |
asm!("rdrand {reg}", reg=out(reg)n); | |
} | |
let high_mask = !((!0) << 16); | |
n &= high_mask; | |
n &= !0xfff; | |
n as *mut u8 | |
} | |
pub fn struct_to_buf<'a, T: Copy + Sized>( | |
item: &'a T | |
) -> Result<&'a [u8], ByteBufferParseError> { | |
//check_buf_to_slice::<T>(buf, count)?; | |
unsafe { | |
Ok(&*core::ptr::slice_from_raw_parts( | |
item as *const T as *const u8, | |
core::mem::size_of::<T>(), | |
)) | |
} | |
} | |
pub fn struct_to_buf_mut<'a, T: Copy + Sized>( | |
item: &'a mut T | |
) -> Result<&'a mut [u8], ByteBufferParseError> { | |
//check_buf_to_slice::<T>(buf, count)?; | |
unsafe { | |
Ok(&mut *core::ptr::slice_from_raw_parts_mut( | |
item as *mut T as *mut u8, | |
core::mem::size_of::<T>(), | |
)) | |
} | |
} | |
const PROT_NONE: i32 = 0; | |
const PROT_READ: i32 = 1; | |
const PROT_WRITE: i32 = 2; | |
const PROT_EXEC: i32 = 4; | |
const PT_NONE: u64 = 0; | |
const PT_READ: u16 = 4; | |
const PT_WRITE: u16 = 2; | |
const PT_EXEC: u16 = 1; | |
fn elf_flags_to_mmap_flags(flags: u16) -> i32 { | |
let mut mmap_flags = 0; | |
if PT_READ & flags != 0 { | |
mmap_flags |= PROT_READ; | |
} | |
if PT_WRITE & flags != 0 { | |
mmap_flags |= PROT_WRITE; | |
} | |
if PT_EXEC & flags != 0 { | |
mmap_flags |= PROT_EXEC; | |
} | |
mmap_flags | |
} | |
const MAP_ANON: i32 = 0x1000; | |
const MAP_FIXED: i32 = 0x10; | |
const MAP_PRIVATE: i32 = 2; | |
use std::io::SeekFrom; | |
pub fn main() { | |
//pub fn main(_argc: isize, argv: *const *const u8) -> isize { | |
//let a = unsafe { mmap(0x2000 as *mut u8, 0x1000, 7, 0x1002, 0, 0) }; | |
//assert_eq!(a, core::ptr::null_mut()); | |
let args: Vec<String> = env::args().collect(); | |
let mut file = fs::OpenOptions::new() | |
.read(true) | |
.open(&args[1]).unwrap(); | |
let mut hdr: Elf64Header = Default::default(); | |
let mut hdr_slice = struct_to_buf_mut(&mut hdr).unwrap(); | |
file.read(hdr_slice).unwrap(); | |
let base_address = rand_addr(); | |
let mut ph: ElfProgramHeader = Default::default(); | |
let mut base_address = !0; | |
let mut end_address = 0; | |
for i in 0..u64::from(hdr.phnum) { | |
let mut ph_slice = struct_to_buf_mut(&mut ph).unwrap(); | |
file.seek(SeekFrom::Start(u64::from(hdr.phoff) + i * u64::from(hdr.phentsize))).unwrap(); | |
file.read(ph_slice).unwrap(); | |
if ph.vaddr < base_address { | |
base_address = ph.vaddr; | |
} | |
if ph.memsz + ph.vaddr > end_address { | |
end_address = ph.memsz + ph.vaddr; | |
} | |
} | |
let image_len = (end_address - base_address) as usize; | |
let image = map(core::ptr::null_mut(), image_len, PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0); | |
for i in 0..u64::from(hdr.phnum) { | |
let mut ph_slice = struct_to_buf_mut(&mut ph).unwrap(); | |
file.seek(SeekFrom::Start(u64::from(hdr.phoff) + i * u64::from(hdr.phentsize))).unwrap(); | |
file.read(ph_slice).unwrap(); | |
if ph.type_ == PT_LOAD { | |
let prot = elf_flags_to_mmap_flags(ph.flags as u16); | |
file.seek(SeekFrom::Start(u64::from(ph.offset))).unwrap(); | |
let ph_start = ph.offset as usize; | |
let ph_end = ph_start + ph.filesz as usize; | |
let addr = &mut image[ph_start] as *mut u8; | |
//let _ = map(addr, ph.memsz as usize, PROT_WRITE, MAP_FIXED|MAP_ANON| MAP_PRIVATE, 0, 0); | |
file.read(&mut image[ph_start..ph_end]).unwrap(); | |
for i in ph.filesz..ph.memsz { | |
image[i as usize] = 0; | |
} | |
//let _ = map(addr, ph.memsz as usize, prot, MAP_FIXED|MAP_ANON| MAP_PRIVATE, 0, 0); | |
} | |
} | |
/* | |
//file.seek(SeekFrom::Start(hdr.phoff)).unwrap(); | |
for i in 0..u64::from(hdr.phnum) { | |
println!("phoff {:x} {:x} {:x}", hdr.phoff, u64::from(hdr.phoff) + i * u64::from(hdr.phentsize), hdr.phentsize); | |
let mut ph_slice = struct_to_buf_mut(&mut ph).unwrap(); | |
file.seek(SeekFrom::Start(u64::from(hdr.phoff) + i * u64::from(hdr.phentsize))).unwrap(); | |
file.read(ph_slice).unwrap(); | |
println!("{:x?}", ph); | |
if ph.type_ == PT_LOAD { | |
println!("Loading"); | |
let prot = elf_flags_to_mmap_flags(ph.flags as u16); | |
if prot == PROT_NONE { | |
continue; | |
} | |
file.seek(SeekFrom::Start(u64::from(ph.offset) + i)).unwrap(); | |
let addr =unsafe { (base_address.add(ph.vaddr as usize)) }; | |
let a2 = unsafe { addr as usize & !0xfff }; | |
println!("Putting at {:x?}", addr); | |
let mut mem = map(a2 as *mut u8, ph.memsz as usize, PROT_WRITE, MAP_FIXED|MAP_ANON | MAP_PRIVATE, 0, 0); | |
println!("args {:x?}", &mut mem[..ph.filesz as usize][0] as *mut u8); | |
file.read(&mut mem[..ph.filesz as usize]).unwrap(); | |
let mut mem = map(addr as *mut u8, ph.memsz as usize, prot, MAP_FIXED|MAP_ANON | MAP_PRIVATE, 0, 0); | |
//fn map(addr: *mut u8, len: usize, prot: i32, flags: i32, fd: i32, offset: u32) -> &'static [u8] { | |
} | |
} | |
*/ | |
/* | |
let file = fs::read(&args[1]).unwrap(); | |
let hdr = buf_to_struct::<Elf64Header>(&file).unwrap().clone(); | |
println!("{:?}", hdr); | |
assert_eq!(usize::from(hdr.shentsize), core::mem::size_of::<ElfSectionHeader>()); | |
assert!(hdr.ident.starts_with(ELFMAG)); | |
let sections = buf_to_slice::<ElfSectionHeader>(&file[(hdr.shoff as usize)..], usize::from(hdr.shnum)).unwrap(); | |
let strtab_off = sections[hdr.shstrndx as usize].offset as usize; | |
let strtab_len = sections[hdr.shstrndx as usize].size as usize; | |
let strtab = &file[strtab_off..strtab_off+strtab_len]; | |
//println!("{:#x?}", sections); | |
for section in sections { | |
if section.type_ == 0 { | |
continue; | |
} | |
let null_index = index_of(&strtab[section.name as usize..], 0u8).unwrap(); | |
let name = str::from_utf8(&strtab[section.name as usize..section.name as usize+null_index]).unwrap(); | |
println!("{:?}", name); | |
let name = CStr::from_bytes_with_nul(&strtab[section.name as usize.. section.name as usize+null_index+1]).unwrap(); | |
println!("{:?}", name); | |
} | |
let phs = buf_to_slice::<ElfProgramHeader>(&file[(hdr.phoff as usize)..], usize::from(hdr.phnum)).unwrap(); | |
println!("{:#x?}", phs); | |
let mut base = !0; | |
for ph in phs { | |
if ph.type_ == PT_LOAD { | |
base = std::cmp::min(base, ph.vaddr); | |
} | |
//let null_index = index_of(&strtab[ph.name as usize..], 0u8).unwrap(); | |
//let name = CStr::from_bytes_with_nul(&strtab[ph.name as usize.. ph.name as usize+null_index+1]).unwrap(); | |
//println!("{:?}", name); | |
} | |
*/ | |
doneit(); | |
} | |
#[no_mangle] | |
#[inline(never)] | |
pub fn doneit() { | |
println!("done"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment