From 4b7f25c79823f2876d57bf02103a63360b671a3e Mon Sep 17 00:00:00 2001 From: Lucia Ceionia Date: Wed, 14 Sep 2022 17:03:50 -0500 Subject: [PATCH] Initial Commit --- .gitignore | 7 + Cargo.toml | 9 + src/lestub | Bin 0 -> 563 bytes src/main.rs | 580 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 596 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lestub create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b037580 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +debug/ +target/ +Cargo.lock +a.out +*.exe +*.elf +*.bin diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b64bb66 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "elf2le" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +object = { version="0.29.0", features=["write"] } diff --git a/src/lestub b/src/lestub new file mode 100644 index 0000000000000000000000000000000000000000..9a9a0d7a3e14a63f87a8eb8b75c929828ffd5aa4 GIT binary patch literal 563 zcmeZ`VqjonU|?VY;{X4F1S=2&fdiPtPc#6f`Q&%;F>K*HtGI*F=d5B#MrN@>K~a8s zQDUw_a$;Uyeu+X-szOm|o { + print!("Absolute\tsize: {}\ttarget: ", rel.size()); + match rel.target() { + object::RelocationTarget::Symbol(s) => { + let sym = obj_file.symbol_by_index(s).unwrap(); + let sym_sec_idx = sym.section_index(); + let sym_sec = if let Some(idx) = sym_sec_idx { + Some(obj_file.section_by_index(idx).unwrap()) + } else { + None + }; + let sym_sec_name = if let Some(sec) = &sym_sec { + sec.name().unwrap() + } else { + "None" + }; + let implicit_addend = if rel.has_implicit_addend() { + let mut arr: [u8; 4] = [0; 4]; + arr.copy_from_slice(§ion.data().unwrap()[ind as usize..ind as usize+4]); + u32::from_le_bytes(arr) + } else { + 0 + }; + println!("{} (0x{:04x}) (in {})\taddend: {}\timplicit addend: {}", sym.name().unwrap(), sym.address(), sym_sec_name, rel.addend(), implicit_addend); + }, + object::RelocationTarget::Section(sec) => println!("{}", obj_file.section_by_index(sec).unwrap().name().unwrap()), + object::RelocationTarget::Absolute => println!("Absolute"), + _ => println!("Err"), + }; + }, + object::RelocationKind::PltRelative | + object::RelocationKind::Relative => { + print!("Relative\tsize: {}\ttarget: ", rel.size()); + match rel.target() { + object::RelocationTarget::Symbol(s) => { + let sym = obj_file.symbol_by_index(s).unwrap(); + let sym_sec_idx = sym.section_index(); + let sym_sec = if let Some(idx) = sym_sec_idx { + Some(obj_file.section_by_index(idx).unwrap()) + } else { + None + }; + let sym_sec_name = if let Some(sec) = &sym_sec { + sec.name().unwrap() + } else { + "None" + }; + let implicit_addend = if rel.has_implicit_addend() { + let mut arr: [u8; 4] = [0; 4]; + arr.copy_from_slice(§ion.data().unwrap()[ind as usize..ind as usize+4]); + u32::from_le_bytes(arr) + } else { + 0 + }; + println!("{} (0x{:04x}) (in {})\taddend: {}\timplicit addend: {}", sym.name().unwrap(), sym.address(), sym_sec_name, rel.addend(), implicit_addend); + }, + object::RelocationTarget::Section(sec) => println!("{}", obj_file.section_by_index(sec).unwrap().name().unwrap()), + object::RelocationTarget::Absolute => println!("Absolute"), + _ => println!("Err"), + }; + }, + _ => { + println!("Unsupported Relocation Type"); + }, + } + } +} + +struct LEHeader { + num_text_pages: u32, + num_data_pages: u32, + last_page_bytes: u32, + fixup_page_offsets: Vec, + fixup_records: Vec +} + +fn write_le_header(new_header: &LEHeader, le_stub: &mut std::fs::File) -> std::result::Result> { + let le_header_offset = 0x80; + let object_table_offset = 0xC4 + le_header_offset; + let page_table_offset = 0xF4 + le_header_offset; + + // Number of memory pages 14h + le_stub.write_at(&(new_header.num_text_pages + new_header.num_data_pages).to_le_bytes(), le_header_offset + 0x14)?; + // Object Table 1 page map index + le_stub.write_at(&(1 as u32).to_le_bytes(), object_table_offset + 0xC)?; + // Object Table 1 page map entries + le_stub.write_at(&new_header.num_text_pages.to_le_bytes(), object_table_offset + 0x10)?; + // Object Table 2 page map index + le_stub.write_at(&(new_header.num_text_pages + 1).to_le_bytes(), object_table_offset + 0x18 + 0xC)?; + // Object Table 2 page map entries + le_stub.write_at(&new_header.num_data_pages.to_le_bytes(), object_table_offset + 0x18 + 0x10)?; + + // Page Table + le_stub.seek(std::io::SeekFrom::Start(page_table_offset))?; + for p_idx in 1..(new_header.num_text_pages + new_header.num_data_pages + 1) { + le_stub.write(&((p_idx as u32) << 8).to_be_bytes())?; + } + // Resident Name Table + let name_table_offset = le_stub.stream_position()?; + le_stub.write(b"\x05ELFLE\0\0")?; + // Entry Table + le_stub.write(&[0u8, 0u8])?; + + // Fixup page table + let fixup_page_table_offset = le_stub.stream_position()?; + for offset in new_header.fixup_page_offsets.iter() { + le_stub.write(&offset.to_le_bytes())?; + } + // Fixup records table + let fixup_record_table_offset = le_stub.stream_position()?; + le_stub.write(&new_header.fixup_records)?; + + // Fixup Section length 30h + le_stub.write_at( + &(new_header.fixup_page_offsets.len() as u32 * 4 + new_header.fixup_records.len() as u32).to_le_bytes(), + le_header_offset + 0x30 + )?; + // Resource table offset, Resource table entries, Resident name table offset 50h 54h 58h + le_stub.write_at(&(name_table_offset as u32).to_le_bytes(), le_header_offset + 0x50)?; + le_stub.write_at(&[0u8, 0u8, 0u8, 0u8], le_header_offset + 0x54)?; + le_stub.write_at(&(name_table_offset as u32).to_le_bytes(), le_header_offset + 0x58)?; + // Entry table offset 5C + le_stub.write_at(&(name_table_offset as u32 + 8).to_le_bytes(), le_header_offset + 0x5C)?; + // Fixup page table offset 68h + le_stub.write_at(&(fixup_page_table_offset as u32 - le_header_offset as u32).to_le_bytes(), le_header_offset + 0x68)?; + // Fixup record table offset 6Ch + le_stub.write_at(&(fixup_record_table_offset as u32 - le_header_offset as u32).to_le_bytes(), le_header_offset + 0x6C)?; + // Data pages offset 80h + let data_pages_offset = 0x1000 + + ((new_header.fixup_records.len() + new_header.fixup_page_offsets.len() * 4 + 0x68 + le_header_offset as usize) / 0x1000) * 0x1000; + le_stub.write_at(&(data_pages_offset as u32).to_le_bytes(), le_header_offset + 0x80)?; + + Ok(data_pages_offset as u32) +} + +fn output_le_relocations(obj_file: &object::File, le_header: &mut LEHeader) { + let mut current_page = 0; + let mut reloc_idx = 0; + // start with 0 + le_header.fixup_page_offsets.push(0x00000000); + let current_section = obj_file.section_by_name(".text").unwrap(); + let mut text_relocations: Vec<(u64, Relocation)> = current_section.relocations().collect(); + text_relocations.sort_by(|(loca, _), (locb, _)| loca.cmp(locb)); + println!("\t[LE Text Relocations]"); + for (loc, rel) in text_relocations { + match rel.target() { + object::RelocationTarget::Symbol(s) => { + let sym = obj_file.symbol_by_index(s).unwrap(); + let sec_index = obj_file.section_by_index(sym.section_index().unwrap()).unwrap(); + let sec = sec_index.name().unwrap(); + // Type + le_header.fixup_records.push(match rel.kind() { + object::RelocationKind::Absolute => 0x07, + object::RelocationKind::Relative => 0x08, + object::RelocationKind::PltRelative => 0x08, + _ => panic!() + }); + // Flags + let dwordoffset = sym.address() >= 0x10000; + if dwordoffset { + // 32-bit Target Offset Flag + le_header.fixup_records.push(0x10); + } else { + le_header.fixup_records.push(0); + } + let page = loc / 0x1000; + let src_in_page = loc % 0x1000; + // Source Offset in Page + le_header.fixup_records.extend_from_slice(&(src_in_page as u16).to_le_bytes()); + // Target Object + let target_sec = match sec { + ".text" => 1, + ".data" => 2, + _ => panic!() + }; + le_header.fixup_records.push(target_sec); + // Target Offset + let target_offset = sym.address() as u32 + + if rel.has_implicit_addend() & (rel.kind() == object::RelocationKind::Absolute) { + let mut arr: [u8; 4] = [0; 4]; + arr.copy_from_slice(¤t_section.data().unwrap()[loc as usize..loc as usize+4]); + u32::from_le_bytes(arr) + } else { 0 }; + if dwordoffset { + // 32-bit offset + le_header.fixup_records.extend_from_slice(&(target_offset as u32).to_le_bytes()); + } else { + // 16-bit offset + le_header.fixup_records.extend_from_slice(&(target_offset as u16).to_le_bytes()); + } + + + while page > current_page { + // new page + le_header.fixup_page_offsets.push(reloc_idx); + current_page += 1; + println!("\nFixup page rollover at {:05x} for page {}", reloc_idx, current_page); + } + print!("{}:0x{:05x}->{}:0x{:05x} ", current_page, loc, target_sec, target_offset); + if dwordoffset { + // 32-bit offset + reloc_idx += 2; + } + } + _ => eprintln!("Unsupported Relocation") + } + reloc_idx += 7; + } + while current_page < le_header.num_text_pages as u64 { + le_header.fixup_page_offsets.push(reloc_idx); + current_page += 1; + } + println!(); + current_page = 0; + let current_section = obj_file.section_by_name(".data").unwrap(); + let mut data_relocations: Vec<(u64, Relocation)> = current_section.relocations().collect(); + data_relocations.sort_by(|(loca, _), (locb, _)| loca.cmp(locb)); + println!("\t[LE Data Relocations]"); + for (loc, rel) in data_relocations { + match rel.target() { + object::RelocationTarget::Symbol(s) => { + let sym = obj_file.symbol_by_index(s).unwrap(); + let sec_index = obj_file.section_by_index(sym.section_index().unwrap()).unwrap(); + let sec = sec_index.name().unwrap(); + // Type + le_header.fixup_records.push(match rel.kind() { + object::RelocationKind::Absolute => 0x07, + object::RelocationKind::Relative => 0x08, + object::RelocationKind::PltRelative => 0x08, + _ => panic!() + }); + // Flags + let dwordoffset = sym.address() >= 0x10000; + if dwordoffset { + // 32-bit Target Offset Flag + le_header.fixup_records.push(0x10); + } else { + le_header.fixup_records.push(0); + } + let page = loc / 0x1000; + let src_in_page = loc % 0x1000; + // Source Offset in Page + le_header.fixup_records.extend_from_slice(&(src_in_page as u16).to_le_bytes()); + // Target Object + let target_sec = match sec { + ".text" => 1, + ".data" => 2, + _ => panic!() + }; + le_header.fixup_records.push(target_sec); + // Target Offset + let target_offset = sym.address() as u32 + + if rel.has_implicit_addend() & (rel.kind() == object::RelocationKind::Absolute) { + let mut arr: [u8; 4] = [0; 4]; + arr.copy_from_slice(¤t_section.data().unwrap()[loc as usize..loc as usize+4]); + u32::from_le_bytes(arr) + } else { 0 }; + if dwordoffset { + // 32-bit offset + le_header.fixup_records.extend_from_slice(&(target_offset as u32).to_le_bytes()); + } else { + // 16-bit offset + le_header.fixup_records.extend_from_slice(&(target_offset as u16).to_le_bytes()); + } + + + while page > current_page { + // new page + le_header.fixup_page_offsets.push(reloc_idx); + current_page += 1; + println!("\nFixup page rollover at {:05x} for page {}", reloc_idx, current_page); + } + print!("{}:0x{:05x}->{}:0x{:05x} ", current_page, loc, target_sec, target_offset); + if dwordoffset { + // 32-bit offset + reloc_idx += 2; + } + } + _ => eprintln!("Unsupported Relocation") + } + reloc_idx += 7; + } + while current_page < le_header.num_data_pages as u64 { + le_header.fixup_page_offsets.push(reloc_idx); + current_page += 1; + } + println!(); + // End of Fixup page table + le_header.fixup_page_offsets.push(reloc_idx); + + println!("{} bytes of relocations", reloc_idx); +} + +fn main() -> std::result::Result<(), Box> { + let args: Vec = env::args().collect(); + let path = &args[1]; + let data = fs::read(path)?; + let obj_file = object::File::parse(&*data)?; + + for section in obj_file.sections() { + println!("\tSECTION [{}]\tKIND {}", section.name().unwrap(), match section.kind() { + object::SectionKind::Text => "text", + object::SectionKind::Data => "data", + object::SectionKind::ReadOnlyData => "rodata", + object::SectionKind::UninitializedData => "bss", + _ => "Other", + }); + println!("\tRELOCATIONS FOR [{}]", section.name().unwrap()); + print_section_relocations(§ion, &obj_file); + println!(); + } + for symbol in obj_file.symbols() { + let sym_sec_idx = symbol.section_index(); + let sym_sec = if let Some(idx) = sym_sec_idx { + Some(obj_file.section_by_index(idx)?) + } else { + None + }; + let sym_sec_name = if let Some(sec) = &sym_sec { + sec.name().unwrap() + } else { + "None" + }; + println!("SYMBOL [{}]\tKIND {}\tSECTION {}", symbol.name().unwrap(), match symbol.kind() { + object::SymbolKind::Text => "Func", + object::SymbolKind::Data => "Data", + object::SymbolKind::Section => "Section", + object::SymbolKind::Label => "Label", + _ => "Other", + }, sym_sec_name); + } + + let mut new_elf = object::write::Object::new(object::BinaryFormat::Elf, object::Architecture::I386, object::Endianness::Little); + let text_sec_name = Vec::from(".text"); + let null_seg = Vec::new(); + let new_text_sec = new_elf.add_section(null_seg, text_sec_name, object::SectionKind::Text); + let data_sec_name = Vec::from(".data"); + let null_seg = Vec::new(); + let new_data_sec = new_elf.add_section(null_seg, data_sec_name, object::SectionKind::Data); + + let mut new_sym_map = std::collections::HashMap::new(); + // get start section first + if let Some(start_section) = obj_file.section_by_name(".text.start") { + let new_symbol = new_elf.add_symbol(object::write::Symbol { + name: Vec::from(".text.start"), + value: 0x0, + size: 0x0, + kind: object::SymbolKind::Text, + scope: object::SymbolScope::Compilation, + weak: false, + section: object::write::SymbolSection::Section(new_text_sec), + flags: object::SymbolFlags::None, + }); + new_elf.add_symbol_data(new_symbol, new_text_sec, start_section.data().unwrap(), 1); + new_sym_map.insert(String::from(".text.start"), new_symbol); + } + for section in obj_file.sections() { + if section.name().unwrap() == ".text.start" { continue } + match section.kind() { + object::SectionKind::Text => { + let new_symbol = new_elf.add_symbol(object::write::Symbol { + name: Vec::from(section.name().unwrap()), + value: 0x0, + size: 0x0, + kind: object::SymbolKind::Text, + scope: object::SymbolScope::Compilation, + weak: false, + section: object::write::SymbolSection::Section(new_text_sec), + flags: object::SymbolFlags::None, + }); + new_elf.add_symbol_data(new_symbol, new_text_sec, section.data().unwrap(), 1); + new_sym_map.insert(String::from(section.name().unwrap()), new_symbol); + }, + object::SectionKind::Data | object::SectionKind::ReadOnlyData => { + let new_symbol = new_elf.add_symbol(object::write::Symbol { + name: Vec::from(section.name().unwrap()), + value: 0x0, + size: 0x0, + kind: object::SymbolKind::Data, + scope: object::SymbolScope::Compilation, + weak: false, + section: object::write::SymbolSection::Section(new_data_sec), + flags: object::SymbolFlags::None, + }); + new_elf.add_symbol_data(new_symbol, new_data_sec, section.data().unwrap(), 1); + new_sym_map.insert(String::from(section.name().unwrap()), new_symbol); + }, + object::SectionKind::UninitializedData => { + let new_symbol = new_elf.add_symbol(object::write::Symbol { + name: Vec::from(section.name().unwrap()), + value: 0x0, + size: 0x0, + kind: object::SymbolKind::Data, + scope: object::SymbolScope::Compilation, + weak: false, + section: object::write::SymbolSection::Section(new_data_sec), + flags: object::SymbolFlags::None, + }); + let bss_zeros = vec![0; section.size() as usize]; + new_elf.add_symbol_data(new_symbol, new_data_sec, &bss_zeros, 1); + new_sym_map.insert(String::from(section.name().unwrap()), new_symbol); + } + _ => {} + } + } + for symbol in obj_file.symbols() { + let sym_sec_idx = symbol.section_index(); + let sym_sec = if let Some(idx) = sym_sec_idx { + Some(obj_file.section_by_index(idx)?) + } else { + None + }; + let new_offset = if let Some(sec) = &sym_sec { + let old_section_name = sec.name().unwrap(); + match new_sym_map.get(old_section_name) { + Some(id) => new_elf.symbol(*id).value + symbol.address(), + None => symbol.address() + } + } else { + symbol.address() + }; + match symbol.kind() { + object::SymbolKind::Text => { + let new_symbol = new_elf.add_symbol(object::write::Symbol { + name: Vec::from(symbol.name().unwrap()), + value: new_offset, + size: symbol.size(), + kind: object::SymbolKind::Text, + scope: object::SymbolScope::Compilation, + weak: false, + section: object::write::SymbolSection::Section(new_text_sec), + flags: object::SymbolFlags::None, + }); + new_sym_map.insert(String::from(symbol.name().unwrap()), new_symbol); + } + object::SymbolKind::Data => { + let new_symbol = new_elf.add_symbol(object::write::Symbol { + name: Vec::from(symbol.name().unwrap()), + value: new_offset, + size: symbol.size(), + kind: object::SymbolKind::Data, + scope: object::SymbolScope::Compilation, + weak: false, + section: object::write::SymbolSection::Section(new_data_sec), + flags: object::SymbolFlags::None, + }); + new_sym_map.insert(String::from(symbol.name().unwrap()), new_symbol); + } + _ => {} + } + } + + for section in obj_file.sections() { + if section.kind() != object::SectionKind::Text && + section.kind() != object::SectionKind::Data && + section.kind() != object::SectionKind::ReadOnlyData && + section.kind() != object::SectionKind::UninitializedData { + continue + } + let new_src_sym_id = new_sym_map.get(section.name().unwrap()).unwrap(); + let base_addr = new_elf.symbol(*new_src_sym_id).value; + for (src, reloc) in section.relocations() { + match reloc.target() { + RelocationTarget::Symbol(sym_idx) => { + let old_sym = obj_file.symbol_by_index(sym_idx).unwrap(); + let old_sec = obj_file.section_by_index(old_sym.section_index().unwrap()).unwrap(); + let new_sym = if let Some(new_sym) = new_sym_map.get(old_sym.name().unwrap()) { + Some(new_sym) + } else { + if let Some(new_sym) = new_sym_map.get(old_sec.name().unwrap()) { + Some(new_sym) + } else { + None + } + }; + if let Some(new) = new_sym { + let new_src_sec_id = new_elf.symbol(*new_src_sym_id).section.id().unwrap(); + new_elf.add_relocation(new_src_sec_id, object::write::Relocation { + offset: base_addr + src, + size: reloc.size(), + kind: reloc.kind(), + encoding: reloc.encoding(), + symbol: *new, + addend: reloc.addend(), + })?; + println!("reloc {:04x} in {} -> {} in {} {:04x}@{:04x} Became {:04x} in {} -> {} in {} ({:04x})", + src, + section.name().unwrap(), + old_sym.name().unwrap(), + old_sec.name().unwrap(), + old_sym.address(), + old_sec.address(), + base_addr + src, + new_elf.section(new_src_sec_id).name().unwrap(), + new_elf.symbol(*new).name().unwrap(), + new_elf.section(new_elf.symbol(*new).section.id().unwrap()).name().unwrap(), + new_elf.symbol(*new).value + ); + } else { + println!("Couldn't find new equivalent of {:04x} -> symbol {} in {} ({:04x}@{:04x})", src, + old_sym.name().unwrap(), + old_sec.name().unwrap(), + old_sym.address(), + old_sec.address() + ); + } + }, + _ => { panic!("Unsupported Relocation Type"); }, + } + } + } + + { + let mut out_elf = fs::File::create("new.elf")?; + new_elf.write_stream(&mut out_elf)?; + } + + println!("\n\n\tWrote [new.elf]! Trying to produce LE executable."); + + let new_file = fs::read("new.elf")?; + let new_obj = object::File::parse(&*new_file)?; + + println!(); + let text_len = new_obj.section_by_name(".text").unwrap().data().unwrap().len(); + let data_len = new_obj.section_by_name(".data").unwrap().data().unwrap().len(); + let text_pages = if text_len % 0x1000 == 0 { + text_len / 0x1000 + } else { + (text_len / 0x1000) + 1 + }; + let data_pages = if data_len % 0x1000 == 0 { + data_len / 0x1000 + } else { + (data_len / 0x1000) + 1 + }; + println!("[.text] size: 0x{:08x} ({} pages)", text_len, text_pages); + println!("[.data] size: 0x{:08x} ({} pages)", data_len, data_pages); + + let text_sec = new_obj.section_by_name(".text").unwrap(); + println!("\n\tRELOCATIONS FOR [.text]"); + print_section_relocations(&text_sec, &new_obj); + println!("\n\tRELOCATIONS FOR [.data]"); + let data_sec = new_obj.section_by_name(".data").unwrap(); + print_section_relocations(&data_sec, &new_obj); + + let le_stub: Vec = Vec::from(*LE_STUB); + let mut out_file = fs::File::create("a.exe")?; + out_file.write(&le_stub)?; + out_file.set_len(0x2000)?; + let mut header = LEHeader { + num_text_pages: text_pages as u32, + num_data_pages: data_pages as u32, + last_page_bytes: 0x1000, + fixup_page_offsets: Vec::new(), + fixup_records: Vec::new(), + }; + output_le_relocations(&new_obj, &mut header); + let data_pages_offset = write_le_header(&header, &mut out_file)?; + println!("Data Pages Offset: 0x{:04x}", data_pages_offset); + out_file.write_all_at(new_obj.section_by_name(".text").unwrap().data().unwrap(), data_pages_offset as u64)?; + let data_loc = (header.num_text_pages * 0x1000) + data_pages_offset; + out_file.write_all_at(new_obj.section_by_name(".data").unwrap().data().unwrap(), data_loc as u64)?; + + Ok(()) +}