theoka
theoka

Reputation: 63

properly decipher registry REG_BINARY with python 3 winreg?

trying to read the information of the subkey Render in the key "Computer\HKEY_USERS\S-1-5-19\Software\Microsoft\Windows\CurrentVersion\Audio\Journal".

windows makes changes to that registry REG_BINARY entry when switching audio output devices.

i've tried to decode with ascii, utf-8, cp1252 and iso-8859-15 and a couple others from this list. ascii and utf-8 seem to yield the most "readable" string but they still don't decode the full string.

import winreg
from codecs import decode

reg_hive = winreg.HKEY_USERS
main_key = r"S-1-5-19\Software\Microsoft\Windows\CurrentVersion\Audio\Journal"

with winreg.ConnectRegistry(None, reg_hive) as hive:
    with winreg.OpenKey(
            hive, main_key, 0, winreg.KEY_READ) as target_key:

        renderInfo = winreg.EnumValue(target_key, 2)

        # print(renderInfo)
        print(decode(renderInfo[1], "ascii", "ignore"))

it prints this:

SWD\MMDEVAPI{0.0.0.00000000}.{01921bca-0488-492c-b0ac-c8a3f5e42f9d}S@SWD\MMDEVAPI{0.0.0.00000000}.{93f5ee17-4bef-4b43-8507-e27271065e61} iW@

