Skip to content

Commit

Permalink
⭐️ support Windows 2025 on arm via SSH
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-rock committed Nov 2, 2024
1 parent 0e24f42 commit 568dc10
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 29 deletions.
46 changes: 34 additions & 12 deletions providers/os/detector/detector_win.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,41 @@ import (
"go.mondoo.com/cnquery/v11/providers/os/registry"
)

// runtimeWindowsDetector uses powershell to gather information about the windows system
func runtimeWindowsDetector(pf *inventory.Platform, conn shared.Connection) (bool, error) {
// most systems support wmi, but windows on arm does not ship with wmic, therefore we are trying to use windows
// builds from registry key first. If that fails, we try to use wmi
// see https://techcommunity.microsoft.com/t5/windows-it-pro-blog/wmi-command-line-wmic-utility-deprecation-next-steps/ba-p/4039242

if pf.Labels == nil {
pf.Labels = map[string]string{}
}

// try to get build + ubr number (win 10+, 2019+)
current, err := win.GetWindowsOSBuild(conn)
if err == nil && current.UBR > 0 {
pf.Name = "windows"
pf.Title = current.ProductName
pf.Version = current.CurrentBuild
pf.Build = strconv.Itoa(current.UBR)
pf.Arch = current.Architecture

var productType string
switch current.ProductType {
case "WinNT":
productType = "1" // Workstation
case "ServerNT":
productType = "3" // Server
case "LanmanNT":
productType = "2" // Domain Controller
}

pf.Labels["windows.mondoo.com/product-type"] = productType
pf.Labels["windows.mondoo.com/display-version"] = current.DisplayVersion
return true, nil
}

// fallback to wmi if the registry key is not available
data, err := win.GetWmiInformation(conn)
if err != nil {
log.Debug().Err(err).Msg("could not gather wmi information")
Expand All @@ -29,20 +63,8 @@ func runtimeWindowsDetector(pf *inventory.Platform, conn shared.Connection) (boo

// FIXME: we need to ask wmic cpu get architecture
pf.Arch = data.OSArchitecture

if pf.Labels == nil {
pf.Labels = map[string]string{}
}
pf.Labels["windows.mondoo.com/product-type"] = data.ProductType

// optional: try to get the ubr number (win 10 + 2019)
current, err := win.GetWindowsOSBuild(conn)
if err != nil {
log.Debug().Err(err).Msg("could not parse windows current version")
} else if current.UBR > 0 {
pf.Build = strconv.Itoa(current.UBR)
}

return true, nil
}

Expand Down
24 changes: 18 additions & 6 deletions providers/os/detector/windows/build_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,16 @@ func (b BuildVersion) OSBuild() string {
}

type WindowsCurrentVersion struct {
CurrentBuild string `json:"CurrentBuild"`
EditionID string `json:"EditionID"`
ReleaseId string `json:"ReleaseId"`
CurrentBuild string `json:"CurrentBuild"`
EditionID string `json:"EditionID"`
ReleaseId string `json:"ReleaseId"`
InstallationType string `json:"InstallationType"`
ProductName string `json:"ProductName"`
DisplayVersion string `json:"DisplayVersion"`
// Update Build Revision
UBR int `json:"UBR"`
UBR int `json:"UBR"`
Architecture string `json:"Architecture"`
ProductType string `json:"ProductType"`
}

func ParseWinRegistryCurrentVersion(r io.Reader) (*WindowsCurrentVersion, error) {
Expand All @@ -82,8 +87,15 @@ func ParseWinRegistryCurrentVersion(r io.Reader) (*WindowsCurrentVersion, error)

// powershellGetWindowsOSBuild runs a powershell script to retrieve the current version from windows
func powershellGetWindowsOSBuild(conn shared.Connection) (*WindowsCurrentVersion, error) {
pscommand := "Get-ItemProperty -Path 'HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion' -Name CurrentBuild, UBR, EditionID | ConvertTo-Json"
cmd, err := conn.RunCommand(powershell.Wrap(pscommand))
pscommand := `
$info = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\ProductOptions' -Name ProductType
$sysInfo = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name CurrentBuild, UBR, InstallationType, EditionID, ProductName, DisplayVersion
$sysInfo | Add-Member -MemberType NoteProperty -Name Architecture -Value $env:PROCESSOR_ARCHITECTURE
$sysInfo | Add-Member -MemberType NoteProperty -Name ProductType -Value $info.ProductType
$sysInfo | Select-Object CurrentBuild, UBR, InstallationType, EditionID, ProductName, DisplayVersion, Architecture, ProductType | ConvertTo-Json
`

cmd, err := conn.RunCommand(powershell.Encode(pscommand))
if err != nil {
return nil, err
}
Expand Down
40 changes: 30 additions & 10 deletions providers/os/detector/windows/build_version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,37 @@ import (
// UBR - Update Build Revision
func TestParseWinRegistryCurrentVersion(t *testing.T) {

data := `{
"CurrentBuild": "17763",
"UBR": 720,
"EditionID": "ServerDatacenterEval",
"ReleaseId": "1809"
}`
t.Run("parse windows version", func(t *testing.T) {
data := `{
"CurrentBuild": "17763",
"UBR": 720,
"EditionID": "ServerDatacenterEval",
"ReleaseId": "1809"
}`

m, err := ParseWinRegistryCurrentVersion(strings.NewReader(data))
assert.Nil(t, err)
m, err := ParseWinRegistryCurrentVersion(strings.NewReader(data))
assert.Nil(t, err)

assert.Equal(t, "17763", m.CurrentBuild, "buildnumber should be parsed properly")
assert.Equal(t, 720, m.UBR, "ubr should be parsed properly")
assert.Equal(t, "17763", m.CurrentBuild, "buildnumber should be parsed properly")
assert.Equal(t, 720, m.UBR, "ubr should be parsed properly")
})

t.Run("parse windows version with architecture", func(t *testing.T) {
data := `{
"CurrentBuild": "26100",
"UBR": 2033,
"InstallationType": "Client",
"EditionID": "Enterprise",
"ProductName": "Windows 10 Enterprise",
"DisplayVersion": "24H2",
"Architecture": "ARM64",
"ProductType": "WinNT"
}`
m, err := ParseWinRegistryCurrentVersion(strings.NewReader(data))
assert.Nil(t, err)

assert.Equal(t, "26100", m.CurrentBuild, "buildnumber should be parsed properly")
assert.Equal(t, 2033, m.UBR, "ubr should be parsed properly")
assert.Equal(t, "ARM64", m.Architecture, "architecture should be parsed properly")
})
}
5 changes: 4 additions & 1 deletion providers/os/resources/powershell/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func Encode(cmd string) string {
return fmt.Sprintf("powershell.exe -NoProfile -EncodedCommand %s", encodedScript)
}

// The Encode equivalent for running powershell script in unix systems
// EncodeUnix is equivalent to Encode for running powershell script on unix systems
func EncodeUnix(cmd string) string {
// avoid messages to stderr that are not required in our execution
script := "$ProgressPreference='SilentlyContinue';" + cmd
Expand Down Expand Up @@ -60,6 +60,9 @@ func ToBase64String(script string) (string, error) {
return base64.StdEncoding.EncodeToString([]byte(encoded)), nil
}

// Wrap runs a powershell script by calling powershell. Note that this is not encoded and therefore does not support
// multiline scripts or special characters. You should use Encode for that or ensure the script is a single line and
// does use semicolons to separate commands.
func Wrap(cmd string) string {
return fmt.Sprintf("powershell -c \"%s\"", cmd)
}

0 comments on commit 568dc10

Please sign in to comment.