Skip to content

Instantly share code, notes, and snippets.

@iankronquist
Created November 7, 2020 07:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iankronquist/45805ac40e93e3236f537a16010b1dad to your computer and use it in GitHub Desktop.
Save iankronquist/45805ac40e93e3236f537a16010b1dad to your computer and use it in GitHub Desktop.
dumb elf loader
#![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