go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/windows/registrykey_powershell.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package windows 5 6 import ( 7 "encoding/binary" 8 "encoding/json" 9 "fmt" 10 "io" 11 "math" 12 "strconv" 13 "strings" 14 15 "github.com/rs/zerolog/log" 16 ) 17 18 // RegistryKeyItem represents a registry key item and its properties 19 const getRegistryKeyItemScript = ` 20 $path = '%s' 21 $reg = Get-Item ('Registry::' + $path) 22 if ($reg -eq $null) { 23 Write-Error "Could not find registry key" 24 exit 1 25 } 26 $properties = @() 27 $reg.Property | ForEach-Object { 28 $fetchKeyValue = $_ 29 if ("(default)".Equals($_)) { $fetchKeyValue = '' } 30 $data = $(Get-ItemProperty ('Registry::' + $path)).$_; 31 $kind = $reg.GetValueKind($fetchKeyValue); 32 if ($kind -eq 7) { 33 $data = $(Get-ItemProperty ('Registry::' + $path)) | Select-Object -ExpandProperty $_ 34 } 35 $entry = New-Object psobject -Property @{ 36 "key" = $_ 37 "value" = New-Object psobject -Property @{ 38 "data" = $data; 39 "kind" = $kind; 40 } 41 } 42 $properties += $entry 43 } 44 ConvertTo-Json -Depth 3 -Compress $properties 45 ` 46 47 func GetRegistryKeyItemScript(path string) string { 48 return fmt.Sprintf(getRegistryKeyItemScript, path) 49 } 50 51 // getRegistryKeyChildItemsScript represents a registry key item and its children 52 const getRegistryKeyChildItemsScript = ` 53 $path = '%s' 54 $children = Get-ChildItem -Path ('Registry::' + $path) -rec -ea SilentlyContinue 55 56 $properties = @() 57 $children | ForEach-Object { 58 $entry = New-Object psobject -Property @{ 59 "name" = $_.PSChildName 60 "path" = $_.Name 61 "properties" = $_.Property 62 "children" = $_.SubKeyCount 63 } 64 $properties += $entry 65 } 66 ConvertTo-Json -compress $properties 67 ` 68 69 func GetRegistryKeyChildItemsScript(path string) string { 70 return fmt.Sprintf(getRegistryKeyChildItemsScript, path) 71 } 72 73 type keyKindRaw struct { 74 Kind int 75 Data interface{} 76 } 77 78 func (k *RegistryKeyValue) UnmarshalJSON(b []byte) error { 79 var raw keyKindRaw 80 81 // try to unmarshal the type 82 err := json.Unmarshal(b, &raw) 83 if err != nil { 84 return err 85 } 86 k.Kind = raw.Kind 87 88 if raw.Data == nil { 89 return nil 90 } 91 92 // see https://docs.microsoft.com/en-us/powershell/scripting/samples/working-with-registry-entries?view=powershell-7 93 switch raw.Kind { 94 case NONE: 95 // ignore 96 case SZ: // Any string value 97 value, ok := raw.Data.(string) 98 if !ok { 99 return fmt.Errorf("registry key value is not a string: %v", raw.Data) 100 } 101 k.String = value 102 case EXPAND_SZ: // A string that can contain environment variables that are dynamically expanded 103 value, ok := raw.Data.(string) 104 if !ok { 105 return fmt.Errorf("registry key value is not a string: %v", raw.Data) 106 } 107 k.String = value 108 case BINARY: // Binary data 109 rawData, ok := raw.Data.([]interface{}) 110 if !ok { 111 return fmt.Errorf("registry key value is not a byte array: %v", raw.Data) 112 } 113 data := make([]byte, len(rawData)) 114 for i, v := range rawData { 115 val, ok := v.(float64) 116 if !ok { 117 return fmt.Errorf("registry key value is not a byte array: %v", raw.Data) 118 } 119 data[i] = byte(val) 120 } 121 k.Binary = data 122 case DWORD: // A number that is a valid UInt32 123 data, ok := raw.Data.(float64) 124 if !ok { 125 return fmt.Errorf("registry key value is not a number: %v", raw.Data) 126 } 127 number := int64(data) 128 // string fallback 129 k.Number = number 130 k.String = strconv.FormatInt(number, 10) 131 case DWORD_BIG_ENDIAN: 132 log.Warn().Msg("DWORD_BIG_ENDIAN for registry key is not supported") 133 case LINK: 134 log.Warn().Msg("LINK for registry key is not supported") 135 case MULTI_SZ: // A multiline string 136 switch value := raw.Data.(type) { 137 case string: 138 k.String = value 139 if value != "" { 140 k.MultiString = []string{value} 141 } 142 case []interface{}: 143 if len(value) > 0 { 144 var multiString []string 145 for _, v := range value { 146 multiString = append(multiString, v.(string)) 147 } 148 // NOTE: this is to be consistent with the output before we moved to multi-datatype support for registry keys 149 k.String = strings.Join(multiString, " ") 150 k.MultiString = multiString 151 } 152 } 153 case RESOURCE_LIST: 154 log.Warn().Msg("RESOURCE_LIST for registry key is not supported") 155 case FULL_RESOURCE_DESCRIPTOR: 156 log.Warn().Msg("FULL_RESOURCE_DESCRIPTOR for registry key is not supported") 157 case RESOURCE_REQUIREMENTS_LIST: 158 log.Warn().Msg("RESOURCE_REQUIREMENTS_LIST for registry key is not supported") 159 case QWORD: // 8 bytes of binary data 160 f, ok := raw.Data.(float64) 161 if !ok { 162 return fmt.Errorf("registry key value is not a number: %v", raw.Data) 163 } 164 buf := make([]byte, 8) 165 binary.LittleEndian.PutUint64(buf[:], math.Float64bits(f)) 166 k.Binary = buf 167 } 168 return nil 169 } 170 171 func ParsePowershellRegistryKeyItems(r io.Reader) ([]RegistryKeyItem, error) { 172 data, err := io.ReadAll(r) 173 if err != nil { 174 return nil, err 175 } 176 177 var items []RegistryKeyItem 178 err = json.Unmarshal(data, &items) 179 if err != nil { 180 return nil, err 181 } 182 183 return items, nil 184 } 185 186 func ParsePowershellRegistryKeyChildren(r io.Reader) ([]RegistryKeyChild, error) { 187 data, err := io.ReadAll(r) 188 if err != nil { 189 return nil, err 190 } 191 192 var children []RegistryKeyChild 193 err = json.Unmarshal(data, &children) 194 if err != nil { 195 return nil, err 196 } 197 198 return children, nil 199 }