Export parser::parse in xpat

This commit is contained in:
luk
2026-01-01 19:04:20 +00:00
parent 40372db656
commit 99f8f23b5d
6 changed files with 313 additions and 162 deletions

99
Cargo.lock generated Normal file
View File

@@ -0,0 +1,99 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "libm"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "proc-macro2"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "sub_core"
version = "0.1.0"
[[package]]
name = "sub_libm"
version = "0.1.0"
dependencies = [
"libm",
]
[[package]]
name = "sub_macros"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "sub_pe"
version = "0.1.0"
dependencies = [
"sub_core",
]
[[package]]
name = "sub_winu"
version = "0.1.0"
dependencies = [
"sub_core",
"sub_pe",
]
[[package]]
name = "sub_xpat"
version = "0.1.0"
dependencies = [
"sub_core",
"sub_macros",
]
[[package]]
name = "syn"
version = "2.0.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "x"
version = "0.1.0"
dependencies = [
"sub_core",
"sub_libm",
"sub_macros",
"sub_pe",
"sub_winu",
"sub_xpat",
]

View File

@@ -1,35 +1,27 @@
use crate::scannable::{ChunkIter, Scannable};
use core::ops::{Bound, Range, RangeBounds, RangeFull};
use core::fmt::{Display, Formatter};
use core::ops::{Bound, Range, RangeBounds, RangeFull};
const SEP: &str = " | ";
pub struct HexDump<'s, T: Scannable + ?Sized, R: RangeBounds<usize>>(pub &'s T, pub R);
#[allow(clippy::needless_lifetimes)]
pub fn hex<
'a,
T: Scannable + ?Sized,
R: RangeBounds<usize>
>(
pub fn hex<'a, T: Scannable + ?Sized, R: RangeBounds<usize>>(
data: &'a T,
range:R
range: R,
) -> HexDump<'a, T, R> {
HexDump(data, range)
}
impl<'s, T: Scannable> HexDump<'s, T, RangeFull> {
pub fn new(scannable: &'s T) -> Self {
Self(scannable, ..)
}
}
impl<'s, T: Scannable + ?Sized, R: RangeBounds<usize>> Display for HexDump<'s, T, R> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
// calculate the end and the start addresses
let (start, end) = {
let r = self.0.range();
@@ -37,32 +29,39 @@ impl<'s, T: Scannable + ?Sized, R: RangeBounds<usize>> Display for HexDump<'s, T
Bound::Included(i) => *i,
Bound::Excluded(i) => i.saturating_add(1),
Bound::Unbounded => 0,
}.max(r.start);
}
.max(r.start);
let end = match self.1.end_bound() {
Bound::Included(i) => *i,
Bound::Excluded(i) => i.saturating_sub(1),
Bound::Unbounded => usize::MAX,
}.min(r.end);
}
.min(r.end);
(start, end)
};
// the number of digits the address column should have
let digits = if end == 0 { 4 } else {
let digits = if end == 0 {
4
} else {
(end.ilog(16) as usize + 1).max(4)
};
for (mut addr, chunk) in ChunkIter::new(self.0, start) {
for chunk in chunk.chunks(16) {
if addr > end { return Ok(()) }
if addr > end {
return Ok(());
}
let chunk = &chunk[..(1 + end - addr).min(chunk.len())];
//╶───╴Column╶────────────────────────────────╴
write!(f, "{:0digits$X}{SEP}", addr, digits = digits)?;
//╶───╴Bytes╶─────────────────────────────────╴
for (i, byte) in chunk.iter().enumerate() {
if i != 0 { write!(f, " ")?; }
if i != 0 {
write!(f, " ")?;
}
write!(f, "{byte:02X}")?;
}
for i in (chunk.len()..16) {

View File

@@ -1,12 +1,13 @@
#![no_std] #![allow(unused)]
#![no_std]
#![allow(unused)]
pub mod atoms {
include!("atoms.rs");
}
pub mod hexdump;
pub mod scannable;
pub mod scanner;
pub mod hexdump;
#[cfg(feature = "alloc")]
extern crate alloc;
@@ -19,19 +20,19 @@ pub mod parser;
//
pub mod prelude {
pub use sub_macros::pattern;
pub use crate::atoms::Pattern;
pub use crate::scanner::Scanner;
pub use crate::hexdump::hex;
pub use crate::scanner::Scanner;
pub use sub_macros::pattern;
}
pub mod public {
pub use crate::atoms::Atom;
pub use crate::scannable::Scannable;
pub use crate::scanner::{
exec, scan_for_aob, make_aob
};
pub use crate::scannable::ChunkIter;
pub use crate::hexdump::HexDump;
}
pub use crate::scannable::ChunkIter;
pub use crate::scannable::Scannable;
pub use crate::scanner::{exec, make_aob, scan_for_aob};
#[cfg(feature = "alloc")]
pub use crate::parser::parse;
}

View File

@@ -1,6 +1,6 @@
use core::{cmp, fmt, mem, str};
use super::atoms::Atom;
use alloc::vec::Vec;
use core::{cmp, fmt, mem, str};
/// Special skip value to indicate to use platform pointer size instead.
pub(crate) const PTR_SKIP: u8 = 0;
@@ -14,7 +14,12 @@ pub struct ParsePatError {
impl fmt::Display for ParsePatError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Syntax Error @{}: {}.", self.position, self.kind.to_str())
write!(
f,
"Syntax Error @{}: {}.",
self.position,
self.kind.to_str()
)
}
}
@@ -34,7 +39,7 @@ pub enum PatError {
ReadOperand,
SubPattern,
SubOverflow,
DoubleNibble
DoubleNibble,
}
impl PatError {
pub fn to_str(self) -> &'static str {
@@ -163,7 +168,7 @@ pub fn parse(pat: &str) -> Result<Vec<Atom>, ParsePatError> {
Err(kind) => {
let position = pat_end.as_ptr() as usize - pat.as_ptr() as usize;
Err(ParsePatError { kind, position })
},
}
}
}
// This is preferable but currently limited by macro rules...
@@ -201,7 +206,7 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
_ => return Err(PatError::StackInvalid),
};
result.push(atom);
},
}
// End recursive operator
b'}' => {
// Unbalanced recursion
@@ -210,7 +215,7 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
}
depth -= 1;
result.push(Atom::Pop);
},
}
// Start subpattern
b'(' => {
subs.push(SubPattern::default());
@@ -221,7 +226,7 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
// Add a new case, update the case offset later
sub.case = result.len();
result.push(Atom::Case(0));
},
}
// Case subpattern
b'|' => {
// Should already have started a subpattern
@@ -241,7 +246,7 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
result[sub.case] = Atom::Case(case_offset as u8);
sub.case = result.len();
result.push(Atom::Case(0));
},
}
// End subpattern
b')' => {
// Should already have started a subpattern
@@ -259,7 +264,7 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
}
result[brk] = Atom::Break(brk_offset as u8);
}
},
}
// Skip many operator
b'[' => {
// Parse the lower bound
@@ -275,7 +280,7 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
if lower_bound >= 16384 {
return Err(PatError::ManyOverflow);
}
},
}
_ => return Err(PatError::ManyInvalid),
}
}
@@ -304,7 +309,7 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
if upper_bound >= 16384 {
return Err(PatError::ManyOverflow);
}
},
}
_ => return Err(PatError::ManyInvalid),
}
}
@@ -315,52 +320,64 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
result.push(Atom::Rangext((many_skip >> 8) as u8));
}
result.push(Atom::Many((many_skip & 0xff) as u8));
}
else {
} else {
return Err(PatError::ManyRange);
}
},
}
// Match a byte
b'0'..=b'9' | b'A'..=b'F' | b'a'..=b'f' | b'.' => {
let mut mask = 0xFF;
// High nibble of the byte
let hi = if chr == b'.' { mask &= 0x0F;0 }
else if chr >= b'a' { chr - b'a' + 10 }
else if chr >= b'A' { chr - b'A' + 10 }
else { chr - b'0' };
let hi = if chr == b'.' {
mask &= 0x0F;
0
} else if chr >= b'a' {
chr - b'a' + 10
} else if chr >= b'A' {
chr - b'A' + 10
} else {
chr - b'0'
};
chr = iter.next().cloned().ok_or(PatError::UnpairedHexDigit)?;
// Low nibble of the byte
let lo = if chr >= b'a' && chr <= b'f' { chr - b'a' + 10 }
else if chr >= b'A' && chr <= b'F' { chr - b'A' + 10 }
else if chr >= b'0' && chr <= b'9' { chr - b'0' }
else if chr == b'.' { mask &= 0xF0; 0 }
else { return Err(PatError::UnpairedHexDigit); };
let lo = if chr >= b'a' && chr <= b'f' {
chr - b'a' + 10
} else if chr >= b'A' && chr <= b'F' {
chr - b'A' + 10
} else if chr >= b'0' && chr <= b'9' {
chr - b'0'
} else if chr == b'.' {
mask &= 0xF0;
0
} else {
return Err(PatError::UnpairedHexDigit);
};
if mask == 0 { return Err(PatError::DoubleNibble); };
if mask == 0 {
return Err(PatError::DoubleNibble);
};
// mask out nibble
if mask != 0xFF { result.push(Atom::Fuzzy(mask)) }
if mask != 0xFF {
result.push(Atom::Fuzzy(mask))
}
// Add byte to the pattern
result.push(Atom::Byte((hi << 4) + lo));
},
}
// Match raw bytes
b'"' => {
loop {
b'"' => loop {
if let Some(chr) = iter.next().cloned() {
if chr != b'"' {
result.push(Atom::Byte(chr));
}
else {
} else {
break;
}
}
else {
} else {
return Err(PatError::UnclosedQuote);
}
}
},
// Save the cursor
b'\'' => {
@@ -370,7 +387,7 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
}
result.push(Atom::Save(save));
save += 1;
},
}
// Skip bytes
b'?' => {
// match result.last_mut() {
@@ -385,26 +402,26 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
}
}
result.push(Atom::Skip(1));
},
}
b'=' => {
let op = iter.next().cloned().ok_or(PatError::CheckOperand)?;
result.push( match op {
result.push(match op {
b'0'..=b'9' => Atom::Check(op - b'0'),
b'A'..=b'Z' => Atom::Check(10 + (op - b'A')),
b'a'..=b'z' => Atom::Check(10 + (op - b'a')),
_ => return Err(PatError::CheckOperand)
_ => return Err(PatError::CheckOperand),
});
},
}
b'@' => {
let op = iter.next().cloned().ok_or(PatError::AlignedOperand)?;
result.push( match op {
result.push(match op {
b'0'..=b'9' => Atom::Aligned(op - b'0'),
b'A'..=b'Z' => Atom::Aligned(10 + (op - b'A')),
b'a'..=b'z' => Atom::Aligned(10 + (op - b'a')),
_ => return Err(PatError::AlignedOperand)
_ => return Err(PatError::AlignedOperand),
});
},
}
b'i' => {
let atom = match iter.next().cloned() {
Some(b'1') => Atom::ReadI8(save),
@@ -417,7 +434,7 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
}
save += 1;
result.push(atom);
},
}
b'u' => {
let atom = match iter.next().cloned() {
Some(b'1') => Atom::ReadU8(save),
@@ -430,22 +447,21 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
}
save += 1;
result.push(atom);
},
}
b'z' => {
if save >= u8::MAX {
return Err(PatError::SaveOverflow);
}
result.push(Atom::Zero(save));
save += 1;
},
}
// Allow spaces as padding
b' ' | b'\n' | b'\r' | b'\t' => {},
b' ' | b'\n' | b'\r' | b'\t' => {}
// Everything else is illegal
_ => {
return Err(PatError::UnknownChar);
},
}
}
// Converted from str originally, should be safe
*pat = unsafe { str::from_utf8_unchecked(iter.as_slice()) };
@@ -462,10 +478,7 @@ fn parse_helper(pat: &mut &str, result: &mut Vec<Atom>) -> Result<(), PatError>
// Remove redundant atoms at the end
fn is_redundant(atom: &Atom) -> bool {
match atom {
| Atom::Skip(_)
| Atom::Rangext(_)
| Atom::Pop
| Atom::Many(_) => true,
Atom::Skip(_) | Atom::Rangext(_) | Atom::Pop | Atom::Many(_) => true,
_ => false,
}
}

