github.com/google/osv-scalibr@v0.4.1/common/windows/registry/offline.go (about)

     1  // Copyright 2025 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package registry
    16  
    17  import (
    18  	"errors"
    19  	"io"
    20  	"os"
    21  
    22  	"www.velocidex.com/golang/regparser"
    23  )
    24  
    25  var (
    26  	errFailedToReadClassName = errors.New("failed to read class name")
    27  	errFailedToOpenKey       = errors.New("failed to open key")
    28  	errFailedToFindValue     = errors.New("could not find value")
    29  )
    30  
    31  // OfflineOpener is an opener for the offline registry.
    32  type OfflineOpener struct {
    33  	Filepath string
    34  }
    35  
    36  // NewOfflineOpener creates a new OfflineOpener, allowing to open a registry from a file.
    37  func NewOfflineOpener(filepath string) *OfflineOpener {
    38  	return &OfflineOpener{filepath}
    39  }
    40  
    41  // Open the offline registry.
    42  func (o *OfflineOpener) Open() (Registry, error) {
    43  	f, err := os.Open(o.Filepath)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	reg, err := regparser.NewRegistry(f)
    49  	if err != nil {
    50  		_ = f.Close()
    51  		return nil, err
    52  	}
    53  
    54  	return &OfflineRegistry{reg, f}, nil
    55  }
    56  
    57  // OfflineRegistry wraps the regparser library to provide offline (from file) parsing of the Windows
    58  // registry.
    59  type OfflineRegistry struct {
    60  	registry *regparser.Registry
    61  	reader   io.ReadCloser
    62  }
    63  
    64  // OpenKey open the requested registry key.
    65  // Note that for offline keys, the hive is not used.
    66  func (o *OfflineRegistry) OpenKey(_ string, path string) (Key, error) {
    67  	key := o.registry.OpenKey(path)
    68  	if key == nil {
    69  		return nil, errFailedToOpenKey
    70  	}
    71  
    72  	return &OfflineKey{key: key}, nil
    73  }
    74  
    75  // Close closes the underlying reader.
    76  func (o *OfflineRegistry) Close() error {
    77  	return o.reader.Close()
    78  }
    79  
    80  // OfflineKey wraps a regparser.CM_KEY_NODE to provide an implementation of the registry.Key
    81  // interface.
    82  type OfflineKey struct {
    83  	key *regparser.CM_KEY_NODE
    84  }
    85  
    86  // Name returns the name of the key.
    87  func (o *OfflineKey) Name() string {
    88  	return o.key.Name()
    89  }
    90  
    91  // SubkeyNames returns the names of the subkeys of the key.
    92  func (o *OfflineKey) SubkeyNames() ([]string, error) {
    93  	var names []string
    94  	for _, subkey := range o.key.Subkeys() {
    95  		names = append(names, subkey.Name())
    96  	}
    97  
    98  	return names, nil
    99  }
   100  
   101  // Subkeys returns the subkeys of the key.
   102  func (o *OfflineKey) Subkeys() ([]Key, error) {
   103  	var subkeys []Key
   104  	for _, subkey := range o.key.Subkeys() {
   105  		subkeys = append(subkeys, &OfflineKey{subkey})
   106  	}
   107  
   108  	return subkeys, nil
   109  }
   110  
   111  // Close closes the key.
   112  // For offline keys, this is a no-op.
   113  func (o *OfflineKey) Close() error {
   114  	return nil
   115  }
   116  
   117  // ClassName returns the class name of the key.
   118  func (o *OfflineKey) ClassName() ([]byte, error) {
   119  	// retrieve the class name offset and skip both the first block and the first 4 bytes that
   120  	// represents the size of the block.
   121  	classOffset := int64(o.key.Class()) + 4096 + 4
   122  	classLen := o.key.ClassLength()
   123  	buffer := make([]byte, classLen)
   124  
   125  	if n, err := o.key.Reader.ReadAt(buffer, classOffset); err != nil || n != int(classLen) {
   126  		return nil, errFailedToReadClassName
   127  	}
   128  
   129  	return buffer, nil
   130  }
   131  
   132  // Value returns the value with the given name.
   133  func (o *OfflineKey) Value(name string) (Value, error) {
   134  	for _, value := range o.key.Values() {
   135  		if value.ValueName() == name {
   136  			return &OfflineValue{value}, nil
   137  		}
   138  	}
   139  
   140  	return nil, errFailedToFindValue
   141  }
   142  
   143  // ValueBytes directly returns the content (as bytes) of the named value.
   144  func (o *OfflineKey) ValueBytes(name string) ([]byte, error) {
   145  	value, err := o.Value(name)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	return value.Data()
   151  }
   152  
   153  // ValueString directly returns the content (as string) of the named value.
   154  func (o *OfflineKey) ValueString(name string) (string, error) {
   155  	value, err := o.Value(name)
   156  	if err != nil {
   157  		return "", err
   158  	}
   159  
   160  	return value.DataString()
   161  }
   162  
   163  // Values returns the different values contained in the key.
   164  func (o *OfflineKey) Values() ([]Value, error) {
   165  	var values []Value
   166  	for _, value := range o.key.Values() {
   167  		values = append(values, &OfflineValue{value})
   168  	}
   169  
   170  	return values, nil
   171  }
   172  
   173  // OfflineValue wraps a regparser.CM_KEY_VALUE to provide an implementation of the registry.Value
   174  // interface.
   175  type OfflineValue struct {
   176  	value *regparser.CM_KEY_VALUE
   177  }
   178  
   179  // Name returns the name of the value.
   180  func (o *OfflineValue) Name() string {
   181  	return o.value.ValueName()
   182  }
   183  
   184  // Data returns the data contained in the value.
   185  func (o *OfflineValue) Data() ([]byte, error) {
   186  	return o.value.ValueData().Data, nil
   187  }
   188  
   189  // DataString returns the data contained in the value as a string. Note that if the original data
   190  // is not a string it will be converted.
   191  func (o *OfflineValue) DataString() (string, error) {
   192  	return o.value.ValueData().GoString(), nil
   193  }