Additive-progression deobfuscation
Some SA17-derived stores ship with an extra obfuscation pass on top
of the page-store layer. The most prominent example is the Intuit
QuickBooks Desktop .QBW format, which is an SA17 page store with
a deterministic additive-progression keystream XORed across each
page.
opensqlany::ApModel is the in-memory adapter that peels this
layer off. It does not commit the result to disk and does not break
any DRM - the obfuscation is a public, deterministic byte
transformation that the lawful owner of the file can already
reverse via the QuickBooks application.
Peeling the AP layer off
use opensqlany::{ApModel, PageStore};
let store = PageStore::open("Company.QBW")?;
let model = ApModel::learn(&store);
let page = store.page(1)?;
let plain = model.deobfuscate_with_store(page.bytes(), page.index(), &store);
// `plain` is now SA17 plaintext for this page; proceed as for a
// plaintext SA17 file (e.g. feed it to `SlottedPage::parse` via
// `Page::from_bytes`).
# Ok::<(), opensqlany::Error>(())
ApModel::learn scans every "pure-AP" page in the store (page types
'@', 'C', 'H', 'M') to calibrate the per-block bv value used
by the keystream formula below. It always succeeds - there is no
separate detection step. Use the CLI's ap-info subcommand to check
how many blocks were calibrated directly (learned_block_count) versus
falling back to on-demand recovery (deobfuscate_with_store) or the
block-0 approximation (deobfuscate).
bv is constant per 16-page block on the original corpus, but not on
every file - some QuickBooks Enterprise 24.0 files vary bv from page
to page within a block. deobfuscate_with_store always resolves bv
for the specific page being decoded rather than trusting its block's
learned value, so it stays correct either way; only the cheaper
block-only deobfuscate is affected by this.
Companion: OpenQBW
OpenQBW is the companion
project that builds the QuickBooks business-object layer on top.
It uses ApModel for the obfuscation peel, then drives
opensqlany for the page walk, then layers Intuit's schema on
top of the resulting catalog rows.
Full algorithm
See Specification, section "AP keystream", for the byte-level derivation of the keystream from the file header.