generate a minimal, fake .dll with the host-exposed symbols

This commit is contained in:
Folkert 2022-08-29 23:58:05 +02:00
parent f918f16b0f
commit 62a818883a
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
2 changed files with 239 additions and 14 deletions

View file

@ -15,11 +15,176 @@ pub fn generate(target: &Triple, custom_names: &[String]) -> object::read::Resul
match target.binary_format {
target_lexicon::BinaryFormat::Elf => create_dylib_elf64(DUMMY_ELF64, custom_names),
target_lexicon::BinaryFormat::Macho => todo!("macho dylib creation"),
target_lexicon::BinaryFormat::Coff => todo!("coff dylib creation"),
target_lexicon::BinaryFormat::Coff => create_dylib_pe(custom_names),
other => unimplemented!("dylib creation for {:?}", other),
}
}
fn create_dylib_pe(custom_names: &[String]) -> object::read::Result<Vec<u8>> {
use object::pe;
use object::LittleEndian as LE;
fn synthetic_image_export_directory(
name: &str,
virtual_address: u32,
custom_names: &[String],
) -> pe::ImageExportDirectory {
use object::{U16, U32};
let directory_size = std::mem::size_of::<pe::ImageExportDirectory>() as u32;
// actual data is after the directory and the name of the dll file (null-terminated)
let start = virtual_address + directory_size + name.len() as u32 + 1;
let address_of_functions = start;
let address_of_names = address_of_functions + 4 * custom_names.len() as u32;
let address_of_name_ordinals = address_of_names + 4 * custom_names.len() as u32;
pe::ImageExportDirectory {
characteristics: U32::new(LE, 0),
time_date_stamp: U32::new(LE, 0),
major_version: U16::new(LE, 0),
minor_version: U16::new(LE, 0),
name: U32::new(LE, virtual_address + directory_size),
base: U32::new(LE, 0),
number_of_functions: U32::new(LE, custom_names.len() as u32),
number_of_names: U32::new(LE, custom_names.len() as u32),
address_of_functions: U32::new(LE, address_of_functions),
address_of_names: U32::new(LE, address_of_names),
address_of_name_ordinals: U32::new(LE, address_of_name_ordinals),
}
}
fn synthetic_export_dir(virtual_address: u32, custom_names: &[String]) -> Vec<u8> {
let mut vec = vec![0; std::mem::size_of::<pe::ImageExportDirectory>()];
let ptr = vec.as_mut_ptr();
let name = "roc-cheaty-lib.dll";
let directory = synthetic_image_export_directory(name, virtual_address, custom_names);
unsafe {
std::ptr::write_unaligned(ptr as *mut pe::ImageExportDirectory, directory);
}
// write the .dll name, null-terminated
vec.extend(name.as_bytes());
vec.push(0);
let n = custom_names.len();
// Unsure what this one does; it does not seem important for our purposes
//
// Export Address Table -- Ordinal Base 0
// [ 1] +base[ 1] 1020 Export RVA
// [ 2] +base[ 2] d030 Export RVA
for _ in custom_names {
vec.extend(42u32.to_le_bytes());
}
// Maps the index to a name
//
// [Ordinal/Name Pointer] Table
// [ 1] _CRT_INIT
// [ 2] _CRT_MT
let mut acc = directory.address_of_name_ordinals.get(LE) + n as u32 * 2;
for name in custom_names {
vec.extend(acc.to_le_bytes());
acc += name.len() as u32 + 1;
}
// the ordinals, which just map to the index in our case
for (i, _) in custom_names.iter().enumerate() {
vec.extend((i as u16).to_le_bytes());
}
// write out the names of the symbols, as null-terminated strings
for name in custom_names {
vec.extend(name.as_bytes());
vec.push(0);
}
vec
}
fn synthetic_dll(custom_names: &[String]) -> Vec<u8> {
let mut out_data = Vec::new();
let mut writer = object::write::pe::Writer::new(true, 8, 8, &mut out_data);
// fairly randomly chosen. Not sure if this is relevant
let virtual_address = 0x138;
let exports = synthetic_export_dir(virtual_address, custom_names);
// Reserve file ranges and virtual addresses.
writer.reserve_dos_header_and_stub();
// we will have one header: the export directory
writer.reserve_nt_headers(1);
writer.set_data_directory(
pe::IMAGE_DIRECTORY_ENTRY_EXPORT,
virtual_address,
exports.len() as _,
);
writer.reserve_section_headers(1);
// we store the export directory in a .rdata section
let rdata_section: (_, Vec<u8>) = {
let range = writer.reserve_section(
*b".rdata\0\0",
1073741888,
// virtual size
exports.len() as u32,
// size_of_raw_data
exports.len() as u32,
);
(range.file_offset, exports)
};
// Start writing.
writer.write_dos_header_and_stub().unwrap();
// the header on my machine
let headers = object::write::pe::NtHeaders {
machine: 34404,
time_date_stamp: 1661696130,
characteristics: 8226,
major_linker_version: 14,
minor_linker_version: 0,
address_of_entry_point: 4560,
image_base: 6442450944,
major_operating_system_version: 6,
minor_operating_system_version: 0,
major_image_version: 0,
minor_image_version: 0,
major_subsystem_version: 6,
minor_subsystem_version: 0,
subsystem: 2,
dll_characteristics: 352,
size_of_stack_reserve: 1048576,
size_of_stack_commit: 4096,
size_of_heap_reserve: 1048576,
size_of_heap_commit: 4096,
};
writer.write_nt_headers(headers);
writer.write_section_headers();
let (offset, data) = rdata_section;
writer.write_section(offset, &data);
debug_assert_eq!(writer.reserved_len() as usize, writer.len());
out_data
}
Ok(synthetic_dll(custom_names))
}
#[derive(Debug)]
struct Dynamic {
tag: u32,
@ -412,3 +577,50 @@ fn create_dylib_elf64(in_data: &[u8], custom_names: &[String]) -> object::read::
Ok(out_data)
}
#[cfg(test)]
mod tests {
use super::*;
use object::Object;
fn check_exports(target: &Triple) {
let custom_names = ["foo".to_string(), "bar".to_string()];
let bytes = generate(target, &custom_names).unwrap();
let object = object::File::parse(bytes.as_slice()).unwrap();
let exports = object.exports().unwrap();
for custom in custom_names {
assert!(
exports.iter().any(|e| e.name() == custom.as_bytes()),
"missing {}",
&custom
);
}
}
#[test]
fn check_exports_elf64() {
let target = target_lexicon::Triple {
architecture: target_lexicon::Architecture::X86_64,
operating_system: target_lexicon::OperatingSystem::Linux,
binary_format: target_lexicon::BinaryFormat::Elf,
..target_lexicon::Triple::host()
};
check_exports(&target);
}
#[test]
fn check_exports_coff() {
let target = target_lexicon::Triple {
architecture: target_lexicon::Architecture::X86_64,
operating_system: target_lexicon::OperatingSystem::Windows,
binary_format: target_lexicon::BinaryFormat::Coff,
..target_lexicon::Triple::host()
};
check_exports(&target);
}
}