Skip to content

Invoke-DbaDbDecryptObject - Read the family GUID from sys.database_recovery_status instead of DBCC DBINFO - #10680

Open
howarthcd wants to merge 1 commit into
dataplat:developmentfrom
howarthcd:invoke-dbadbdecryptobject-nodac-enhance
Open

Invoke-DbaDbDecryptObject - Read the family GUID from sys.database_recovery_status instead of DBCC DBINFO#10680
howarthcd wants to merge 1 commit into
dataplat:developmentfrom
howarthcd:invoke-dbadbdecryptobject-nodac-enhance

Conversation

@howarthcd

@howarthcd howarthcd commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

(do Invoke-DbaDbDecryptObject)

Reads the database family GUID from the catalog view sys.database_recovery_status instead of scraping dbi_familyGUID out of the text that DBCC DBINFO prints under trace flag 3604. The family GUID is one of the three inputs to the RC4 key that the -DataPages path derives, and this changes only where that one value comes from. The DBCC PAGE read that pulls the ciphertext is untouched, so the page reader, the seek, the scan and the chain walk are all unchanged.

The two sources were measured against each other, as the sixteen bytes that actually feed the SHA1, on SQL Server 2019, 2022 and 2025, for a user database and for master, and they agree everywhere. So the swap leaves every derived key identical while dropping a trace flag toggle and a regex over prose in favour of a typed uniqueidentifier read that a parameterised DB_ID() selects.

Type of Change

  • Bug fix (non-breaking change)
  • New feature (non-breaking change, adds functionality)
  • Breaking change (affects multiple commands or functionality, fixes # )
  • Ran manual Pester test and has passed (Invoke-ManualPester -Path <command> -ScriptAnalyzer -Compliance)
  • Adding code coverage to existing functionality
  • Pester test is included
  • If new file reference added for test, has is been added to github.com/dataplat/appveyor-lab ?
  • Unit test is included
  • Documentation
  • Build system

Purpose

The -DataPages path rebuilds the RC4 key from public metadata, and one of its three inputs is the database family GUID. It obtained that GUID by setting trace flag 3604, running DBCC DBINFO, and pulling dbi_familyGUID out of the text that arrives on the connection with a regex.

The catalog view sys.database_recovery_status exposes the same value as a typed uniqueidentifier in its family_guid column, and has done since SQL Server 2005, the same floor the rest of the feature already documents. Reading it there is a typed read in place of a text parse, with no trace flag toggle and no regex over prose.

Approach

The failure this change has to avoid is a quiet one. Per the scheme documented on the original pull request, a wrong family GUID, or the right GUID in the wrong byte orientation, does not throw; it produces a plausible looking but wrong keystream, so an object reads as garbage of the right length rather than failing. The source therefore could not be swapped on the strength of it looking cleaner, and was measured instead.

Both paths end as a System.Guid fed through ToByteArray, which are the sixteen bytes that seed the SHA1. Comparing those bytes on SQL Server 2019, 2022 and 2025, for a freshly created user database and for master, they are byte identical every time, and an end to end decrypt of an encrypted object then recovers the original text exactly on all three.

DBCC PAGE, which reads the ciphertext out of sys.sysobjvalues, is unchanged and still requires sysadmin, so the up front sysadmin check stays. sys.database_recovery_status needs only VIEW SERVER STATE, which a sysadmin already holds, so the swap loosens nothing.

The comment based help never named DBCC DBINFO, so it needed no change; the inline comments and the private helper's parameter help were updated to name the catalog view.

Testing

The -DataPages half of the suite is the part this change touches, and all of it passed against SQL Server 2025, including the two tests that read the family GUID to build their own oracle and so exercise the swapped source directly:

  • Should return the same text by seek, by page list and by page chain
  • Should return the same text whether the rows were seeked or scanned

Every other -DataPages case passed with them: in row and off row definitions, a blob tree several levels deep, non ASCII text returned unchanged, a read only database snapshot, more than one database in a single call, colliding schema and object names, and the page dump parsing and multi chunk unit tests. An end to end decrypt of a freshly created encrypted procedure recovered the original text exactly, Unicode intact.

The default DAC method is unchanged by this pull request and was not re-exercised here: the tests that cover it need a dedicated admin connection that the environment these tests ran in could not reach, so they are not reported above.

Commands to test

# reads the definitions without a DAC and without writing anything, as before
Invoke-DbaDbDecryptObject -SqlInstance sql01 -Database db1 -DataPages

# a single object, to eyeball that the recovered text matches the original
Invoke-DbaDbDecryptObject -SqlInstance sql01 -Database db1 -ObjectName MyProc -DataPages

…covery_status instead of DBCC DBINFO

The DataPages method needed the database family GUID to rebuild the keystream and
scraped it out of DBCC DBINFO's text dump under trace flag 3604. It now reads
family_guid from the catalog view sys.database_recovery_status, a typed
uniqueidentifier present since SQL Server 2005. The two sources were measured byte
for byte across SQL Server 2019, 2022 and 2025, for user databases and master, and
always agree, so the keystream input is unchanged while a trace flag toggle and a
regex over prose are dropped. DBCC PAGE still reads the ciphertext and still needs
sysadmin, so the sysadmin check stays; the catalog view needs only VIEW SERVER
STATE, which a sysadmin already holds.

(do Invoke-DbaDbDecryptObject)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant