#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::string::{String, ToString};
#[cfg(not(feature = "std"))]
use alloc::vec;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use goblin::elf::header::{ET_DYN, ET_EXEC, ET_REL};
use goblin::elf::program_header::PT_LOAD;
use goblin::elf::section_header::{SHT_NOBITS, SHT_NULL};
use goblin::elf::Elf;
use thiserror::Error;
use crate::limits::{LimitsError, VerifyLimits};
pub const TA_SIGNATURE_SECTION: &str = ".ta_signature";
pub const SHSTRTAB_SECTION: &str = ".shstrtab";
pub const SYMTAB_SECTION: &str = ".symtab";
pub const STRTAB_SECTION: &str = ".strtab";
#[derive(Debug, Error)]
pub enum PlainError {
#[error("ELF 解析失败: {0}")]
Goblin(String),
#[error("ELF 中已存在节 `{0}`,请先删除 `.ta_signature` 后再签名")]
TaSignatureSectionExists(String),
#[error("ELF 中不存在节 `{0}`")]
TaSignatureSectionMissing(String),
#[error("节 `{0}` 无文件内容(SHT_NOBITS)")]
TaSignatureSectionNobits(String),
#[error("节 `{0}` 为空")]
TaSignatureSectionEmpty(String),
#[error("无效的节名字符串表")]
InvalidShstrtab,
#[cfg(feature = "std")]
#[error("ELF 重写失败: {0}")]
ElfRewrite(String),
#[error("节 `{name}` 的文件范围越界: offset={offset} size={size} file_len={file_len}")]
SectionOutOfBounds {
name: String,
offset: u64,
size: u64,
file_len: usize,
},
#[error("{0}")]
ResourceLimit(#[from] LimitsError),
}
impl From<goblin::error::Error> for PlainError {
fn from(e: goblin::error::Error) -> Self {
PlainError::Goblin(e.to_string())
}
}
pub fn has_ta_signature_section(elf: &Elf<'_>) -> Result<bool, PlainError> {
let shstrtab = &elf.shdr_strtab;
for sh in &elf.section_headers {
if sh.sh_type == SHT_NULL {
continue;
}
let name = shstrtab.get_at(sh.sh_name).ok_or(PlainError::InvalidShstrtab)?;
if name == TA_SIGNATURE_SECTION {
return Ok(true);
}
}
Ok(false)
}
pub fn build_plain_bin(bytes: &[u8]) -> Result<Vec<u8>, PlainError> {
build_plain_bin_with_limits(bytes, &VerifyLimits::default())
}
pub fn build_plain_bin_with_limits(bytes: &[u8], limits: &VerifyLimits) -> Result<Vec<u8>, PlainError> {
limits.check_elf_input_len(bytes.len())?;
let elf = Elf::parse(bytes)?;
limits.check_section_header_count(elf.section_headers.len())?;
limits.check_program_header_count(elf.program_headers.len())?;
if has_ta_signature_section(&elf)? {
return Err(PlainError::TaSignatureSectionExists(
TA_SIGNATURE_SECTION.into(),
));
}
build_plain_from_elf(&elf, bytes, false, limits)
}
pub fn plain_bytes_from_signed_elf(bytes: &[u8]) -> Result<Vec<u8>, PlainError> {
plain_bytes_from_signed_elf_with_limits(bytes, &VerifyLimits::default())
}
pub fn plain_bytes_from_signed_elf_with_limits(
bytes: &[u8],
limits: &VerifyLimits,
) -> Result<Vec<u8>, PlainError> {
limits.check_elf_input_len(bytes.len())?;
let elf = Elf::parse(bytes)?;
limits.check_section_header_count(elf.section_headers.len())?;
limits.check_program_header_count(elf.program_headers.len())?;
build_plain_from_elf(&elf, bytes, true, limits)
}
pub fn ta_signature_section_bytes<'a>(bytes: &'a [u8]) -> Result<&'a [u8], PlainError> {
ta_signature_section_bytes_with_limits(bytes, &VerifyLimits::default())
}
pub fn ta_signature_section_bytes_with_limits<'a>(
bytes: &'a [u8],
limits: &VerifyLimits,
) -> Result<&'a [u8], PlainError> {
limits.check_elf_input_len(bytes.len())?;
let elf = Elf::parse(bytes)?;
limits.check_section_header_count(elf.section_headers.len())?;
limits.check_program_header_count(elf.program_headers.len())?;
let shstrtab = &elf.shdr_strtab;
for sh in &elf.section_headers {
if sh.sh_type == SHT_NULL {
continue;
}
let name = shstrtab.get_at(sh.sh_name).ok_or(PlainError::InvalidShstrtab)?;
if name != TA_SIGNATURE_SECTION {
continue;
}
if sh.sh_type == SHT_NOBITS {
return Err(PlainError::TaSignatureSectionNobits(
TA_SIGNATURE_SECTION.into(),
));
}
let off = sh.sh_offset as usize;
let sz = sh.sh_size as usize;
if sz == 0 {
return Err(PlainError::TaSignatureSectionEmpty(
TA_SIGNATURE_SECTION.into(),
));
}
limits.check_ta_signature_payload_len(sz)?;
let end = off.checked_add(sz).ok_or(PlainError::InvalidShstrtab)?;
if end > bytes.len() {
return Err(PlainError::SectionOutOfBounds {
name: TA_SIGNATURE_SECTION.into(),
offset: sh.sh_offset,
size: sh.sh_size,
file_len: bytes.len(),
});
}
return Ok(&bytes[off..end]);
}
Err(PlainError::TaSignatureSectionMissing(
TA_SIGNATURE_SECTION.into(),
))
}
fn extend_plain(out: &mut Vec<u8>, data: &[u8], limits: &VerifyLimits) -> Result<(), PlainError> {
let new_len = out
.len()
.checked_add(data.len())
.ok_or(LimitsError {
context: "plain 输出长度",
max: limits.max_plain_output_bytes,
got: usize::MAX,
})?;
if new_len > limits.max_plain_output_bytes {
return Err(LimitsError {
context: "plain 输出长度",
max: limits.max_plain_output_bytes,
got: new_len,
}
.into());
}
out.extend_from_slice(data);
Ok(())
}
fn build_plain_from_elf(
elf: &Elf<'_>,
bytes: &[u8],
allow_ta_signature: bool,
limits: &VerifyLimits,
) -> Result<Vec<u8>, PlainError> {
match elf.header.e_type {
ET_EXEC | ET_DYN => build_plain_from_load_segments(elf, bytes, limits),
ET_REL => build_plain_from_sections(elf, bytes, allow_ta_signature, limits),
_ => build_plain_from_sections(elf, bytes, allow_ta_signature, limits),
}
}
fn build_plain_from_load_segments(
elf: &Elf<'_>,
bytes: &[u8],
limits: &VerifyLimits,
) -> Result<Vec<u8>, PlainError> {
let mut out = Vec::new();
let phnum = elf.header.e_phnum as usize;
let phent = elf.header.e_phentsize as usize;
let phoff = elf.header.e_phoff as usize;
if phnum > 0 {
let ph_len = phnum.saturating_mul(phent);
let ph_end = phoff.checked_add(ph_len).ok_or(PlainError::InvalidShstrtab)?;
if ph_end > bytes.len() {
return Err(PlainError::SectionOutOfBounds {
name: "<program headers>".into(),
offset: phoff as u64,
size: ph_len as u64,
file_len: bytes.len(),
});
}
extend_plain(&mut out, &bytes[phoff..ph_end], limits)?;
}
let ehsize = elf.header.e_ehsize as usize;
let ph_exclude = if phnum > 0 {
let ph_len = phnum.saturating_mul(phent);
Some((phoff, phoff + ph_len))
} else {
None
};
let mut ranges: Vec<(usize, usize)> = Vec::new();
for ph in &elf.program_headers {
if ph.p_type != PT_LOAD || ph.p_filesz == 0 {
continue;
}
let start = ph.p_offset as usize;
let len = ph.p_filesz as usize;
let end = start.checked_add(len).ok_or(PlainError::SectionOutOfBounds {
name: "<PT_LOAD>".into(),
offset: ph.p_offset,
size: ph.p_filesz,
file_len: bytes.len(),
})?;
if end > bytes.len() {
return Err(PlainError::SectionOutOfBounds {
name: "<PT_LOAD>".into(),
offset: ph.p_offset,
size: ph.p_filesz,
file_len: bytes.len(),
});
}
ranges.push((start, end));
}
ranges.sort_by_key(|(s, _)| *s);
let mut merged: Vec<(usize, usize)> = Vec::new();
for (s, e) in ranges {
if let Some((_, last_e)) = merged.last_mut() {
if s <= *last_e {
if e > *last_e {
*last_e = e;
}
continue;
}
}
merged.push((s, e));
}
for (s, e) in merged {
let mut parts = vec![(s, e)];
parts = subtract_range(parts, (0, ehsize));
if let Some(ph) = ph_exclude {
parts = subtract_range(parts, ph);
}
for (ps, pe) in parts {
extend_plain(&mut out, &bytes[ps..pe], limits)?;
}
}
Ok(out)
}
fn subtract_range(ranges: Vec<(usize, usize)>, cut: (usize, usize)) -> Vec<(usize, usize)> {
let (cut_s, cut_e) = cut;
if cut_s >= cut_e {
return ranges;
}
let mut out = Vec::new();
for (s, e) in ranges {
if e <= cut_s || s >= cut_e {
out.push((s, e));
continue;
}
if s < cut_s {
out.push((s, cut_s));
}
if e > cut_e {
out.push((cut_e, e));
}
}
out
}
fn build_plain_from_sections(
elf: &Elf<'_>,
bytes: &[u8],
allow_ta_signature: bool,
limits: &VerifyLimits,
) -> Result<Vec<u8>, PlainError> {
let mut out = Vec::new();
let phnum = elf.header.e_phnum as usize;
let phent = elf.header.e_phentsize as usize;
let phoff = elf.header.e_phoff as usize;
if phnum > 0 {
let ph_len = phnum.saturating_mul(phent);
let ph_end = phoff.checked_add(ph_len).ok_or(PlainError::InvalidShstrtab)?;
if ph_end > bytes.len() {
return Err(PlainError::SectionOutOfBounds {
name: "<program headers>".into(),
offset: phoff as u64,
size: ph_len as u64,
file_len: bytes.len(),
});
}
extend_plain(&mut out, &bytes[phoff..ph_end], limits)?;
}
let shstrtab = &elf.shdr_strtab;
for sh in &elf.section_headers {
let name = shstrtab.get_at(sh.sh_name).ok_or(PlainError::InvalidShstrtab)?;
if name == TA_SIGNATURE_SECTION {
if !allow_ta_signature {
return Err(PlainError::TaSignatureSectionExists(
TA_SIGNATURE_SECTION.into(),
));
}
continue;
}
if name == SHSTRTAB_SECTION || name == SYMTAB_SECTION || name == STRTAB_SECTION {
continue;
}
if sh.sh_type == SHT_NULL {
continue;
}
if sh.sh_type == SHT_NOBITS {
continue;
}
let off = sh.sh_offset as usize;
let sz = sh.sh_size as usize;
let end = off.checked_add(sz).ok_or(PlainError::InvalidShstrtab)?;
if end > bytes.len() {
return Err(PlainError::SectionOutOfBounds {
name: name.to_string(),
offset: sh.sh_offset,
size: sh.sh_size,
file_len: bytes.len(),
});
}
extend_plain(&mut out, &bytes[off..end], limits)?;
}
Ok(out)
}