github.com/iDigitalFlame/xmt@v0.5.4/device/regedit/entry.go (about)

     1  // Copyright (C) 2020 - 2023 iDigitalFlame
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU General Public License as published by
     5  // the Free Software Foundation, either version 3 of the License, or
     6  // any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  //
    16  
    17  // Package regedit is a helper package that allows easy access to the Windows
    18  // registry on Windows systems and allows non-Windows systems to read data generated
    19  // from registry entries.
    20  package regedit
    21  
    22  import (
    23  	"unsafe"
    24  
    25  	"github.com/iDigitalFlame/xmt/data"
    26  	"github.com/iDigitalFlame/xmt/device/winapi"
    27  	"github.com/iDigitalFlame/xmt/device/winapi/registry"
    28  )
    29  
    30  // Entry represents a Windows registry entry.
    31  //
    32  // This may represent a Key or a Value. Values will also include their data in
    33  // the 'Data' byte array and can be translated using any of the 'To*' functions
    34  // to get the data represented in its proper type cast.
    35  type Entry struct {
    36  	Name string
    37  	Data []byte
    38  	Type uint32
    39  }
    40  
    41  // IsKey returns true if this entry represents a SubKey.
    42  func (e Entry) IsKey() bool {
    43  	return e.Type == 0
    44  }
    45  
    46  // IsZero returns true if this entry is an invalid Entry.
    47  func (e Entry) IsZero() bool {
    48  	return e.Type == 0 && len(e.Data) == 0 && len(e.Name) == 0
    49  }
    50  
    51  // ToBinary will attempt to return the data in the Data buffer as a binary
    52  // array.
    53  //
    54  // This function returns an error if conversion fails or the specified type is
    55  // not a BINARY type.
    56  func (e Entry) ToBinary() ([]byte, error) {
    57  	if e.Type != registry.TypeBinary {
    58  		return nil, registry.ErrUnexpectedType
    59  	}
    60  	return e.Data, nil
    61  }
    62  
    63  // ToString will attempt to return the data in the Data buffer as a string.
    64  //
    65  // This function returns an error if conversion fails or the specified type is
    66  // not a STRING or EXPAND_STRING type.
    67  func (e Entry) ToString() (string, error) {
    68  	if e.Type != registry.TypeString && e.Type != registry.TypeExpandString {
    69  		return "", registry.ErrUnexpectedType
    70  	}
    71  	if len(e.Data) < 3 {
    72  		return "", registry.ErrUnexpectedSize
    73  	}
    74  	return winapi.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(&e.Data[0]))[: len(e.Data)/2 : len(e.Data)/2]), nil
    75  }
    76  
    77  // ToInteger will attempt to return the data in the Data buffer as an unsigned
    78  // integer.
    79  //
    80  // This function returns an error if conversion fails or the specified type is
    81  // not a DWORD or QWORD type.
    82  func (e Entry) ToInteger() (uint64, error) {
    83  	switch e.Type {
    84  	case registry.TypeDword:
    85  		if len(e.Data) != 4 {
    86  			return 0, registry.ErrUnexpectedSize
    87  		}
    88  		_ = e.Data[3]
    89  		return uint64(e.Data[0]) | uint64(e.Data[1])<<8 | uint64(e.Data[2])<<16 | uint64(e.Data[3])<<24, nil
    90  	case registry.TypeQword:
    91  		if len(e.Data) != 8 {
    92  			return 0, registry.ErrUnexpectedSize
    93  		}
    94  		_ = e.Data[7]
    95  		return uint64(e.Data[0]) | uint64(e.Data[1])<<8 | uint64(e.Data[2])<<16 | uint64(e.Data[3])<<24 |
    96  			uint64(e.Data[4])<<32 | uint64(e.Data[5])<<40 | uint64(e.Data[6])<<48 | uint64(e.Data[7])<<56, nil
    97  	}
    98  	return 0, registry.ErrUnexpectedType
    99  }
   100  
   101  // ToStringList will attempt to return the data in the Data buffer as a string
   102  // array.
   103  //
   104  // This function returns an error if conversion fails or the specified type is
   105  // not a MULTI_STRING type.
   106  func (e Entry) ToStringList() ([]string, error) {
   107  	if e.Type != registry.TypeStringList {
   108  		return nil, registry.ErrUnexpectedType
   109  	}
   110  	if len(e.Data) < 3 {
   111  		return nil, registry.ErrUnexpectedSize
   112  	}
   113  	v := (*[1 << 29]uint16)(unsafe.Pointer(&e.Data[0]))[: len(e.Data)/2 : len(e.Data)/2]
   114  	if len(v) == 0 {
   115  		return nil, nil
   116  	}
   117  	if v[len(v)-1] == 0 {
   118  		v = v[:len(v)-1]
   119  	}
   120  	r := make([]string, 0, len(v))
   121  	for i, n := 0, 0; i < len(v); i++ {
   122  		if v[i] > 0 {
   123  			continue
   124  		}
   125  		r = append(r, string(winapi.UTF16Decode(v[n:i])))
   126  		n = i + 1
   127  	}
   128  	return r, nil
   129  }
   130  
   131  // MarshalStream writes the data for this Entry to the supplied Writer.
   132  func (e Entry) MarshalStream(w data.Writer) error {
   133  	if err := w.WriteString(e.Name); err != nil {
   134  		return err
   135  	}
   136  	if err := w.WriteUint32(e.Type); err != nil {
   137  		return err
   138  	}
   139  	return w.WriteBytes(e.Data)
   140  }
   141  
   142  // UnmarshalStream reads the data for this Entry from the supplied Reader.
   143  func (e *Entry) UnmarshalStream(r data.Reader) error {
   144  	if err := r.ReadString(&e.Name); err != nil {
   145  		return err
   146  	}
   147  	if err := r.ReadUint32(&e.Type); err != nil {
   148  		return err
   149  	}
   150  	return r.ReadBytes(&e.Data)
   151  }