Replace specific ASINs, the book title, and the license voucher GUID with generic placeholders ahead of making the repo public. No technical content changes (the format census, vault structure, and analysis are unaffected). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 KiB
KFX + .kinf2024 decryption — follow-up research notes
Status: parked (not implemented). This documents what we found while trying to test Phase 4 (KFX) on a real Microsoft Store Kindle book, why it's blocked, and what a future effort would need to do.
Date investigated: 2026-06-24. Machine: Windows 11, Kindle installed via Microsoft Store.
TL;DR
- The target book (a KFX title from a local Kindle library) is in KFX
form: a DRMION-wrapped
.azwplus a separate.voucherand.resresources. - Decrypting KFX requires a per-book content key that is itself encrypted in a voucher, unlockable only with the Kindle account's DSN + account secret.
- Those credentials live in
.kinf2024— a key store newer than and structurally different from the.kinf2018that DeDRM supports. There is no published reverse-engineering of.kinf2024(it is not in DeDRM_tools). - Therefore the blocker is the key, not the KFX format. Even a perfect KFX
decryptor cannot be tested on this book until
.kinf2024is cracked. That is novel reverse-engineering of a deliberately-obfuscated, rotating format — outside this project's original "port the DeDRM logic" premise.
A follow-up has two independent parts: (A) a KFX decryption engine (a
tractable port of DeDRM) and (B) .kinf2024 key extraction (a research
problem). (A) is useless on Store-Kindle books without (B).
Evidence we gathered
The Kindle install (Microsoft Store)
- Package:
AMZNKindle.AmazonKindleReadingApp_m1sc522ngdk36 - Content root:
%LOCALAPPDATA%\Packages\AMZNKindle.AmazonKindleReadingApp_m1sc522ngdk36\LocalState\Classic\Content\ - Key store:
…\LocalState\Classic\Data\storage\.kinf2024
The book folder (…\Content\<ASIN>_EBOK\)
| File | Size | Notes |
|---|---|---|
<ASIN>_EBOK.azw |
1,166,844 | DRMION container (see magic below) |
amzn1.drm-voucher.v1.<uuid>.voucher |
1,199 | Encrypted DRM voucher (holds the content key) |
CR!….azw.res (many) |
1–6 MB each | KFX resource containers (images/fonts/text) |
<ASIN>_EBOK.mbpV2 |
554 | annotations/sidecar |
<ASIN>_EBOK.phl |
1,129 | popular-highlights sidecar |
First 16 bytes of <ASIN>_EBOK.azw:
ea 44 52 4d 49 4f 4e ee e0 01 00 ea ee 9e 81 83 .DRMION.........
… "ProtectedData" … "amz…"
\xeaDRMION\xee is the KFX DRMION magic. Note: this is a loose DRMION
file, not a .kfx-zip. (Our current Format::detect only recognizes DRMION
inside a ZIP — see "DRMLibre changes needed" below.)
.kinf2024 facts
- Size: 86,477 bytes.
- Zero
/(0x2F) bytes..kinf2018/.kinf2011are/-delimited record streams; the absence of any/means.kinf2024uses a completely different serialization — this is not a renamed.kinf2018. - Content is char-map-style encoded (first 64 chars looked like
bAY8YkY8bkd8AfdYA69v08b799A1AC0YC0009SAAY0d8CM94Ytz992zhb6Yt9J0J). - No legacy
.kinf2018/.kinf2011exists anywhere on this machine (no old desktop "Kindle for PC" install to fall back on).
There is also a Google-Drive-synced ~\My Drive\Documents\My Kindle Content\
with many classic *_EBOK.azw files from an older install. Those are also
account-locked and would need the same Kindle key material to decrypt.
Background: how KFX decryption works (the tractable part — a port)
Reference: DeDRM_tools/DeDRM_plugin/kfxdedrm.py, ion.py, kfxtables.py.
- Identify the pieces. A KFX book is a DRMION content file (
…\xeaDRMION\xee) plus a*.voucher(theDrmIonVoucher). Both are Amazon Ion binary. - Parse the voucher (Ion) to read the encryption algorithm (AES), transform
(CBC), hash (SHA-256) and the lock parameters:
CLIENT_ID= DSN,ACCOUNT_SECRET= account token. - Derive the voucher key. Build
shared = "PIDv3" + algos + lockparams, run it through 13 obfuscation variants (ion.pyobfuscate*/process_V*, ~lines 1051–1610), thenkey = HMAC_SHA256(secret, b"PIDv3")[:32]. AES-256-CBC decrypt the voucher with its IV, PKCS#7-unpad → aKeySet→ theSecretKey(DrmIonVoucher.decryptvoucher, ~lines 1643–1699). - Decrypt the DRMION content with the
SecretKey(AES-CBC) and repackage a DRM-free KFX. (Output is decrypted KFX, not EPUB — same limitation DeDRM has; a separate KFX→EPUB converter would be needed for a readable EPUB.)
What a Rust port needs:
- A minimal Amazon-Ion binary reader (enough for the voucher + DRMION).
- The YJ shared symbol table (the 2 MB
kfxtables.py) embedded as a compressedinclude_bytes!blob, to resolve symbol IDs used in the voucher. - LZMA (
lzma-rs) for compressed Ion LOBs — verify it handles KFX's framing. - Crypto we already have: AES-256-CBC (add 256-bit path to
crypto.rs), HMAC-SHA256 (hmaccrate), SHA-256, PKCS#7 unpad.
Risk: the 13-variant key derivation and the symbol-table embedding are fiddly, and "Amazon changes KFX DRM frequently" (DeDRM's own warning).
Background: .kinf2018 (closest documented relative of .kinf2024)
Reference: kindlekey.py::getDBfromFile, ~lines 419–572, and getK4Pids
(kgenpids.py ~219–309). For version 6 / .kinf2018:
- File =
/-separated records; first record is a DPAPI-protected header (UnprotectHeaderData) decoded withtestMap1, containing[Version:6][Build:b][Cksum:…][Guid:g]. - Per-key derivation:
salt = str(0x6D8*build) + guid,sp = GetUserName() + "+@#$%+" + GetIDString(),passwd = encode(SHA256(sp), charMap5),key = PBKDF2(passwd, salt, 10000, 0x400)[:32]. - Each value: rearrange (prime-offset) →
decode(testMap8)→iv||ciphertext, withiv = iv[:12] + 00 00 00 02, then AES-CTR decrypt →decode(charMap5). GetIDString()= disk volume serial / MAC-derived machine id.
The useful outputs are kindle.account.tokens (account secret) and DSN
(or the inputs to derive it: MazamaRandomNumber + IDString + UsernameHash).
These feed both getK4Pids (for MOBI/AZW PIDs) and the KFX voucher lock
params (CLIENT_ID=DSN, ACCOUNT_SECRET=token).
What's unknown for .kinf2024: the serialization (no / records), the
header/version encoding, the per-value cipher and key derivation, and what
machine/account entropy it binds to. All of this must be reverse-engineered.
What a follow-up needs to do
Part A — KFX decryption engine (port; ~the original Phase 4)
- Extend
Format::detectto recognize a loose DRMION file (starts with\xeaDRMION\xee) as KFX, in addition to KFX-ZIP, and to recognize a Kindle content folder (.azwDRMION +*.voucher+*.res). - Minimal Ion binary reader (
kindle/kfx/ion.rs). - Embedded YJ symbol table (
kindle/kfx/symbols.rs, compressed blob). - Voucher key derivation + DRMION decrypt (
kindle/kfx/voucher.rs). - Add AES-256-CBC + HMAC-SHA256 helpers to
crypto.rs; addlzma-rs. - Repackage decrypted KFX; wire the
KfxZip/Kfxbranch indecrypt.rs.
Part B — .kinf2024 key extraction (research; the real blocker)
- Decode the container. Figure out
.kinf2024's framing (it is not/-delimited). Inspect for length-prefixes / a new separator / base-N blocks. - Header + version. Find how the
[Version:…][Build:…][Guid:…](or its successor) is stored; confirm whether the header is still DPAPI-protected. - Per-value crypto. Determine the v7+ key derivation and cipher (is it still PBKDF2 + AES-CTR? new salt/entropy? new char-maps?).
- Goal outputs:
kindle.account.tokensandDSN(orMazamaRandomNumberIDString+UsernameHash).
- Likely needs dynamic analysis of the Store Kindle app (the encoder is in the
app), or watching for any community RE of
.kinf2024.
Once Part B yields DSN + account secret, Part A can be validated on the Narnia
book, and getK4Pids (already ported in kindle/pid.rs: encode,
encode_hash, generate_device_pid) lets the existing MOBI path also
decrypt account-locked classic .azw files.
DRMLibre changes already implied
crates/drmlibre-core/src/format.rs: add loose-DRMION detection + aKfx(non-zip) variant; currently such a file returns "can't determine file type".crates/drmlibre-core/src/kindle/pid.rs:encode/encode_hash/generate_device_pidare ported and tested but#[allow(dead_code)]untilgetK4Pids(the Kindle-DB → PID path) is wired — that wiring is the natural companion to Part B.crates/drmlibre-core/src/extract/: akindleextractor would live here, alongside the working Windows Adobe extractor.
Open questions / risks
.kinf2024may bind to hardware/account in new ways; extraction might only work on the same machine where Kindle is activated (acceptable for our use).- KFX DRM is a moving target; a port valid today may break on future books.
- Output of Part A is decrypted KFX, not EPUB — readability needs a separate KFX→EPUB step (out of scope here).
Update — Store/UWP Kindle key vault + book census (same investigation)
Follow-up attempt to decrypt the user's My Kindle Content library and automate
Kindle key extraction. Result: same blocker, now confirmed harder.
Every book is account-locked
Census of ~\My Drive\Documents\My Kindle Content\*_EBOK\*.azw (16 books):
| Format | Count | Notes |
|---|---|---|
| KFX (DRMION) | 14 | needs voucher key (account DSN/secret) |
Topaz (TPZ0) |
1 | different cipher, out of v0.1 scope |
| MOBI | 1 | crypto_type 2 (HUFF/CDIC), needs a PID |
So none are decryptable without the Kindle account key — even the single MOBI is PID-locked (not the fixed-key type-1 we hoped for).
The Store/UWP key vault (…\Classic\Data\storage\)
| File | Size | Observation |
|---|---|---|
.kinf2024 |
86,477 | char-map-encoded; no / records (unlike .kinf2018) |
main_shared.blob |
256 | not a DPAPI blob (starts a8 90 2d 20…, not 01 00 00 00 d0 8c 9d df) |
main_shared.salt |
32 | salt for deriving the blob's key |
main_shared.blob.sha256 |
32 | integrity hash |
main_shared.salt + a custom-encrypted main_shared.blob is a new master-key
vault: the blob's encryption key is derived inside the UWP Kindle app (not via
DPAPI), so it can't be recovered by reading files. .kinf2024's per-value
protection presumably chains off this master key.
Accessible-credential paths — all exhausted
HKCU\Software\Amazon: absent (UWP app doesn't use it).main_shared.blob: not DPAPI →CryptUnprotectDatadoesn't apply.- No
.kinf2018/.kinf2011anywhere (no older desktop K4PC install). - No plaintext
kindle.info/token files.
Conclusion: extracting the account key from this machine's Store Kindle is not feasible by static file inspection. It would require dynamic analysis of the running UWP app (hooking the DataProtection/key-derivation calls or dumping the decrypted master key from memory) — specialized RE, uncertain, and outside what the available tooling can do.
Realistic ways to actually decrypt these books
- Use a Kindle version DeDRM supports. Kindle for PC ≤ 1.39 (the desktop
.exe, not the Store app) stores keys in.kinf2018and downloads MOBI/older-KFX. De-register the Store app, install the older client, re-download, and the key is then extractable. This is the standard community workaround for the Store-app/.kinf2024problem. - A registered eInk Kindle's serial number lets
getKindlePids(already ported,kindle/pid.rs) derive PIDs for books delivered to that device — though app/account downloads usually won't match. - Wait for / import community RE of the
.kinf2024+main_sharedvault.
What DRMLibre can do to support path (1) — a port, achievable
- Port
kindlekey.py.kinf2018extractor (DPAPI header unprotect, thetestMap1/testMap8/charMap5decoders,GetIDString= volume serial [we already havevolume_serial()], PBKDF2, AES-CTR). → produces the kindle key DB. - Port
kgenpids.pygetK4Pids(primitivesencode/encode_hash/generate_device_pidalready ported + tested inkindle/pid.rs). - Wire the kindle DB → PIDs → existing MOBI decryptor; add Topaz + KFX engines.
This would NOT decrypt the books currently on this machine (they're .kinf2024),
but would make the tool "just work" against a supported Kindle install.
References
- DeDRM_tools:
DeDRM_plugin/kfxdedrm.py,ion.py(DrmIonVoucher,obfuscate*/process_V*),kfxtables.py(symbol table),kindlekey.py(getDBfromFile,.kinf2018v5/v6),kgenpids.py(getK4Pids). - Our notes on the KFX crypto core are in the project plan
(
~/.claude/plans/fizzy-sparking-wadler.md, Phase 4 section).