Add XPRESS9, XPRESS10, LZ4 and SCRUB support#60
Conversation
Replace compression dispatch with ntcompress, which handles all seven ESE record compression formats. XPRESS9 and XPRESS10 previously raised NotImplementedError. LZ4 was not recognized. SCRUB was silently passed through as raw bytes. ntcompress is added as a dependency because implementing these four codecs inline would be substantial and hard to maintain. It is a pure-Python package with no transitive dependencies. Closes fox-it#10
|
Thanks for the contribution. Though I'd rather we expand the compression algorithms we support in Would you be open to contributing the additional algorithm implementations (and/or improvements) to |
|
@Schamper Done. I contributed the algorithms to dissect.util and rewired dissect.database to use them:
The dissect.util PR adds compress and decompress for XPRESS9, plus compress functions for lzxpress, lzxpress_huffman, lz4, and lznt1 (which previously only had decompress). Also adds CRC-64/NVME and fixes two bugs in lzxpress_huffman decompress (missing EOF symbol handling, missing v10.0 uint32 match-length escape). The dissect.database PR is the same scope as #60 but uses dissect.util instead of ntcompress. All seven ESE compression schemes are handled, with CRC verification. Tested against all 15 databases in the test fixtures. This PR can be closed! |
Summary
This swaps the compression dispatch in
compression.pyfor ntcompress, which handles every ESE record compression format. XPRESS9 and XPRESS10 no longer raiseNotImplementedError(the state since #4). LZ4 (scheme 0x07) and SCRUB (scheme 0x04) are also handled now instead of silently passing through as raw bytes.This was reported in #10 and came up in fox-it/dissect.esedb#24 (comment) where XPRESS10-compressed tagged values in NTDS databases were causing decoding errors. @Schamper noted it was not actively being worked on.
What changed
Two files, net -43 lines.
dissect/database/ese/compression.pygoes from 51 lines to 6:The
decompress(buf)anddecompress_size(buf)signatures match the existing call sites inrecord.pyL295,record.pyL338, andtable.pyL216. Nothing else needs to change.pyproject.toml: added"ntcompress"todependencies.Format coverage
ntcompress
ntcompress(docs, PyPI) is a pure-Python library with no runtime dependencies, licensed Apache-2.0. Implementing XPRESS9, XPRESS10, LZ4 and SCRUB inline would be substantial and hard to maintain. ntcompress already has the codecs, a large test suite, and no transitive dependencies, so adding it keeps the dependency footprint minimal.esent.dllandntdll.dllacross 16 Windows builds (XP SP3 through Server 2025)RtlDecompressBufferEx(format=0x0006)on Server 2025 Build 26100Testing
tests/_data/ese/parsed, every column value accessed -- 236,369 records, 3.7M+ values, zero errorsWindows.edb.gzis the one that matters most here. It has records using all 7 ESE compression schemes, including the XPRESS9, XPRESS10, LZ4, and SCRUB records that previously failed or were returned as raw bytes.Full fixture results (15 databases)
Windows.edb.gzntds/large/ntds.dit.gzntds/goad/ntds.dit.gzntds/fve/ntds.dit.gzntds/adam/adamntds.dit.gzlarge.edb.gztools/CertLog.edb.gztext.edb.gzmulti.edb.gzbinary.edb.gzdefault.edb.gzbasic.edb.gzindex.edb.gztools/Current.mdb.gztools/SRUDB.dat.gzNote
Claude was used to help research and write the code for ntcompress. I have read all documentation, all markdown files, and verified the output.