use color_eyre::eyre::Result;
use goblin::elf::{Elf, SectionHeader};
use goblin::error::Error as GoblinError;
trait ElfExt
{
fn get_section_by_name(&self, name: &str) -> Option<&goblin::elf::SectionHeader>;
}
impl<'a> ElfExt for Elf<'a>
{
fn get_section_by_name(&self, name: &str) -> Option<&goblin::elf::SectionHeader>
{
for section in &self.section_headers {
let parsed_name = self.shdr_strtab.get_at(section.sh_name)?;
if parsed_name == name {
return Some(section);
}
}
None
}
}
trait SectionHeaderExt
{
fn get_data<'s>(&'s self, parent_data: &'s [u8]) -> Result<&'s [u8]>;
}
impl SectionHeaderExt for SectionHeader
{
fn get_data<'s>(&'s self, parent_data: &'s [u8]) -> Result<&'s [u8]>
{
let start_idx = self.sh_offset as usize;
let size = self.sh_size;
let end_idx = start_idx + size as usize;
let data: &[u8] = parent_data.get(start_idx..end_idx)
.ok_or_else(|| GoblinError::Malformed(format!(
"ELF section header does not point to a valid section (offset [{}..{}])",
start_idx,
end_idx,
)))?;
Ok(data)
}
}
pub fn extract_binary(elf_data: &[u8]) -> Result<Vec<u8>>
{
let elf = Elf::parse(elf_data)?;
let text = elf
.get_section_by_name(".text")
.ok_or_else(|| GoblinError::Malformed("ELF .text section not found".into()))?
.get_data(elf_data)?;
let arm_exidx = elf
.get_section_by_name(".ARM.exidx")
.map(|v| v.get_data(elf_data).ok())
.flatten();
let arm_exidx_len = arm_exidx.map(|sect| sect.len()).unwrap_or(0);
let data = elf
.get_section_by_name(".data")
.ok_or_else(|| GoblinError::Malformed("ELF .data section not found".into()))?
.get_data(elf_data)?;
let mut extracted = Vec::with_capacity(text.len() + arm_exidx_len + data.len());
extracted.extend_from_slice(text);
if let Some(arm_exidx) = arm_exidx {
extracted.extend_from_slice(arm_exidx);
}
extracted.extend_from_slice(data);
Ok(extracted)
}