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  }