From 5fcf920675366217d0e5e30d960b19d41b027b26 Mon Sep 17 00:00:00 2001 From: oddcoder <ahmedsoliman@oddcoder.com> Date: Tue, 23 Jun 2020 10:25:18 +0200 Subject: [PATCH] Fix bugs in handling non pie elfs The problem here was that we alway added the base address, and we assumed that all addresses we access are relative but this is not the case in case of non pie binaries. The issue is that all addresses were base+offset. so if we added the base again it will ofcourse generate wrong address. --- src/ld_so/linker.rs | 80 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index d6acf240..00ef6172 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -10,6 +10,7 @@ use core::{ }; use goblin::{ elf::{ + header::ET_DYN, program_header, r#dyn::{Dyn, DT_DEBUG}, reloc, sym, Elf, @@ -199,7 +200,11 @@ impl Linker { let value: usize; if let Some(name_res) = elf.dynstrtab.get(sym.st_name) { name = name_res?.to_string(); - value = mmap.as_ptr() as usize + sym.st_value as usize; + value = if is_pie_enabled(elf) { + mmap.as_ptr() as usize + sym.st_value as usize + } else { + sym.st_value as usize + }; } else { continue; } @@ -256,7 +261,7 @@ impl Linker { None => return Ok(()), }; let elf = Elf::parse(self.objects.get(&root.name).unwrap())?; - for section in elf.section_headers { + for section in &elf.section_headers { let name = match elf.shdr_strtab.get(section.sh_name) { Some(x) => match x { Ok(y) => y, @@ -265,7 +270,11 @@ impl Linker { _ => continue, }; if name == ".init_array" { - let addr = mmap.as_ptr() as usize + section.vm_range().start; + let addr = if is_pie_enabled(&elf) { + mmap.as_ptr() as usize + section.vm_range().start + } else { + section.vm_range().start + }; for i in (0..section.sh_size).step_by(8) { unsafe { call_inits_finis(addr + i as usize) }; } @@ -287,7 +296,7 @@ impl Linker { None => return Ok(()), }; let elf = Elf::parse(self.objects.get(&root.name).unwrap())?; - for section in elf.section_headers { + for section in &elf.section_headers { let name = match elf.shdr_strtab.get(section.sh_name) { Some(x) => match x { Ok(y) => y, @@ -296,7 +305,11 @@ impl Linker { _ => continue, }; if name == ".fini_array" { - let addr = mmap.as_ptr() as usize + section.vm_range().start; + let addr = if is_pie_enabled(&elf) { + mmap.as_ptr() as usize + section.vm_range().start + } else { + section.vm_range().start + }; for i in (0..section.sh_size).step_by(8) { unsafe { call_inits_finis(addr + i as usize) }; } @@ -396,7 +409,12 @@ impl Linker { }; if same_elf { let addr = dso.as_ref().unwrap().base_addr; - let size = bounds.1; + let size = if is_pie_enabled(&elf) { + bounds.1 + } else { + bounds.1 - bounds.0 + }; + // Fill the gaps i the binary let mut ranges = Vec::new(); for ph in elf.program_headers.iter() { @@ -405,7 +423,11 @@ impl Linker { let vaddr = ph.p_vaddr as usize - voff; let vsize = ((ph.p_memsz as usize + voff + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; - ranges.push((vaddr, vsize)); + if is_pie_enabled(&elf) { + ranges.push((vaddr, vsize)); + } else { + ranges.push((vaddr - addr, vsize)); + } } } ranges.sort(); @@ -651,7 +673,11 @@ impl Linker { (0, 0) }; - let ptr = unsafe { mmap.as_mut_ptr().add(rel.r_offset as usize) }; + let ptr = if is_pie_enabled(&elf) { + unsafe { mmap.as_mut_ptr().add(rel.r_offset as usize) } + } else { + rel.r_offset as *mut u8 + }; let set_u64 = |value| { // println!(" set_u64 {:#x}", value); @@ -717,7 +743,12 @@ impl Linker { }; let bytes: [u8; size_of::<Dyn>() / 2] = unsafe { transmute((&_r_debug) as *const RTLDDebug as usize) }; - let start = dyn_start_addr + i * size_of::<Dyn>() + size_of::<Dyn>() / 2; + let start = if is_pie_enabled(elf) { + dyn_start_addr + i * size_of::<Dyn>() + size_of::<Dyn>() / 2 + } else { + dyn_start_addr + i * size_of::<Dyn>() + size_of::<Dyn>() / 2 + - mmap.as_mut_ptr() as usize + }; mmap[start..start + size_of::<Dyn>() / 2].clone_from_slice(&bytes); } } @@ -748,7 +779,11 @@ impl Linker { None => continue, }; let res = unsafe { - let ptr = mmap.as_mut_ptr().add(vaddr); + let ptr = if is_pie_enabled(elf) { + mmap.as_mut_ptr().add(vaddr) + } else { + vaddr as *const u8 + }; if self.verbose { println!(" prot {:#x}, {:#x}: {:p}, {:#x}", vaddr, vsize, ptr, prot); } @@ -780,7 +815,11 @@ impl Linker { println!("entry {}", elf_name); } if Some(*elf_name) == primary_opt { - entry_opt = Some(mmap.as_mut_ptr() as usize + elf.header.e_entry as usize); + if is_pie_enabled(&elf) { + entry_opt = Some(mmap.as_mut_ptr() as usize + elf.header.e_entry as usize); + } else { + entry_opt = Some(elf.header.e_entry as usize); + } } // Relocate @@ -815,7 +854,6 @@ impl Linker { } } } - // Protect pages for ph in elf.program_headers.iter() { if let program_header::PT_LOAD = ph.p_type { @@ -838,7 +876,11 @@ impl Linker { } let res = unsafe { - let ptr = mmap.as_mut_ptr().add(vaddr); + let ptr = if is_pie_enabled(&elf) { + mmap.as_mut_ptr().add(vaddr) + } else { + vaddr as *const u8 + }; if self.verbose { println!(" prot {:#x}, {:#x}: {:p}, {:#x}", vaddr, vsize, ptr, prot); } @@ -852,12 +894,20 @@ impl Linker { } } unsafe { _r_debug.state = RTLDState::RT_CONSISTENT }; - _dl_debug_state(); + //_dl_debug_state(); Ok(entry_opt) } } -unsafe extern "C" fn call_inits_finis(addr: usize) { +unsafe fn call_inits_finis(addr: usize) { let func = transmute::<usize, *const Option<extern "C" fn()>>(addr); (*func).map(|x| x()); } + +fn is_pie_enabled(elf: &Elf) -> bool { + if elf.header.e_type == ET_DYN { + true + } else { + false + } +} -- GitLab