Skip to content

Commit

Permalink
Update the registry interface to make it easier to use.
Browse files Browse the repository at this point in the history
1. Specifying the hive is now done at the `OpenKey()` level: this allows more flexibility when working with the registry.
2. The `Value(name)` method is now available in the interface to retrieve a specific value;
3. The `DataString()` method is now available to directly retrieve a value as string.

PiperOrigin-RevId: 703125086
  • Loading branch information
tooryx authored and copybara-github committed Dec 5, 2024
1 parent d884fb1 commit 646b696
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 11 deletions.
21 changes: 20 additions & 1 deletion common/windows/registry/offline.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
var (
errFailedToReadClassName = errors.New("failed to read class name")
errFailedToOpenKey = errors.New("failed to open key")
errFailedToFindValue = errors.New("could not find value")
)

// OfflineRegistry wraps the regparser library to provide offline (from file) parsing of the Windows
Expand All @@ -51,7 +52,8 @@ func NewFromFile(path string) (*OfflineRegistry, error) {
}

// OpenKey open the requested registry key.
func (o *OfflineRegistry) OpenKey(path string) (Key, error) {
// Note that for offline keys, the hive is not used.
func (o *OfflineRegistry) OpenKey(_ string, path string) (Key, error) {
key := o.registry.OpenKey(path)
if key == nil {
return nil, errFailedToOpenKey
Expand Down Expand Up @@ -117,6 +119,17 @@ func (o *OfflineKey) ClassName() ([]byte, error) {
return buffer, nil
}

// Value returns the value with the given name.
func (o *OfflineKey) Value(name string) (Value, error) {
for _, value := range o.key.Values() {
if value.ValueName() == name {
return &OfflineValue{value}, nil
}
}

return nil, errFailedToFindValue
}

// Values returns the different values contained in the key.
func (o *OfflineKey) Values() ([]Value, error) {
var values []Value
Expand All @@ -142,3 +155,9 @@ func (o *OfflineValue) Name() string {
func (o *OfflineValue) Data() ([]byte, error) {
return o.value.ValueData().Data, nil
}

// DataString returns the data contained in the value as a string. Note that if the original data
// is not a string it will be converted.
func (o *OfflineValue) DataString() (string, error) {
return o.value.ValueData().GoString(), nil
}
10 changes: 8 additions & 2 deletions common/windows/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ package registry

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

// Close closes the registry hive.
Close() error
Expand All @@ -43,6 +43,9 @@ type Key interface {
// SubkeyNames returns the names of the subkeys of the key.
SubkeyNames() ([]string, error)

// Value returns the value with the given name.
Value(name string) (Value, error)

// Values returns the different values of the key.
Values() ([]Value, error)
}
Expand All @@ -54,4 +57,7 @@ type Value interface {

// Data returns the data of the value.
Data() ([]byte, error)

// DataString returns the data of the value as a string.
DataString() (string, error)
}
6 changes: 3 additions & 3 deletions detector/weakcredentials/winlocal/samreg/samreg.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func NewFromFile(path string) (*SAMRegistry, error) {

// UsersRIDs returns the list of local user RIDs.
func (s *SAMRegistry) UsersRIDs() ([]string, error) {
key, err := s.OpenKey(samRegistryPathUsers)
key, err := s.OpenKey("HKLM", samRegistryPathUsers)
if err != nil {
return nil, errFailedToParseUsers
}
Expand All @@ -73,7 +73,7 @@ func (s *SAMRegistry) UsersRIDs() ([]string, error) {
// UserInfo returns the UserInfo for a given user RID.
func (s *SAMRegistry) UserInfo(userRID string) (*UserInfo, error) {
keyPath := fmt.Sprintf(`%s\%s`, samRegistryPathUsers, userRID)
key, err := s.OpenKey(keyPath)
key, err := s.OpenKey("HKLM", keyPath)
if err != nil {
return nil, fmt.Errorf("SAM hive: failed to load user registry for RID %q", userRID)
}
Expand Down Expand Up @@ -125,7 +125,7 @@ func (s *SAMRegistry) UserInfo(userRID string) (*UserInfo, error) {
// DeriveSyskey loads the domain F structure from the SAM hive and then uses it to derive the
// syskey.
func (s *SAMRegistry) DeriveSyskey(syskey []byte) ([]byte, error) {
key, err := s.OpenKey(samRegistryPathDomains)
key, err := s.OpenKey("HKLM", samRegistryPathDomains)
if err != nil {
return nil, errFailedToOpenDomain
}
Expand Down
4 changes: 2 additions & 2 deletions detector/weakcredentials/winlocal/systemreg/systemreg.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (s *SystemRegistry) Syskey() ([]byte, error) {
var syskey string
currentControlSet := fmt.Sprintf(`ControlSet%03d\Control\Lsa\`, currentSet)
for _, k := range syskeyPaths {
key, err := s.OpenKey(currentControlSet + k)
key, err := s.OpenKey("HKLM", currentControlSet+k)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -91,7 +91,7 @@ func (s *SystemRegistry) Syskey() ([]byte, error) {
}

func (s *SystemRegistry) currentControlSet() (uint32, error) {
key, err := s.OpenKey(`Select`)
key, err := s.OpenKey("HKLM", `Select`)
if err != nil {
return 0, err
}
Expand Down
25 changes: 22 additions & 3 deletions testing/mockregistry/mockregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

var (
errFailedToOpenKey = errors.New("failed to open key")
errValueNotFound = errors.New("value not found")
)

// MockRegistry mocks registry access.
Expand All @@ -31,7 +32,8 @@ type MockRegistry struct {
}

// OpenKey open the requested registry key.
func (o *MockRegistry) OpenKey(path string) (registry.Key, error) {
// Note that for mock registry, the hive is not used.
func (o *MockRegistry) OpenKey(_ string, path string) (registry.Key, error) {
if key, ok := o.Keys[path]; ok {
return key, nil
}
Expand Down Expand Up @@ -82,15 +84,27 @@ func (o *MockKey) ClassName() ([]byte, error) {
return []byte(o.KClassName), nil
}

// Value returns the value with the given name.
func (o *MockKey) Value(name string) (registry.Value, error) {
for _, value := range o.KValues {
if value.Name() == name {
return value, nil
}
}

return nil, errValueNotFound
}

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

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

// Name returns the name of the value.
Expand All @@ -102,3 +116,8 @@ func (o *MockValue) Name() string {
func (o *MockValue) Data() ([]byte, error) {
return o.VData, nil
}

// DataString returns the data contained in the value as a string.
func (o *MockValue) DataString() (string, error) {
return o.VDataString, nil
}

0 comments on commit 646b696

Please sign in to comment.