const EHDR: usize = 64;
const PHDR: usize = 56;
const SHDR: usize = 64;
const P_DYNSTR: u64 = 0x00e8;
const P_DYNSYM: u64 = 0x00f0;
const P_RELA: u64 = 0x0120;
const P_DYNAMIC: u64 = 0x0138;
const P_PLT: u64 = 0x01c0;
const P_TEXT: u64 = 0x01d0;
const P_GOT: u64 = 0x01e0;
const P_SHSTR: u64 = 0x01e8;
const P_SHDRS: u64 = 0x0208;
const FILE_END: u64 = P_SHDRS + 5 * SHDR as u64;
const DYNSTR: &[u8] = b"\0strcpy\0";
const SHSTR: &[u8] = b"\0.plt\0.text\0.got.plt\0.shstrtab\0";
pub fn elf_with_plt_call() -> Vec<u8> {
let mut f = vec![0u8; FILE_END as usize];
f[0..4].copy_from_slice(b"\x7fELF");
f[4] = 2; f[5] = 1; f[6] = 1; put16(&mut f, 16, 2); put16(&mut f, 18, 62); put32(&mut f, 20, 1); put64(&mut f, 24, P_TEXT); put64(&mut f, 32, EHDR as u64); put64(&mut f, 40, P_SHDRS); put16(&mut f, 52, EHDR as u16); put16(&mut f, 54, PHDR as u16); put16(&mut f, 56, 3); put16(&mut f, 58, SHDR as u16); put16(&mut f, 60, 5); put16(&mut f, 62, 4);
let ph = EHDR;
phdr(&mut f, ph, 1, 7, 0, 0, FILE_END, 0x1000); phdr(&mut f, ph + PHDR, 2, 6, P_DYNAMIC, P_DYNAMIC, 128, 8); phdr(&mut f, ph + 2 * PHDR, 0x6474_e551, 6, 0, 0, 0, 16);
let o = P_DYNSTR as usize;
f[o..o + DYNSTR.len()].copy_from_slice(DYNSTR);
let s = P_DYNSYM as usize + 24;
put32(&mut f, s, 1); f[s + 4] = 0x12; put16(&mut f, s + 6, 0);
let r = P_RELA as usize;
put64(&mut f, r, P_GOT); put64(&mut f, r + 8, (1u64 << 32) | 7);
let dyn_entries: [(u64, u64); 8] = [
(5, P_DYNSTR), (10, 8), (6, P_DYNSYM), (11, 24), (23, P_RELA), (2, 24), (20, 7), (0, 0), ];
for (i, (tag, val)) in dyn_entries.iter().enumerate() {
let d = P_DYNAMIC as usize + i * 16;
put64(&mut f, d, *tag);
put64(&mut f, d + 8, *val);
}
let p = P_PLT as usize;
f[p] = 0xff;
f[p + 1] = 0x25;
let disp = (P_GOT as i64 - (P_PLT as i64 + 6)) as i32;
f[p + 2..p + 6].copy_from_slice(&disp.to_le_bytes());
let t = P_TEXT as usize;
f[t] = 0xe8;
let rel = (P_PLT as i64 - (P_TEXT as i64 + 5)) as i32;
f[t + 1..t + 5].copy_from_slice(&rel.to_le_bytes());
f[t + 5] = 0xc3;
let sh = P_SHSTR as usize;
f[sh..sh + SHSTR.len()].copy_from_slice(SHSTR);
const PROGBITS: u32 = 1;
const STRTAB: u32 = 3;
const ALLOC_EXEC: u64 = 0x2 | 0x4;
const ALLOC_WRITE: u64 = 0x2 | 0x1;
let sd = P_SHDRS as usize;
shdr(&mut f, sd + SHDR, 1, PROGBITS, ALLOC_EXEC, P_PLT, 16); shdr(&mut f, sd + 2 * SHDR, 6, PROGBITS, ALLOC_EXEC, P_TEXT, 16); shdr(&mut f, sd + 3 * SHDR, 12, PROGBITS, ALLOC_WRITE, P_GOT, 8); shdr(&mut f, sd + 4 * SHDR, 21, STRTAB, 0, 0, SHSTR.len() as u64); put64(&mut f, sd + 4 * SHDR + 24, P_SHSTR);
f
}
fn put16(f: &mut [u8], at: usize, v: u16) {
f[at..at + 2].copy_from_slice(&v.to_le_bytes());
}
fn put32(f: &mut [u8], at: usize, v: u32) {
f[at..at + 4].copy_from_slice(&v.to_le_bytes());
}
fn put64(f: &mut [u8], at: usize, v: u64) {
f[at..at + 8].copy_from_slice(&v.to_le_bytes());
}
fn bl_word(pc: u64, target: u64) -> u32 {
let off = target.wrapping_sub(pc);
0x9400_0000 | ((off >> 2) as u32 & 0x03ff_ffff)
}
pub fn elf_aarch64_call() -> Vec<u8> {
let text_len = 0x1cusize;
let shstr = b"\0.text\0.shstrtab\0";
const SHDRS: u64 = 0x0208;
const SHSTR_OFF: u64 = 0x01f0;
let file_end = SHDRS + 3 * SHDR as u64;
let mut f = vec![0u8; file_end as usize];
f[0..4].copy_from_slice(b"\x7fELF");
f[4] = 2; f[5] = 1; f[6] = 1; put16(&mut f, 16, 2); put16(&mut f, 18, 183); put32(&mut f, 20, 1); put64(&mut f, 24, P_TEXT); put64(&mut f, 32, EHDR as u64); put64(&mut f, 40, SHDRS); put16(&mut f, 52, EHDR as u16); put16(&mut f, 54, PHDR as u16); put16(&mut f, 56, 2); put16(&mut f, 58, SHDR as u16); put16(&mut f, 60, 3); put16(&mut f, 62, 2);
phdr(&mut f, EHDR, 1, 7, 0, 0, file_end, 0x1000); phdr(&mut f, EHDR + PHDR, 0x6474_e551, 6, 0, 0, 0, 16);
let t = P_TEXT as usize;
put32(&mut f, t, bl_word(P_TEXT, P_TEXT + 0x10));
put32(&mut f, t + 4, 0xd280_0540); put32(&mut f, t + 8, 0xd65f_03c0); put32(&mut f, t + 12, 0xd503_201f); put32(&mut f, t + 16, 0x8b02_0020); put32(&mut f, t + 20, 0xf940_0001); put32(&mut f, t + 24, 0xd65f_03c0);
let s = SHSTR_OFF as usize;
f[s..s + shstr.len()].copy_from_slice(shstr);
const PROGBITS: u32 = 1;
const STRTAB: u32 = 3;
const ALLOC_EXEC: u64 = 0x2 | 0x4;
let sd = SHDRS as usize;
shdr(
&mut f,
sd + SHDR,
1,
PROGBITS,
ALLOC_EXEC,
P_TEXT,
text_len as u64,
); shdr(&mut f, sd + 2 * SHDR, 7, STRTAB, 0, 0, shstr.len() as u64); put64(&mut f, sd + 2 * SHDR + 24, SHSTR_OFF);
f
}
pub fn elf_aarch64_plt_call() -> Vec<u8> {
let mut f = vec![0u8; FILE_END as usize];
f[0..4].copy_from_slice(b"\x7fELF");
f[4] = 2; f[5] = 1; f[6] = 1; put16(&mut f, 16, 2); put16(&mut f, 18, 183); put32(&mut f, 20, 1); put64(&mut f, 24, P_TEXT); put64(&mut f, 32, EHDR as u64); put64(&mut f, 40, P_SHDRS); put16(&mut f, 52, EHDR as u16); put16(&mut f, 54, PHDR as u16); put16(&mut f, 56, 3); put16(&mut f, 58, SHDR as u16); put16(&mut f, 60, 5); put16(&mut f, 62, 4);
let ph = EHDR;
phdr(&mut f, ph, 1, 7, 0, 0, FILE_END, 0x1000); phdr(&mut f, ph + PHDR, 2, 6, P_DYNAMIC, P_DYNAMIC, 128, 8); phdr(&mut f, ph + 2 * PHDR, 0x6474_e551, 6, 0, 0, 0, 16);
let o = P_DYNSTR as usize;
f[o..o + DYNSTR.len()].copy_from_slice(DYNSTR);
let s = P_DYNSYM as usize + 24;
put32(&mut f, s, 1); f[s + 4] = 0x12; put16(&mut f, s + 6, 0);
let r = P_RELA as usize;
put64(&mut f, r, P_GOT); put64(&mut f, r + 8, (1u64 << 32) | 1026);
let dyn_entries: [(u64, u64); 8] = [
(5, P_DYNSTR), (10, 8), (6, P_DYNSYM), (11, 24), (23, P_RELA), (2, 24), (20, 7), (0, 0), ];
for (i, (tag, val)) in dyn_entries.iter().enumerate() {
let d = P_DYNAMIC as usize + i * 16;
put64(&mut f, d, *tag);
put64(&mut f, d + 8, *val);
}
let p = P_PLT as usize;
put32(&mut f, p, adrp_word(P_PLT, P_GOT & !0xfff, 17));
put32(&mut f, p + 4, ldr_imm_word(17, 16, P_GOT & 0xfff));
put32(&mut f, p + 8, br_word(16));
let t = P_TEXT as usize;
put32(&mut f, t, bl_word(P_TEXT, P_PLT));
put32(&mut f, t + 4, 0xd65f_03c0);
let sh = P_SHSTR as usize;
f[sh..sh + SHSTR.len()].copy_from_slice(SHSTR);
const PROGBITS: u32 = 1;
const STRTAB: u32 = 3;
const ALLOC_EXEC: u64 = 0x2 | 0x4;
const ALLOC_WRITE: u64 = 0x2 | 0x1;
let sd = P_SHDRS as usize;
shdr(&mut f, sd + SHDR, 1, PROGBITS, ALLOC_EXEC, P_PLT, 16); shdr(&mut f, sd + 2 * SHDR, 6, PROGBITS, ALLOC_EXEC, P_TEXT, 16); shdr(&mut f, sd + 3 * SHDR, 12, PROGBITS, ALLOC_WRITE, P_GOT, 8); shdr(&mut f, sd + 4 * SHDR, 21, STRTAB, 0, 0, SHSTR.len() as u64); put64(&mut f, sd + 4 * SHDR + 24, P_SHSTR);
f
}
fn adrp_word(pc: u64, page: u64, rd: u32) -> u32 {
let off = (page as i64 - (pc & !0xfff) as i64) >> 12;
let immlo = (off & 3) as u32;
let immhi = ((off >> 2) & 0x7ffff) as u32;
0x9000_0000 | (immhi << 5) | (immlo << 29) | rd
}
fn ldr_imm_word(rn: u32, rt: u32, offs: u64) -> u32 {
0xf940_0000 | (rt & 0x1f) | ((rn & 0x1f) << 5) | (((offs >> 3) as u32 & 0xfff) << 10)
}
fn br_word(rn: u32) -> u32 {
0xd61f_0000 | ((rn & 0x1f) << 5)
}
pub fn elf_with_eh_frame_hdr() -> Vec<u8> {
const HDR_OFF: u64 = 0x0080; let starts = [0x1000u64, 0x1400u64];
let mut f = vec![0u8; 0x100];
f[0..4].copy_from_slice(b"\x7fELF");
f[4] = 2; f[5] = 1; f[6] = 1; put16(&mut f, 16, 3); put16(&mut f, 18, 62); put32(&mut f, 20, 1); put64(&mut f, 32, EHDR as u64); put16(&mut f, 52, EHDR as u16); put16(&mut f, 54, PHDR as u16); put16(&mut f, 56, 1);
phdr(&mut f, EHDR, 0x6474_e550, 4, HDR_OFF, HDR_OFF, 0x40, 4);
let h = HDR_OFF as usize;
f[h] = 1; f[h + 1] = 0x1b; f[h + 2] = 0x03; f[h + 3] = 0x3b; put32(&mut f, h + 4, 0); put32(&mut f, h + 8, starts.len() as u32); let mut row = h + 12;
for &s in &starts {
let delta = (s as i64 - HDR_OFF as i64) as i32;
put32(&mut f, row, delta as u32); put32(&mut f, row + 4, delta as u32); row += 8;
}
f
}
pub const PE_SIG: usize = 0x80;
pub const COFF: usize = PE_SIG + 0x04;
pub const OPT: usize = COFF + 0x14;
pub const DATA_DIRS: usize = OPT + 0x70;
pub const SECTION_TABLE: usize = DATA_DIRS + 16 * 8;
pub const PE_TEXT: u64 = 0x1000;
pub const PE_IDATA: u64 = 0x2000;
pub const PE_IMPORT_DIR: u64 = 0x2000;
pub const PE_INT: u64 = 0x2100;
pub const PE_IAT: u64 = 0x2210;
pub const PE_DLL_NAME: u64 = 0x2200;
pub const PE_HINT_NAME: u64 = 0x2300;
pub const PE_LOAD_CONFIG: u64 = 0x2400;
const PE_FILE_END: usize = 0x2500;
pub fn pe_with_iat_call() -> Vec<u8> {
let mut f = vec![0u8; PE_FILE_END];
f[0..2].copy_from_slice(b"MZ");
put32(&mut f, 0x3c, PE_SIG as u32);
f[PE_SIG..PE_SIG + 4].copy_from_slice(b"PE\0\0");
put16(&mut f, COFF, 0x8664); put16(&mut f, COFF + 0x02, 2); put32(&mut f, COFF + 0x04, 1); put16(&mut f, COFF + 0x10, 0xF0); put16(&mut f, COFF + 0x12, 0x22);
put16(&mut f, OPT, 0x20b); put32(&mut f, OPT + 0x04, 0x100); put32(&mut f, OPT + 0x08, 0x500); put32(&mut f, OPT + 0x10, PE_TEXT as u32); put32(&mut f, OPT + 0x14, PE_TEXT as u32); put64(&mut f, OPT + 0x18, 0); put32(&mut f, OPT + 0x20, 0x1000); put32(&mut f, OPT + 0x24, 0x200); put16(&mut f, OPT + 0x28, 6); put16(&mut f, OPT + 0x30, 6); put32(&mut f, OPT + 0x38, 0x3000); put32(&mut f, OPT + 0x3c, 0x200); put16(&mut f, OPT + 0x44, 3); put16(&mut f, OPT + 0x46, 0x40); put64(&mut f, OPT + 0x48, 0x10_0000); put64(&mut f, OPT + 0x50, 0x1000); put64(&mut f, OPT + 0x58, 0x10_0000); put64(&mut f, OPT + 0x60, 0x1000); put32(&mut f, OPT + 0x6c, 16); let dir = |i: usize| OPT + 0x70 + i * 8;
put32(&mut f, dir(1), PE_IMPORT_DIR as u32); put32(&mut f, dir(1) + 4, 0x30);
put32(&mut f, dir(10), PE_LOAD_CONFIG as u32); put32(&mut f, dir(10) + 4, 0x40);
put32(&mut f, dir(12), PE_IAT as u32); put32(&mut f, dir(12) + 4, 0x10);
const SECTION: u32 = 0x6000_0020; let sh = |i: usize| SECTION_TABLE + i * 40;
f[sh(0)..sh(0) + 8].copy_from_slice(b".text\x00\x00\x00");
put32(&mut f, sh(0) + 0x08, 0x200); put32(&mut f, sh(0) + 0x0c, PE_TEXT as u32); put32(&mut f, sh(0) + 0x10, 0x200); put32(&mut f, sh(0) + 0x14, PE_TEXT as u32); put32(&mut f, sh(0) + 0x24, SECTION); f[sh(1)..sh(1) + 8].copy_from_slice(b".idata\x00\x00");
put32(&mut f, sh(1) + 0x08, 0x500);
put32(&mut f, sh(1) + 0x0c, PE_IDATA as u32);
put32(&mut f, sh(1) + 0x10, 0x500);
put32(&mut f, sh(1) + 0x14, PE_IDATA as u32);
put32(&mut f, sh(1) + 0x24, 0xC000_0040);
let desc = PE_IMPORT_DIR as usize;
put32(&mut f, desc, PE_INT as u32); put32(&mut f, desc + 0x0c, PE_DLL_NAME as u32); put32(&mut f, desc + 0x10, PE_IAT as u32);
put32(&mut f, PE_INT as usize, PE_HINT_NAME as u32);
put32(&mut f, PE_INT as usize + 8, PE_HINT_NAME as u32 + 0x20);
f[PE_DLL_NAME as usize..PE_DLL_NAME as usize + 13].copy_from_slice(b"kernel32.dll\0");
put16(&mut f, PE_HINT_NAME as usize, 1);
f[PE_HINT_NAME as usize + 2..PE_HINT_NAME as usize + 14].copy_from_slice(b"ExitProcess\0");
put16(&mut f, PE_HINT_NAME as usize + 0x20, 2);
f[PE_HINT_NAME as usize + 0x22..PE_HINT_NAME as usize + 0x2e].copy_from_slice(b"MessageBoxA\0");
put32(&mut f, PE_LOAD_CONFIG as usize, 0x60); put64(
&mut f,
PE_LOAD_CONFIG as usize + 0x58,
0xdead_beef_cafe_f00d,
);
let t = PE_TEXT as usize;
f[t..t + 4].copy_from_slice(&[0x48, 0x83, 0xec, 0x28]); f[t + 4] = 0xff;
f[t + 5] = 0x15;
let disp = (PE_IAT as i64 - (PE_TEXT as i64 + 0x0a)) as i32;
f[t + 6..t + 0x0a].copy_from_slice(&disp.to_le_bytes());
f[t + 0x0a..t + 0x0e].copy_from_slice(&[0x48, 0x83, 0xc4, 0x28]); f[t + 0x0e] = 0xc3;
f
}
pub fn pe_with_driver() -> Vec<u8> {
let mut f = vec![0u8; 0x6800];
f[0..2].copy_from_slice(b"MZ");
put32(&mut f, 0x3c, 0x80);
f[0x80..0x84].copy_from_slice(b"PE\0\0");
put16(&mut f, 0x84, 0x8664); put16(&mut f, 0x86, 4); put32(&mut f, 0x88, 1); put16(&mut f, 0x94, 0xf0); put16(&mut f, 0x96, 0x22); put16(&mut f, 0x98, 0x20b); put32(&mut f, 0x9c, 0x200); put32(&mut f, 0xa0, 0x400); put32(&mut f, 0xa8, 0x1000); put32(&mut f, 0xac, 0x1000); put64(&mut f, 0xb0, 0); put32(&mut f, 0xb8, 0x1000); put32(&mut f, 0xbc, 0x200); put16(&mut f, 0xc0, 6); put16(&mut f, 0xc8, 6); put32(&mut f, 0xd0, 0x6800); put32(&mut f, 0xd4, 0x200); put16(&mut f, 0xdc, 1); put16(&mut f, 0xde, 0x40); put64(&mut f, 0xe0, 0x10_0000); put64(&mut f, 0xe8, 0x1000); put64(&mut f, 0xf0, 0x10_0000); put64(&mut f, 0xf8, 0x1000); put32(&mut f, 0x104, 16); let dir = |i: usize| 0x108 + i * 8;
put32(&mut f, dir(1), 0x3000); put32(&mut f, dir(1) + 4, 0x28);
put32(&mut f, dir(3), 0x2800); put32(&mut f, dir(3) + 4, 0x24);
put32(&mut f, dir(12), 0x3300); put32(&mut f, dir(12) + 4, 0x40);
const SEC_RX: u32 = 0x6000_0020; const SEC_R: u32 = 0x4000_0040; const SEC_RW: u32 = 0xC000_0040; let sh = |i: usize| 0x188 + i * 40;
f[sh(0)..sh(0) + 8].copy_from_slice(b".text\x00\x00\x00");
put32(&mut f, sh(0) + 0x08, 0x240); put32(&mut f, sh(0) + 0x0c, 0x1000); put32(&mut f, sh(0) + 0x10, 0x240); put32(&mut f, sh(0) + 0x14, 0x1000); put32(&mut f, sh(0) + 0x24, SEC_RX);
f[sh(1)..sh(1) + 8].copy_from_slice(b".rdata\x00\x00");
put32(&mut f, sh(1) + 0x08, 0x400);
put32(&mut f, sh(1) + 0x0c, 0x2000);
put32(&mut f, sh(1) + 0x10, 0x400);
put32(&mut f, sh(1) + 0x14, 0x2000);
put32(&mut f, sh(1) + 0x24, SEC_R);
f[sh(2)..sh(2) + 8].copy_from_slice(b".pdata\x00\x00");
put32(&mut f, sh(2) + 0x08, 0x30);
put32(&mut f, sh(2) + 0x0c, 0x2800);
put32(&mut f, sh(2) + 0x10, 0x30);
put32(&mut f, sh(2) + 0x14, 0x2800);
put32(&mut f, sh(2) + 0x24, SEC_R);
f[sh(3)..sh(3) + 8].copy_from_slice(b".idata\x00\x00");
put32(&mut f, sh(3) + 0x08, 0x600);
put32(&mut f, sh(3) + 0x0c, 0x3000);
put32(&mut f, sh(3) + 0x10, 0x600);
put32(&mut f, sh(3) + 0x14, 0x3000);
put32(&mut f, sh(3) + 0x24, SEC_RW);
put16(&mut f, 0x2000, 0x12);
put16(&mut f, 0x2000 + 2, 0x12);
put64(&mut f, 0x2000 + 8, 0x2010);
f[0x2010..0x2010 + b"\\Device\\Knifelab\0".len()].copy_from_slice(b"\\Device\\Knifelab\0");
put16(&mut f, 0x2080, 0x16);
put16(&mut f, 0x2080 + 2, 0x16);
put64(&mut f, 0x2080 + 8, 0x2090);
f[0x2090..0x2090 + b"\\DosDevices\\Knifelab\0".len()]
.copy_from_slice(b"\\DosDevices\\Knifelab\0");
put32(&mut f, 0x2800, 0x1000);
put32(&mut f, 0x2800 + 4, 0x1050);
put32(&mut f, 0x2800 + 8, 0);
put32(&mut f, 0x2800 + 12, 0x1100);
put32(&mut f, 0x2800 + 16, 0x1150);
put32(&mut f, 0x2800 + 20, 0);
put32(&mut f, 0x2800 + 24, 0x1200);
put32(&mut f, 0x2800 + 28, 0x1220);
put32(&mut f, 0x2800 + 32, 0);
let desc = 0x3000;
put32(&mut f, desc, 0x3100); put32(&mut f, desc + 0x10, 0x3300); put32(&mut f, desc + 0x0c, 0x3200); put32(&mut f, 0x3100, 0x3400);
put32(&mut f, 0x3100 + 8, 0x3420);
put32(&mut f, 0x3100 + 16, 0x3430);
put32(&mut f, 0x3100 + 24, 0x3440);
put32(&mut f, 0x3100 + 32, 0x3450);
put64(&mut f, 0x3100 + 40, 0x8000_0000_0000_0000 | 1310);
put32(&mut f, 0x3300, 0x3400); put32(&mut f, 0x3308, 0x3420); put32(&mut f, 0x3310, 0x3430); put32(&mut f, 0x3318, 0x3440); put32(&mut f, 0x3320, 0x3450); f[0x3200..0x3200 + b"ntoskrnl.dll\0".len()].copy_from_slice(b"ntoskrnl.dll\0");
f[0x3400..0x3400 + b"\0\0IoCreateDevice\0".len()].copy_from_slice(b"\0\0IoCreateDevice\0");
f[0x3420..0x3420 + b"\0\0IoCreateSymbolicLink\0".len()]
.copy_from_slice(b"\0\0IoCreateSymbolicLink\0");
f[0x3430..0x3430 + b"\0\0IoGetCurrentIrpStackLocation\0".len()]
.copy_from_slice(b"\0\0IoGetCurrentIrpStackLocation\0");
f[0x3440..0x3440 + b"\0\0MmMapIoSpace\0".len()].copy_from_slice(b"\0\0MmMapIoSpace\0");
f[0x3450..0x3450 + b"\0\0RtlCopyMemory\0".len()].copy_from_slice(b"\0\0RtlCopyMemory\0");
let e = 0x1000usize;
f[e] = 0x53; f[e + 1..e + 5].copy_from_slice(&[0x48, 0x83, 0xec, 0x28]); f[e + 5..e + 8].copy_from_slice(&[0x48, 0x89, 0xcb]); f[e + 8..e + 11].copy_from_slice(&[0x48, 0x8d, 0x0d]); put32(&mut f, e + 11, (0x2000u64 - (e as u64 + 15)) as u32);
f[e + 15..e + 17].copy_from_slice(&[0x33, 0xd2]); f[e + 17..e + 23].copy_from_slice(&[0x41, 0xb8, 0x22, 0, 0, 0]); f[e + 23..e + 26].copy_from_slice(&[0x4c, 0x8d, 0x0d]); put32(&mut f, e + 26, (0x2020u64 - (e as u64 + 30)) as u32);
f[e + 30..e + 32].copy_from_slice(&[0xff, 0x15]); put32(&mut f, e + 32, (0x3300u64 - (e as u64 + 36)) as u32);
f[e + 36..e + 39].copy_from_slice(&[0x48, 0x8d, 0x05]);
put32(&mut f, e + 39, (0x1100u64 - (e as u64 + 43)) as u32);
f[e + 43..e + 50].copy_from_slice(&[0x48, 0x89, 0x83, 0xe0, 0, 0, 0]);
f[e + 50..e + 53].copy_from_slice(&[0x48, 0x8d, 0x0d]);
put32(&mut f, e + 53, (0x2080u64 - (e as u64 + 57)) as u32);
f[e + 57..e + 59].copy_from_slice(&[0xff, 0x15]);
put32(&mut f, e + 59, (0x3308u64 - (e as u64 + 63)) as u32);
f[e + 63..e + 65].copy_from_slice(&[0x33, 0xc0]); f[e + 65..e + 69].copy_from_slice(&[0x48, 0x83, 0xc4, 0x28]); f[e + 69] = 0x5b; f[e + 70] = 0xc3;
let d = 0x1100usize;
f[d..d + 4].copy_from_slice(&[0x48, 0x83, 0xec, 0x28]); f[d + 4..d + 6].copy_from_slice(&[0xff, 0x15]); put32(&mut f, d + 6, (0x3310u64 - (d as u64 + 10)) as u32);
f[d + 10..d + 13].copy_from_slice(&[0x8b, 0x40, 0x10]); f[d + 13..d + 18].copy_from_slice(&[0x3d, 0x40, 0xc0, 0x22, 0x00]); f[d + 18..d + 20].copy_from_slice(&[0x75, 0x2a]); f[d + 20..d + 23].copy_from_slice(&[0x8b, 0x40, 0x18]); f[d + 23..d + 26].copy_from_slice(&[0x83, 0xe8, 0x04]); f[d + 26..d + 29].copy_from_slice(&[0x41, 0x89, 0xc0]); f[d + 29..d + 32].copy_from_slice(&[0x48, 0x8d, 0x0d]); put32(&mut f, d + 32, (0x2010u64 - (d as u64 + 36)) as u32);
f[d + 36..d + 39].copy_from_slice(&[0x48, 0x8d, 0x15]); put32(&mut f, d + 39, (0x2090u64 - (d as u64 + 43)) as u32);
f[d + 43..d + 45].copy_from_slice(&[0xff, 0x15]); put32(&mut f, d + 45, (0x3320u64 - (d as u64 + 49)) as u32);
f[d + 49..d + 52].copy_from_slice(&[0x48, 0x8d, 0x0d]); put32(&mut f, d + 52, (0x2010u64 - (d as u64 + 56)) as u32);
f[d + 56..d + 58].copy_from_slice(&[0xff, 0x15]); put32(&mut f, d + 58, (0x3318u64 - (d as u64 + 62)) as u32);
f[d + 62..d + 64].copy_from_slice(&[0x33, 0xc0]); f[d + 64..d + 68].copy_from_slice(&[0x48, 0x83, 0xc4, 0x28]);
f[d + 68] = 0xc3;
let h = 0x1200usize;
f[h..h + 4].copy_from_slice(&[0x48, 0x83, 0xec, 0x28]); f[h + 4..h + 6].copy_from_slice(&[0xff, 0x15]); put32(&mut f, h + 6, (0x3328u64 - (h as u64 + 10)) as u32);
f[h + 10..h + 12].copy_from_slice(&[0x33, 0xc0]); f[h + 12..h + 16].copy_from_slice(&[0x48, 0x83, 0xc4, 0x28]);
f[h + 16] = 0xc3;
f
}
pub fn macho_with_bind() -> Vec<u8> {
let mut f = vec![0u8; 0x3000];
const PATH: &str = "/usr/lib/libSystem.B.dylib";
let path_len = PATH.len() + 1;
let path_padded = (path_len + 7) & !7;
let seg_size = 72 + 80; let dylib_size = 24 + path_padded; let dyld_size = 48; let main_size = 24; let total_cmds = seg_size + dylib_size + dyld_size + main_size;
let seg = 32usize;
let dylib = seg + seg_size;
let dyld = dylib + dylib_size;
let main = dyld + dyld_size;
put32(&mut f, 0, 0xfeed_facf); put32(&mut f, 4, 0x0100_0007); put32(&mut f, 8, 0x3); put32(&mut f, 12, 0x2); put32(&mut f, 16, 4); put32(&mut f, 20, total_cmds as u32); put32(&mut f, 24, 0x0020_0000);
put32(&mut f, seg, 0x19); put32(&mut f, seg + 4, seg_size as u32);
f[seg + 8..seg + 24].copy_from_slice(b"__TEXT\0\0\0\0\0\0\0\0\0\0");
put64(&mut f, seg + 24, 0x1000); put64(&mut f, seg + 32, 0x1000); put64(&mut f, seg + 40, 0x1000); put64(&mut f, seg + 48, 0x10); put32(&mut f, seg + 56, 0x5); put32(&mut f, seg + 60, 0x5); put32(&mut f, seg + 64, 1); put32(&mut f, seg + 68, 0); let s = seg + 72;
f[s..s + 16].copy_from_slice(b"__text\0\0\0\0\0\0\0\0\0\0");
f[s + 16..s + 32].copy_from_slice(b"__TEXT\0\0\0\0\0\0\0\0\0\0");
put64(&mut f, s + 32, 0x1000); put64(&mut f, s + 40, 0x10); put32(&mut f, s + 48, 0x1000); put32(&mut f, s + 52, 0x2); put32(&mut f, s + 56, 0); put32(&mut f, s + 60, 0); put32(&mut f, s + 64, 0x0000_0400);
put32(&mut f, dylib, 0xc); put32(&mut f, dylib + 4, dylib_size as u32);
put32(&mut f, dylib + 8, 24); put32(&mut f, dylib + 12, 0); put32(&mut f, dylib + 16, 0x10000); put32(&mut f, dylib + 20, 0x10000); f[dylib + 24..dylib + 24 + PATH.len()].copy_from_slice(PATH.as_bytes());
put32(&mut f, dyld, 0x8000_0022);
put32(&mut f, dyld + 4, 0x30);
put32(&mut f, dyld + 8, 0); put32(&mut f, dyld + 12, 0); put32(&mut f, dyld + 16, 0x2200); put32(&mut f, dyld + 20, 8); put32(&mut f, dyld + 24, 0); put32(&mut f, dyld + 28, 0); put32(&mut f, dyld + 32, 0); put32(&mut f, dyld + 36, 0); put32(&mut f, dyld + 40, 0); put32(&mut f, dyld + 44, 0);
put32(&mut f, main, 0x8000_0028); put32(&mut f, main + 4, main_size as u32);
put64(&mut f, main + 8, 0x1000); put64(&mut f, main + 16, 0x1000);
let b = 0x2200usize;
f[b] = 0x40; f[b + 1..b + 6].copy_from_slice(b"puts\0");
f[b + 6] = 0x90; f[b + 7] = 0x00;
f[0x1000..0x1007].copy_from_slice(&[0x48, 0xc7, 0xc0, 0, 0, 0, 0]);
f[0x1007] = 0xc3;
f
}
#[allow(clippy::too_many_arguments)]
fn phdr(f: &mut [u8], at: usize, ty: u32, flags: u32, off: u64, va: u64, sz: u64, align: u64) {
put32(f, at, ty);
put32(f, at + 4, flags);
put64(f, at + 8, off);
put64(f, at + 16, va);
put64(f, at + 24, va); put64(f, at + 32, sz); put64(f, at + 40, sz); put64(f, at + 48, align);
}
fn shdr(f: &mut [u8], at: usize, name: u32, ty: u32, flags: u64, addr: u64, size: u64) {
put32(f, at, name);
put32(f, at + 4, ty);
put64(f, at + 8, flags);
put64(f, at + 16, addr);
put64(f, at + 24, addr); put64(f, at + 32, size);
put64(f, at + 48, 1); }
#[cfg(test)]
mod tests {
use super::*;
use crate::model::{Format, SymKind};
#[test]
fn the_fixture_parses_as_an_elf() {
let bytes = elf_with_plt_call();
let bin = crate::formats::analyze("fixture.elf", &bytes).expect("parses");
assert_eq!(bin.format, Format::Elf);
assert_eq!(bin.bits, 64);
assert_eq!(bin.entry, P_TEXT);
assert!(bin.sections.iter().any(|s| s.name == ".plt" && s.exec));
}
#[test]
fn the_relocation_names_the_got_slot() {
let bytes = elf_with_plt_call();
let bin = crate::formats::analyze("fixture.elf", &bytes).unwrap();
let slot = bin
.symbols
.iter()
.find(|s| s.kind == SymKind::Import)
.expect("an import symbol from .rela.plt");
assert_eq!(slot.addr, P_GOT);
assert_eq!(slot.name, "strcpy");
}
#[test]
fn the_pe_fixture_parses_and_imports() {
let bytes = pe_with_iat_call();
let bin = crate::formats::analyze("fixture.exe", &bytes).expect("parses");
assert_eq!(bin.format, Format::Pe);
assert_eq!(bin.bits, 64);
assert_eq!(bin.entry, PE_TEXT);
assert_eq!(bin.image_base, 0);
assert!(bin.sections.iter().any(|s| s.name == ".text" && s.exec));
let lib = bin
.imports
.iter()
.find(|l| l.name == "kernel32.dll")
.expect("the DLL is imported");
assert_eq!(lib.functions, ["ExitProcess", "MessageBoxA"]);
let slot = bin
.symbols
.iter()
.find(|s| s.name == "kernel32!ExitProcess")
.expect("IAT slot symbol");
assert_eq!(slot.addr, PE_IAT);
assert_eq!(slot.kind, SymKind::Import);
}
#[test]
fn the_pe_load_config_is_parseable() {
let bytes = pe_with_iat_call();
let bin = crate::formats::analyze("fixture.exe", &bytes).unwrap();
let lc = bin.hardening.load_config.expect("load config parsed");
assert_eq!(lc.security_cookie, 0xdead_beef_cafe_f00d);
}
#[test]
fn the_pe_engine_names_the_iat_call() {
let bytes = pe_with_iat_call();
let bin = crate::formats::analyze("fixture.exe", &bytes).unwrap();
let an = crate::analysis::engine::analyze(&bin, &bytes, 1000, &crate::db::Db::default());
let refs = an.xrefs_to.get(&PE_IAT).expect("the slot is referenced");
assert!(refs
.iter()
.any(|x| crate::analysis::engine::XrefKind::Call == x.kind));
let caller = an.functions.iter().find(|f| f.addr == PE_TEXT).unwrap();
assert_eq!(caller.calls, vec![PE_IAT]);
}
#[test]
fn the_driver_fixture_parses_as_native_with_kernel_imports() {
let bytes = pe_with_driver();
let bin = crate::formats::analyze("fixture.sys", &bytes).expect("parses");
assert_eq!(bin.format, Format::Pe);
assert_eq!(bin.bits, 64);
assert_eq!(bin.subsystem.as_deref(), Some("native"));
assert_eq!(bin.entry, 0x1000);
let lib = bin
.imports
.iter()
.find(|l| l.name == "ntoskrnl.dll")
.expect("ntoskrnl imported");
assert!(lib.functions.iter().any(|n| n == "IoCreateDevice"));
assert!(lib.functions.iter().any(|n| n == "MmMapIoSpace"));
assert!(lib.ordinals.iter().any(|o| o.is_some()));
assert!(lib.functions.iter().any(|n| n == "KeInitializeMutex"));
assert!(bin.func_hints.contains(&0x1100));
}
#[test]
fn the_driver_fixture_engine_lands_on_pdata_and_kernel_sinks() {
let bytes = pe_with_driver();
let bin = crate::formats::analyze("fixture.sys", &bytes).unwrap();
let an = crate::analysis::engine::analyze(&bin, &bytes, 100_000, &crate::db::Db::default());
assert!(an.functions.iter().any(|f| f.addr == 0x1000), "DriverEntry");
assert!(
an.functions.iter().any(|f| f.addr == 0x1100),
"DispatchDeviceControl, seeded from .pdata"
);
let hits = crate::analysis::sinks::find(&an);
let phys = hits
.iter()
.find(|h| h.api == "MmMapIoSpace")
.expect("physical-mem sink flagged from kernel catalog");
assert!(!phys.sites.is_empty());
let dev = hits
.iter()
.find(|h| h.api == "IoCreateDevice")
.expect("device-create flagged from kernel catalog");
assert!(!dev.sites.is_empty());
}
#[test]
fn the_macho_fixture_parses_and_binds() {
let bytes = macho_with_bind();
let bin = crate::formats::analyze("fixture.bin", &bytes).expect("parses");
assert_eq!(bin.format, Format::MachO);
assert_eq!(bin.bits, 64);
assert_eq!(bin.entry, 0x1000);
assert!(bin
.sections
.iter()
.any(|s| s.name == "__TEXT,__text" && s.exec));
assert!(!bin.imports.is_empty(), "bind bytecode produced imports");
assert!(bin
.imports
.iter()
.flat_map(|l| l.functions.iter())
.any(|n| n == "puts"));
assert!(bin.libs.iter().any(|l| l.contains("libSystem")));
}
#[test]
fn the_macho_engine_entries_into_text() {
let bytes = macho_with_bind();
let bin = crate::formats::analyze("fixture.bin", &bytes).unwrap();
let an = crate::analysis::engine::analyze(&bin, &bytes, 1000, &crate::db::Db::default());
assert!(
an.functions.iter().any(|f| f.addr == 0x1000),
"entry function at 0x1000"
);
}
}