Fix/datetime filetime decoding - #29
Merged
scudette merged 2 commits intoMay 25, 2026
Merged
Conversation
ESE DateTime columns with Flags=0 are ambiguous: some applications
(e.g. SRUM) store genuine OLE variant doubles while others (e.g. ADCS
CA) store Windows FILETIMEs in the same column type with identical
catalog metadata. The previous code unconditionally used the OLE double
decoder for Flags=0, which corrupted ADCS timestamps to 1899-12-30.
The two encodings are structurally distinguishable by examining the raw
bytes as a float64:
- A valid OLE double for any date 1900-2100 produces a normal IEEE 754
float in the range [2.0, ~73050] (high byte 0x40).
- A valid FILETIME for any date 1601-9999 produces a subnormal float
on the order of 10^-299 when misread as a double (high byte <= 0x01).
The gap between these ranges is many orders of magnitude with no
overlap. A threshold of 1.0 is used: values above it are decoded as OLE
doubles, values at or below it are decoded as Windows FILETIMEs.
Zero values (unset fields) produce 0.0 in both interpretations and
fall through to the FILETIME path, returning the FILETIME epoch
(1601-01-01) rather than the OLE epoch (1899-12-30).
Confirmed against:
- ADCS CA (ESSOS-CA.edb): SubmittedWhen, ResolvedWhen, NotBefore,
NotAfter now decode correctly (e.g. 2025-08-19T13:04:11Z)
- SRUM (SRUDB.dat): TimeStamp continues to decode correctly
- UAL (Current.mdb): DateTime columns continue to decode correctly
- WebCache (WebCacheV01.dat): unaffected (uses LongLong not DateTime)
Changes.md was added as part of the DateTime fix investigation but is not appropriate for the repository root. The fix rationale and technical details are documented in the commit message and PR description instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request: fix: auto-detect DateTime encoding for Flags=0 columns
Title
fix: auto-detect DateTime encoding for Flags=0 columns
Description
Problem
When parsing Active Directory Certificate Services (ADCS) CA databases (
CertLog\*.edb) usingparse_ese, allDateTimecolumn fields —SubmittedWhen,ResolvedWhen,RevokedWhen,RevokedEffectiveWhenin theRequeststable andNotBefore,NotAfterin theCertificatestable — return1899-12-30T00:00:00Zinstead of the correct timestamps. The same database parsed bycertutilandesedbexportproduces correct timestamps.Root Cause
ADCS CA databases store
DateTimecolumn values as Windows FILETIMEs (64-bit integers, 100-nanosecond intervals since 1601-01-01 UTC) but declare those columns withFlags=0in the ESE catalog.The existing
Flags=0decoder callsmath.Float64frombits()on the raw bytes, treating them as an OLE variant double. A real FILETIME value for a date in 2025–2026 is approximately134,000,000,000,000,000. When those 8 bytes are reinterpreted as an IEEE 754 double, the result is a subnormal float on the order of10^-299. Multiplying by 86400 yields essentially zero, so the computed Unix timestamp is always approximately-2,209,334,400— which is1899-12-30— regardless of the actual date stored. This corruption is irreversible.Why Not Just Force FILETIME for All Flags=0 Columns
The initial fix unconditionally decoded all
Flags=0DateTime columns as FILETIMEs. This was reverted after the test suite revealed thatSRUDB.dat(Windows SRUM) also usesDateTimecolumns withFlags=0but stores genuine OLE variant doubles. Both databases have identical catalog metadata — same type byte (0x08), sameColumnFlags(0x00000000). The encoding is determined by the application that wrote the data, not the ESE schema.The Fix — Self-Discriminating Value Inspection
Although catalog metadata cannot distinguish the two encodings, the raw 8-byte values occupy completely different regions of the IEEE 754 float64 number space:
2E D8 82 2D A1 B7 E5 4044477.04[2.0, ~73050]— normal float30 20 6E C1 09 11 DC 01~1.05e-299[0, ~10^-295]— subnormal float[2.0, ~73050](high byte0x40).10^-299(high byte0x01or lower).The fix adds a single
if days_since_1900 > 1.0check inside the existingFlags=0branch in both the fixed-column and tagged-column decoders:Validation
ADCS CA database (
ESSOS-CA.edb) — previously broken, now fixed:SubmittedWhen30 20 6E C1 09 11 DC 011899-12-30T00:00:00Z2025-08-19T13:04:11ZSubmittedWhen20 DC 98 CC 09 11 DC 011899-12-30T00:00:00Z2025-08-19T13:04:29ZSubmittedWhen20 3B 91 BD 9B D1 DC 011899-12-30T00:00:00Z2026-04-21T14:32:54ZSRUM database (
SRUDB.dat) — must remain correct, and does:TimeStamp2E D8 82 2D A1 B7 E5 4044477.04(> 1.0 → OLE path)2021-10-08T00:53:00Z✓All existing test fixtures pass without modification.
Files Changed
parser/catalog.go— addedif days_since_1900 > 1.0branch in both the fixed-column and tagged-columnDateTimedecoders underFlags=0No imports were added or removed. No other files were modified.