trying to decrypt those weird characters (close to "S@" and "iW@ ", stackoverflow doesn't seem to be able to render them.) so i can make better sense of it all.

sofar my guesses are:

Upvotes: 2

Views: 1655

Answers (1)

JosefZ
JosefZ

Reputation: 30103

To decipher a complex binary value, you need to know it's structure. Let's start analyzing the following raw output:

reg query HKEY_USERS\S-1-5-19\Software\Microsoft\Windows\CurrentVersion\Audio\Journal -v Render

HKEY_USERS\S-1-5-19\Software\Microsoft\Windows\CurrentVersion\Audio\Journal
    Render    REG_BINARY    5300570044005C004D004D004400450056004100500049005C007B0030002E0030002E0030002E00300030003000300030003000300030007D002E007B00650033003700380063003900310035002D0064003000360039002D0034003900310066002D0038003000380039002D006200310030003600310039003000360061006100390034007D000000000000000000010000004B000000000000000000000000000000000000006A9A65709D075F400000000000000000000000000000000000000000000000005300570044005C004D004D004400450056004100500049005C007B0030002E0030002E0030002E00300030003000300030003000300030007D002E007B00340032003000620063006500650064002D0037003500370031002D0034003300350030002D0039003300610034002D003500650030006300310065003200350035003400660030007D0000000000000000000300000001000000000000000000000000000000000000004162F5AC588503400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

The following table shows above Render registry entry structure (based on my own finding):

offset bytes caption    format comment                                       
------ ----- -------    ------ -------                                       
0x0000  136  DeviceID   char[] (68 chars; encoding UCS-2 LE)                 
0x0088   8   unknown_88 uint64                                               
0x0090   4   EndPointNo uint32 (LE); constant for given end point            
0x0094   4   Counter_94 uint32 (LE); increases if a sound is played          
0x0098   8   unknown_98 uint64                                               
0x00a0   8   unknown_a0 uint64                                               
0x00a8   8   Strange_a8 uint64 changes unpredictably even for identical sound
0x00b0   8   unknown_b0 uint64                                               
0x00b8   8   unknown_b8 uint64                                               
0x00c0   8   unknown_c0 uint64                                               

The above structure iterates three (or even more?) times within the Render value. All the parts denominated unknown_* constantly keep zero value so it's hard to guess their format and meaning, and don't change even if the system sounds (audio alerts) are forced to be shown visually (in the Ease of access settings).

Let's decrypt the Render value - as soon as it's structure is at least partially known. In Python, use the struct module (performs conversions between Python values and C structs represented as Python bytes objects).

Unfortunately, I don't speak Python fluently enough; so here is my Powershell solution:

#Requires -RunAsAdministrator
[CmdletBinding()]param(
    [Parameter(Mandatory=$False,Position=0)][string]$PlaySound=''
)
if ( $PlaySound -eq '' ) {
    Write-Information -infa Continue "Retrieving current values"
} else {
    # play the file once
    $soundLoc = "c:\WINDOWS\Media\notify.wav"
    Write-Information -infa Continue "Playing $soundLoc"
    $sound = new-Object System.Media.SoundPlayer
    $sound.SoundLocation = $soundLoc
    $sound.Play()
    Start-Sleep -Seconds 1
}
$key = 'Registry::HKEY_USERS\S-1-5-19\Software\Microsoft\Windows' +
        '\CurrentVersion\Audio\Journal'
$keyItems = Get-ItemProperty -Path $key
$render = $keyItems.Render
$csvStruct=@"
offset,bytes,caption,format,comment
0x0000,  136,DeviceID,char[],(68 chars; encoding UCS-2 LE)
0x0088,    8,unknown_88,uint64,
0x0090,    4,EndPointNo,uint32,(LE); constant for given end point
0x0094,    4,Counter_94,uint32,(LE); increases if a sound is played
0x0098,    8,unknown_98,uint64,
0x00a0,    8,unknown_a0,uint64,
0x00a8,    8,Strange_a8,uint64,changes unpredictably even for identical sound
0x00b0,    8,unknown_b0,uint64,
0x00b8,    8,unknown_b8,uint64,
0x00c0,    8,unknown_c0,uint64,
"@ | ConvertFrom-Csv
$unicode=[System.Text.UnicodeEncoding]::Unicode
$renderParts=@()
$renderInfos=@()
$renderNext = $renderCnt = 0
While ( $renderNext -lt $render.Count -and (   <# go over all value  #>
        $render[$renderNext + 0xaf] -ne 0 -or  <# 0x40 if not zero   #>
        $render[$renderNext]        -ne 0 )) { <# DeviceID not empty #>
    for ($i=0; $i -lt $csvStruct.Count; $i++) {
        $part = $csvStruct[$i]
        If ( $part.format -eq 'char[]' ) {
            $renderParts += @{
                $part.caption = $unicode.GetString($render,
                    $renderNext + [int]$part.offset, [int]$part.bytes)
            }
        } else {
            $hexLE='0x'         # Little Endian
            for ($j=[int]$part.bytes -1; $j -ge 0; $j--) {
                $hexLE+= '{0:x2}' -f $render[$renderNext + [int]$part.offset + $j]
            }
            $hexBE='0X'         # Big Endian
            for ($j=0; $j -le [int]$part.bytes -1; $j++) {
                $hexBE+= '{0:x2}' -f $render[$renderNext + [int]$part.offset + $j]
            }
            $hexBE = $hexBE.ToUpper()
            $renderParts += @{ 
                $part.caption = $hexLE 
            }
            if ( $part.caption -eq 'Strange_a8') {
                Write-Information  $(
                   [System.Environment]::NewLine + 
                       $renderParts[ $renderCnt ].DeviceID )
                Write-Information  $(
                    'EndPointNo = '  + 
                       [int32]$renderParts[ 2 + $renderCnt ].EndPointNo +
                    ', Counter_94 = ' + 
                       [int32]$renderParts[ 3 + $renderCnt ].Counter_94 )
                Write-Information  $( "$($part.caption)`t`t$hexLE`t`t$hexBE" )
                #Write-Information  $(
                #    [System.Text.UnicodeEncoding]::Default.GetString($render,
                #         0x00a8 + $renderNext, 8) )
                Write-Information  $(
                    'uint64 {0,20}' -f [uint64]$hexLE )
                Write-Information  $(
                    'uint32 {0,10} {1,10}' -f 
                        [uint32]('0x' + $hexLE.Substring(10,8)),
                        [uint32]('0x' + $hexLE.Substring( 2,8)) )
                Write-Information  $(
                    ' int16 {0,6} {1,6} {2,6} {3,6}' -f 
                        [int16]('0x' + $hexLE.Substring(14,4)),
                        [int16]('0x' + $hexLE.Substring(10,4)),
                        [int16]('0x' + $hexLE.Substring( 6,4)),
                        [int16]('0x' + $hexLE.Substring( 2,4)) )
                Write-Information  $(
                    'uint16 {0,6} {1,6} {2,6} {3,6}' -f 
                        [uint16]('0x' + $hexLE.Substring(14,4)),
                        [uint16]('0x' + $hexLE.Substring(10,4)),
                        [uint16]('0x' + $hexLE.Substring( 6,4)),
                        [uint16]('0x' + $hexLE.Substring( 2,4)) )
                Write-Information  $(
                    '  byte {0,3} {1,3} {2,3} {3,3} {4,3} {5,3} {6,3} {7,3}' -f
                        [byte]('0x' + $hexLE.Substring(16,2)),
                        [byte]('0x' + $hexLE.Substring(14,2)),
                        [byte]('0x' + $hexLE.Substring(12,2)),
                        [byte]('0x' + $hexLE.Substring(10,2)),
                        [byte]('0x' + $hexLE.Substring( 8,2)),
                        [byte]('0x' + $hexLE.Substring( 6,2)),
                        [byte]('0x' + $hexLE.Substring( 4,2)),
                        [byte]('0x' + $hexLE.Substring( 2,2)) )
            }
        }
    }
    $renderNext+=200
    $renderCnt +=10
}
$renderParts

Sample usage:

  • D:\PShell\tests\MMDEVAPI_Journal_Registry.ps1 retrieves current Render value.
  • D:\PShell\tests\MMDEVAPI_Journal_Registry.ps1 x plays a system sound before retrieving the Render value (added after the Render value suddenly became zeroized for unknown reason).
  • D:\PShell\tests\MMDEVAPI_Journal_Registry.ps1 -InformationAction Continue prints additional analysis of the Strange_a8 subvalue (shows it as structured in various numeric types).

Output (formatted by piping to Format-Table -Autosize):

Name       Value                                                               
----       -----                                                               
DeviceID   SWD\MMDEVAPI\{0.0.0.00000000}.{e378c915-d069-491f-8089-b1061906aa94}
unknown_88 0x0000000000000000                                                  
EndPointNo 0x00000001                                                          
Counter_94 0x0000004a                                                          
unknown_98 0x0000000000000000                                                  
unknown_a0 0x0000000000000000                                                  
Strange_a8 0x405eb3c4b7ffda6f                                                  
unknown_b0 0x0000000000000000                                                  
unknown_b8 0x0000000000000000                                                  
unknown_c0 0x0000000000000000                                                  
DeviceID   SWD\MMDEVAPI\{0.0.0.00000000}.{420bceed-7571-4350-93a4-5e0c1e2554f0}
unknown_88 0x0000000000000000                                                  
EndPointNo 0x00000003                                                          
Counter_94 0x00000001                                                          
unknown_98 0x0000000000000000                                                  
unknown_a0 0x0000000000000000                                                  
Strange_a8 0x40038558acf56241                                                  
unknown_b0 0x0000000000000000                                                  
unknown_b8 0x0000000000000000                                                  
unknown_c0 0x0000000000000000                                                  

Note. The EndPointNo part seems to be related to the obscure registry key HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio as shown in the following script:

$computer = $env:COMPUTERNAME
$namespace = "ROOT\CIMV2"
$classname = "Win32_PnPEntity"
$auxPnP = Get-WmiObject -Class        $classname `
                        -ComputerName $computer  `
                        -Namespace    $namespace |
  Where-object PNPDeviceID -match `
    '\{[0-9a-fA-F]\.[0-9a-fA-F]\.[0-9a-fA-F]\.[0-9a-fA-F]{8}\}' |
        Select-Object * -ExcludeProperty PSComputerName, 
            Scope, Path, Options, ClassPath, Properties, 
            SystemProperties, Qualifiers, Site, Container
$auxPnP | Format-List -property Name,DeviceID <# -Property [a-z]* <##>

'--- DeviceId index ---'
$devIndex = '{1da5d803-d492-4edd-8c23-e0c0ffee7f0e},0' 
$devDriver= '{b3f8fa53-0004-438e-9003-51a46e139bfc},6'
$devDevice= '{a45c254e-df1c-4efd-8020-67d146a850e0},2'

$keyAbove = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows' +
            '\CurrentVersion\MMDevices\Audio'
$keyTempl = $keyAbove + '\Render\DeviceIdEnding\Properties'
$auxPnP | ForEach-Object {
    $DeviceIdEnding = $_.DeviceID.Substring($_.DeviceID.LastIndexOf('.')+1)
    $keyItems = Get-ItemProperty -ErrorAction SilentlyContinue  `
                  -Path $keyTempl.Replace('DeviceIdEnding',$DeviceIdEnding)
    if ( $keyItems ) {
        @($DeviceIdEnding, 
          $keyItems.$devIndex,  # index
          $keyItems.$devDriver, # driver
          $keyItems.$devDevice  # device
          ) -join "`t"
    }
}

Output:

Name     : Mikrofon (VIA HD Audio)
DeviceID : SWD\MMDEVAPI\{0.0.1.00000000}.{D2E3C581-8C7B-4A32-A35B-1F42DA18733D}

Name     : Reproduktory (VIA HD Audio)
DeviceID : SWD\MMDEVAPI\{0.0.0.00000000}.{E378C915-D069-491F-8089-B1061906AA94}

Name     : Headphone (VIA HD Audio)
DeviceID : SWD\MMDEVAPI\{0.0.0.00000000}.{420BCEED-7571-4350-93A4-5E0C1E2554F0}

--- DeviceId index ---
{E378C915-D069-491F-8089-B1061906AA94}  1   VIA HD Audio    Reproduktory
{420BCEED-7571-4350-93A4-5E0C1E2554F0}  3   VIA HD Audio    Headphone

Upvotes: 2

Related Questions