From 8b338dc95ef778b7fafc77fcd56d9f5d36bbf56c Mon Sep 17 00:00:00 2001 From: kernelstub Date: Sun, 14 Jun 2026 00:10:43 +0200 Subject: [PATCH] fix: CLSID data truncation & more (1.0.1) --- modules/advanced/module.go | 1 + modules/clsid/module.go | 42 +++++---- modules/dllsearch/module.go | 4 +- modules/drivers/module.go | 3 +- modules/env/module.go | 8 +- modules/mitigations/module.go | 3 +- modules/pipes/module.go | 3 +- modules/policy/module.go | 1 + modules/registry/module.go | 1 + modules/scheduled/module.go | 3 +- modules/services/module.go | 6 +- modules/startup/module.go | 2 + modules/tokens/module.go | 15 +++- windows/advanced/advanced_windows.go | 4 +- windows/advanced/builtin_windows.go | 3 - windows/advanced/helpers_windows.go | 7 -- windows/registry/registry_windows.go | 125 ++++++++++++++++++++++++--- windows/registry/types_registry.go | 3 + 18 files changed, 175 insertions(+), 59 deletions(-) diff --git a/modules/advanced/module.go b/modules/advanced/module.go index cefd343..dc9d8e1 100644 --- a/modules/advanced/module.go +++ b/modules/advanced/module.go @@ -145,6 +145,7 @@ func (m module) Run(ctx *core.Context) error { ctx.Logger.Info("No findings returned for " + m.name + ".") return nil } + ctx.Logger.Info(fmt.Sprintf("%s findings returned: %d", m.name, len(findings))) for _, finding := range findings { ctx.Logger.Success(fmt.Sprintf("%s %s > %s", finding.Severity, finding.Target, finding.Reason)) if finding.Name != "" || finding.Value != "" { diff --git a/modules/clsid/module.go b/modules/clsid/module.go index 34d8ceb..ab65d10 100644 --- a/modules/clsid/module.go +++ b/modules/clsid/module.go @@ -8,13 +8,10 @@ import ( "sync" "ferrum/core" - "ferrum/internal" win "ferrum/windows/facade" ) const maxProcessWorkers = 16 -const maxVerboseCLSID = 40 -const maxProcMonCandidates = 120 func init() { core.Register(Module{}) @@ -41,14 +38,16 @@ func (Module) Run(ctx *core.Context) error { ctx.Logger.Info("Inspecting process security context...") enriched := enrichProcesses(ctx, processes) sortProcesses(enriched) + ctx.Logger.Info(fmt.Sprintf("Processes enumerated: %d", len(enriched))) interesting := 0 for _, process := range enriched { - if !process.Interesting { - continue + status := "process" + if process.Interesting { + interesting++ + status = "privileged/elevated process" } - interesting++ - ctx.Logger.Success(fmt.Sprintf("%s[%d] > %s", process.Name, process.PID, process.Label())) + ctx.Logger.Success(fmt.Sprintf("%s[%d] > %s > %s", process.Name, process.PID, status, process.Label())) if process.Detail != "" { ctx.Logger.Verbose(fmt.Sprintf("%s[%d] : %s", process.Name, process.PID, process.Detail)) } @@ -62,6 +61,7 @@ func (Module) Run(ctx *core.Context) error { if err != nil { ctx.Logger.Error(fmt.Sprintf("CLSID ProcMon candidate scan: %v", err)) } else { + ctx.Logger.Info(fmt.Sprintf("CLSID ProcMon candidates: %d", len(candidates))) reportProcMonCandidates(ctx, candidates, interestingProcesses(enriched)) } @@ -76,11 +76,16 @@ func (Module) Run(ctx *core.Context) error { ctx.Logger.Info("No HKCU CLSID registrations found.") return nil } + ctx.Logger.Info(fmt.Sprintf("HKCU CLSID values enumerated: %d", len(entries))) for _, entry := range summarizeCLSID(entries) { - ctx.Logger.Success(fmt.Sprintf("HKCU\\Software\\Classes\\CLSID\\%s > %s", entry.CLSID, entry.Kind)) + name := entry.Name + if name == "" { + name = "(Default)" + } + ctx.Logger.Success(fmt.Sprintf("%s > %s > %s", entry.Path, name, entry.Kind)) if entry.Value != "" { - ctx.Logger.Verbose(fmt.Sprintf("%s : %s", entry.CLSID, entry.Value)) + ctx.Logger.Verbose(fmt.Sprintf("%s : type=%d value=%s", entry.CLSID, entry.Type, entry.Value)) } } @@ -106,19 +111,17 @@ func reportProcMonCandidates(ctx *core.Context, candidates []win.CLSIDProcMonCan processLabel := "NT AUTHORITY\\SYSTEM / privileged COM client" if len(processes) > 0 { names := make([]string, 0, len(processes)) - for _, process := range internal.Limit(processes, 8) { + for _, process := range processes { names = append(names, fmt.Sprintf("%s[%d]", process.Name, process.PID)) } - processLabel = strings.Join(names, ", ") + processLabel = fmt.Sprintf("%d privileged/elevated process(es)", len(processes)) + ctx.Logger.Info("Privileged/elevated process set: " + strings.Join(names, ", ")) } - for _, candidate := range internal.Limit(candidates, maxProcMonCandidates) { + for _, candidate := range candidates { ctx.Logger.Success(fmt.Sprintf("%s > %s > %s", processLabel, candidate.Path, candidate.Result)) ctx.Logger.Verbose(fmt.Sprintf("%s : CLSID=%s machine=%s", candidate.Kind, candidate.CLSID, candidate.MachineValue)) } - if len(candidates) > maxProcMonCandidates { - ctx.Logger.Info(fmt.Sprintf("CLSID ProcMon candidate output limited to %d of %d rows.", maxProcMonCandidates, len(candidates))) - } ctx.Logger.Info("These rows model the ProcMon filter Result=NAME NOT FOUND for HKCU COM override paths. Confirm live process access with ProcMon or ETW before treating a candidate as reachable.") } @@ -222,7 +225,7 @@ func detail(token win.TokenInfo) string { parts = append(parts, "elevated=true") } if len(token.Privileges) > 0 { - parts = append(parts, "privileges="+strings.Join(internal.Limit(token.Privileges, 8), ",")) + parts = append(parts, "privileges="+strings.Join(token.Privileges, ",")) } return strings.Join(parts, " ") } @@ -241,7 +244,10 @@ func summarizeCLSID(entries []win.CLSIDEntry) []win.CLSIDEntry { if entries[i].CLSID != entries[j].CLSID { return entries[i].CLSID < entries[j].CLSID } - return entries[i].Kind < entries[j].Kind + if entries[i].Path != entries[j].Path { + return entries[i].Path < entries[j].Path + } + return entries[i].Name < entries[j].Name }) - return internal.Limit(entries, maxVerboseCLSID) + return entries } diff --git a/modules/dllsearch/module.go b/modules/dllsearch/module.go index 6159a95..aaba09c 100644 --- a/modules/dllsearch/module.go +++ b/modules/dllsearch/module.go @@ -4,7 +4,6 @@ import ( "fmt" "ferrum/core" - "ferrum/internal" win "ferrum/windows/facade" ) @@ -24,6 +23,7 @@ func (Module) Run(ctx *core.Context) error { if err != nil { return err } + ctx.Logger.Info(fmt.Sprintf("DLL search entries enumerated: %d", len(findings))) reported := 0 for _, finding := range findings { if finding.Severity == "Info" { @@ -37,7 +37,7 @@ func (Module) Run(ctx *core.Context) error { if reported == 0 { ctx.Logger.Info("No risky DLL search path entries matched the default heuristics.") } - for _, finding := range internal.Limit(findings, 40) { + for _, finding := range findings { if finding.Severity == "Info" { ctx.Logger.Verbose(fmt.Sprintf("KnownDLL : %s", finding.Path)) } diff --git a/modules/drivers/module.go b/modules/drivers/module.go index 95e96fa..1d1207f 100644 --- a/modules/drivers/module.go +++ b/modules/drivers/module.go @@ -24,8 +24,10 @@ func (Module) Run(ctx *core.Context) error { if err != nil { return err } + ctx.Logger.Info(fmt.Sprintf("Drivers enumerated: %d", len(drivers))) reported := 0 for _, driver := range drivers { + ctx.Logger.Verbose(fmt.Sprintf("driver inventory : name=%s state=%s start=%s path=%s", driver.Name, driver.State, driver.StartType, driver.BinaryPath)) reason := driverReason(driver) if reason == "" { continue @@ -37,7 +39,6 @@ func (Module) Run(ctx *core.Context) error { if reported == 0 { ctx.Logger.Info("No driver load paths matched the suspicious-path heuristics.") } - ctx.Logger.Verbose(fmt.Sprintf("Drivers enumerated: %d", len(drivers))) return nil } diff --git a/modules/env/module.go b/modules/env/module.go index 3c59105..4eb64f8 100644 --- a/modules/env/module.go +++ b/modules/env/module.go @@ -24,15 +24,21 @@ func (Module) Run(ctx *core.Context) error { if err != nil { return err } + ctx.Logger.Info(fmt.Sprintf("Environment variables inspected: %d", len(vars))) + reported := 0 for _, env := range vars { + ctx.Logger.Verbose(fmt.Sprintf("environment inventory : %s=%s", env.Name, env.Value)) reason := envReason(env) if reason == "" { continue } + reported++ ctx.Logger.Success(fmt.Sprintf("%s > %s", env.Name, reason)) ctx.Logger.Verbose(fmt.Sprintf("%s=%s", env.Name, env.Value)) } - ctx.Logger.Verbose(fmt.Sprintf("Environment variables inspected: %d", len(vars))) + if reported == 0 { + ctx.Logger.Info("No environment variables matched the default audit heuristics.") + } return nil } diff --git a/modules/mitigations/module.go b/modules/mitigations/module.go index 689f14c..86a76cd 100644 --- a/modules/mitigations/module.go +++ b/modules/mitigations/module.go @@ -24,8 +24,10 @@ func (Module) Run(ctx *core.Context) error { if err != nil { return err } + ctx.Logger.Info(fmt.Sprintf("Process mitigations sampled: %d", len(items))) reported := 0 for _, item := range items { + ctx.Logger.Verbose(fmt.Sprintf("mitigation inventory : name=%s pid=%d DEP=%s ASLR=%s StrictHandle=%s CFG=%s", item.Name, item.PID, item.DEP, item.ASLR, item.Strict, item.CFG)) reason := mitigationReason(item) if reason == "" { continue @@ -37,7 +39,6 @@ func (Module) Run(ctx *core.Context) error { if reported == 0 { ctx.Logger.Info("No accessible process mitigation gaps matched the default heuristics.") } - ctx.Logger.Verbose(fmt.Sprintf("Processes sampled: %d", len(items))) return nil } diff --git a/modules/pipes/module.go b/modules/pipes/module.go index 85705b6..b6d1874 100644 --- a/modules/pipes/module.go +++ b/modules/pipes/module.go @@ -5,7 +5,6 @@ import ( "strings" "ferrum/core" - "ferrum/internal" win "ferrum/windows/facade" ) @@ -35,7 +34,7 @@ func (Module) Run(ctx *core.Context) error { ctx.Logger.Success(fmt.Sprintf("%s > %s", pipe.Name, reason)) } ctx.Logger.Verbose(fmt.Sprintf("Pipes enumerated: %d", len(pipes))) - for _, pipe := range internal.Limit(pipes, 50) { + for _, pipe := range pipes { ctx.Logger.Verbose(fmt.Sprintf("pipe : %s", pipe.Name)) } if reported == 0 { diff --git a/modules/policy/module.go b/modules/policy/module.go index 068e446..1a1da3e 100644 --- a/modules/policy/module.go +++ b/modules/policy/module.go @@ -23,6 +23,7 @@ func (Module) Run(ctx *core.Context) error { if err != nil { return err } + ctx.Logger.Info(fmt.Sprintf("Policy checks returned: %d", len(findings))) for _, finding := range findings { ctx.Logger.Success(fmt.Sprintf("%s %s > %s", finding.Severity, finding.Name, finding.Reason)) ctx.Logger.Verbose(fmt.Sprintf("%s = %s", finding.Name, finding.Value)) diff --git a/modules/registry/module.go b/modules/registry/module.go index 79b4027..5b122da 100644 --- a/modules/registry/module.go +++ b/modules/registry/module.go @@ -27,6 +27,7 @@ func (Module) Run(ctx *core.Context) error { ctx.Logger.Info("No sensitive registry findings matched the configured checks.") return nil } + ctx.Logger.Info(fmt.Sprintf("Registry findings returned: %d", len(findings))) for _, finding := range findings { ctx.Logger.Success(fmt.Sprintf("%s %s\\%s > %s: %s", finding.Severity, finding.Scope, finding.Path, finding.Name, finding.Reason)) if finding.Value != "" { diff --git a/modules/scheduled/module.go b/modules/scheduled/module.go index e035748..a4a764f 100644 --- a/modules/scheduled/module.go +++ b/modules/scheduled/module.go @@ -24,8 +24,10 @@ func (Module) Run(ctx *core.Context) error { if err != nil { return err } + ctx.Logger.Info(fmt.Sprintf("Scheduled tasks enumerated: %d", len(tasks))) reported := 0 for _, task := range tasks { + ctx.Logger.Verbose(fmt.Sprintf("task inventory : path=%s enabled=%s author=%s command=%s", task.Path, task.Enabled, task.Author, task.Command)) reason := taskReason(task) if reason == "" { continue @@ -37,7 +39,6 @@ func (Module) Run(ctx *core.Context) error { if reported == 0 { ctx.Logger.Info("No scheduled tasks matched the default interesting-command heuristics.") } - ctx.Logger.Verbose(fmt.Sprintf("Tasks enumerated: %d", len(tasks))) return nil } diff --git a/modules/services/module.go b/modules/services/module.go index eed35ea..b6bea34 100644 --- a/modules/services/module.go +++ b/modules/services/module.go @@ -5,7 +5,6 @@ import ( "strings" "ferrum/core" - "ferrum/internal" win "ferrum/windows/facade" ) @@ -25,8 +24,10 @@ func (Module) Run(ctx *core.Context) error { if err != nil { return err } + ctx.Logger.Info(fmt.Sprintf("Services enumerated: %d", len(services))) reported := 0 for _, service := range services { + ctx.Logger.Verbose(fmt.Sprintf("service inventory : name=%s display=%s state=%s start=%s account=%s pid=%d type=%d path=%s", service.Name, service.DisplayName, service.State, service.StartType, service.Account, service.ProcessID, service.ServiceType, service.BinaryPath)) reasons := serviceReasons(service) if len(reasons) == 0 { continue @@ -38,7 +39,6 @@ func (Module) Run(ctx *core.Context) error { if reported == 0 { ctx.Logger.Info("No service configuration stood out from the default heuristics.") } - ctx.Logger.Verbose(fmt.Sprintf("Services enumerated: %d", len(services))) return nil } @@ -58,7 +58,7 @@ func serviceReasons(service win.ServiceInfo) []string { if service.StartType == "Auto" && service.State != "Running" { reasons = append(reasons, "auto-start not running") } - return internal.Limit(reasons, 4) + return reasons } func isUnquotedPathWithSpaces(path string) bool { diff --git a/modules/startup/module.go b/modules/startup/module.go index 329e500..0d96c88 100644 --- a/modules/startup/module.go +++ b/modules/startup/module.go @@ -24,11 +24,13 @@ func (Module) Run(ctx *core.Context) error { if err != nil { return err } + ctx.Logger.Info(fmt.Sprintf("Startup entries enumerated: %d", len(entries))) if len(entries) == 0 { ctx.Logger.Info("No startup entries found in common locations.") return nil } for _, entry := range entries { + ctx.Logger.Verbose(fmt.Sprintf("startup inventory : scope=%s location=%s name=%s command=%s", entry.Scope, entry.Location, entry.Name, entry.Command)) ctx.Logger.Success(fmt.Sprintf("%s\\%s > %s", entry.Scope, entry.Name, startupReason(entry.Command))) ctx.Logger.Verbose(fmt.Sprintf("%s : %s", entry.Location, entry.Command)) } diff --git a/modules/tokens/module.go b/modules/tokens/module.go index e1b2884..b56c694 100644 --- a/modules/tokens/module.go +++ b/modules/tokens/module.go @@ -8,7 +8,6 @@ import ( "sync" "ferrum/core" - "ferrum/internal" win "ferrum/windows/facade" ) @@ -35,19 +34,24 @@ func (Module) Run(ctx *core.Context) error { } return findings[i].Name < findings[j].Name }) + ctx.Logger.Info(fmt.Sprintf("Process tokens inspected: %d", len(findings))) reported := 0 for _, finding := range findings { + if finding.Error != "" { + ctx.Logger.Verbose(fmt.Sprintf("%s[%d] : token inspection failed: %s", finding.Name, finding.PID, finding.Error)) + continue + } + ctx.Logger.Verbose(fmt.Sprintf("token inventory : name=%s pid=%d %s privileges=%s", finding.Name, finding.PID, finding.Process.Label(), strings.Join(finding.Privileges, ","))) if finding.Score == 0 { continue } reported++ ctx.Logger.Success(fmt.Sprintf("%s[%d] > %s", finding.Name, finding.PID, strings.Join(finding.Reasons, ", "))) - ctx.Logger.Verbose(fmt.Sprintf("%s[%d] : %s privileges=%s", finding.Name, finding.PID, finding.Process.Label(), strings.Join(internal.Limit(finding.Privileges, 12), ","))) + ctx.Logger.Verbose(fmt.Sprintf("%s[%d] : %s privileges=%s", finding.Name, finding.PID, finding.Process.Label(), strings.Join(finding.Privileges, ","))) } if reported == 0 { ctx.Logger.Info("No accessible process tokens matched the sensitive privilege heuristics.") } - ctx.Logger.Verbose(fmt.Sprintf("Processes inspected: %d", len(processes))) return nil } @@ -55,6 +59,7 @@ type tokenFinding struct { win.Process Reasons []string Score int + Error string } func inspect(processes []win.Process) []tokenFinding { @@ -78,6 +83,8 @@ func inspect(processes []win.Process) []tokenFinding { finding.Elevated = token.Elevated finding.Privileges = token.Privileges finding.Reasons, finding.Score = tokenReasons(token) + } else { + finding.Error = err.Error() } results <- finding } @@ -130,5 +137,5 @@ func tokenReasons(token win.TokenInfo) ([]string, int) { score += points } } - return internal.Limit(reasons, 8), score + return reasons, score } diff --git a/windows/advanced/advanced_windows.go b/windows/advanced/advanced_windows.go index e63f660..4f2ed19 100644 --- a/windows/advanced/advanced_windows.go +++ b/windows/advanced/advanced_windows.go @@ -10,8 +10,6 @@ import ( wintypes "ferrum/windows/types" ) -const advancedLimit = 120 - type AdvancedFinding = wintypes.AdvancedFinding func EnumerateAdvancedFindings(check string) ([]AdvancedFinding, error) { @@ -84,5 +82,5 @@ func EnumerateAdvancedFindings(check string) ([]AdvancedFinding, error) { findings = genericSurfaceFindings(check) } sortAdvanced(findings) - return limitAdvanced(findings), nil + return findings, nil } diff --git a/windows/advanced/builtin_windows.go b/windows/advanced/builtin_windows.go index 6026164..6a3dfba 100644 --- a/windows/advanced/builtin_windows.go +++ b/windows/advanced/builtin_windows.go @@ -159,9 +159,6 @@ func protocolFindings() []AdvancedFinding { findings = append(findings, AdvancedFinding{Area: "Protocols", Target: root.name + `\` + base + `\` + key, Name: value.Name, Value: value.Value, Severity: "Medium", Reason: "custom URL protocol handler"}) } } - if len(findings) >= advancedLimit { - return findings - } } } return findings diff --git a/windows/advanced/helpers_windows.go b/windows/advanced/helpers_windows.go index d2310a2..6ec2165 100644 --- a/windows/advanced/helpers_windows.go +++ b/windows/advanced/helpers_windows.go @@ -146,10 +146,3 @@ func sortAdvanced(findings []AdvancedFinding) { return findings[i].Target < findings[j].Target }) } - -func limitAdvanced(findings []AdvancedFinding) []AdvancedFinding { - if len(findings) <= advancedLimit { - return findings - } - return findings[:advancedLimit] -} diff --git a/windows/registry/registry_windows.go b/windows/registry/registry_windows.go index ba46ead..acffaa5 100644 --- a/windows/registry/registry_windows.go +++ b/windows/registry/registry_windows.go @@ -4,6 +4,7 @@ package registry import ( "fmt" + "strings" "syscall" "unsafe" @@ -26,6 +27,7 @@ var ( procRegCloseKey = advapi32.NewProc("RegCloseKey") procRegEnumKeyEx = advapi32.NewProc("RegEnumKeyExW") procRegEnumValue = advapi32.NewProc("RegEnumValueW") + procRegQueryInfoKey = advapi32.NewProc("RegQueryInfoKeyW") procRegQueryValueEx = advapi32.NewProc("RegQueryValueExW") ) @@ -36,7 +38,8 @@ type RegistryValue struct { } func EnumerateHKCUCLSID() ([]CLSIDEntry, error) { - root, err := openKey(hkeyCurrentUser, `Software\Classes\CLSID`) + const base = `Software\Classes\CLSID` + root, err := openKey(hkeyCurrentUser, base) if err != nil { return nil, err } @@ -49,19 +52,58 @@ func EnumerateHKCUCLSID() ([]CLSIDEntry, error) { entries := make([]CLSIDEntry, 0) for _, clsid := range clsids { - for _, kind := range []string{"InprocServer32", "LocalServer32", "TreatAs", "ProgID"} { - subkey, err := openKey(root, clsid+`\`+kind) - if err != nil { - continue - } - value, _ := queryDefaultValue(subkey) - procRegCloseKey.Call(subkey) - entries = append(entries, CLSIDEntry{CLSID: clsid, Kind: kind, Value: value}) - } + clsidPath := clsid + walkCLSIDKey(root, base, clsid, clsidPath, &entries) } return entries, nil } +func walkCLSIDKey(parent uintptr, base, clsid, relative string, entries *[]CLSIDEntry) { + key, err := openKey(parent, relative) + if err != nil { + return + } + defer procRegCloseKey.Call(key) + + path := base + `\` + relative + kind := clsidKind(relative) + values, err := enumValues(key) + if err == nil { + for _, value := range values { + *entries = append(*entries, CLSIDEntry{ + CLSID: clsid, + Kind: kind, + Path: `HKCU\` + path, + Name: displayRegistryName(value.Name), + Type: value.Type, + Value: value.Value, + }) + } + } + subkeyNames, err := enumSubkeys(key) + if err != nil { + return + } + for _, subkey := range subkeyNames { + walkCLSIDKey(parent, base, clsid, relative+`\`+subkey, entries) + } +} + +func clsidKind(relative string) string { + parts := strings.Split(relative, `\`) + if len(parts) < 2 { + return "(CLSID)" + } + return parts[1] +} + +func displayRegistryName(name string) string { + if name == "" { + return "(Default)" + } + return name +} + func EnumerateCLSIDProcMonCandidates() ([]CLSIDProcMonCandidate, error) { root, err := openKey(hkeyLocalMachine, `Software\Classes\CLSID`) if err != nil { @@ -124,9 +166,17 @@ func CloseKey(key uintptr) { } func enumSubkeys(key uintptr) ([]string, error) { + info, err := queryKeyInfo(key) + if err != nil { + return nil, err + } + nameCapacity := info.MaxSubKeyLen + 1 + if nameCapacity < 256 { + nameCapacity = 256 + } keys := []string{} for index := uint32(0); ; index++ { - name := make([]uint16, 256) + name := make([]uint16, nameCapacity) length := uint32(len(name)) ret, _, _ := procRegEnumKeyEx.Call(key, uintptr(index), uintptr(unsafe.Pointer(&name[0])), uintptr(unsafe.Pointer(&length)), 0, 0, 0, 0) if ret == errorNoMoreItems { @@ -183,11 +233,28 @@ func registryValues(parent uintptr, path string) ([]RegistryValue, error) { } defer procRegCloseKey.Call(key) + return enumValues(key) +} + +func enumValues(key uintptr) ([]RegistryValue, error) { + info, err := queryKeyInfo(key) + if err != nil { + return nil, err + } + nameCapacity := info.MaxValueNameLen + 1 + if nameCapacity < 512 { + nameCapacity = 512 + } + dataCapacity := info.MaxValueLen + if dataCapacity < 8192 { + dataCapacity = 8192 + } + values := []RegistryValue{} for index := uint32(0); ; index++ { - name := make([]uint16, 512) + name := make([]uint16, nameCapacity) nameLen := uint32(len(name)) - data := make([]byte, 8192) + data := make([]byte, dataCapacity) dataLen := uint32(len(data)) var typ uint32 ret, _, _ := procRegEnumValue.Call(key, uintptr(index), uintptr(unsafe.Pointer(&name[0])), uintptr(unsafe.Pointer(&nameLen)), 0, uintptr(unsafe.Pointer(&typ)), uintptr(unsafe.Pointer(&data[0])), uintptr(unsafe.Pointer(&dataLen))) @@ -226,3 +293,35 @@ func registryDataString(data []byte, typ uint32) string { } return fmt.Sprintf("%x", data) } + +type keyInfo struct { + MaxSubKeyLen uint32 + MaxValueNameLen uint32 + MaxValueLen uint32 +} + +func queryKeyInfo(key uintptr) (keyInfo, error) { + var subKeys uint32 + var maxSubKeyLen uint32 + var values uint32 + var maxValueNameLen uint32 + var maxValueLen uint32 + ret, _, _ := procRegQueryInfoKey.Call( + key, + 0, + 0, + 0, + uintptr(unsafe.Pointer(&subKeys)), + uintptr(unsafe.Pointer(&maxSubKeyLen)), + 0, + uintptr(unsafe.Pointer(&values)), + uintptr(unsafe.Pointer(&maxValueNameLen)), + uintptr(unsafe.Pointer(&maxValueLen)), + 0, + 0, + ) + if ret != 0 { + return keyInfo{}, syscall.Errno(ret) + } + return keyInfo{MaxSubKeyLen: maxSubKeyLen, MaxValueNameLen: maxValueNameLen, MaxValueLen: maxValueLen}, nil +} diff --git a/windows/registry/types_registry.go b/windows/registry/types_registry.go index 30b8759..157d4e1 100644 --- a/windows/registry/types_registry.go +++ b/windows/registry/types_registry.go @@ -3,6 +3,9 @@ package registry type CLSIDEntry struct { CLSID string Kind string + Path string + Name string + Type uint32 Value string }