View File

@@ -9,33 +9,40 @@ pub trait Scannable {
/// given an address will return the next chunk, None if there are no more hcunks
fn next_chunk(&self, address: usize) -> Option<(usize, &[u8])>;
}
impl Scannable for [u8] {
fn range(&self) -> Range<usize> { 0..self.len() }
fn range(&self) -> Range<usize> {
0..self.len()
}
fn chunk_at(&self, address: usize) -> Option<&[u8]> {
self.get(address..)
}
fn next_chunk(&self, _address: usize) -> Option<(usize, &[u8])> { None }
fn next_chunk(&self, _address: usize) -> Option<(usize, &[u8])> {
None
}
}
/// In case you want to scan with a specific address
impl Scannable for (usize, &[u8]) {
fn range(&self) -> Range<usize> { self.0..(self.0 + self.1.len()) }
fn range(&self) -> Range<usize> {
self.0..(self.0 + self.1.len())
}
fn chunk_at(&self, address: usize) -> Option<&[u8]> {
match address.overflowing_sub(self.0) {
(address, false) => self.1.get(address..),
(_, true) => None,
}
}
fn next_chunk(&self, _address: usize) -> Option<(usize, &[u8])> { None }
fn next_chunk(&self, _address: usize) -> Option<(usize, &[u8])> {
None
}
}
pub struct ChunkIter<'l, T: Scannable + ?Sized>(&'l T, usize, bool);
impl<'l, T: Scannable + ?Sized> ChunkIter<'l, T> {
pub const fn new(scannable : &'l T, start: usize) -> Self {
pub const fn new(scannable: &'l T, start: usize) -> Self {
Self(scannable, start, true)
}
}
@@ -43,12 +50,11 @@ impl<'l, T: Scannable + ?Sized> ChunkIter<'l, T> {
impl<'l, T: Scannable + ?Sized> Iterator for ChunkIter<'l, T> {
type Item = (usize, &'l [u8]);
fn next(&mut self) -> Option<Self::Item> {
// if this is the first time being called, use chunk_at instead of next_chunk
if self.2 {
self.2 = false;
if let Some(chunk) = self.0.chunk_at(self.1) {
return Some((self.1, chunk))
return Some((self.1, chunk));
}
}
@@ -59,6 +65,5 @@ impl<'l, T: Scannable + ?Sized> Iterator for ChunkIter<'l, T> {
Some((addr, block))
}
}
}
}

View File

@@ -1,7 +1,7 @@
use core::ops::{Range, RangeBounds, Bound};
use crate::atoms::{Pattern, Atom};
use crate::atoms::{Atom, Pattern};
use crate::scannable::Scannable;
use sub_core::{pod::Pod};
use core::ops::{Bound, Range, RangeBounds};
use sub_core::pod::Pod;
const SKIP_VA: u32 = size_of::<usize>() as u32;
@@ -20,15 +20,19 @@ pub struct Scanner<'a, S: Scannable + ?Sized> {
}
impl<'a, S: Scannable + ?Sized> Scanner<'a, S> {
pub fn new(bin: &'a S, pat: Pattern<'a>, r: impl RangeBounds<usize>) -> Self {
let range = limit_range(bin, r);
let cursor = range.start;
Self { bin, pat, range, cursor }
Self {
bin,
pat,
range,
cursor,
}
}
pub fn get<const LEN: usize>(&mut self) -> Option<[usize;LEN]> {
let mut m = [0usize;LEN];
pub fn get<const LEN: usize>(&mut self) -> Option<[usize; LEN]> {
let mut m = [0usize; LEN];
self.next(&mut m).then_some(m)
}
@@ -51,7 +55,15 @@ impl<'a, S: Scannable + ?Sized> Scanner<'a, S> {
while self.range.contains(&self.cursor) {
let current_cursor = self.cursor;
self.cursor += 1;
if exec(self.bin, current_cursor, self.pat, saves, self.range.clone()).is_some() {
if exec(
self.bin,
current_cursor,
self.pat,
saves,
self.range.clone(),
)
.is_some()
{
return true;
}
}
@@ -69,7 +81,6 @@ pub fn exec<Binary: Scannable + ?Sized>(
saves: &mut [usize],
range: Range<usize>,
) -> Option<usize> {
let mut cursor = address;
let mut pc = 0;
@@ -89,11 +100,14 @@ pub fn exec<Binary: Scannable + ?Sized>(
while let Some(atom) = pattern.get(pc).cloned() {
pc += 1;
match atom {
// Compare bytes
Atom::Byte(pat_byte) => {
let Some(byte) = read::<_, u8>(bin, cursor) else { return None; };
if byte & mask != pat_byte & mask { return None; }
let Some(byte) = read::<_, u8>(bin, cursor) else {
return None;
};
if byte & mask != pat_byte & mask {
return None;
}
cursor += 1;
mask = 0xFF;
}
@@ -124,7 +138,7 @@ pub fn exec<Binary: Scannable + ?Sized>(
Some(Atom::Push(_)) => counter += 1,
Some(Atom::Pop) => counter -= 1,
None => return Some(cursor),
_ => (/**/)
_ => (),
}
pc += 1;
}
@@ -162,29 +176,39 @@ pub fn exec<Binary: Scannable + ?Sized>(
}
Atom::Jump1 => {
let Some(sbyte) = read::<_, i8>(bin, cursor) else { return None };
let Some(sbyte) = read::<_, i8>(bin, cursor) else {
return None;
};
cursor = cursor.wrapping_add(sbyte as usize).wrapping_add(1);
}
Atom::Jump4 => {
let Some(sdword) = read::<_, i32>(bin, cursor) else { return None };
let Some(sdword) = read::<_, i32>(bin, cursor) else {
return None;
};
cursor = cursor.wrapping_add(sdword as usize).wrapping_add(4);
}
Atom::Ptr => {
let Some(sptr) = read::<_, usize>(bin, cursor) else { return None };
let Some(sptr) = read::<_, usize>(bin, cursor) else {
return None;
};
cursor = sptr;
}
Atom::Pir(slot) => {
let Some(sdword) = read::<_, i32>(bin, cursor) else { return None };
let Some(sdword) = read::<_, i32>(bin, cursor) else {
return None;
};
let base = saves.get(slot as usize).cloned().unwrap_or(cursor);
cursor = base.wrapping_add(sdword as usize);
}
Atom::Check(slot) => {
if let Some(&rva) = saves.get(slot as usize) {
if rva != cursor { return None; }
if rva != cursor {
return None;
}
}
}
@@ -195,33 +219,57 @@ pub fn exec<Binary: Scannable + ?Sized>(
}
Atom::ReadU8(slot) => {
let Some(value) = read::<_, u8>(bin, cursor) else { return None };
if let Some(slot) = saves.get_mut(slot as usize) { *slot = value as _ }
let Some(value) = read::<_, u8>(bin, cursor) else {
return None;
};
if let Some(slot) = saves.get_mut(slot as usize) {
*slot = value as _
}
cursor = cursor.wrapping_add(1)
}
Atom::ReadI8(slot) => {
let Some(value) = read::<_, i8>(bin, cursor) else { return None };
if let Some(slot) = saves.get_mut(slot as usize) { *slot = value as _ }
let Some(value) = read::<_, i8>(bin, cursor) else {
return None;
};
if let Some(slot) = saves.get_mut(slot as usize) {
*slot = value as _
}
cursor = cursor.wrapping_add(1)
}
Atom::ReadU16(slot) => {
let Some(value) = read::<_, u16>(bin, cursor) else { return None };
if let Some(slot) = saves.get_mut(slot as usize) { *slot = value as _ }
let Some(value) = read::<_, u16>(bin, cursor) else {
return None;
};
if let Some(slot) = saves.get_mut(slot as usize) {
*slot = value as _
}
cursor = cursor.wrapping_add(2)
}
Atom::ReadI16(slot) => {
let Some(value) = read::<_, i16>(bin, cursor) else { return None };
if let Some(slot) = saves.get_mut(slot as usize) { *slot = value as _ }
let Some(value) = read::<_, i16>(bin, cursor) else {
return None;
};
if let Some(slot) = saves.get_mut(slot as usize) {
*slot = value as _
}
cursor = cursor.wrapping_add(2)
}
Atom::ReadU32(slot) => {
let Some(value) = read::<_, u32>(bin, cursor) else { return None };
if let Some(slot) = saves.get_mut(slot as usize) { *slot = value as _ }
let Some(value) = read::<_, u32>(bin, cursor) else {
return None;
};
if let Some(slot) = saves.get_mut(slot as usize) {
*slot = value as _
}
cursor = cursor.wrapping_add(4)
}
Atom::ReadI32(slot) => {
let Some(value) = read::<_, i32>(bin, cursor) else { return None };
if let Some(slot) = saves.get_mut(slot as usize) { *slot = value as _ }
let Some(value) = read::<_, i32>(bin, cursor) else {
return None;
};
if let Some(slot) = saves.get_mut(slot as usize) {
*slot = value as _
}
cursor = cursor.wrapping_add(4)
}
Atom::Zero(slot) => {
@@ -246,12 +294,11 @@ pub fn exec<Binary: Scannable + ?Sized>(
}
}
None => return Some(cursor),
_ => (/**/)
_ => (),
}
pc += 1;
}
// panic!("{pc}, {:X?} == {:02X}", pattern.get(pc), bin.chunk_at(cursor).unwrap([1]));
} else {
// if the case fails go to the location defined by next
pc += next as usize;
@@ -266,9 +313,8 @@ pub fn exec<Binary: Scannable + ?Sized>(
Some(cursor)
}
#[inline(always)]
pub fn exec_many<Binary: Scannable + ?Sized >(
pub fn exec_many<Binary: Scannable + ?Sized>(
bin: &Binary,
address: usize,
pattern: Pattern,
@@ -309,7 +355,6 @@ pub fn exec_many<Binary: Scannable + ?Sized >(
}
}
#[inline(always)]
pub fn scan_for_aob<Binary: Scannable + ?Sized>(
bin: &Binary,
@@ -319,16 +364,13 @@ pub fn scan_for_aob<Binary: Scannable + ?Sized>(
let mut address = range.start;
let upper_bounds = range.end;
while address < upper_bounds {
// get the current chunk for the given address
let chunk = match bin.chunk_at(address) {
Some(chunk) => chunk,
// the address is out of bounds, try to shift the address so its back in bounds
None => match bin.next_chunk(address) {
// the next chunk is in bounds so we will just correct the address and use that chunk instead
Some((naddr, nchunk)) if naddr < upper_bounds => {
address = naddr;
@@ -337,33 +379,36 @@ pub fn scan_for_aob<Binary: Scannable + ?Sized>(
// no hope, give up
_ => return None,
}
},
};
// try to find the aob in the current chunk
if let Some(offset) = chunk.windows(aob.len())
.take(upper_bounds.saturating_sub(address)).position(|c| c == aob) {
if let Some(offset) = chunk
.windows(aob.len())
.take(upper_bounds.saturating_sub(address))
.position(|c| c == aob)
{
// we got a hit, return it
return Some(address + offset)
return Some(address + offset);
}
// the AOB was not found in the current chunk, now check if its contiguous between chunks:
if let Some((naddr, nchunk)) = bin.next_chunk(address) {
// next chunk is out of bounds, give up
if naddr - aob.len() > upper_bounds { return None }
if naddr - aob.len() > upper_bounds {
return None;
}
// if chunks are contiguous and the aob is greater than one byte,
// check if the aob is on a chunk border
if address + chunk.len() == naddr && aob.len() > 1 {
// check if the aob is between two chunks :)
for i in 1..aob.len()-1 {
for i in 1..aob.len() - 1 {
let (p1, p2) = aob.split_at(i);
if chunk.ends_with(p1) && nchunk.starts_with(p2) {
// aob was found between two chunks
// return this address
return Some(address + chunk.len() - i)
return Some(address + chunk.len() - i);
}
}
}
@@ -373,17 +418,18 @@ pub fn scan_for_aob<Binary: Scannable + ?Sized>(
// TODO When i originally wrote this I put a subtraction
// I don't remember why but I think it should be an addition
let naddr = naddr + aob.len();
debug_assert!(naddr > address, "debug assertion failed: {naddr:X} > {address:X}");
debug_assert!(
naddr > address,
"debug assertion failed: {naddr:X} > {address:X}"
);
address = naddr;
} else {
return None
return None;
}
}
None
}
/// Limits a selected range into the range of the binary...
fn limit_range<Binary: Scannable + ?Sized>(
bin: &Binary,
@@ -398,7 +444,7 @@ fn limit_range<Binary: Scannable + ?Sized>(
let end = match range.end_bound() {
Bound::Included(v) => bin_range.end.min(v.saturating_add(1)),
Bound::Excluded(v) => bin_range.end.min(*v),
Bound::Unbounded => bin_range.end
Bound::Unbounded => bin_range.end,
};
start..end
}
@@ -422,15 +468,3 @@ pub fn make_aob<'b>(pattern: &[Atom], buffer: &'b mut [u8]) -> &'b [u8] {
}
&buffer[..i]
}