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 }