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

     1  //go:build windows
     2  // +build windows
     3  
     4  // Copyright (C) 2020 - 2023 iDigitalFlame
     5  //
     6  // This program is free software: you can redistribute it and/or modify
     7  // it under the terms of the GNU General Public License as published by
     8  // the Free Software Foundation, either version 3 of the License, or
     9  // any later version.
    10  //
    11  // This program is distributed in the hope that it will be useful,
    12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  // GNU General Public License for more details.
    15  //
    16  // You should have received a copy of the GNU General Public License
    17  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    18  //
    19  
    20  package regedit
    21  
    22  import (
    23  	"sort"
    24  	"strings"
    25  	"syscall"
    26  
    27  	"github.com/iDigitalFlame/xmt/device/winapi"
    28  	"github.com/iDigitalFlame/xmt/device/winapi/registry"
    29  )
    30  
    31  type entryList []Entry
    32  
    33  func (e entryList) Len() int {
    34  	return len(e)
    35  }
    36  func (e entryList) Swap(i, j int) {
    37  	e[i], e[j] = e[j], e[i]
    38  }
    39  
    40  // Dir returns a list of registry entries for the supplied key or an error if
    41  // the path does not exist.
    42  //
    43  // The key path can either be a "reg" style path (ex: HKLM\System or
    44  // HKCU\Software) or PowerShell style (ex: HKLM:\System or HKCU:\Software).
    45  //
    46  // Returns device.ErrNoWindows on non-Windows devices.
    47  func Dir(key string) ([]Entry, error) {
    48  	k, err := read(key, false)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	s, err := k.SubKeys()
    53  	if err != nil {
    54  		k.Close()
    55  		return nil, err
    56  	}
    57  	v, err := k.Values()
    58  	if err != nil {
    59  		k.Close()
    60  		return nil, err
    61  	}
    62  	n := len(v) + len(s)
    63  	if n == 0 {
    64  		k.Close()
    65  		return nil, nil
    66  	}
    67  	var (
    68  		r = make(entryList, n, n+1)
    69  		c int
    70  	)
    71  	for i := range s {
    72  		r[c].Name = s[i]
    73  		c++
    74  	}
    75  	var d bool
    76  	for i := range v {
    77  		if r[c].Name = v[i]; len(v[i]) == 0 {
    78  			d, r[c].Name = true, "(Default)"
    79  		}
    80  		r[c].Data, r[c].Type, err = readFullValueData(k, v[i])
    81  		if c++; err != nil {
    82  			break
    83  		}
    84  	}
    85  	if !d {
    86  		e := Entry{Name: "(Default)"}
    87  		if e.Data, e.Type, _ = readFullValueData(k, ""); e.Type == 0 {
    88  			e.Type = registry.TypeString
    89  		}
    90  		r = append(r, e)
    91  	}
    92  	k.Close()
    93  	sort.Sort(r)
    94  	return r, err
    95  }
    96  func (e entryList) Less(i, j int) bool {
    97  	if len(e[j].Name) == 9 && e[j].Name[0] == '(' && e[j].Name[1] == 'D' && e[j].Name[8] == ')' {
    98  		return false
    99  	}
   100  	if e[i].Type == 0 && e[j].Type > 0 {
   101  		return true
   102  	}
   103  	if e[j].Type == 0 && e[i].Type > 1 {
   104  		return false
   105  	}
   106  	return e[i].Name < e[j].Name
   107  }
   108  func increaseSlash(i int, s string) int {
   109  	if len(s) <= i {
   110  		return i
   111  	}
   112  	if s[i] == '\\' {
   113  		return i + 1
   114  	}
   115  	return i
   116  }
   117  
   118  // Get returns a single registry entry for the supplied value name under the
   119  // key path specified or an error if any of the paths do not exist.
   120  //
   121  // The key path can either be a "reg" style path (ex: HKLM\System or
   122  // HKCU\Software) or PowerShell style (ex: HKLM:\System or HKCU:\Software).
   123  //
   124  // Returns device.ErrNoWindows on non-Windows devices.
   125  func Get(key, value string) (Entry, error) {
   126  	var (
   127  		k, err = read(key, false)
   128  		e      Entry
   129  	)
   130  	if err != nil {
   131  		return e, err
   132  	}
   133  	e.Name = value
   134  	e.Data, e.Type, err = readFullValueData(k, value)
   135  	k.Close()
   136  	return e, err
   137  }
   138  func read(k string, w bool) (registry.Key, error) {
   139  	h, d, err := translateRootKey(k)
   140  	if err != nil {
   141  		return 0, err
   142  	}
   143  	if d >= len(k) || h == 0 {
   144  		return 0, registry.ErrNotExist
   145  	}
   146  	if w {
   147  		// 0x2001F - KEY_READ | KEY_WRITE
   148  		x, _, err := registry.Create(h, k[d:], 0x2001F)
   149  		return x, err
   150  	}
   151  	// 0x20019 - KEY_READ
   152  	return registry.Open(h, k[d:], 0x20019)
   153  }
   154  func translateRootKey(v string) (registry.Key, int, error) {
   155  	if len(v) < 4 || (v[0] != 'H' && v[0] != 'h') {
   156  		return 0, 0, registry.ErrNotExist
   157  	}
   158  	i := strings.IndexByte(v, ':')
   159  	if i == -1 {
   160  		if i = strings.IndexByte(v, '\\'); i == -1 {
   161  			return 0, 0, registry.ErrNotExist
   162  		}
   163  	}
   164  	if len(v) > 6 && v[4] == '_' {
   165  		if i < 5 {
   166  			return 0, 0, registry.ErrNotExist
   167  		}
   168  		switch v[i-1] {
   169  		case 'E', 'e':
   170  			return registry.KeyLocalMachine, increaseSlash(i+1, v), nil
   171  		case 'R', 'r':
   172  			return registry.KeyCurrentUser, increaseSlash(i+1, v), nil
   173  		case 'S', 's':
   174  			return registry.KeyUsers, increaseSlash(i+1, v), nil
   175  		case 'G', 'g':
   176  			return registry.KeyCurrentConfig, increaseSlash(i+1, v), nil
   177  		case 'A', 'a':
   178  			return registry.KeyPerformanceData, increaseSlash(i+1, v), nil
   179  		case 'T', 't':
   180  			return registry.KeyClassesRoot, increaseSlash(i+1, v), nil
   181  		}
   182  		return 0, 0, registry.ErrNotExist
   183  	}
   184  	if i == 3 && (v[2] == 'U' || v[2] == 'u') {
   185  		return registry.KeyUsers, increaseSlash(4, v), nil
   186  	}
   187  	if i < 4 {
   188  		return 0, 0, registry.ErrNotExist
   189  	}
   190  	switch v[i-1] {
   191  	case 'M', 'm':
   192  		return registry.KeyLocalMachine, increaseSlash(i+1, v), nil
   193  	case 'U', 'u':
   194  		return registry.KeyCurrentUser, increaseSlash(i+1, v), nil
   195  	case 'C', 'c':
   196  		return registry.KeyCurrentConfig, increaseSlash(i+1, v), nil
   197  	case 'D', 'd':
   198  		return registry.KeyPerformanceData, increaseSlash(i+1, v), nil
   199  	case 'R', 'r':
   200  		return registry.KeyClassesRoot, increaseSlash(i+1, v), nil
   201  	}
   202  	return 0, 0, registry.ErrNotExist
   203  }
   204  func readFullValueData(k registry.Key, n string) ([]byte, uint32, error) {
   205  	v, err := winapi.UTF16PtrFromString(n)
   206  	if err != nil {
   207  		return nil, 0, err
   208  	}
   209  	var t, s uint32
   210  	if err = syscall.RegQueryValueEx(syscall.Handle(k), v, nil, &t, nil, &s); err != nil {
   211  		return nil, 0, err
   212  	}
   213  	b := make([]byte, s)
   214  	if err = syscall.RegQueryValueEx(syscall.Handle(k), v, nil, &t, &b[0], &s); err != nil {
   215  		return nil, t, err
   216  	}
   217  	return b, t, err
   218  }