use std::path::Path;
const MH_MAGIC_64: u32 = 0xfeed_facf;
const LC_SEGMENT_64: u32 = 0x19;
const LC_SYMTAB: u32 = 0x02;
const LC_DYSYMTAB: u32 = 0x0b;
const LC_DYLD_INFO: u32 = 0x22;
const LC_DYLD_INFO_ONLY: u32 = 0x8000_0022;
const LC_FUNCTION_STARTS: u32 = 0x26;
const LC_DATA_IN_CODE: u32 = 0x29;
const LC_CODE_SIGNATURE: u32 = 0x1d;
const LC_DYLD_CHAINED_FIXUPS: u32 = 0x8000_0034;
const LC_DYLD_EXPORTS_TRIE: u32 = 0x8000_0033;
const CPU_TYPE_ARM64: u32 = 0x0100_000c;
const MACH_HEADER_64_SIZE: usize = 32;
const SEGMENT_COMMAND_64_SIZE: usize = 72;
const SECTION_64_SIZE: usize = 80;
const NEW_LC_SIZE: usize = SEGMENT_COMMAND_64_SIZE + SECTION_64_SIZE;
const VM_PROT_READ: u32 = 0x01;
fn u32_le(bytes: &[u8], off: usize) -> Result<u32, String> {
bytes
.get(off..off + 4)
.and_then(|s| s.try_into().ok())
.map(u32::from_le_bytes)
.ok_or_else(|| format!("truncated u32 at offset {off}"))
}
fn u64_le(bytes: &[u8], off: usize) -> Result<u64, String> {
bytes
.get(off..off + 8)
.and_then(|s| s.try_into().ok())
.map(u64::from_le_bytes)
.ok_or_else(|| format!("truncated u64 at offset {off}"))
}
fn put_u32(bytes: &mut [u8], off: usize, value: u32) -> Result<(), String> {
bytes
.get_mut(off..off + 4)
.ok_or_else(|| format!("truncated u32 write at offset {off}"))?
.copy_from_slice(&value.to_le_bytes());
Ok(())
}
fn put_u64(bytes: &mut [u8], off: usize, value: u64) -> Result<(), String> {
bytes
.get_mut(off..off + 8)
.ok_or_else(|| format!("truncated u64 write at offset {off}"))?
.copy_from_slice(&value.to_le_bytes());
Ok(())
}
fn round_up(value: u64, align: u64) -> u64 {
if align == 0 {
return value;
}
value.div_ceil(align) * align
}
struct OffsetField {
at: usize,
}
struct Layout {
page_size: u64,
linkedit_lc_off: usize,
linkedit_fileoff: u64,
linkedit_vmaddr: u64,
code_sig: Option<CodeSig>,
first_section_offset: u64,
end_of_lc: usize,
linkedit_pointers: Vec<OffsetField>,
}
struct CodeSig {
lc_off: usize,
dataoff: u64,
}
fn name_eq(slot: &[u8], want: &[u8]) -> bool {
slot.len() >= want.len()
&& &slot[..want.len()] == want
&& slot[want.len()..].iter().all(|&b| b == 0)
}
fn read_layout(bytes: &[u8]) -> Result<Layout, String> {
if u32_le(bytes, 0)? != MH_MAGIC_64 {
return Err("not a 64-bit little-endian Mach-O (bad magic)".to_string());
}
let cputype = u32_le(bytes, 4)?;
let page_size: u64 = if cputype == CPU_TYPE_ARM64 {
0x4000
} else {
0x1000
};
let ncmds = u32_le(bytes, 16)?;
let mut linkedit_lc_off: Option<usize> = None;
let mut linkedit_fileoff = 0u64;
let mut linkedit_vmaddr = 0u64;
let mut code_sig: Option<CodeSig> = None;
let mut first_section_offset = u64::MAX;
let mut linkedit_pointers: Vec<OffsetField> = Vec::new();
let mut off = MACH_HEADER_64_SIZE;
for _ in 0..ncmds {
let cmd = u32_le(bytes, off)?;
let cmdsize = u32_le(bytes, off + 4)? as usize;
if cmdsize < 8 || off + cmdsize > bytes.len() {
return Err(format!(
"malformed load command at {off} (cmdsize {cmdsize})"
));
}
match cmd {
LC_SEGMENT_64 => {
let segname = bytes
.get(off + 8..off + 24)
.ok_or_else(|| format!("truncated segname at {off}"))?;
let fileoff = u64_le(bytes, off + 40)?;
let nsects = u32_le(bytes, off + 64)?;
if name_eq(segname, b"__LINKEDIT") {
linkedit_lc_off = Some(off);
linkedit_fileoff = fileoff;
linkedit_vmaddr = u64_le(bytes, off + 24)?;
}
let mut soff = off + SEGMENT_COMMAND_64_SIZE;
for _ in 0..nsects {
let sect_off = u32_le(bytes, soff + 48)? as u64;
if sect_off != 0 && sect_off < first_section_offset {
first_section_offset = sect_off;
}
soff = soff
.checked_add(SECTION_64_SIZE)
.ok_or_else(|| "section table offset overflow".to_string())?;
}
}
LC_DYLD_INFO | LC_DYLD_INFO_ONLY => {
for field in [8, 16, 24, 32, 40] {
linkedit_pointers.push(OffsetField { at: off + field });
}
}
LC_SYMTAB => {
linkedit_pointers.push(OffsetField { at: off + 8 });
linkedit_pointers.push(OffsetField { at: off + 16 });
}
LC_DYSYMTAB => {
for field in [32, 40, 48, 56, 64, 72] {
linkedit_pointers.push(OffsetField { at: off + field });
}
}
LC_FUNCTION_STARTS | LC_DATA_IN_CODE | LC_DYLD_CHAINED_FIXUPS | LC_DYLD_EXPORTS_TRIE => {
linkedit_pointers.push(OffsetField { at: off + 8 });
}
LC_CODE_SIGNATURE => {
code_sig = Some(CodeSig {
lc_off: off,
dataoff: u32_le(bytes, off + 8)? as u64,
});
}
_ => {}
}
off = off
.checked_add(cmdsize)
.ok_or_else(|| "load command offset overflow".to_string())?;
}
let linkedit_lc_off =
linkedit_lc_off.ok_or_else(|| "no __LINKEDIT segment to anchor the new section".to_string())?;
if first_section_offset == u64::MAX {
return Err("no mapped section to bound the header slack".to_string());
}
Ok(Layout {
page_size,
linkedit_lc_off,
linkedit_fileoff,
linkedit_vmaddr,
code_sig,
first_section_offset,
end_of_lc: off,
linkedit_pointers,
})
}
fn build_segment_lc(
body_len: u64,
delta: u64,
fileoff: u64,
vmaddr: u64,
) -> Result<Vec<u8>, String> {
let mut lc = vec![0u8; NEW_LC_SIZE];
put_u32(&mut lc, 0, LC_SEGMENT_64)?;
put_u32(&mut lc, 4, NEW_LC_SIZE as u32)?;
lc.get_mut(8..12)
.ok_or_else(|| "new LC too short for segname".to_string())?
.copy_from_slice(b"SMOL"); put_u64(&mut lc, 24, vmaddr)?; put_u64(&mut lc, 32, delta)?; put_u64(&mut lc, 40, fileoff)?; put_u64(&mut lc, 48, body_len)?; put_u32(&mut lc, 56, VM_PROT_READ)?; put_u32(&mut lc, 60, VM_PROT_READ)?; put_u32(&mut lc, 64, 1)?; put_u32(&mut lc, 68, 0)?; let s = SEGMENT_COMMAND_64_SIZE;
lc.get_mut(s..s + 9)
.ok_or_else(|| "new LC too short for sectname".to_string())?
.copy_from_slice(b"__DECMPFS"); lc.get_mut(s + 16..s + 20)
.ok_or_else(|| "new LC too short for section segname".to_string())?
.copy_from_slice(b"SMOL"); put_u64(&mut lc, s + 32, vmaddr)?; put_u64(&mut lc, s + 40, body_len)?; put_u32(&mut lc, s + 48, fileoff as u32)?; put_u32(&mut lc, s + 52, 2)?; Ok(lc)
}
#[allow(dead_code)] pub(crate) fn inject_payload(stub: &[u8], section_body: &[u8]) -> Result<Vec<u8>, String> {
match stub.first().copied() {
Some(0xcf) if stub.get(0..4) == Some(&MH_MAGIC_64.to_le_bytes()) => {
inject_macho(stub, section_body)
}
Some(0x7f) if stub.get(0..4) == Some(b"\x7fELF") => Ok(append_footer(stub, section_body)),
Some(0x4d) if stub.get(0..2) == Some(b"MZ") => Ok(append_footer(stub, section_body)),
_ => Err("unrecognized stub format: not a 64-bit LE Mach-O, ELF, or PE".to_string()),
}
}
fn append_footer(stub: &[u8], footer: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(stub.len() + footer.len());
out.extend_from_slice(stub);
out.extend_from_slice(footer);
out
}
fn inject_macho(stub: &[u8], section_body: &[u8]) -> Result<Vec<u8>, String> {
let layout = read_layout(stub)?;
let slack = (layout.first_section_offset as usize)
.checked_sub(layout.end_of_lc)
.ok_or_else(|| {
"first mapped section precedes the end of load commands (corrupt layout)".to_string()
})?;
if slack < NEW_LC_SIZE {
return Err(format!(
"header slack {slack} < {NEW_LC_SIZE} bytes for the new segment command; \
rebuild the stub with -headerpad,0x1000"
));
}
let body_len = section_body.len() as u64;
let delta = round_up(body_len, layout.page_size);
let new_fileoff = layout.linkedit_fileoff;
let new_vmaddr = layout.linkedit_vmaddr;
let linkedit_start = layout.linkedit_fileoff as usize;
let linkedit_end = match &layout.code_sig {
Some(sig) => sig.dataoff as usize,
None => stub.len(),
};
if linkedit_end < linkedit_start {
return Err("code signature precedes __LINKEDIT (corrupt layout)".to_string());
}
if (linkedit_end as u64)
.checked_add(delta)
.is_none_or(|end| end > u64::from(u32::MAX))
{
return Err(format!(
"injected payload too large: file offset {linkedit_end} + {delta} exceeds the u32 Mach-O limit"
));
}
let end_after_new_lc = layout
.end_of_lc
.checked_add(NEW_LC_SIZE)
.ok_or_else(|| "end-of-load-commands offset overflow".to_string())?;
let stub_before_linkedit = stub
.get(0..layout.linkedit_lc_off)
.ok_or_else(|| "__LINKEDIT LC offset out of range".to_string())?;
let stub_lc_tail = stub
.get(layout.linkedit_lc_off..layout.end_of_lc)
.ok_or_else(|| "load-command tail out of range".to_string())?;
let stub_headerpad_tail = stub
.get(end_after_new_lc..linkedit_start)
.ok_or_else(|| "headerpad tail out of range".to_string())?;
let stub_linkedit_body = stub
.get(linkedit_start..linkedit_end)
.ok_or_else(|| "__LINKEDIT body out of range".to_string())?;
let new_lc = build_segment_lc(body_len, delta, new_fileoff, new_vmaddr)?;
let pad_len = (delta - body_len) as usize;
let mut out: Vec<u8> = Vec::with_capacity(stub.len() + delta as usize);
out.extend_from_slice(stub_before_linkedit);
out.extend_from_slice(&new_lc);
out.extend_from_slice(stub_lc_tail);
out.extend_from_slice(stub_headerpad_tail);
out.extend_from_slice(section_body);
out.resize(
out
.len()
.checked_add(pad_len)
.ok_or_else(|| "padded section length overflow".to_string())?,
0,
);
out.extend_from_slice(stub_linkedit_body);
let ncmds = u32_le(&out, 16)?;
put_u32(&mut out, 16, ncmds + 1)?;
let sizeofcmds = u32_le(&out, 20)?;
put_u32(&mut out, 20, sizeofcmds + NEW_LC_SIZE as u32)?;
let le_lc = layout
.linkedit_lc_off
.checked_add(NEW_LC_SIZE)
.ok_or_else(|| "__LINKEDIT LC offset overflow after splice".to_string())?;
let le_fileoff = u64_le(&out, le_lc + 40)?;
put_u64(&mut out, le_lc + 40, le_fileoff + delta)?;
let le_vmaddr = u64_le(&out, le_lc + 24)?;
put_u64(&mut out, le_lc + 24, le_vmaddr + delta)?;
if let Some(sig) = &layout.code_sig {
let remaining = sig
.dataoff
.checked_sub(layout.linkedit_fileoff)
.ok_or_else(|| "code signature precedes __LINKEDIT fileoff".to_string())?;
put_u64(&mut out, le_lc + 48, remaining)?; put_u64(&mut out, le_lc + 32, round_up(remaining, layout.page_size))?; }
for field in &layout.linkedit_pointers {
let at = if field.at >= layout.linkedit_lc_off {
field
.at
.checked_add(NEW_LC_SIZE)
.ok_or_else(|| "linkedit-pointer offset overflow after splice".to_string())?
} else {
field.at
};
let current = u32_le(&out, at)?;
if current != 0 {
put_u32(&mut out, at, current + delta as u32)?;
}
}
if let Some(sig) = &layout.code_sig {
let sig_lc = sig
.lc_off
.checked_add(NEW_LC_SIZE)
.ok_or_else(|| "code-signature LC offset overflow after splice".to_string())?;
let sig_cmdsize = u32_le(&out, sig_lc + 4)? as usize;
let ncmds = u32_le(&out, 16)?;
put_u32(&mut out, 16, ncmds - 1)?;
let sizeofcmds = u32_le(&out, 20)?;
put_u32(&mut out, 20, sizeofcmds - sig_cmdsize as u32)?;
let zero_range = out
.get_mut(sig_lc..sig_lc + sig_cmdsize)
.ok_or_else(|| "code-signature LC out of range after splice".to_string())?;
zero_range.fill(0);
}
Ok(out)
}
#[cfg(not(target_os = "macos"))]
#[allow(dead_code)] pub(crate) fn resign(_path: &Path) -> Result<(), String> {
Ok(())
}
#[cfg(target_os = "macos")]
#[allow(dead_code)] pub(crate) fn resign(path: &Path) -> Result<(), String> {
let status = std::process::Command::new("codesign")
.arg("-s")
.arg("-")
.arg("-f")
.arg(path)
.status()
.map_err(|source| format!("spawn codesign for {}: {source}", path.display()))?;
if !status.success() {
return Err(format!("codesign failed for {} ({status})", path.display()));
}
Ok(())
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
use crate::exe::section::{build_footer, find_footer};
#[cfg(target_os = "macos")]
use crate::exe::section::{build_section_payload, find_section, parse_section_payload};
#[cfg(target_os = "macos")]
fn minimal_macho64_with_linkedit(
first_section_offset: u32,
linkedit_fileoff: u64,
linkedit_body: &[u8],
) -> Vec<u8> {
let linkedit_lc_off = MACH_HEADER_64_SIZE + NEW_LC_SIZE; let linkedit_len = linkedit_body.len() as u64;
let mut m = vec![0u8; linkedit_fileoff as usize + linkedit_body.len()];
put_u32(&mut m, 0, MH_MAGIC_64).expect("header magic");
put_u32(&mut m, 16, 2).expect("ncmds"); put_u32(&mut m, 20, (NEW_LC_SIZE + SEGMENT_COMMAND_64_SIZE) as u32).expect("sizeofcmds");
let text = MACH_HEADER_64_SIZE;
put_u32(&mut m, text, LC_SEGMENT_64).expect("text cmd");
put_u32(&mut m, text + 4, NEW_LC_SIZE as u32).expect("text cmdsize");
m[text + 8..text + 14].copy_from_slice(b"__TEXT");
put_u32(&mut m, text + 64, 1).expect("text nsects");
let text_sect = text + SEGMENT_COMMAND_64_SIZE;
m[text_sect..text_sect + 6].copy_from_slice(b"__text");
m[text_sect + 16..text_sect + 22].copy_from_slice(b"__TEXT");
put_u32(&mut m, text_sect + 48, first_section_offset).expect("text section offset");
put_u32(&mut m, linkedit_lc_off, LC_SEGMENT_64).expect("linkedit cmd");
put_u32(&mut m, linkedit_lc_off + 4, SEGMENT_COMMAND_64_SIZE as u32).expect("linkedit cmdsize");
m[linkedit_lc_off + 8..linkedit_lc_off + 18].copy_from_slice(b"__LINKEDIT");
put_u64(
&mut m,
linkedit_lc_off + 24,
0x1_0000_0000 + linkedit_fileoff,
)
.expect("linkedit vmaddr");
put_u64(&mut m, linkedit_lc_off + 40, linkedit_fileoff).expect("linkedit fileoff");
put_u64(&mut m, linkedit_lc_off + 48, linkedit_len).expect("linkedit filesize");
m[linkedit_fileoff as usize..linkedit_fileoff as usize + linkedit_body.len()]
.copy_from_slice(linkedit_body);
m
}
#[cfg(target_os = "macos")]
#[test]
fn macho_injection_round_trips_through_find_section() {
let body = build_section_payload(0x0123_4567_89ab_cdef, b"the-zstd-payload-bytes");
let stub = minimal_macho64_with_linkedit(512, 600, b"LINKEDIT-CONTENT");
let out = inject_payload(&stub, &body).expect("inject");
let raw = find_section(&out).expect("section found");
let got = parse_section_payload(raw).expect("parses");
assert_eq!(got.content_hash, 0x0123_4567_89ab_cdef);
assert_eq!(got.payload, b"the-zstd-payload-bytes");
}
#[test]
fn elf_and_pe_injection_append_the_footer_verbatim() {
let footer = build_footer(0xfeed_face_dead_beef, b"the-zstd-bytes");
let mut elf_stub = vec![0u8; 64];
elf_stub[0..4].copy_from_slice(b"\x7fELF");
let elf_out = inject_payload(&elf_stub, &footer).expect("inject elf");
assert_eq!(
find_footer(&elf_out).expect("footer found"),
footer.as_slice()
);
let mut pe_stub = vec![0u8; 64];
pe_stub[0..2].copy_from_slice(b"MZ");
let pe_out = inject_payload(&pe_stub, &footer).expect("inject pe");
assert_eq!(
find_footer(&pe_out).expect("footer found"),
footer.as_slice()
);
}
#[test]
fn dispatch_rejects_unknown_format() {
assert!(inject_payload(b"not an object file", b"x").is_err());
}
#[cfg(not(target_os = "macos"))]
#[test]
fn resign_is_a_no_op_off_macos() {
assert!(resign(Path::new("/does/not/exist")).is_ok());
}
}