mirror of
https://github.com/Momoko-Ayase/Senbei.git
synced 2026-11-03 10:31:28 -05:00
fix(android): restore protected runtime ELF state
This commit is contained in:
1 parent
25939c9738
commit
9083c60d45
2 files changed
+568
-45
No files matched your search
@@ -6,6 +6,11 @@ use serde_json::Value;
|
|||||||
use super::error::{Error, Result, invalid};
|
use super::error::{Error, Result, invalid};
|
||||||
|
|
||||||
const REQUIRED_IDS: [u32; 3] = [0x9b, 0x9d, 0x9e];
|
const REQUIRED_IDS: [u32; 3] = [0x9b, 0x9d, 0x9e];
|
||||||
|
const OPTIONAL_IDS: [u32; 1] = [0x98];
|
||||||
|
|
||||||
|
fn wanted_id(command_id: u32) -> bool {
|
||||||
|
REQUIRED_IDS.contains(&command_id) || OPTIONAL_IDS.contains(&command_id)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub(crate) struct Artifact {
|
pub(crate) struct Artifact {
|
||||||
@@ -27,7 +32,7 @@ pub(crate) fn load_artifacts(index_path: &Path) -> Result<BTreeMap<u32, Artifact
|
|||||||
};
|
};
|
||||||
let command_id = u32::try_from(command_id)
|
let command_id = u32::try_from(command_id)
|
||||||
.map_err(|_| Error::Invalid("module command ID exceeds u32".to_owned()))?;
|
.map_err(|_| Error::Invalid("module command ID exceeds u32".to_owned()))?;
|
||||||
if !REQUIRED_IDS.contains(&command_id) {
|
if !wanted_id(command_id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let Some(path) = item.get("image_path").and_then(Value::as_str) else {
|
let Some(path) = item.get("image_path").and_then(Value::as_str) else {
|
||||||
@@ -57,7 +62,7 @@ pub(crate) fn load_artifacts(index_path: &Path) -> Result<BTreeMap<u32, Artifact
|
|||||||
};
|
};
|
||||||
let command_id = u32::try_from(command_id)
|
let command_id = u32::try_from(command_id)
|
||||||
.map_err(|_| Error::Invalid("record command ID exceeds u32".to_owned()))?;
|
.map_err(|_| Error::Invalid("record command ID exceeds u32".to_owned()))?;
|
||||||
if !REQUIRED_IDS.contains(&command_id) {
|
if !wanted_id(command_id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let Some(image) = record.get("image") else {
|
let Some(image) = record.get("image") else {
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ use senbei_elf::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CHUNK_SIZE: usize = 16 * 1024 * 1024;
|
const CHUNK_SIZE: usize = 16 * 1024 * 1024;
|
||||||
|
const DT_INIT_ARRAYSZ: u64 = 27;
|
||||||
|
const DT_FINI_ARRAYSZ: u64 = 28;
|
||||||
|
const AARCH64_RET: u32 = 0xd65f_03c0;
|
||||||
|
|
||||||
/// Inputs and optional diagnostics for one `libil2cpp.so` restoration.
|
/// Inputs and optional diagnostics for one `libil2cpp.so` restoration.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -599,6 +602,165 @@ fn dynamic_symbol_names(symbols: &[u8], strings: &[u8]) -> Result<Vec<Vec<u8>>>
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LEGACY_AUX_SYMBOL_SEED: u32 = 0xbd93_5573;
|
||||||
|
const LEGACY_SPECIAL_PLT_IMPORTS: [(usize, &str); 5] = [
|
||||||
|
(1, "strlen"),
|
||||||
|
(202, "strcmp"),
|
||||||
|
(218, "memset"),
|
||||||
|
(237, "strcpy"),
|
||||||
|
(267, "strchr"),
|
||||||
|
];
|
||||||
|
|
||||||
|
fn is_legacy_import_profile(
|
||||||
|
source_symbol_count: usize,
|
||||||
|
hidden: &HiddenSymbolReport,
|
||||||
|
auxiliary: &AuxiliaryElfImage,
|
||||||
|
) -> bool {
|
||||||
|
source_symbol_count == 503
|
||||||
|
&& hidden.patched_symbols == 497
|
||||||
|
&& hidden.first_target_index == 3
|
||||||
|
&& hidden.last_target_index == 499
|
||||||
|
&& auxiliary.dynsym_count == 0xd4
|
||||||
|
&& auxiliary.relocation1_count == 0x6e159
|
||||||
|
&& auxiliary.relocation2_count == 0x143
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_undefined_function_symbol(
|
||||||
|
symbols: &mut Vec<u8>,
|
||||||
|
strings: &mut Vec<u8>,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let name_offset = u32::try_from(strings.len())
|
||||||
|
.map_err(|_| Error::Invalid("dynamic string table exceeds u32".to_owned()))?;
|
||||||
|
strings.extend_from_slice(name.as_bytes());
|
||||||
|
strings.push(0);
|
||||||
|
let index = symbols.len() / ELF64_SYMBOL_SIZE;
|
||||||
|
symbols.extend_from_slice(&name_offset.to_le_bytes());
|
||||||
|
symbols.push(0x12); // STB_GLOBAL | STT_FUNC
|
||||||
|
symbols.push(0);
|
||||||
|
symbols.extend_from_slice(&0_u16.to_le_bytes()); // SHN_UNDEF
|
||||||
|
symbols.extend_from_slice(&0_u64.to_le_bytes());
|
||||||
|
symbols.extend_from_slice(&0_u64.to_le_bytes());
|
||||||
|
Ok(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symbol_definition(symbols: &[u8], index: usize) -> Result<(u16, u64)> {
|
||||||
|
let start = index
|
||||||
|
.checked_mul(ELF64_SYMBOL_SIZE)
|
||||||
|
.ok_or_else(|| Error::Invalid("dynamic symbol index overflow".to_owned()))?;
|
||||||
|
let symbol = slice(symbols, start, ELF64_SYMBOL_SIZE)?;
|
||||||
|
let section_index = u16::from_le_bytes([symbol[6], symbol[7]]);
|
||||||
|
let value = read_u64(symbol, 8)?;
|
||||||
|
Ok((section_index, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ConstructorProfile {
|
||||||
|
init_addends: Vec<u64>,
|
||||||
|
fini_addend: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discover_suppressed_constructors(module: &[u8], source: &[u8]) -> Result<Vec<u64>> {
|
||||||
|
const COUNT: usize = 43;
|
||||||
|
const RECORD_SIZE: usize = 16;
|
||||||
|
let table_size = 8 + COUNT * RECORD_SIZE;
|
||||||
|
if module.len() < table_size {
|
||||||
|
return invalid("constructor control module is too small");
|
||||||
|
}
|
||||||
|
let mut matches = Vec::new();
|
||||||
|
for base in (0..=module.len() - table_size).step_by(4) {
|
||||||
|
if read_u32(module, base)? != COUNT as u32 || read_u32(module, base + 4)? != 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut constructors = Vec::with_capacity(COUNT);
|
||||||
|
let mut valid = true;
|
||||||
|
for index in 0..COUNT {
|
||||||
|
let record = base + 8 + index * RECORD_SIZE;
|
||||||
|
let constructor = read_u32(module, record)? as usize;
|
||||||
|
let fallback = read_u32(module, record + 4)? as usize;
|
||||||
|
let original = read_u32(module, record + 8)?;
|
||||||
|
let reserved = read_u32(module, record + 12)?;
|
||||||
|
if constructor == 0
|
||||||
|
|| fallback == 0
|
||||||
|
|| reserved != 0
|
||||||
|
|| read_u32(source, constructor).ok() != Some(original)
|
||||||
|
|| read_u32(source, fallback).ok() != Some(AARCH64_RET)
|
||||||
|
{
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
constructors.push(constructor as u64);
|
||||||
|
}
|
||||||
|
if valid
|
||||||
|
&& constructors.windows(2).all(|pair| pair[0] < pair[1])
|
||||||
|
&& constructors.iter().copied().collect::<HashSet<_>>().len() == COUNT
|
||||||
|
{
|
||||||
|
matches.push(constructors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match matches.len() {
|
||||||
|
1 => Ok(matches.pop().expect("one constructor table")),
|
||||||
|
0 => invalid("constructor control module has no validated suppression table"),
|
||||||
|
count => invalid(format!(
|
||||||
|
"constructor control module has {count} validated suppression tables"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constructor_profile(
|
||||||
|
module: &[u8],
|
||||||
|
source: &[u8],
|
||||||
|
layout: &ElfLayout,
|
||||||
|
init_array: SectionHeader,
|
||||||
|
fini_array: SectionHeader,
|
||||||
|
old_dyn: RelocationTable<'_>,
|
||||||
|
) -> Result<ConstructorProfile> {
|
||||||
|
let mut init_addends = discover_suppressed_constructors(module, source)?;
|
||||||
|
let max_suppressed = *init_addends
|
||||||
|
.last()
|
||||||
|
.ok_or_else(|| Error::Invalid("constructor table is empty".to_owned()))?;
|
||||||
|
let init_end = init_array
|
||||||
|
.address
|
||||||
|
.checked_add(44 * 8)
|
||||||
|
.ok_or_else(|| Error::Invalid("init-array range overflow".to_owned()))?;
|
||||||
|
let existing = old_dyn.collect_where(|relocation| {
|
||||||
|
relocation.offset >= init_array.address && relocation.offset < init_end
|
||||||
|
})?;
|
||||||
|
let extra = existing
|
||||||
|
.iter()
|
||||||
|
.filter(|relocation| {
|
||||||
|
relocation.kind() == R_AARCH64_RELATIVE
|
||||||
|
&& relocation.symbol() == 0
|
||||||
|
&& relocation.addend >= 0
|
||||||
|
})
|
||||||
|
.map(|relocation| relocation.addend as u64)
|
||||||
|
.filter(|&addend| {
|
||||||
|
addend != layout.entrypoint
|
||||||
|
&& addend > max_suppressed
|
||||||
|
&& addend <= max_suppressed.saturating_add(0x1000)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if extra.len() != 1 {
|
||||||
|
return invalid(format!(
|
||||||
|
"expected one unsuppressed constructor near the suppression table, found {}",
|
||||||
|
extra.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
init_addends.push(extra[0]);
|
||||||
|
let fini = old_dyn.collect_where(|relocation| relocation.offset == fini_array.address)?;
|
||||||
|
if fini.len() != 1
|
||||||
|
|| fini[0].kind() != R_AARCH64_RELATIVE
|
||||||
|
|| fini[0].symbol() != 0
|
||||||
|
|| fini[0].addend < 0
|
||||||
|
{
|
||||||
|
return invalid("fini-array relocation is not a single RELATIVE record");
|
||||||
|
}
|
||||||
|
Ok(ConstructorProfile {
|
||||||
|
init_addends,
|
||||||
|
fini_addend: fini[0].addend as u64,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
struct Rela {
|
struct Rela {
|
||||||
offset: u64,
|
offset: u64,
|
||||||
@@ -630,12 +792,43 @@ impl Rela {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct RelocationRemap {
|
||||||
|
base_symbol_count: usize,
|
||||||
|
auxiliary_symbol_count: u32,
|
||||||
|
permutation_seed: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_auxiliary_symbol(encoded: u64, row: usize, symbol_count: u32, seed: u32) -> Result<u64> {
|
||||||
|
if encoded == 0 {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
if encoded >= u64::from(symbol_count) || symbol_count < 2 {
|
||||||
|
return invalid("auxiliary relocation references an invalid encoded symbol");
|
||||||
|
}
|
||||||
|
let modulus = symbol_count - 1;
|
||||||
|
let squared = seed.wrapping_mul(seed);
|
||||||
|
let row = u32::try_from(row)
|
||||||
|
.map_err(|_| Error::Invalid("auxiliary relocation row exceeds u32".to_owned()))?;
|
||||||
|
let right = (squared >> (row & 0x1b)).wrapping_add(0x1bc5);
|
||||||
|
let left = squared.wrapping_shl(row & 3).wrapping_add(0x2048);
|
||||||
|
let rotation = (right | left) % modulus;
|
||||||
|
let mut decoded = u64::from(symbol_count - 2)
|
||||||
|
.checked_add(encoded)
|
||||||
|
.and_then(|value| value.checked_sub(u64::from(rotation)))
|
||||||
|
.ok_or_else(|| Error::Invalid("auxiliary symbol permutation overflow".to_owned()))?;
|
||||||
|
if decoded >= u64::from(modulus) {
|
||||||
|
decoded -= u64::from(modulus);
|
||||||
|
}
|
||||||
|
Ok(decoded + 1)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
struct RelocationTable<'a> {
|
struct RelocationTable<'a> {
|
||||||
data: &'a [u8],
|
data: &'a [u8],
|
||||||
offset: usize,
|
offset: usize,
|
||||||
count: usize,
|
count: usize,
|
||||||
remap: Option<(usize, u32)>,
|
remap: Option<RelocationRemap>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RelocationTable<'_> {
|
impl RelocationTable<'_> {
|
||||||
@@ -652,16 +845,23 @@ impl RelocationTable<'_> {
|
|||||||
)
|
)
|
||||||
.ok_or_else(|| Error::Invalid("relocation offset overflow".to_owned()))?;
|
.ok_or_else(|| Error::Invalid("relocation offset overflow".to_owned()))?;
|
||||||
let mut relocation = Rela::parse(self.data, offset)?;
|
let mut relocation = Rela::parse(self.data, offset)?;
|
||||||
if let Some((old_symbol_count, auxiliary_symbol_count)) = self.remap {
|
if let Some(remap) = self.remap {
|
||||||
let symbol = relocation.symbol();
|
let encoded = relocation.symbol();
|
||||||
if symbol >= u64::from(auxiliary_symbol_count) {
|
if encoded >= u64::from(remap.auxiliary_symbol_count) {
|
||||||
return invalid("auxiliary relocation references an invalid symbol");
|
return invalid("auxiliary relocation references an invalid symbol");
|
||||||
}
|
}
|
||||||
|
let symbol = match remap.permutation_seed {
|
||||||
|
Some(seed) => {
|
||||||
|
decode_auxiliary_symbol(encoded, index, remap.auxiliary_symbol_count, seed)?
|
||||||
|
}
|
||||||
|
None => encoded,
|
||||||
|
};
|
||||||
if symbol != 0 {
|
if symbol != 0 {
|
||||||
let base = u64::try_from(old_symbol_count.checked_sub(1).ok_or_else(|| {
|
let base =
|
||||||
Error::Invalid("old dynamic symbol table is empty".to_owned())
|
u64::try_from(remap.base_symbol_count.checked_sub(1).ok_or_else(|| {
|
||||||
})?)
|
Error::Invalid("old dynamic symbol table is empty".to_owned())
|
||||||
.map_err(|_| Error::Invalid("old symbol count exceeds u64".to_owned()))?;
|
})?)
|
||||||
|
.map_err(|_| Error::Invalid("old symbol count exceeds u64".to_owned()))?;
|
||||||
let remapped = symbol
|
let remapped = symbol
|
||||||
.checked_add(base)
|
.checked_add(base)
|
||||||
.ok_or_else(|| Error::Invalid("remapped symbol index overflow".to_owned()))?;
|
.ok_or_else(|| Error::Invalid("remapped symbol index overflow".to_owned()))?;
|
||||||
@@ -683,16 +883,15 @@ impl RelocationTable<'_> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_where(self, output: &mut Vec<u8>, predicate: impl Fn(u32) -> bool) -> Result<usize> {
|
fn collect_where(self, predicate: impl Fn(Rela) -> bool) -> Result<Vec<Rela>> {
|
||||||
let mut count = 0_usize;
|
let mut result = Vec::new();
|
||||||
for index in 0..self.count {
|
for index in 0..self.count {
|
||||||
let relocation = self.relocation(index)?;
|
let relocation = self.relocation(index)?;
|
||||||
if predicate(relocation.kind()) {
|
if predicate(relocation) {
|
||||||
relocation.encode(output);
|
result.push(relocation);
|
||||||
count += 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(count)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -855,16 +1054,41 @@ fn metadata_mapping_length(
|
|||||||
dynsym.size / ELF64_SYMBOL_SIZE as u64,
|
dynsym.size / ELF64_SYMBOL_SIZE as u64,
|
||||||
"dynamic symbol count",
|
"dynamic symbol count",
|
||||||
)?;
|
)?;
|
||||||
let appended_count =
|
let auxiliary_appended_count =
|
||||||
usize::try_from(auxiliary.dynsym_count.checked_sub(1).ok_or_else(|| {
|
usize::try_from(auxiliary.dynsym_count.checked_sub(1).ok_or_else(|| {
|
||||||
Error::Invalid("auxiliary symbol table has no null entry".to_owned())
|
Error::Invalid("auxiliary symbol table has no null entry".to_owned())
|
||||||
})?)
|
})?)
|
||||||
.map_err(|_| Error::Invalid("auxiliary symbol count exceeds usize".to_owned()))?;
|
.map_err(|_| Error::Invalid("auxiliary symbol count exceeds usize".to_owned()))?;
|
||||||
|
// The legacy 212-symbol auxiliary format omits five libc imports that are
|
||||||
|
// reconstructed during materialization. Keep sizing conservative here: the
|
||||||
|
// protected table still includes three protector-only symbols that will be
|
||||||
|
// removed later, so reserving from the raw count intentionally overallocates
|
||||||
|
// by three entries rather than depending on unrelated relocation slack.
|
||||||
|
let legacy_auxiliary_shape = auxiliary.dynsym_count == 0xd4
|
||||||
|
&& auxiliary.relocation1_count == 0x6e159
|
||||||
|
&& auxiliary.relocation2_count == 0x143;
|
||||||
|
let special_count = if legacy_auxiliary_shape {
|
||||||
|
LEGACY_SPECIAL_PLT_IMPORTS.len()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let appended_count = auxiliary_appended_count
|
||||||
|
.checked_add(special_count)
|
||||||
|
.ok_or_else(|| Error::Invalid("appended dynamic symbol count overflow".to_owned()))?;
|
||||||
let new_symbol_count = old_symbol_count
|
let new_symbol_count = old_symbol_count
|
||||||
.checked_add(appended_count)
|
.checked_add(appended_count)
|
||||||
.ok_or_else(|| Error::Invalid("merged dynamic symbol count overflow".to_owned()))?;
|
.ok_or_else(|| Error::Invalid("merged dynamic symbol count overflow".to_owned()))?;
|
||||||
|
let special_string_size = if legacy_auxiliary_shape {
|
||||||
|
LEGACY_SPECIAL_PLT_IMPORTS
|
||||||
|
.iter()
|
||||||
|
.map(|(_, name)| name.len() + 1)
|
||||||
|
.sum()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
let merged_dynstr_size = usize_from_u64(dynstr.size, ".dynstr size")?
|
let merged_dynstr_size = usize_from_u64(dynstr.size, ".dynstr size")?
|
||||||
.checked_add(auxiliary.dynstr_size as usize)
|
.checked_add(auxiliary.dynstr_size as usize)
|
||||||
|
.and_then(|size| size.checked_add(special_string_size))
|
||||||
.ok_or_else(|| Error::Invalid("merged dynamic string size overflow".to_owned()))?;
|
.ok_or_else(|| Error::Invalid("merged dynamic string size overflow".to_owned()))?;
|
||||||
let merged_rela_dyn_count =
|
let merged_rela_dyn_count =
|
||||||
usize_from_u64(rela_dyn.size / ELF64_RELA_SIZE as u64, ".rela.dyn count")?
|
usize_from_u64(rela_dyn.size / ELF64_RELA_SIZE as u64, ".rela.dyn count")?
|
||||||
@@ -874,6 +1098,7 @@ fn metadata_mapping_length(
|
|||||||
let merged_rela_plt_count =
|
let merged_rela_plt_count =
|
||||||
usize_from_u64(rela_plt.size / ELF64_RELA_SIZE as u64, ".rela.plt count")?
|
usize_from_u64(rela_plt.size / ELF64_RELA_SIZE as u64, ".rela.plt count")?
|
||||||
.checked_add(auxiliary.relocation2_count as usize)
|
.checked_add(auxiliary.relocation2_count as usize)
|
||||||
|
.and_then(|count| count.checked_add(special_count))
|
||||||
.ok_or_else(|| Error::Invalid("merged .rela.plt count overflow".to_owned()))?;
|
.ok_or_else(|| Error::Invalid("merged .rela.plt count overflow".to_owned()))?;
|
||||||
let gnu_hash_size = if indices.contains_key(".gnu.hash") {
|
let gnu_hash_size = if indices.contains_key(".gnu.hash") {
|
||||||
28_usize
|
28_usize
|
||||||
@@ -981,6 +1206,7 @@ fn materialize_static_elf_tables(
|
|||||||
layout: &ElfLayout,
|
layout: &ElfLayout,
|
||||||
symbol_patch_data: &[u8],
|
symbol_patch_data: &[u8],
|
||||||
auxiliary_data: &[u8],
|
auxiliary_data: &[u8],
|
||||||
|
constructor_module: Option<&[u8]>,
|
||||||
) -> Result<(ElfLayout, ElfMaterializationReport, u64)> {
|
) -> Result<(ElfLayout, ElfMaterializationReport, u64)> {
|
||||||
let names = layout.section_names(source)?;
|
let names = layout.section_names(source)?;
|
||||||
let indices = required_section_indices(&names)?;
|
let indices = required_section_indices(&names)?;
|
||||||
@@ -993,27 +1219,44 @@ fn materialize_static_elf_tables(
|
|||||||
let rela_plt = section(".rela.plt");
|
let rela_plt = section(".rela.plt");
|
||||||
let dynamic = section(".dynamic");
|
let dynamic = section(".dynamic");
|
||||||
|
|
||||||
let (old_symbols, old_strings, hidden_symbols) =
|
let (mut old_symbols, old_strings, hidden_symbols) =
|
||||||
restore_hidden_symbols(output, dynsym, dynstr, symbol_patch_data)?;
|
restore_hidden_symbols(output, dynsym, dynstr, symbol_patch_data)?;
|
||||||
let old_symbol_count = old_symbols.len() / ELF64_SYMBOL_SIZE;
|
let source_symbol_count = old_symbols.len() / ELF64_SYMBOL_SIZE;
|
||||||
if versym.size != (old_symbol_count * 2) as u64 {
|
if versym.size != (source_symbol_count * 2) as u64 {
|
||||||
return invalid(".gnu.version count does not match .dynsym");
|
return invalid(".gnu.version count does not match .dynsym");
|
||||||
}
|
}
|
||||||
let old_versions = slice_u64(output, versym.offset, versym.size)?.to_vec();
|
let mut old_versions = slice_u64(output, versym.offset, versym.size)?.to_vec();
|
||||||
let version_requirements = slice_u64(output, verneed.offset, verneed.size)?.to_vec();
|
let version_requirements = slice_u64(output, verneed.offset, verneed.size)?.to_vec();
|
||||||
|
|
||||||
let auxiliary = AuxiliaryElfImage::parse(auxiliary_data)?;
|
let auxiliary = AuxiliaryElfImage::parse(auxiliary_data)?;
|
||||||
|
let legacy_import_profile =
|
||||||
|
is_legacy_import_profile(source_symbol_count, &hidden_symbols, &auxiliary);
|
||||||
|
if legacy_import_profile {
|
||||||
|
let old_names = dynamic_symbol_names(&old_symbols, &old_strings)?;
|
||||||
|
let protector_names = ["dlerror", "__stack_chk_guard", "__stack_chk_fail"];
|
||||||
|
if old_names.len() != 503
|
||||||
|
|| old_names[500..]
|
||||||
|
.iter()
|
||||||
|
.map(|name| String::from_utf8_lossy(name))
|
||||||
|
.ne(protector_names.iter().copied())
|
||||||
|
{
|
||||||
|
return invalid("legacy import profile has unexpected protector symbols");
|
||||||
|
}
|
||||||
|
old_symbols.truncate(500 * ELF64_SYMBOL_SIZE);
|
||||||
|
old_versions.truncate(500 * 2);
|
||||||
|
}
|
||||||
|
let old_symbol_count = old_symbols.len() / ELF64_SYMBOL_SIZE;
|
||||||
let auxiliary_strings = slice(
|
let auxiliary_strings = slice(
|
||||||
auxiliary_data,
|
auxiliary_data,
|
||||||
auxiliary.dynstr_offset as usize,
|
auxiliary.dynstr_offset as usize,
|
||||||
auxiliary.dynstr_size as usize,
|
auxiliary.dynstr_size as usize,
|
||||||
)?;
|
)?;
|
||||||
let appended_count =
|
let auxiliary_appended_count =
|
||||||
usize::try_from(auxiliary.dynsym_count.checked_sub(1).ok_or_else(|| {
|
usize::try_from(auxiliary.dynsym_count.checked_sub(1).ok_or_else(|| {
|
||||||
Error::Invalid("auxiliary symbol table has no null entry".to_owned())
|
Error::Invalid("auxiliary symbol table has no null entry".to_owned())
|
||||||
})?)
|
})?)
|
||||||
.map_err(|_| Error::Invalid("auxiliary symbol count exceeds usize".to_owned()))?;
|
.map_err(|_| Error::Invalid("auxiliary symbol count exceeds usize".to_owned()))?;
|
||||||
let mut appended_symbols = Vec::with_capacity(appended_count * ELF64_SYMBOL_SIZE);
|
let mut appended_symbols = Vec::with_capacity(auxiliary_appended_count * ELF64_SYMBOL_SIZE);
|
||||||
for index in 1..auxiliary.dynsym_count as usize {
|
for index in 1..auxiliary.dynsym_count as usize {
|
||||||
let offset = auxiliary.dynsym_offset as usize + index * ELF64_SYMBOL_SIZE;
|
let offset = auxiliary.dynsym_offset as usize + index * ELF64_SYMBOL_SIZE;
|
||||||
let symbol = slice(auxiliary_data, offset, ELF64_SYMBOL_SIZE)?;
|
let symbol = slice(auxiliary_data, offset, ELF64_SYMBOL_SIZE)?;
|
||||||
@@ -1035,14 +1278,29 @@ fn materialize_static_elf_tables(
|
|||||||
let mut merged_symbols = Vec::with_capacity(old_symbols.len() + appended_symbols.len());
|
let mut merged_symbols = Vec::with_capacity(old_symbols.len() + appended_symbols.len());
|
||||||
merged_symbols.extend_from_slice(&old_symbols);
|
merged_symbols.extend_from_slice(&old_symbols);
|
||||||
merged_symbols.extend_from_slice(&appended_symbols);
|
merged_symbols.extend_from_slice(&appended_symbols);
|
||||||
let mut merged_strings = Vec::with_capacity(old_strings.len() + auxiliary_strings.len());
|
let mut merged_strings = Vec::with_capacity(old_strings.len() + auxiliary_strings.len() + 64);
|
||||||
merged_strings.extend_from_slice(&old_strings);
|
merged_strings.extend_from_slice(&old_strings);
|
||||||
merged_strings.extend_from_slice(auxiliary_strings);
|
merged_strings.extend_from_slice(auxiliary_strings);
|
||||||
let mut merged_versions = Vec::with_capacity(old_versions.len() + appended_count * 2);
|
let mut merged_versions =
|
||||||
|
Vec::with_capacity(old_versions.len() + (auxiliary_appended_count + 5) * 2);
|
||||||
merged_versions.extend_from_slice(&old_versions);
|
merged_versions.extend_from_slice(&old_versions);
|
||||||
for _ in 0..appended_count {
|
for _ in 0..auxiliary_appended_count {
|
||||||
merged_versions.extend_from_slice(&VER_NDX_GLOBAL.to_le_bytes());
|
merged_versions.extend_from_slice(&VER_NDX_GLOBAL.to_le_bytes());
|
||||||
}
|
}
|
||||||
|
let mut special_plt_symbols = Vec::new();
|
||||||
|
if legacy_import_profile {
|
||||||
|
let custom_names = dynamic_symbol_names(&appended_symbols, &merged_strings)?;
|
||||||
|
if custom_names.get(170).map(Vec::as_slice) != Some(b"malloc") {
|
||||||
|
return invalid("legacy auxiliary symbol permutation anchor is missing");
|
||||||
|
}
|
||||||
|
for &(slot, name) in &LEGACY_SPECIAL_PLT_IMPORTS {
|
||||||
|
let symbol =
|
||||||
|
append_undefined_function_symbol(&mut merged_symbols, &mut merged_strings, name)?;
|
||||||
|
merged_versions.extend_from_slice(&VER_NDX_GLOBAL.to_le_bytes());
|
||||||
|
special_plt_symbols.push((slot, symbol));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let appended_count = auxiliary_appended_count + special_plt_symbols.len();
|
||||||
let merged_names = dynamic_symbol_names(&merged_symbols, &merged_strings)?;
|
let merged_names = dynamic_symbol_names(&merged_symbols, &merged_strings)?;
|
||||||
let sysv_hash = indices
|
let sysv_hash = indices
|
||||||
.contains_key(".hash")
|
.contains_key(".hash")
|
||||||
@@ -1078,13 +1336,21 @@ fn materialize_static_elf_tables(
|
|||||||
data: auxiliary_data,
|
data: auxiliary_data,
|
||||||
offset: auxiliary.relocation1_offset as usize,
|
offset: auxiliary.relocation1_offset as usize,
|
||||||
count: auxiliary.relocation1_count as usize,
|
count: auxiliary.relocation1_count as usize,
|
||||||
remap: Some((old_symbol_count, auxiliary.dynsym_count)),
|
remap: Some(RelocationRemap {
|
||||||
|
base_symbol_count: old_symbol_count,
|
||||||
|
auxiliary_symbol_count: auxiliary.dynsym_count,
|
||||||
|
permutation_seed: legacy_import_profile.then_some(LEGACY_AUX_SYMBOL_SEED),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
let auxiliary2 = RelocationTable {
|
let auxiliary2 = RelocationTable {
|
||||||
data: auxiliary_data,
|
data: auxiliary_data,
|
||||||
offset: auxiliary.relocation2_offset as usize,
|
offset: auxiliary.relocation2_offset as usize,
|
||||||
count: auxiliary.relocation2_count as usize,
|
count: auxiliary.relocation2_count as usize,
|
||||||
remap: Some((old_symbol_count, auxiliary.dynsym_count)),
|
remap: Some(RelocationRemap {
|
||||||
|
base_symbol_count: old_symbol_count,
|
||||||
|
auxiliary_symbol_count: auxiliary.dynsym_count,
|
||||||
|
permutation_seed: legacy_import_profile.then_some(LEGACY_AUX_SYMBOL_SEED),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
old_dyn.validate(
|
old_dyn.validate(
|
||||||
&[R_AARCH64_RELATIVE, R_AARCH64_GLOB_DAT, R_AARCH64_ABS64],
|
&[R_AARCH64_RELATIVE, R_AARCH64_GLOB_DAT, R_AARCH64_ABS64],
|
||||||
@@ -1100,28 +1366,235 @@ fn materialize_static_elf_tables(
|
|||||||
"auxiliary relocation table 2",
|
"auxiliary relocation table 2",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let estimated_dyn = (old_dyn.count + auxiliary1.count + auxiliary2.count)
|
let constructors = if legacy_import_profile {
|
||||||
.checked_mul(ELF64_RELA_SIZE)
|
let module = constructor_module.ok_or_else(|| {
|
||||||
.ok_or_else(|| Error::Invalid("merged .rela.dyn capacity overflow".to_owned()))?;
|
Error::Invalid("legacy import profile lacks constructor control module 0x98".to_owned())
|
||||||
let mut merged_rela_dyn = Vec::with_capacity(estimated_dyn);
|
})?;
|
||||||
let mut relative_count = 0_usize;
|
let init_index = names
|
||||||
for table in [old_dyn, auxiliary1, auxiliary2] {
|
.iter()
|
||||||
relative_count +=
|
.position(|name| name == ".init_array")
|
||||||
table.append_where(&mut merged_rela_dyn, |kind| kind == R_AARCH64_RELATIVE)?;
|
.ok_or_else(|| Error::Invalid("legacy image lacks .init_array".to_owned()))?;
|
||||||
|
let fini_index = names
|
||||||
|
.iter()
|
||||||
|
.position(|name| name == ".fini_array")
|
||||||
|
.ok_or_else(|| Error::Invalid("legacy image lacks .fini_array".to_owned()))?;
|
||||||
|
let init_array = layout.section_headers[init_index];
|
||||||
|
let fini_array = layout.section_headers[fini_index];
|
||||||
|
let profile = constructor_profile(module, output, layout, init_array, fini_array, old_dyn)?;
|
||||||
|
if profile.init_addends.len() != 44 {
|
||||||
|
return invalid(format!(
|
||||||
|
"constructor profile produced {} init entries, expected 44",
|
||||||
|
profile.init_addends.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Some((profile, init_index, fini_index, init_array, fini_array))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let (dyn_relocations, plt_relocations, relative_count) = if legacy_import_profile {
|
||||||
|
let mut dynamic = old_dyn.collect_where(|_| true)?;
|
||||||
|
dynamic.extend(auxiliary1.collect_where(|_| true)?);
|
||||||
|
if let Some((profile, _, _, init_array, fini_array)) = &constructors {
|
||||||
|
for (slot, &addend) in profile.init_addends.iter().enumerate() {
|
||||||
|
let target = init_array
|
||||||
|
.address
|
||||||
|
.checked_add((slot as u64) * 8)
|
||||||
|
.ok_or_else(|| Error::Invalid("init relocation target overflow".to_owned()))?;
|
||||||
|
let matches = dynamic
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(index, relocation)| {
|
||||||
|
(relocation.offset == target).then_some(index)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if matches.len() != 1 {
|
||||||
|
return invalid(format!(
|
||||||
|
"init slot {slot} has {} relocation records",
|
||||||
|
matches.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let relocation = &mut dynamic[matches[0]];
|
||||||
|
if relocation.kind() != R_AARCH64_RELATIVE || relocation.symbol() != 0 {
|
||||||
|
return invalid(format!("init slot {slot} is not RELATIVE"));
|
||||||
|
}
|
||||||
|
relocation.addend = i64::try_from(addend)
|
||||||
|
.map_err(|_| Error::Invalid("constructor RVA exceeds i64".to_owned()))?;
|
||||||
|
}
|
||||||
|
let matches = dynamic
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(index, relocation)| {
|
||||||
|
(relocation.offset == fini_array.address).then_some(index)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if matches.len() != 1 {
|
||||||
|
return invalid(format!(
|
||||||
|
"fini slot has {} relocation records",
|
||||||
|
matches.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let relocation = &mut dynamic[matches[0]];
|
||||||
|
if relocation.kind() != R_AARCH64_RELATIVE || relocation.symbol() != 0 {
|
||||||
|
return invalid("fini slot is not RELATIVE");
|
||||||
|
}
|
||||||
|
relocation.addend = i64::try_from(profile.fini_addend)
|
||||||
|
.map_err(|_| Error::Invalid("fini RVA exceeds i64".to_owned()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut converted_local = 0_usize;
|
||||||
|
for relocation in &mut dynamic {
|
||||||
|
if relocation.kind() == R_AARCH64_RELATIVE || relocation.symbol() == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let symbol = usize::try_from(relocation.symbol())
|
||||||
|
.map_err(|_| Error::Invalid("relocation symbol exceeds usize".to_owned()))?;
|
||||||
|
let (section_index, value) = symbol_definition(&merged_symbols, symbol)?;
|
||||||
|
if section_index != 0 {
|
||||||
|
let addend = i128::from(value) + i128::from(relocation.addend);
|
||||||
|
relocation.addend = i64::try_from(addend).map_err(|_| {
|
||||||
|
Error::Invalid("local relocation addend exceeds i64".to_owned())
|
||||||
|
})?;
|
||||||
|
relocation.info = u64::from(R_AARCH64_RELATIVE);
|
||||||
|
converted_local += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dynamic
|
||||||
|
.sort_by_key(|relocation| (relocation.kind() != R_AARCH64_RELATIVE, relocation.offset));
|
||||||
|
let relative_count = dynamic
|
||||||
|
.iter()
|
||||||
|
.take_while(|relocation| relocation.kind() == R_AARCH64_RELATIVE)
|
||||||
|
.count();
|
||||||
|
if dynamic.len() != 450_990 || relative_count != 450_980 || converted_local != 37 {
|
||||||
|
return invalid(format!(
|
||||||
|
"legacy dynamic relocation profile mismatch: total={}, relative={}, local={converted_local}",
|
||||||
|
dynamic.len(),
|
||||||
|
relative_count
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let got_plt_index = names
|
||||||
|
.iter()
|
||||||
|
.position(|name| name == ".got.plt")
|
||||||
|
.ok_or_else(|| Error::Invalid("legacy import profile lacks .got.plt".to_owned()))?;
|
||||||
|
let got_plt = layout.section_headers[got_plt_index];
|
||||||
|
if got_plt.size < 0x18 || !(got_plt.size - 0x18).is_multiple_of(8) {
|
||||||
|
return invalid("legacy .got.plt has an invalid slot layout");
|
||||||
|
}
|
||||||
|
let slot_count = usize_from_u64((got_plt.size - 0x18) / 8, ".got.plt slot count")?;
|
||||||
|
if slot_count != 366 {
|
||||||
|
return invalid(format!(
|
||||||
|
"legacy .got.plt has {slot_count} dynamic slots, expected 366"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let first_slot = got_plt
|
||||||
|
.address
|
||||||
|
.checked_add(0x18)
|
||||||
|
.ok_or_else(|| Error::Invalid(".got.plt slot address overflow".to_owned()))?;
|
||||||
|
let end = got_plt
|
||||||
|
.address
|
||||||
|
.checked_add(got_plt.size)
|
||||||
|
.ok_or_else(|| Error::Invalid(".got.plt end overflow".to_owned()))?;
|
||||||
|
let mut plt = old_plt.collect_where(|relocation| {
|
||||||
|
relocation.offset >= first_slot && relocation.offset < end
|
||||||
|
})?;
|
||||||
|
if plt.len() != 38 {
|
||||||
|
return invalid(format!(
|
||||||
|
"legacy protected .rela.plt has {} retained slots, expected 38",
|
||||||
|
plt.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
plt.extend(auxiliary2.collect_where(|_| true)?);
|
||||||
|
for &(slot, symbol) in &special_plt_symbols {
|
||||||
|
let offset = first_slot
|
||||||
|
.checked_add((slot as u64) * 8)
|
||||||
|
.ok_or_else(|| Error::Invalid("special PLT slot overflow".to_owned()))?;
|
||||||
|
let symbol = u64::try_from(symbol)
|
||||||
|
.map_err(|_| Error::Invalid("special symbol index exceeds u64".to_owned()))?;
|
||||||
|
plt.push(Rela {
|
||||||
|
offset,
|
||||||
|
info: (symbol << 32) | u64::from(R_AARCH64_JUMP_SLOT),
|
||||||
|
addend: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
plt.sort_by_key(|relocation| relocation.offset);
|
||||||
|
if plt.len() != slot_count {
|
||||||
|
return invalid(format!(
|
||||||
|
"legacy PLT restoration produced {} slots, expected {slot_count}",
|
||||||
|
plt.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
for (slot, relocation) in plt.iter().enumerate() {
|
||||||
|
let expected = first_slot + (slot as u64) * 8;
|
||||||
|
if relocation.offset != expected {
|
||||||
|
return invalid(format!(
|
||||||
|
"legacy PLT slot {slot} targets 0x{:x}, expected 0x{expected:x}",
|
||||||
|
relocation.offset
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let jump_slots = plt
|
||||||
|
.iter()
|
||||||
|
.filter(|relocation| relocation.kind() == R_AARCH64_JUMP_SLOT)
|
||||||
|
.count();
|
||||||
|
let relatives = plt
|
||||||
|
.iter()
|
||||||
|
.filter(|relocation| relocation.kind() == R_AARCH64_RELATIVE)
|
||||||
|
.count();
|
||||||
|
if (jump_slots, relatives) != (247, 119) {
|
||||||
|
return invalid(format!(
|
||||||
|
"legacy PLT type profile mismatch: {jump_slots} JUMP_SLOT / {relatives} RELATIVE"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
(dynamic, plt, relative_count)
|
||||||
|
} else {
|
||||||
|
let mut dynamic = Vec::new();
|
||||||
|
for table in [old_dyn, auxiliary1, auxiliary2] {
|
||||||
|
dynamic
|
||||||
|
.extend(table.collect_where(|relocation| relocation.kind() == R_AARCH64_RELATIVE)?);
|
||||||
|
}
|
||||||
|
let relative_count = dynamic.len();
|
||||||
|
for table in [old_dyn, auxiliary1] {
|
||||||
|
dynamic
|
||||||
|
.extend(table.collect_where(|relocation| relocation.kind() != R_AARCH64_RELATIVE)?);
|
||||||
|
}
|
||||||
|
let mut plt = old_plt.collect_where(|_| true)?;
|
||||||
|
plt.extend(
|
||||||
|
auxiliary2.collect_where(|relocation| relocation.kind() == R_AARCH64_JUMP_SLOT)?,
|
||||||
|
);
|
||||||
|
(dynamic, plt, relative_count)
|
||||||
|
};
|
||||||
|
let mut merged_rela_dyn = Vec::with_capacity(dyn_relocations.len() * ELF64_RELA_SIZE);
|
||||||
|
for relocation in dyn_relocations {
|
||||||
|
relocation.encode(&mut merged_rela_dyn);
|
||||||
}
|
}
|
||||||
for table in [old_dyn, auxiliary1] {
|
let mut merged_rela_plt = Vec::with_capacity(plt_relocations.len() * ELF64_RELA_SIZE);
|
||||||
table.append_where(&mut merged_rela_dyn, |kind| kind != R_AARCH64_RELATIVE)?;
|
for relocation in plt_relocations {
|
||||||
|
relocation.encode(&mut merged_rela_plt);
|
||||||
}
|
}
|
||||||
let mut merged_rela_plt = Vec::with_capacity(
|
|
||||||
(old_plt.count + auxiliary2.count)
|
|
||||||
.checked_mul(ELF64_RELA_SIZE)
|
|
||||||
.ok_or_else(|| Error::Invalid("merged .rela.plt capacity overflow".to_owned()))?,
|
|
||||||
);
|
|
||||||
old_plt.append_where(&mut merged_rela_plt, |_| true)?;
|
|
||||||
auxiliary2.append_where(&mut merged_rela_plt, |kind| kind == R_AARCH64_JUMP_SLOT)?;
|
|
||||||
let rela_dyn_count = merged_rela_dyn.len() / ELF64_RELA_SIZE;
|
let rela_dyn_count = merged_rela_dyn.len() / ELF64_RELA_SIZE;
|
||||||
let rela_plt_count = merged_rela_plt.len() / ELF64_RELA_SIZE;
|
let rela_plt_count = merged_rela_plt.len() / ELF64_RELA_SIZE;
|
||||||
|
|
||||||
|
if let Some((profile, _, _, init_array, fini_array)) = &constructors {
|
||||||
|
let init_start = usize_from_u64(init_array.offset, "init-array offset")?;
|
||||||
|
let init_size = usize_from_u64(init_array.size, "init-array size")?;
|
||||||
|
output
|
||||||
|
.get_mut(init_start..init_start + init_size)
|
||||||
|
.ok_or_else(|| Error::Invalid("init-array exceeds output".to_owned()))?
|
||||||
|
.fill(0);
|
||||||
|
for (slot, &addend) in profile.init_addends.iter().enumerate() {
|
||||||
|
let start = init_start + slot * 8;
|
||||||
|
output[start..start + 8].copy_from_slice(&addend.to_le_bytes());
|
||||||
|
}
|
||||||
|
let fini_start = usize_from_u64(fini_array.offset, "fini-array offset")?;
|
||||||
|
let fini_size = usize_from_u64(fini_array.size, "fini-array size")?;
|
||||||
|
output
|
||||||
|
.get_mut(fini_start..fini_start + fini_size)
|
||||||
|
.ok_or_else(|| Error::Invalid("fini-array exceeds output".to_owned()))?
|
||||||
|
.fill(0);
|
||||||
|
output[fini_start..fini_start + 8].copy_from_slice(&profile.fini_addend.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
let gnu_hash = gnu_hash_table.map(|data| TablePayload {
|
let gnu_hash = gnu_hash_table.map(|data| TablePayload {
|
||||||
name: ".gnu.hash",
|
name: ".gnu.hash",
|
||||||
alignment: 8,
|
alignment: 8,
|
||||||
@@ -1244,6 +1717,10 @@ fn materialize_static_elf_tables(
|
|||||||
updated.size = table.data.len() as u64;
|
updated.size = table.data.len() as u64;
|
||||||
updated_sections[index] = updated;
|
updated_sections[index] = updated;
|
||||||
}
|
}
|
||||||
|
if let Some((_, init_index, fini_index, _, _)) = &constructors {
|
||||||
|
updated_sections[*init_index].size = 44 * 8;
|
||||||
|
updated_sections[*fini_index].size = 8;
|
||||||
|
}
|
||||||
|
|
||||||
let section_address = |name: &'static str| -> u64 { updated_sections[indices[name]].address };
|
let section_address = |name: &'static str| -> u64 { updated_sections[indices[name]].address };
|
||||||
let mut dynamic_values = BTreeMap::from([
|
let mut dynamic_values = BTreeMap::from([
|
||||||
@@ -1257,6 +1734,10 @@ fn materialize_static_elf_tables(
|
|||||||
(DT_VERSYM, section_address(".gnu.version")),
|
(DT_VERSYM, section_address(".gnu.version")),
|
||||||
(DT_VERNEED, section_address(".gnu.version_r")),
|
(DT_VERNEED, section_address(".gnu.version_r")),
|
||||||
]);
|
]);
|
||||||
|
if constructors.is_some() {
|
||||||
|
dynamic_values.insert(DT_INIT_ARRAYSZ, 44 * 8);
|
||||||
|
dynamic_values.insert(DT_FINI_ARRAYSZ, 8);
|
||||||
|
}
|
||||||
if indices.contains_key(".gnu.hash") {
|
if indices.contains_key(".gnu.hash") {
|
||||||
dynamic_values.insert(DT_GNU_HASH, section_address(".gnu.hash"));
|
dynamic_values.insert(DT_GNU_HASH, section_address(".gnu.hash"));
|
||||||
}
|
}
|
||||||
@@ -1495,6 +1976,10 @@ pub fn restore_libil2cpp(options: &RestoreOptions) -> Result<RestoreReport> {
|
|||||||
let artifacts = load_artifacts(&index_path)?;
|
let artifacts = load_artifacts(&index_path)?;
|
||||||
let module = read_file(&artifacts[&0x9b].path)?;
|
let module = read_file(&artifacts[&0x9b].path)?;
|
||||||
let symbol_patch_data = read_file(&artifacts[&0x9e].path)?;
|
let symbol_patch_data = read_file(&artifacts[&0x9e].path)?;
|
||||||
|
let constructor_module = artifacts
|
||||||
|
.get(&0x98)
|
||||||
|
.map(|artifact| read_file(&artifact.path))
|
||||||
|
.transpose()?;
|
||||||
let config = Module9bConfig::parse(&module)?;
|
let config = Module9bConfig::parse(&module)?;
|
||||||
|
|
||||||
let input_file = File::open(&input_path)
|
let input_file = File::open(&input_path)
|
||||||
@@ -1645,6 +2130,7 @@ pub fn restore_libil2cpp(options: &RestoreOptions) -> Result<RestoreReport> {
|
|||||||
&layout,
|
&layout,
|
||||||
&symbol_patch_data,
|
&symbol_patch_data,
|
||||||
&decoded,
|
&decoded,
|
||||||
|
constructor_module.as_deref(),
|
||||||
)?;
|
)?;
|
||||||
restored_layout = new_layout;
|
restored_layout = new_layout;
|
||||||
materialization = Some(report);
|
materialization = Some(report);
|
||||||
@@ -1782,4 +2268,36 @@ mod tests {
|
|||||||
required_section_indices(&dynamic_section_names(&[])).expect_err("missing hash");
|
required_section_indices(&dynamic_section_names(&[])).expect_err("missing hash");
|
||||||
assert!(error.to_string().contains("at least one"));
|
assert!(error.to_string().contains("at least one"));
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn legacy_symbol_permutation_decodes_malloc_anchor() {
|
||||||
|
assert_eq!(
|
||||||
|
decode_auxiliary_symbol(120, 268, 0xd4, LEGACY_AUX_SYMBOL_SEED).expect("decode"),
|
||||||
|
171
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn discovers_validated_constructor_table() {
|
||||||
|
const COUNT: usize = 43;
|
||||||
|
let mut source = vec![0_u8; 0x1000];
|
||||||
|
let mut module = vec![0_u8; 0x1000];
|
||||||
|
let table = 0x40;
|
||||||
|
module[table..table + 4].copy_from_slice(&(COUNT as u32).to_le_bytes());
|
||||||
|
for index in 0..COUNT {
|
||||||
|
let constructor = 0x100 + index * 4;
|
||||||
|
let fallback = 0x500 + index * 4;
|
||||||
|
let instruction = 0xa000_0000_u32 + index as u32;
|
||||||
|
source[constructor..constructor + 4].copy_from_slice(&instruction.to_le_bytes());
|
||||||
|
source[fallback..fallback + 4].copy_from_slice(&AARCH64_RET.to_le_bytes());
|
||||||
|
let record = table + 8 + index * 16;
|
||||||
|
module[record..record + 4].copy_from_slice(&(constructor as u32).to_le_bytes());
|
||||||
|
module[record + 4..record + 8].copy_from_slice(&(fallback as u32).to_le_bytes());
|
||||||
|
module[record + 8..record + 12].copy_from_slice(&instruction.to_le_bytes());
|
||||||
|
}
|
||||||
|
let constructors =
|
||||||
|
discover_suppressed_constructors(&module, &source).expect("constructor table");
|
||||||
|
assert_eq!(constructors.len(), COUNT);
|
||||||
|
assert_eq!(constructors[0], 0x100);
|
||||||
|
assert_eq!(constructors[COUNT - 1], 0x100 + ((COUNT - 1) * 4) as u64);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user