Skip to content

Commit

Permalink
Import and abstract the regparser library to provide offline parsin…
Browse files Browse the repository at this point in the history
…g of the Windows registry.

This abstraction makes testing easier and provide a path for offline parsing of Windows images.

PiperOrigin-RevId: 676808177
  • Loading branch information
tooryx authored and copybara-github committed Oct 3, 2024
1 parent 3b0c869 commit b474763
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 0 deletions.
122 changes: 122 additions & 0 deletions common/windows/registry/offline.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package registry

import (
"errors"
"io"
"os"

"www.velocidex.com/golang/regparser"
)

var (
errFailedToReadClassName = errors.New("failed to read class name")
)

// OfflineRegistry wraps the regparser library to provide offline (from file) parsing of the Windows
// registry.
type OfflineRegistry struct {
registry *regparser.Registry
reader io.ReadCloser
}

// NewFromFile creates a new offline registry abstraction from a file.
func NewFromFile(path string) (*OfflineRegistry, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}

reg, err := regparser.NewRegistry(f)
if err != nil {
f.Close()
return nil, err
}

return &OfflineRegistry{reg, f}, nil
}

// OpenKey open the requested registry key.
func (o *OfflineRegistry) OpenKey(path string) Key {
return &OfflineKey{o.registry.OpenKey(path)}
}

// Close closes the underlying reader.
func (o *OfflineRegistry) Close() error {
return o.reader.Close()
}

// OfflineKey wraps a regparser.CM_KEY_NODE to provide an implementation of the registry.Key
// interface.
type OfflineKey struct {
key *regparser.CM_KEY_NODE
}

// Name returns the name of the key.
func (o *OfflineKey) Name() string {
return o.key.Name()
}

// Subkeys returns the subkeys of the key.
func (o *OfflineKey) Subkeys() []Key {
var subkeys []Key
for _, subkey := range o.key.Subkeys() {
subkeys = append(subkeys, &OfflineKey{subkey})
}

return subkeys
}

// ClassName returns the class name of the key.
func (o *OfflineKey) ClassName() ([]byte, error) {
// retrieve the class name offset and skip both the first block and the first 4 bytes that
// represents the size of the block.
classOffset := int64(o.key.Class()) + 4096 + 4
classLen := o.key.ClassLength()
buffer := make([]byte, classLen)

if n, err := o.key.Reader.ReadAt(buffer, classOffset); err != nil || n != int(classLen) {
return nil, errFailedToReadClassName
}

return buffer, nil
}

// Values returns the different values contained in the key.
func (o *OfflineKey) Values() []Value {
var values []Value
for _, value := range o.key.Values() {
values = append(values, &OfflineValue{value})
}

return values
}

// OfflineValue wraps a regparser.CM_KEY_VALUE to provide an implementation of the registry.Value
// interface.
type OfflineValue struct {
value *regparser.CM_KEY_VALUE
}

// Name returns the name of the value.
func (o *OfflineValue) Name() string {
return o.value.ValueName()
}

// Data returns the data contained in the value.
func (o *OfflineValue) Data() []byte {
return o.value.ValueData().Data
}
51 changes: 51 additions & 0 deletions common/windows/registry/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package registry provides an interface to abstract the Windows registry libraries away.
// This allows providing more functionalities to registry libraries and also provide a better means
// of testing.
package registry

// Registry represents an open registry hive.
type Registry interface {
// OpenKey returns a Key for the given path.
OpenKey(path string) Key

// Close closes the registry hive.
Close() error
}

// Key represents a specific registry key.
type Key interface {
// Name returns the name of the key.
Name() string

// ClassName returns the name of the class for the key.
ClassName() ([]byte, error)

// Subkeys returns the subkeys of the key.
Subkeys() []Key

// Values returns the different values of the key.
Values() []Value
}

// Value represents a value inside a specific key.
type Value interface {
// Name returns the name of the value.
Name() string

// Data returns the data of the value.
Data() []byte
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ require (
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
github.com/containerd/ttrpc v1.2.4 // indirect
github.com/containerd/typeurl/v2 v2.1.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/cli v25.0.3+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v25.0.6+incompatible // indirect
Expand Down Expand Up @@ -81,4 +82,5 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
www.velocidex.com/golang/regparser v0.0.0-20240404115756-2169ac0e3c09 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -327,3 +327,5 @@ modernc.org/sqlite v1.20.3/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
www.velocidex.com/golang/regparser v0.0.0-20240404115756-2169ac0e3c09 h1:G1RWYBXP2lSzxKcrAU1YhiUlBetZ7hGIzIiWuuazvfo=
www.velocidex.com/golang/regparser v0.0.0-20240404115756-2169ac0e3c09/go.mod h1:pxSECT5mWM3goJ4sxB4HCJNKnKqiAlpyT8XnvBwkLGU=
89 changes: 89 additions & 0 deletions testing/mockregistry/mockregistry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package mockregistry provides a mock implementation of the registry.Registry interface.
package mockregistry

import (
"errors"

"github.com/google/osv-scalibr/common/windows/registry"
)

var (
errFailedToReadClassName = errors.New("failed to read class name")
)

// MockRegistry mocks registry access.
type MockRegistry struct {
Keys map[string]registry.Key
}

// OpenKey open the requested registry key.
func (o *MockRegistry) OpenKey(path string) registry.Key {
if key, ok := o.Keys[path]; ok {
return key
}

return nil
}

// Close does nothing when mocking.
func (o *MockRegistry) Close() error {
return nil
}

// MockKey mocks a registry.Key.
type MockKey struct {
KName string
KClassName string
KSubkeys []registry.Key
KValues []registry.Value
}

// Name returns the name of the key.
func (o *MockKey) Name() string {
return o.KName
}

// Subkeys returns the subkeys of the key.
func (o *MockKey) Subkeys() []registry.Key {
return o.KSubkeys
}

// ClassName returns the class name of the key.
func (o *MockKey) ClassName() ([]byte, error) {
return []byte(o.KClassName), nil
}

// Values returns the different values contained in the key.
func (o *MockKey) Values() []registry.Value {
return o.KValues
}

// MockValue mocks a registry.Value.
type MockValue struct {
VName string
VData []byte
}

// Name returns the name of the value.
func (o *MockValue) Name() string {
return o.VName
}

// Data returns the data contained in the value.
func (o *MockValue) Data() []byte {
return o.VData
}

0 comments on commit b474763

Please sign in to comment.