#![allow(clippy::needless_range_loop)]
const PAGE: usize = 4096;
const SECTOR: usize = 512;
const SECTORS: usize = 8;
const TRAILER_START: usize = 0xFF0;
const D5: u8 = 0xD5;
const ZB: u8 = 0x0B;
pub fn oracle_bv_e_page(pn: u64, raw_page: &[u8]) -> u8 {
let p16 = (pn % 16) as u8;
let bias = p16 / 2 * 4;
raw_page[0].wrapping_sub(pn as u8).wrapping_add(bias)
}
pub fn recover_bv_qb_data(pn: u64, raw_page: &[u8]) -> Option<u8> {
if raw_page.len() != PAGE {
return None;
}
let trailer_rc = raw_page[TRAILER_START];
let p16 = (pn % 16) as u8;
let bias = p16 / 2 * 4;
let oracle_bv = raw_page[0].wrapping_sub(pn as u8).wrapping_add(bias);
let mut c_table: [Vec<u8>; SECTORS] = Default::default();
for si in 0..SECTORS {
let off = si * SECTOR;
let end = if si == SECTORS - 1 {
TRAILER_START
} else {
off + SECTOR
};
let sec = &raw_page[off..end];
let oracle_base = oracle_bv
.wrapping_add(pn as u8)
.wrapping_add(si as u8)
.wrapping_sub(bias);
let step = recover_step(sec, oracle_base);
let offset_no_bv = (pn as u8).wrapping_add(si as u8).wrapping_sub(bias);
let mut c = vec![0u8; sec.len()];
for (i, &b) in sec.iter().enumerate() {
c[i] = b
.wrapping_sub(offset_no_bv)
.wrapping_sub((i as u8).wrapping_mul(step));
}
c_table[si] = c;
}
if anchor_present_with_bv(&c_table, oracle_bv, trailer_rc) {
return Some(oracle_bv);
}
for bv in 0u16..=255 {
let bv = bv as u8;
if bv == oracle_bv {
continue;
}
if anchor_present_with_bv(&c_table, bv, trailer_rc) {
return Some(bv);
}
}
None
}
fn anchor_present_with_bv(c_table: &[Vec<u8>; SECTORS], bv: u8, rc: u8) -> bool {
let mut plain = [0u8; TRAILER_START];
let mut cursor = 0usize;
for si in 0..SECTORS {
let c = &c_table[si];
for (i, &cb) in c.iter().enumerate() {
plain[cursor + i] = cb.wrapping_sub(bv);
}
cursor += c.len();
}
if plain.len() < 4 {
return false;
}
let limit = plain.len() - 4;
let mut i = 0;
while i <= limit {
if plain[i] == rc && plain[i + 1] == 0 && plain[i + 2] == D5 && plain[i + 3] == ZB {
return true;
}
i += 1;
}
false
}
fn recover_step(sec: &[u8], base: u8) -> u8 {
let mut best_step = 0u8;
let mut best_peak = 0u16;
for step in 0u16..=255 {
let step = step as u8;
let mut hist = [0u16; 256];
for (i, &b) in sec.iter().enumerate() {
let plain = b
.wrapping_sub(base)
.wrapping_sub((i as u8).wrapping_mul(step));
hist[plain as usize] += 1;
}
let peak = *hist.iter().max().unwrap();
if peak > best_peak {
best_peak = peak;
best_step = step;
}
}
best_step
}
pub fn recover_bv_brute(pn: u64, raw: &[u8]) -> Option<u8> {
if raw.len() != PAGE {
return None;
}
let p16 = (pn % 16) as u8;
let bias = p16 / 2 * 4;
let mut hist_per_sec: [[u32; 256]; SECTORS] = [[0; 256]; SECTORS];
for si in 0..SECTORS {
let off = si * SECTOR;
let end = if si == SECTORS - 1 {
TRAILER_START
} else {
off + SECTOR
};
let sec = &raw[off..end];
let step = recover_step(sec, 0);
for (i, &b) in sec.iter().enumerate() {
let c = b.wrapping_sub((i as u8).wrapping_mul(step));
hist_per_sec[si][c as usize] += 1;
}
}
let mut best_bv = 0u8;
let mut best_count = 0u32;
for bv in 0u16..=255 {
let bv = bv as u8;
let mut total = 0u32;
for si in 0..SECTORS {
let target = bv
.wrapping_add(pn as u8)
.wrapping_add(si as u8)
.wrapping_sub(bias);
total += hist_per_sec[si][target as usize];
}
if total > best_count {
best_count = total;
best_bv = bv;
}
}
if best_count >= (TRAILER_START as u32) * 5 / 100 {
Some(best_bv)
} else {
None
}
}
pub const APAGE_MAGIC: [u8; 8] = [0x24, 0x04, 0x31, 0x00, 0xB4, 0x02, 0x19, 0x00];
pub fn recover_bv_apage(pn: u64, raw_page: &[u8]) -> Option<u8> {
if raw_page.len() != PAGE {
return None;
}
let p16 = (pn % 16) as u8;
let bias = p16 / 2 * 4;
let mut c_table: [Vec<u8>; SECTORS] = Default::default();
for si in 0..SECTORS {
let off = si * SECTOR;
let end = if si == SECTORS - 1 {
TRAILER_START
} else {
off + SECTOR
};
let sec = &raw_page[off..end];
let step = recover_step(sec, 0);
let mut c = vec![0u8; sec.len()];
for (i, &b) in sec.iter().enumerate() {
c[i] = b.wrapping_sub((i as u8).wrapping_mul(step));
}
c_table[si] = c;
}
let mut plain = [0u8; TRAILER_START];
for bv in 0u16..=255 {
let bv = bv as u8;
let mut cursor = 0usize;
for si in 0..SECTORS {
let c = &c_table[si];
let base = bv
.wrapping_add(pn as u8)
.wrapping_add(si as u8)
.wrapping_sub(bias);
for (i, &cb) in c.iter().enumerate() {
plain[cursor + i] = cb.wrapping_sub(base);
}
cursor += c.len();
}
if contains_subseq(&plain, &APAGE_MAGIC) {
return Some(bv);
}
}
None
}
fn contains_subseq(hay: &[u8], needle: &[u8]) -> bool {
if needle.is_empty() || hay.len() < needle.len() {
return false;
}
let limit = hay.len() - needle.len();
let n0 = needle[0];
let mut i = 0;
while i <= limit {
if hay[i] == n0 && &hay[i..i + needle.len()] == needle {
return true;
}
i += 1;
}
false
}
pub fn recover_bv_any(pn: u64, raw: &[u8]) -> Option<u8> {
if let Some(bv) = recover_bv_qb_data(pn, raw) {
return Some(bv);
}
let obv = oracle_bv_e_page(pn, raw);
let plain = deobfuscate_with_bv(raw, pn, obv);
let zeros = plain[..TRAILER_START].iter().filter(|&&b| b == 0).count();
if zeros * 100 / TRAILER_START >= 5 {
return Some(obv);
}
recover_bv_brute(pn, raw)
}
pub fn deobfuscate_with_bv(raw: &[u8], pn: u64, bv: u8) -> Vec<u8> {
assert_eq!(raw.len(), PAGE);
let mut out = vec![0u8; PAGE];
let p16 = (pn % 16) as u8;
let bias = p16 / 2 * 4;
for si in 0..SECTORS {
let off = si * SECTOR;
let end = if si == SECTORS - 1 {
TRAILER_START
} else {
off + SECTOR
};
let sec = &raw[off..end];
let base = bv
.wrapping_add(pn as u8)
.wrapping_add(si as u8)
.wrapping_sub(bias);
let step = recover_step(sec, base);
for (i, &b) in sec.iter().enumerate() {
out[off + i] = b
.wrapping_sub(base)
.wrapping_sub((i as u8).wrapping_mul(step));
}
}
out[TRAILER_START..PAGE].copy_from_slice(&raw[TRAILER_START..PAGE]);
out
}
#[cfg(test)]
mod tests {
use super::*;
fn synth_page(pn: u64, bv: u8, step_per_sec: [u8; SECTORS]) -> ([u8; PAGE], Vec<u8>) {
let p16 = (pn % 16) as u8;
let bias = p16 / 2 * 4;
let mut plain = vec![0u8; PAGE];
for i in (64..TRAILER_START).step_by(37) {
plain[i] = ((i as u32 * 13) & 0xFF) as u8;
}
plain[TRAILER_START] = 0x04;
plain[TRAILER_START + 1] = 0x00;
plain[TRAILER_START + 2] = 0x45;
let mut stored = [0u8; PAGE];
stored[TRAILER_START..].copy_from_slice(&plain[TRAILER_START..]);
for si in 0..SECTORS {
let off = si * SECTOR;
let end = if si == SECTORS - 1 {
TRAILER_START
} else {
off + SECTOR
};
let step = step_per_sec[si];
let base = bv
.wrapping_add(pn as u8)
.wrapping_add(si as u8)
.wrapping_sub(bias);
for i in off..end {
let idx = (i - off) as u8;
stored[i] = plain[i]
.wrapping_add(base)
.wrapping_add(idx.wrapping_mul(step));
}
}
(stored, plain)
}
#[test]
fn brute_recovers_known_bv_zero_dominant() {
let pn = 1234u64;
let bv = 0x5a;
let steps = [0x11u8, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
let (raw, plain) = synth_page(pn, bv, steps);
let recovered = recover_bv_brute(pn, &raw).expect("brute returns Some");
assert_eq!(recovered, bv, "brute should recover the true bv");
let decoded = deobfuscate_with_bv(&raw, pn, recovered);
assert_eq!(&decoded[..TRAILER_START], &plain[..TRAILER_START]);
}
#[test]
fn brute_rejects_random_page() {
let mut raw = [0u8; PAGE];
let mut x = 0x9e3779b9u32;
for b in raw.iter_mut() {
x = x.wrapping_mul(0x85ebca6b).wrapping_add(1);
*b = (x >> 24) as u8;
}
let _ = recover_bv_brute(42, &raw); }
#[test]
fn recover_bv_any_prefers_qb_anchor_when_present() {
let pn = 77u64;
let p16 = (pn % 16) as u8;
let bias = p16 / 2 * 4;
let bv = 0xa3u8;
let mut plain = vec![0u8; PAGE];
plain[TRAILER_START] = 0x07; plain[0x100] = 0x07;
plain[0x101] = 0x00;
plain[0x102] = 0xD5;
plain[0x103] = 0x0B;
let mut raw = [0u8; PAGE];
raw[TRAILER_START..].copy_from_slice(&plain[TRAILER_START..]);
for si in 0..SECTORS {
let off = si * SECTOR;
let end = if si == SECTORS - 1 {
TRAILER_START
} else {
off + SECTOR
};
let base = bv
.wrapping_add(pn as u8)
.wrapping_add(si as u8)
.wrapping_sub(bias);
for i in off..end {
raw[i] = plain[i].wrapping_add(base);
}
}
let recovered = recover_bv_any(pn, &raw).expect("some bv");
assert_eq!(recovered, bv);
}
fn synth_apage(pn: u64, bv: u8, magic_offset: usize) -> [u8; PAGE] {
let p16 = (pn % 16) as u8;
let bias = p16 / 2 * 4;
let mut plain = vec![0u8; PAGE];
for i in 0..TRAILER_START {
if i % 28 == 0 {
plain[i] = 0x60;
} else if i % 28 == 1 {
plain[i] = 0x80;
}
}
plain[magic_offset..magic_offset + APAGE_MAGIC.len()].copy_from_slice(&APAGE_MAGIC);
plain[TRAILER_START] = 0x05;
plain[TRAILER_START + 1] = 0x00;
plain[TRAILER_START + 2] = 0x41;
let mut stored = [0u8; PAGE];
stored[TRAILER_START..].copy_from_slice(&plain[TRAILER_START..]);
for si in 0..SECTORS {
let off = si * SECTOR;
let end = if si == SECTORS - 1 {
TRAILER_START
} else {
off + SECTOR
};
let base = bv
.wrapping_add(pn as u8)
.wrapping_add(si as u8)
.wrapping_sub(bias);
for i in off..end {
stored[i] = plain[i].wrapping_add(base);
}
}
stored
}
#[test]
fn apage_recovers_known_bv() {
let pn = 3599u64;
let bv = 66u8;
let raw = synth_apage(pn, bv, 0xC4);
let recovered = recover_bv_apage(pn, &raw).expect("apage returns Some");
assert_eq!(recovered, bv);
}
#[test]
fn apage_handles_magic_near_sector_boundary() {
let pn = 3620u64;
let bv = 244u8;
let raw = synth_apage(pn, bv, 0x1FE);
let recovered = recover_bv_apage(pn, &raw).expect("apage returns Some");
assert_eq!(recovered, bv);
}
#[test]
fn apage_returns_none_when_magic_absent() {
let mut raw = [0u8; PAGE];
let mut x = 0xDEADBEEFu32;
for b in raw.iter_mut() {
x = x.wrapping_mul(0x85ebca6b).wrapping_add(1);
*b = (x >> 24) as u8;
}
assert!(recover_bv_apage(123, &raw).is_none());
}
#[test]
fn apage_rejects_wrong_size() {
let raw = [0u8; PAGE - 1];
assert!(recover_bv_apage(1, &raw).is_none());
}
#[test]
fn apage_magic_constant_unchanged() {
assert_eq!(
APAGE_MAGIC,
[0x24, 0x04, 0x31, 0x00, 0xB4, 0x02, 0x19, 0x00]
);
}
}