Acquire Windows Build info, No API [Registry & Memory address]

Discussion in 'Scripting' started by Dark Vador, May 26, 2025.

  1. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,462
    6,523
    150
    #1 Dark Vador, May 26, 2025
    Last edited: May 31, 2025 at 19:25
    i was thinking, where this value stored ?
    you can get them via registry, WMI, and also Api
    but where they real found ? { like old ver command }
    so, i arrived to this.

    Ver command -> Return
    `Microsoft Windows [Version 10.0.19044.5854]`

    Major -> 10, Minor -> 0, Build -> 19044, UBR -> 5854
    this is the ps1 equ code. { For NT 10.0 -> Windows 10 & Up}

    upload_2025-5-26_18-42-8.png

    Latest version, include lot of improvent's
    and fail safe check's, fit to powershell 3.0


    01/06/2025 ~ Changes
    Re-Build from scrath
    add new fail safe way, that work even on nt 4.0
    still read from memory, just from other memory [peb]
    nice catch from abbodi1406
    also add new way to fetch ubr value.

    Code:
    
    using namespace System
    using namespace System.Collections.Generic
    using namespace System.Drawing
    using namespace System.IO
    using namespace System.IO.Compression
    using namespace System.Management.Automation
    using namespace System.Management.Automation.CmdletBindingAttribute
    using namespace System.Net
    using namespace System.Numerics
    using namespace System.Reflection
    using namespace System.Reflection.Emit
    using namespace System.Runtime.InteropServices
    using namespace System.Security.AccessControl
    using namespace System.Security.Principal
    using namespace System.ServiceProcess
    using namespace System.Text
    using namespace System.Text.RegularExpressions
    using namespace System.Threading
    using namespace System.Windows.Forms
    
    Add-Type -AssemblyName System.Drawing
    Add-Type -AssemblyName System.Windows.Forms
    
    $ProgressPreference = 'SilentlyContinue'
    
    # Check if $PSVersionTable or $PSVersionTable.PSVersion is null, or Major version is invalid
    if ($null -eq $PSVersionTable -or $null -eq $PSVersionTable.PSVersion -or $null -eq $PSVersionTable.PSVersion.Major) {
        cls
        Write-Host
        Write-Host "Unable to determine PowerShell version." -ForegroundColor Green
        Write-Host "This script requires PowerShell 5.0 or higher!" -ForegroundColor Green
        Write-Host
        Read-Host "Press Enter to exit..."
        Read-Host
        return
    }
    
    # Check if PowerShell version is lower than 5.0
    if ($PSVersionTable.PSVersion.Major -lt 5) {
        cls
        Write-Host
        Write-Host "This script requires PowerShell 5.0 or higher!" -ForegroundColor Green
        Write-Host "Windows 10 & Above are supported." -ForegroundColor Green
        Write-Host
        Read-Host "Press Enter to exit..."
        Read-Host
        return
    }
    
    # Check if the current user is System or an Administrator
    $IsSystem = [WindowsIdentity]::GetCurrent().IsSystem
    $principal = [WindowsPrincipal]::new([WindowsIdentity]::GetCurrent())
    
    # If the user is neither System nor an Administrator, show a warning and stop the script
    if (!$IsSystem -and -not $principal.IsInRole([WindowsBuiltInRole]::Administrator)) {
        cls
        Write-Host
        Write-Host "This script must be run as Administrator or System!" -ForegroundColor Green
        Write-Host "Please run this script as Administrator." -ForegroundColor Green
        Write-Host "(Right-click and select 'Run as Administrator')" -ForegroundColor Green
        Write-Host
        Read-Host "Press Enter to exit..."
        Read-Host
        return
    }
    
    cls
    Write-Host
    Write-Host "=========================================" -ForegroundColor Green
    Write-Host "   PowerShell version is valid," -ForegroundColor Cyan
    Write-Host "   Proceeding with the script..." -ForegroundColor Cyan
    Write-Host "  All system checks passed. Let's roll!" -ForegroundColor Green
    Write-Host "=========================================" -ForegroundColor Green
    Write-Host
    Start-Sleep 4
    
    # Kernel-Mode Windows Versions
    # https://www.geoffchappell.com/studies/windows/km/versions.htm
    
    # ZwQuerySystemInformation
    # https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/query.htm
    
    # RtlGetNtVersionNumbers Function
    # The RtlGetNtVersionNumbers function gets Windows version numbers directly from NTDLL.
    # https://www.geoffchappell.com/studies/windows/win32/ntdll/api/ldrinit/getntversionnumbers.htm
    
    # RtlGetVersion function (wdm.h)
    # https://learn.microsoft.com/en-us/windows/win32/devnotes/rtlgetversion
    # https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion
    
    # Process Environment Block (PEB)
    # https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/index.htm
    
    # KUSER_SHARED_DATA
    # https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
    
    # KUSER_SHARED_DATA structure (ntddk.h)
    # https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
    
    # The read-only user-mode address for the shared data is 0x7FFE0000, both in 32-bit and 64-bit Windows.
    # The only formal definition among headers in the Windows Driver Kit (WDK) or the Software Development Kit (SDK) is in assembly language headers: KS386.
    # INC from the WDK and KSAMD64.
    # INC from the SDK both define MM_SHARED_USER_DATA_VA for the user-mode address.
    
    # That they also define USER_SHARED_DATA for the kernel-mode address suggests that they too are intended for kernel-mode programming,
    # albeit of a sort that is at least aware of what address works for user-mode access.
    
    # Among relatively large structures,
    # the KUSER_SHARED_DATA is highly unusual for having exactly the same layout in 32-bit and 64-bit Windows.
    # This is because the one instance must be simultaneously accessible by both 32-bit and 64-bit code on 64-bit Windows,
    # and it's desired that 32-bit user-mode code can run unchanged on both 32-bit and 64-bit Windows.
    
    # 2.2.9.6 OSEdition Enumeration
    # https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-mde2/d92ead8f-faf3-47a8-a341-1921dc2c463b
    
    # Full Table, include Name, Sku, etc etc provide by [abbodi1406]
    $Global:productTypeTable = @'
    ProductID,OSEdition,DWORD
    ultimate,PRODUCT_ULTIMATE,0x00000001
    homebasic,PRODUCT_HOME_BASIC,0x00000002
    homepremium,PRODUCT_HOME_PREMIUM,0x00000003
    enterprise,PRODUCT_ENTERPRISE,0x00000004
    homebasicn,PRODUCT_HOME_BASIC_N,0x00000005
    business,PRODUCT_BUSINESS,0x00000006
    serverstandard,PRODUCT_STANDARD_SERVER,0x00000007
    serverdatacenter,PRODUCT_DATACENTER_SERVER,0x00000008
    serversbsstandard,PRODUCT_SMALLBUSINESS_SERVER,0x00000009
    serverenterprise,PRODUCT_ENTERPRISE_SERVER,0x0000000A
    starter,PRODUCT_STARTER,0x0000000B
    serverdatacentercore,PRODUCT_DATACENTER_SERVER_CORE,0x0000000C
    serverstandardcore,PRODUCT_STANDARD_SERVER_CORE,0x0000000D
    serverenterprisecore,PRODUCT_ENTERPRISE_SERVER_CORE,0x0000000E
    serverenterpriseia64,PRODUCT_ENTERPRISE_SERVER_IA64,0x0000000F
    businessn,PRODUCT_BUSINESS_N,0x00000010
    serverweb,PRODUCT_WEB_SERVER,0x00000011
    serverhpc,PRODUCT_CLUSTER_SERVER,0x00000012
    serverhomestandard,PRODUCT_HOME_SERVER,0x00000013
    serverstorageexpress,PRODUCT_STORAGE_EXPRESS_SERVER,0x00000014
    serverstoragestandard,PRODUCT_STORAGE_STANDARD_SERVER,0x00000015
    serverstorageworkgroup,PRODUCT_STORAGE_WORKGROUP_SERVER,0x00000016
    serverstorageenterprise,PRODUCT_STORAGE_ENTERPRISE_SERVER,0x00000017
    serverwinsb,PRODUCT_SERVER_FOR_SMALLBUSINESS,0x00000018
    serversbspremium,PRODUCT_SMALLBUSINESS_SERVER_PREMIUM,0x00000019
    homepremiumn,PRODUCT_HOME_PREMIUM_N,0x0000001A
    enterprisen,PRODUCT_ENTERPRISE_N,0x0000001B
    ultimaten,PRODUCT_ULTIMATE_N,0x0000001C
    serverwebcore,PRODUCT_WEB_SERVER_CORE,0x0000001D
    servermediumbusinessmanagement,PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT,0x0000001E
    servermediumbusinesssecurity,PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY,0x0000001F
    servermediumbusinessmessaging,PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING,0x00000020
    serverwinfoundation,PRODUCT_SERVER_FOUNDATION,0x00000021
    serverhomepremium,PRODUCT_HOME_PREMIUM_SERVER,0x00000022
    serverwinsbv,PRODUCT_SERVER_FOR_SMALLBUSINESS_V,0x00000023
    serverstandardv,PRODUCT_STANDARD_SERVER_V,0x00000024
    serverdatacenterv,PRODUCT_DATACENTER_SERVER_V,0x00000025
    serverenterprisev,PRODUCT_ENTERPRISE_SERVER_V,0x00000026
    serverdatacentervcore,PRODUCT_DATACENTER_SERVER_CORE_V,0x00000027
    serverstandardvcore,PRODUCT_STANDARD_SERVER_CORE_V,0x00000028
    serverenterprisevcore,PRODUCT_ENTERPRISE_SERVER_CORE_V,0x00000029
    serverhypercore,PRODUCT_HYPERV,0x0000002A
    serverstorageexpresscore,PRODUCT_STORAGE_EXPRESS_SERVER_CORE,0x0000002B
    serverstoragestandardcore,PRODUCT_STORAGE_STANDARD_SERVER_CORE,0x0000002C
    serverstorageworkgroupcore,PRODUCT_STORAGE_WORKGROUP_SERVER_CORE,0x0000002D
    serverstorageenterprisecore,PRODUCT_STORAGE_ENTERPRISE_SERVER_CORE,0x0000002E
    startern,PRODUCT_STARTER_N,0x0000002F
    professional,PRODUCT_PROFESSIONAL,0x00000030
    professionaln,PRODUCT_PROFESSIONAL_N,0x00000031
    serversolution,PRODUCT_SB_SOLUTION_SERVER,0x00000032
    serverforsbsolutions,PRODUCT_SERVER_FOR_SB_SOLUTIONS,0x00000033
    serversolutionspremium,PRODUCT_STANDARD_SERVER_SOLUTIONS,0x00000034
    serversolutionspremiumcore,PRODUCT_STANDARD_SERVER_SOLUTIONS_CORE,0x00000035
    serversolutionem,PRODUCT_SB_SOLUTION_SERVER_EM,0x00000036
    serverforsbsolutionsem,PRODUCT_SERVER_FOR_SB_SOLUTIONS_EM,0x00000037
    serverembeddedsolution,PRODUCT_SOLUTION_EMBEDDEDSERVER,0x00000038
    serverembeddedsolutioncore,PRODUCT_SOLUTION_EMBEDDEDSERVER_CORE,0x00000039
    professionalembedded,PRODUCT_PROFESSIONAL_EMBEDDED,0x0000003A
    serveressentialmanagement,PRODUCT_ESSENTIALBUSINESS_SERVER_MGMT,0x0000003B
    serveressentialadditional,PRODUCT_ESSENTIALBUSINESS_SERVER_ADDL,0x0000003C
    serveressentialmanagementsvc,PRODUCT_ESSENTIALBUSINESS_SERVER_MGMTSVC,0x0000003D
    serveressentialadditionalsvc,PRODUCT_ESSENTIALBUSINESS_SERVER_ADDLSVC,0x0000003E
    serversbspremiumcore,PRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE,0x0000003F
    serverhpcv,PRODUCT_CLUSTER_SERVER_V,0x00000040
    embedded,PRODUCT_EMBEDDED,0x00000041
    startere,PRODUCT_STARTER_E,0x00000042
    homebasice,PRODUCT_HOME_BASIC_E,0x00000043
    homepremiume,PRODUCT_HOME_PREMIUM_E,0x00000044
    professionale,PRODUCT_PROFESSIONAL_E,0x00000045
    enterprisee,PRODUCT_ENTERPRISE_E,0x00000046
    ultimatee,PRODUCT_ULTIMATE_E,0x00000047
    enterpriseeval,PRODUCT_ENTERPRISE_EVALUATION,0x00000048
    prerelease,PRODUCT_PRERELEASE,0x0000004A
    servermultipointstandard,PRODUCT_MULTIPOINT_STANDARD_SERVER,0x0000004C
    servermultipointpremium,PRODUCT_MULTIPOINT_PREMIUM_SERVER,0x0000004D
    serverstandardeval,PRODUCT_STANDARD_EVALUATION_SERVER,0x0000004F
    serverdatacentereval,PRODUCT_DATACENTER_EVALUATION_SERVER,0x00000050
    prereleasearm,PRODUCT_PRODUCT_PRERELEASE_ARM,0x00000051
    prereleasen,PRODUCT_PRODUCT_PRERELEASE_N,0x00000052
    enterpriseneval,PRODUCT_ENTERPRISE_N_EVALUATION,0x00000054
    embeddedautomotive,PRODUCT_EMBEDDED_AUTOMOTIVE,0x00000055
    embeddedindustrya,PRODUCT_EMBEDDED_INDUSTRY_A,0x00000056
    thinpc,PRODUCT_THINPC,0x00000057
    embeddeda,PRODUCT_EMBEDDED_A,0x00000058
    embeddedindustry,PRODUCT_EMBEDDED_INDUSTRY,0x00000059
    embeddede,PRODUCT_EMBEDDED_E,0x0000005A
    embeddedindustrye,PRODUCT_EMBEDDED_INDUSTRY_E,0x0000005B
    embeddedindustryae,PRODUCT_EMBEDDED_INDUSTRY_A_E,0x0000005C
    professionalplus,PRODUCT_PRODUCT_PROFESSIONAL_PLUS,0x0000005D
    serverstorageworkgroupeval,PRODUCT_STORAGE_WORKGROUP_EVALUATION_SERVER,0x0000005F
    serverstoragestandardeval,PRODUCT_STORAGE_STANDARD_EVALUATION_SERVER,0x00000060
    corearm,PRODUCT_CORE_ARM,0x00000061
    coren,PRODUCT_CORE_N,0x00000062
    corecountryspecific,PRODUCT_CORE_COUNTRYSPECIFIC,0x00000063
    coresinglelanguage,PRODUCT_CORE_SINGLELANGUAGE,0x00000064
    core,PRODUCT_CORE,0x00000065
    professionalwmc,PRODUCT_PROFESSIONAL_WMC,0x00000067
    mobilecore,PRODUCT_MOBILE_CORE,0x00000068
    embeddedindustryeval,PRODUCT_EMBEDDED_INDUSTRY_EVAL,0x00000069
    embeddedindustryeeval,PRODUCT_EMBEDDED_INDUSTRY_E_EVAL,0x0000006A
    embeddedeval,PRODUCT_EMBEDDED_EVAL,0x0000006B
    embeddedeeval,PRODUCT_EMBEDDED_E_EVAL,0x0000006C
    coresystemserver,PRODUCT_NANO_SERVER,0x0000006D
    servercloudstorage,PRODUCT_CLOUD_STORAGE_SERVER,0x0000006E
    coreconnected,PRODUCT_CORE_CONNECTED,0x0000006F
    professionalstudent,PRODUCT_PROFESSIONAL_STUDENT,0x00000070
    coreconnectedn,PRODUCT_CORE_CONNECTED_N,0x00000071
    professionalstudentn,PRODUCT_PROFESSIONAL_STUDENT_N,0x00000072
    coreconnectedsinglelanguage,PRODUCT_CORE_CONNECTED_SINGLELANGUAGE,0x00000073
    coreconnectedcountryspecific,PRODUCT_CORE_CONNECTED_COUNTRYSPECIFIC,0x00000074
    connectedcar,PRODUCT_CONNECTED_CAR,0x00000075
    industryhandheld,PRODUCT_INDUSTRY_HANDHELD,0x00000076
    ppipro,PRODUCT_PPI_PRO,0x00000077
    serverarm64,PRODUCT_ARM64_SERVER,0x00000078
    education,PRODUCT_EDUCATION,0x00000079
    educationn,PRODUCT_EDUCATION_N,0x0000007A
    iotuap,PRODUCT_IOTUAP,0x0000007B
    serverhi,PRODUCT_CLOUD_HOST_INFRASTRUCTURE_SERVER,0x0000007C
    enterprises,PRODUCT_ENTERPRISE_S,0x0000007D
    enterprisesn,PRODUCT_ENTERPRISE_S_N,0x0000007E
    professionals,PRODUCT_PROFESSIONAL_S,0x0000007F
    professionalsn,PRODUCT_PROFESSIONAL_S_N,0x00000080
    enterpriseseval,PRODUCT_ENTERPRISE_S_EVALUATION,0x00000081
    enterprisesneval,PRODUCT_ENTERPRISE_S_N_EVALUATION,0x00000082
    iotuapcommercial,PRODUCT_IOTUAPCOMMERCIAL,0x00000083
    mobileenterprise,PRODUCT_MOBILE_ENTERPRISE,0x00000085
    analogonecore,PRODUCT_HOLOGRAPHIC,0x00000087
    holographic,PRODUCT_HOLOGRAPHIC_BUSINESS,0x00000088
    professionalsinglelanguage,PRODUCT_PRO_SINGLE_LANGUAGE,0x0000008A
    professionalcountryspecific,PRODUCT_PRO_CHINA,0x0000008B
    enterprisesubscription,PRODUCT_ENTERPRISE_SUBSCRIPTION,0x0000008C
    enterprisesubscriptionn,PRODUCT_ENTERPRISE_SUBSCRIPTION_N,0x0000008D
    serverdatacenternano,PRODUCT_DATACENTER_NANO_SERVER,0x0000008F
    serverstandardnano,PRODUCT_STANDARD_NANO_SERVER,0x00000090
    serverdatacenteracor,PRODUCT_DATACENTER_A_SERVER_CORE,0x00000091
    serverstandardacor,PRODUCT_STANDARD_A_SERVER_CORE,0x00000092
    serverdatacentercor,PRODUCT_DATACENTER_WS_SERVER_CORE,0x00000093
    serverstandardcor,PRODUCT_STANDARD_WS_SERVER_CORE,0x00000094
    utilityvm,PRODUCT_UTILITY_VM,0x00000095
    serverdatacenterevalcor,PRODUCT_DATACENTER_EVALUATION_SERVER_CORE,0x0000009F
    serverstandardevalcor,PRODUCT_STANDARD_EVALUATION_SERVER_CORE,0x000000A0
    professionalworkstation,PRODUCT_PRO_WORKSTATION,0x000000A1
    professionalworkstationn,PRODUCT_PRO_WORKSTATION_N,0x000000A2
    serverazure,PRODUCT_AZURE_SERVER,0x000000A3
    professionaleducation,PRODUCT_PRO_FOR_EDUCATION,0x000000A4
    professionaleducationn,PRODUCT_PRO_FOR_EDUCATION_N,0x000000A5
    serverazurecor,PRODUCT_AZURE_SERVER_CORE,0x000000A8
    serverazurenano,PRODUCT_AZURE_NANO_SERVER,0x000000A9
    enterpriseg,PRODUCT_ENTERPRISEG,0x000000AB
    enterprisegn,PRODUCT_ENTERPRISEGN,0x000000AC
    businesssubscription,PRODUCT_BUSINESS,0x000000AD
    businesssubscriptionn,PRODUCT_BUSINESS_N,0x000000AE
    serverrdsh,PRODUCT_SERVERRDSH,0x000000AF
    cloud,PRODUCT_CLOUD,0x000000B2
    cloudn,PRODUCT_CLOUDN,0x000000B3
    hubos,PRODUCT_HUBOS,0x000000B4
    onecoreupdateos,PRODUCT_ONECOREUPDATEOS,0x000000B6
    cloude,PRODUCT_CLOUDE,0x000000B7
    andromeda,PRODUCT_ANDROMEDA,0x000000B8
    iotos,PRODUCT_IOTOS,0x000000B9
    clouden,PRODUCT_CLOUDEN,0x000000BA
    iotedgeos,PRODUCT_IOTEDGEOS,0x000000BB
    iotenterprise,PRODUCT_IOTENTERPRISE,0x000000BC
    modernpc,PRODUCT_LITE,0x000000BD
    iotenterprises,PRODUCT_IOTENTERPRISES,0x000000BF
    systemos,PRODUCT_XBOX_SYSTEMOS,0x000000C0
    nativeos,PRODUCT_XBOX_NATIVEOS,0x000000C1
    gamecorexbox,PRODUCT_XBOX_GAMEOS,0x000000C2
    gameos,PRODUCT_XBOX_ERAOS,0x000000C3
    durangohostos,PRODUCT_XBOX_DURANGOHOSTOS,0x000000C4
    scarletthostos,PRODUCT_XBOX_SCARLETTHOSTOS,0x000000C5
    keystone,PRODUCT_XBOX_KEYSTONE,0x000000C6
    cloudhost,PRODUCT_AZURE_SERVER_CLOUDHOST,0x000000C7
    cloudmos,PRODUCT_AZURE_SERVER_CLOUDMOS,0x000000C8
    cloudcore,PRODUCT_AZURE_SERVER_CLOUDCORE,0x000000C9
    cloudeditionn,PRODUCT_CLOUDEDITIONN,0x000000CA
    cloudedition,PRODUCT_CLOUDEDITION,0x000000CB
    winvos,PRODUCT_VALIDATION,0x000000CC
    iotenterprisesk,PRODUCT_IOTENTERPRISESK,0x000000CD
    iotenterprisek,PRODUCT_IOTENTERPRISEK,0x000000CE
    iotenterpriseseval,PRODUCT_IOTENTERPRISESEVAL,0x000000CF
    agentbridge,PRODUCT_AZURE_SERVER_AGENTBRIDGE,0x000000D0
    nanohost,PRODUCT_AZURE_SERVER_NANOHOST,0x000000D1
    wnc,PRODUCT_WNC,0x000000D2
    serverazurestackhcicor,PRODUCT_AZURESTACKHCI_SERVER_CORE,0x00000196
    serverturbine,PRODUCT_DATACENTER_SERVER_AZURE_EDITION,0x00000197
    serverturbinecor,PRODUCT_DATACENTER_SERVER_CORE_AZURE_EDITION,0x00000198
    '@ | ConvertFrom-Csv
    
    function New-IntPtr {
        param(
            [Parameter(Mandatory=$true)]
            [int]$Size,
    
            [Parameter(Mandatory=$false)]
            [int]$InitialValue = 0,
    
            [switch]$WriteSizeAtZero
        )
    
        # Allocate unmanaged memory
        $ptr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($Size)
    
        # Zero memory
        $Global:type::RtlZeroMemory.Invoke($ptr, [UIntPtr]::new($Size))
    
        # Write size at offset 0 if requested
        if ($WriteSizeAtZero) {
            [Marshal]::WriteInt32($ptr, 0, $Size)
        }
        # Otherwise, if size == 4 and initial value != 0, write initial value
        elseif ($Size -eq 4 -and $InitialValue -ne 0) {
            [Marshal]::WriteInt32($ptr, 0, $InitialValue)
        }
    
        return $ptr
    }
    Function Init-NTDLL {
        $Method = [PSCustomObject]@{
           attributes = [MethodAttributes]::Public -bor [MethodAttributes]::Static -bor [MethodAttributes]::PinvokeImpl
           CallingConventions = [CallingConventions]::Standard
           nativeCallConv = [CallingConvention]::Winapi
           nativeCharSet = [CharSet]::Unicode
           ImplAttributes = [MethodImplAttributes]::PreserveSig
           TypeAttributes = [TypeAttributes]::Public -bor [TypeAttributes]::Abstract -bor [TypeAttributes]::Sealed
        }
    
        # Define dynamic type and methods before main try-finally
        $asmName = New-Object AssemblyName "NativeOSVersionAssembly"
        $asm     = [AppDomain]::CurrentDomain.DefineDynamicAssembly($asmName, [AssemblyBuilderAccess]::Run)
        $mod     = $asm.DefineDynamicModule("NativeOSVersionModule")
        $tb      = $mod.DefineType("NativeMethods", $Method.TypeAttributes)
    
        $NtdllFunctions = @(
            @{ Name = "RtlGetVersion";             Dll = "ntdll.dll"; ReturnType = [int];     Parameters = [IntPtr] },
            @{ Name = "RtlGetCurrentPeb";          Dll = "ntdll.dll"; ReturnType = [IntPtr];  Parameters = @() },
            @{ Name = "RtlGetProductInfo";         Dll = "ntdll.dll"; ReturnType = [bool];    Parameters = [Type[]]@([UInt32], [UInt32], [UInt32], [UInt32], [IntPtr]) },
            @{ Name = "RtlGetNtVersionNumbers";    Dll = "ntdll.dll"; ReturnType = [void];    Parameters = ([IntPtr], [IntPtr], [IntPtr]) },
            @{ Name = "RtlZeroMemory";             Dll = "ntdll.dll"; ReturnType = [void];    Parameters = [Type[]]@([IntPtr], [UIntPtr]) },
            @{ Name = "RtlGetProcessHeaps";        Dll = "ntdll.dll"; ReturnType = [UInt32];  Parameters = [Type[]]@([UInt32], [IntPtr]) },
            @{ Name = "NtQueryInformationProcess"; Dll = "ntdll.dll"; ReturnType = [UInt32];  Parameters = [Type[]]@([IntPtr], [UInt32], [IntPtr], [UInt32], [IntPtr]) },
            @{ Name = "NtQuerySystemInformation";  Dll = "ntdll.dll"; ReturnType = [UInt32];  Parameters = [Type[]]@([UInt32], [IntPtr], [UInt32], [IntPtr]) }
        )
    
        foreach ($func in $NtdllFunctions) {
            $tb.DefinePInvokeMethod($func.Name, $func.Dll,
                $Method.attributes, $Method.CallingConventions,
                $func.ReturnType, $func.Parameters,
                $Method.nativeCallConv, $Method.nativeCharSet
            ).SetImplementationFlags($Method.ImplAttributes)
        }
    
        return $tb.CreateType()
    }
    Function Init-osVersion {
        try {
            # 0x026C, ULONG NtMajorVersion; NT 4.0 and higher
            $NtMajorVersion = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x26C))
    
            # 0x0270, ULONG NtMinorVersion; NT 4.0 and higher
            $NtMinorVersion = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x270))
    
            # 0x0260, ULONG NtBuildNumber; NT 10.0 & higher
            $NtBuildNumber  = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x0260))
    
            if (($NtMajorVersion -lt 10) -or (
                $NtBuildNumber -lt 10240)) {
         
              # this offset for nt 10.0 & Up
              # NT 6.3 end in 9600,
              # nt 10.0 start with 10240 (RTM)
    
              # Before, we stop throw,
              # Try read from PEB memory.
    
              if ([Intptr]::Zero -eq $Global:PebPtr) {
                throw }
    
              $offset = if ([IntPtr]::Size -eq 8) { 0x120 } else { 0x0AC }
              $NtBuildNumber = [int][Marshal]::ReadInt16($Global:PebPtr, $offset)
    
              # 0xAC, 0x0120, USHORT OSBuildNumber; 4.0 and higher
              if ($NtBuildNumber -lt 1381) {
                throw }
            }
    
            # Retrieve the OS version details
            return [PSCustomObject]@{
                Major   = $NtMajorVersion
                Minor   = $NtMinorVersion
                Build   = $NtBuildNumber
                UBR     = $Global:ubr
                Version = ($NtMajorVersion,$NtMinorVersion,$NtBuildNumber)
            }
        }
        catch {}
    
        # Fallback: API
        try {
            $osVersion = Get-NativeOSVersion
            if ($null -eq $Global:osVersion) {
                Throw }
            return $osVersion
        }
        catch {}
           
        # Fallback: REGISTRY
        try {
            $major = (Get-ItemProperty -Path $Global:CurrentVersion -Name CurrentMajorVersionNumber -ErrorAction SilentlyContinue).CurrentMajorVersionNumber
            $minor = (Get-ItemProperty -Path $Global:CurrentVersion -Name CurrentMinorVersionNumber -ErrorAction SilentlyContinue).CurrentMinorVersionNumber
            $build = (Get-ItemProperty -Path $Global:CurrentVersion -Name CurrentBuildNumber -ErrorAction SilentlyContinue).CurrentBuildNumber
            $osVersion = [PSCustomObject]@{
                Major   = [int]$major
                Minor   = [int]$minor
                Build   = [int]$build
                UBR     = $Global:ubr
                Version = @([int]$major, [int]$minor, [int]$build)
            }
            return $osVersion
        }
        catch {}
    
        cls
        Write-Host
        write-host "Failed to retrieve OS version from all methods."
        Write-Host
        read-host
        exit 1
    }
    Function Get-NativeOSVersion {
       
        # Pre-allocate memory
        $majorPtr = New-IntPtr -Size 4 -InitialValue 0
        $minorPtr = New-IntPtr -Size 4 -InitialValue 0
        $buildPtr = New-IntPtr -Size 4 -InitialValue 0
        $versionInfoPtr = New-IntPtr -Size 284 -WriteSizeAtZero
    
        try {
            # First API: RtlGetNtVersionNumbers
            try {
               
                $Global:type::RtlGetNtVersionNumbers($majorPtr, $minorPtr, $buildPtr)
                $major = [Marshal]::ReadInt32($majorPtr)
                $minor = [Marshal]::ReadInt32($minorPtr)
                $build = [Marshal]::ReadInt32($buildPtr) -band 0xFFFF
                if ($major -eq 0 -and $minor -eq 0 -and $build -eq 0) {
                    throw }
    
                return [PSCustomObject]@{
                    Major   = $major
                    Minor   = $minor
                    Build   = $build
                    UBR     = $Global:ubr
                    Version = @($major, $minor, $build)
                }
    
            } catch {}
    
            # Second API: RtlGetVersion
            try {
                $status = $Global:type::RtlGetVersion($versionInfoPtr)
                if ($status -ne 0) { throw }
    
                $major = [Marshal]::ReadInt32($versionInfoPtr, 4)
                $minor = [Marshal]::ReadInt32($versionInfoPtr, 8)
                $build = [Marshal]::ReadInt32($versionInfoPtr, 12)
    
                if ($major -eq 0 -and $minor -eq 0 -and $build -eq 0) { throw }
    
                return [PSCustomObject]@{
                    Major   = $major
                    Minor   = $minor
                    Build   = $build
                    UBR     = $Global:ubr
                    Version = @($major, $minor, $build)
                }
            } catch {}
        }
        finally {
            [Marshal]::FreeHGlobal($majorPtr)
            [Marshal]::FreeHGlobal($minorPtr)
            [Marshal]::FreeHGlobal($buildPtr)
            [Marshal]::FreeHGlobal($versionInfoPtr)
        }
    
        return $null
    }
    function Get-ProductID {
    
        # DigitalProductId4, WCHAR szEditionType[260];
        $DigitalProductId4 = Parse-DigitalProductId4
        if ($DigitalProductId4 -and $DigitalProductId4.EditionType -and
            (-not [String]::IsNullOrWhiteSpace($DigitalProductId4.EditionType))) {
            return $DigitalProductId4.EditionType
        }
    
        # You can also use the OperatingSystemSKU property of the Win32_OperatingSystem WMI class.
        [INT]$productType = 0
        $null = [INT]::TryParse($OperatingSystemInfo.ProductType,[ref]$productType)
        if ($productType -ne 0) {
            # Lookup in the table by converting the hex string to int
            $match = $Global:productTypeTable | Where-Object {
                [Convert]::ToInt32($_.DWORD, 16) -eq $productType
            }
            return $match.ProductID
        }
    
        try {
            $status = $false
            $productTypePtr = New-IntPtr -Size 4 -InitialValue 0
            $status = $Global:type::RtlGetProductInfo(
                $Global:OperatingSystemInfo.dwOSMajorVersion,
                $Global:OperatingSystemInfo.dwOSMinorVersion,
                $Global:OperatingSystemInfo.dwSpMajorVersion,
                $Global:OperatingSystemInfo.dwSpMinorVersion,
                $productTypePtr)
    
            if (-not $status) {
               throw }
    
            [INT]$productType = [Marshal]::ReadInt32($productTypePtr)
            $match = $Global:productTypeTable | Where-Object {
                [Convert]::ToInt32($_.DWORD, 16) -eq $productType
            }
            return $match.ProductID
        }
        catch { }
        Finally {
            # Clean up by freeing the allocated memory
            [Marshal]::FreeHGlobal($productTypePtr)
        }
    
        # Key: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion Propery: EditionID
        $EditionID = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name EditionID -ErrorAction SilentlyContinue).EditionID
        if ($EditionID) {
            return $EditionID }
    
        cls
        Write-Host
        write-host "Failed to Edition ID version from all methods."
        Write-Host
        read-host
        exit 1
    }
    function Get-StringFromBytes {
        param(
            [byte[]]$array,
            [int]$start,
            [int]$length
        )
        if ($start + $length -le $array.Length) {
            return [System.Text.Encoding]::Unicode.GetString($array, $start, $length).TrimEnd([char]0)
        }
        else {
            Write-Warning "Requested string range $start to $($start + $length) exceeds array length $($array.Length)"
            return ""
        }
    }
    function Get-AsciiString {
        param([byte[]]$array, [int]$start, [int]$length)
        if ($start + $length -le $array.Length) {
            return [System.Text.Encoding]::ASCII.GetString($array, $start, $length).TrimEnd([char]0)
        }
        else {
            Write-Warning "Requested ASCII string range $start to $($start + $length) exceeds array length $($array.Length)"
            return ""
        }
    }
    function Get-DigitalProductKey {
        param (
            [Parameter(Mandatory=$true)]
            [byte[]]$bCDKeyArray
        )
    
        # Clone to avoid modifying the original array
        $bCDKey = $bCDKeyArray.Clone()
    
        # Check if it's Windows 8
        $isWin8 = (($bCDKey[14] -shr 3) -band 1)
    
        # Modify byte at index 14 of bCDKey
        $bCDKey[14] = $bCDKey[14] -band 0xF7
    
        # Characters used in Windows key
        $Chars = "BCDFGHJKMPQRTVWXY2346789"
        $KeyOutput = ""
    
        # Standard Base24 decoding from bCDKey
        for ($i = 24; $i -ge 0; $i--) {
            $Cur = 0
            for ($X = 14; $X -ge 0; $X--) {
                $Cur = $Cur * 256
                $Cur += $bCDKey[$X]  # Read directly from bCDKey
                $bCDKey[$X] = [math]::Floor($Cur / 24)
                $Cur = $Cur % 24
            }
            $KeyOutput = $Chars[$Cur] + $KeyOutput
            $Last = $Cur
        }
    
        # If it's Windows 8, put "N" in the right place
        if ($isWin8 -eq 1) {
            $keypart1 = $KeyOutput.Substring(1, $Last)
            $insert = "N"
            $KeyOutput = $keypart1 + $insert + $KeyOutput.Substring($Last + 1)
        }
    
        # Divide keys into 5-character parts
        $a = $KeyOutput.Substring(0, 5)
        $b = $KeyOutput.Substring(5, 5)
        $c = $KeyOutput.Substring(10, 5)
        $d = $KeyOutput.Substring(15, 5)
        $e = $KeyOutput.Substring(20, 5)
    
        # Join them again adding dashes
        return "$a-$b-$c-$d-$e"
    }
    function Get-LatestUBR {
        param (
            [bool]$AutoMode = $false,
            [switch]$UseWinSxS,
            [switch]$UsePackages
        )
    
    # Ensure that either -UseWinSxS or -UsePackages is specified, or AutoMode is enabled
    if (-not $AutoMode -and -not ($UsePackages -or $UseWinSxS)) {
        Write-Error "You must specify either -UseWinSxS or -UsePackages."
        return
    }
    
    if (-not $Global:CurrentVersion) {
        $Global:CurrentVersion = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
    }
    
    $maxUbr = 0
    $FileNames = @()
    
    $winsxsFilter  = '*_10.*_none_*'
    $WinSxSPath    = 'C:\Windows\WinSxS'
    $winsxsRegex   = [regex]'10\.\d+\.\d+\.(\d+)'
    
    $packagefilter = '*~10.*'
    $PackagesPath  = 'C:\Windows\Servicing\Packages'
    $packageRegex  = [regex]'(10|11)\.\d+\.\d+\.(\d+)'
    
    $Global:CurrentVersion = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
    
    function Get-LatestLCUDate {
        try {
            $session = New-Object -ComObject Microsoft.Update.Session
            $searcher = $session.CreateUpdateSearcher()
            $count = $searcher.GetTotalHistoryCount()
            $updates = $searcher.QueryHistory(0, $count)
            $cumulativeUpdates = $updates | Where-Object {
                $_.Title -like '*Cumulative Update*' -and $_.ResultCode -eq 2
            }
            if ($cumulativeUpdates.Count -gt 0) {
                return ($cumulativeUpdates | Sort-Object Date -Descending | Select-Object -First 1).Date.Date
            }
        }
        catch {}
        return $null
    }
    function Get-FilesForLatestMonth {
        param (
            [switch]$UseWinSxS,
            [switch]$UsePackages
        )
    
        if ($UseWinSxS) {
            $PackagesPath = 'C:\Windows\WinSxS'
            $isFolderScan = $true
        }
        elseif ($UsePackages) {
            $PackagesPath = 'C:\Windows\Servicing\Packages'
            $isFolderScan = $false
        }
        else {
            Write-Error "Specify either -UseWinSxS or -UsePackages."
            return
        }
    
        $fileList = @()
        $latestYearMonth = $null
        $targetDate = Get-LatestLCUDate
        $dirInfo = [System.IO.DirectoryInfo] $PackagesPath
    
        if ($isFolderScan) {
           
            foreach ($folder in $dirInfo.EnumerateDirectories($winsxsFilter)) {
                $lastModified = $folder.LastWriteTime
                $ym = $lastModified.ToString('yyyy-MM')
                $ymd = $lastModified.Date
    
                # Prefer exact match on LCU date if available
                if ($targetDate -and $ymd -eq $targetDate) {
                    $fileList += $folder.Name
                }
                elseif (-not $targetDate) {
                    if (-not $latestYearMonth -or $ym -gt $latestYearMonth) {
                        $latestYearMonth = $ym
                        $fileList = @()
                    }
                    if ($ym -eq $latestYearMonth) {
                        $fileList += $folder.Name
                    }
                }
            }
               
        } else {
           
            foreach ($file in $dirInfo.EnumerateFiles($packagefilter)) {
                $lastModified = $file.LastWriteTime
                $ym = $lastModified.ToString('yyyy-MM')
    
                if (-not $latestYearMonth -or $ym -gt $latestYearMonth) {
                    $latestYearMonth = $ym
                    $fileList = @()
                }
                if ($ym -eq $latestYearMonth) {
                    $fileList += $file.Name
                }
            }
               
        }
    
        return $fileList
    }
    function Get-UBRFromFolderName {
        param (
            [Parameter(Mandatory=$true)]
            [string]$folderName
        )
    
        try {
            $folderParts = $folderName.Split('_')
            if ($folderParts.Length -ge 4) {
                $versionPart = $folderParts[3]
                $versionParts = $versionPart.Split('.')
                if ($versionParts.Length -ge 4) {
                    $ubr = $versionParts[3]
                    return [int]$ubr
                }
            }
            return 0
        }
        catch {
            Write-Warning "Error processing folder name: $folderName. Returning 0."
            return 0
        }
    }
    function Get-UBRFromFileName {
        param (
            [Parameter(Mandatory=$true)]
            [string]$fileName
        )
       
        # Extract the part of the file name that contains the version info (before the extension)
        $fileNameWithoutExt = [System.IO.Path]::GetFileNameWithoutExtension($fileName)
    
        # Split by '~' and check if version part exists
        $parts = $fileNameWithoutExt.Split('~')
    
        if ($parts.Length -gt 2) {
            $versionPart = $parts[-1]   # The last part should contain version info like '10.0.19041.5854'
    
            # Split the version part by '.' to get version numbers
            $versionParts = $versionPart.Split('.')
    
            # Check for 4 parts and ensure it starts with "10"
            if ($versionParts.Length -eq 4 -and $versionParts[0] -eq '10') {
                # Extract UBR (fourth part of version)
                return [int]$versionParts[3]
            }
        }
       
        # Return 0 if version doesn't match "10.x.x.x" format
        return 0
    }
    
    # Handle AutoMode
    if ($AutoMode) {
        # If AutoMode is enabled, leave this block empty for now
    }
    elseif ($UsePackages) {
        # Get the files for the latest month from the Packages folder
        $FileNames = Get-FilesForLatestMonth -UsePackages
    }
    elseif ($UseWinSxS) {
        # Get the files for the latest month from the WinSxS folder
        $FileNames = Get-FilesForLatestMonth -UseWinSxS
    }
    
    # Check if any files were found
    if (($UsePackages -or $UseWinSxS) -and $FileNames.Count -eq 0) {
        # No files found, returning 0
        return 0
    }
    
    # Further processing can go here, if required
    
    
    if ($AutoMode) {
    
        # ---------------------
        # >> Auto mode BEGIN <<
        # ---------------------
    
        # 1) Scan Packages folder first
        $dirInfo = [System.IO.DirectoryInfo] $PackagesPath
        try {
            foreach ($file in $dirInfo.EnumerateFiles($packagefilter)) {
                $nameWithoutExt = [System.IO.Path]::GetFileNameWithoutExtension($file.Name)
                $match = $packageRegex.Match($nameWithoutExt)
                #$ubr = Get-UBRFromFileName $file.Name
                if ($match.Success) {
                    $ubr = [int]$match.Groups[2].Value
                    if ($ubr -gt $maxUbr) {
                        $maxUbr = $ubr
                    }
                }
            }
        } catch {
            # Ignore access or IO errors per extension scan
        }
    
        # keep for debug purpose
        # $maxUbr = 0
    
        if ($maxUbr -gt 0) {
            return $maxUbr
        }
    
        # 2) Try update history COM + WinSxS folder scan
        $targetDate = Get-LatestLCUDate
        $dirInfo = [System.IO.DirectoryInfo] $WinSxSPath
        foreach ($folder in $dirInfo.EnumerateDirectories($winsxsFilter)) {
            if (-not $targetDate -or (
                $folder.LastWriteTime.Date -eq $targetDate)) {
                $match = $winsxsRegex.Match($folder.Name)
                #$ubr = Get-UBRFromFolderName -folderName $folder.Name
                if ($match.Success) {
                    $ubr = [int]$match.Groups[1].Value
                    if ($ubr -gt $maxUbr) {
                        $maxUbr = $ubr
                    }
                }
            }
        }
        if ($maxUbr -gt 0) {
            return $maxUbr
        }
    
        # --------------------
        # >> Auto mode END <<
        # --------------------    
       
    } else {
    
        # ---------------------
        # >> Manu mode BEGIN <<
        # ---------------------
    
        foreach ($fileName in $FileNames) {
            if ($UsePackages) {
                $nameWithoutExt = [System.IO.Path]::GetFileNameWithoutExtension($fileName)
                $match = $packageRegex.Match($nameWithoutExt)
                #$ubr = Get-UBRFromFileName $fileName
                if ($match.Success) {
                    $ubr = [int]$match.Groups[2].Value
                    if ($ubr -gt $maxUbr) {
                        $maxUbr = $ubr
                    }
                }
            }
            elseif ($UseWinSxS) {
                $match = $winsxsRegex.Match($fileName)
                #$ubr = Get-UBRFromFolderName -folderName $fileName
                if ($match.Success) {
                    $ubr = [int]$match.Groups[1].Value
                    if ($ubr -gt $maxUbr) {
                        $maxUbr = $ubr
                    }
                }
            }
        }
    
        if ($maxUbr -gt 0) {
            return $maxUbr
        }
    
        # --------------------
        # >> Manu mode END <<
        # --------------------
    }
    
    
    # 3) Fallback: read UBR from registry last (slowest)
    try {
        $regUbr = (Get-ItemProperty -Path $Global:CurrentVersion -Name UBR -ErrorAction Continue).UBR
        if ($regUbr -and ($regUbr -gt $maxUbr)) {
            return $regUbr }
    } catch { }
    
    return 0
    }
    function Parse-DigitalProductId {
        param (
            [string]$RegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion"
        )
    
        try {
            $digitalProductId = (Get-ItemProperty -Path $RegistryPath -ErrorAction Stop).DigitalProductId
        }
        catch {
            Write-Warning "Failed to read DigitalProductId from registry path $RegistryPath"
            return $null
        }
    
        if (-not $digitalProductId) {
            Write-Warning "DigitalProductId property not found in registry."
            return $null
        }
    
        # Ensure byte array
        $byteArray = if ($digitalProductId -is [byte[]]) { $digitalProductId } else { [byte[]]$digitalProductId }
    
        # Define offsets and lengths for each field in one hashtable
        $offsets = @{
            uiSize        = @{ Offset = 0;  Length = 4  }
            MajorVersion  = @{ Offset = 4;  Length = 2  }
            MinorVersion  = @{ Offset = 6;  Length = 2  }
            ProductId     = @{ Offset = 8;  Length = 24 }
            EditionId     = @{ Offset = 36; Length = 16 }
            bCDKey        = @{ Offset = 52; Length = 16 }
        }
    
        # Extract components safely
        $uiSize = [BitConverter]::ToUInt32($byteArray, $offsets.UISize.Offset)
        $productId = Get-AsciiString -array $byteArray -start $offsets.ProductId.Offset -length $offsets.ProductId.Length
        $editionId = Get-AsciiString -array $byteArray -start $offsets.EditionId.Offset -length $offsets.EditionId.Length
    
        # Extract bCDKey array for product key decoding
        $bCDKeyArray = $byteArray[$offsets.bCDKey.Offset..($offsets.bCDKey.Offset + $offsets.bCDKey.Length - 1)]
    
        # Decode Digital Product Key (placeholder function - implement accordingly)
        $digitalProductKey = Get-DigitalProductKey -bCDKeyArray $bCDKeyArray
    
        # Extract MajorVersion and MinorVersion from byte array
        $majorVersion = [BitConverter]::ToUInt16($byteArray, $offsets.MajorVersion.Offset)
        $minorVersion = [BitConverter]::ToUInt16($byteArray, $offsets.MinorVersion.Offset)
    
        # Return structured object
        return [PSCustomObject]@{
            UISize     = $uiSize
            MajorVersion = $majorVersion
            MinorVersion = $minorVersion
            ProductId  = $productId
            EditionId  = $editionId
            DigitalKey = $digitalProductKey
        }
    }
    function Parse-DigitalProductId4 {
        param(
            [string]$RegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion"
        )
    
        # Retrieve DigitalProductId4 from registry
        try {
            $digitalProductId4 = (Get-ItemProperty -Path $RegistryPath -ErrorAction Stop).DigitalProductId4
        }
        catch {
            Write-Warning "Failed to read DigitalProductId4 from registry path $RegistryPath"
            return $null
        }
    
        if (-not $digitalProductId4) {
            Write-Warning "DigitalProductId4 property not found in registry."
            return $null
        }
    
        # Convert directly to byte array if not already one
        $byteArray = if ($digitalProductId4 -is [byte[]]) { $digitalProductId4 } else { [byte[]]$digitalProductId4 }
    
        # Offsets dictionary for structured fields with length included
        $offsets = @{
            uiSize        = @{ Offset = 0;    Length = 4  }
            MajorVersion  = @{ Offset = 4;    Length = 2  }
            MinorVersion  = @{ Offset = 6;    Length = 2  }
            AdvancedPid  = @{ Offset = 8;    Length = 128 }
            ActivationId = @{ Offset = 136;  Length = 128 }
            EditionType  = @{ Offset = 280;  Length = 520 }
            EditionId    = @{ Offset = 888;  Length = 128 }
            KeyType      = @{ Offset = 1016; Length = 128 }
            EULA         = @{ Offset = 1144; Length = 128 }
            bCDKey       = @{ Offset = 808;  Length = 16  }
        }
    
        # Extract values
        $uiSize = if ($byteArray.Length -ge 4) { [BitConverter]::ToUInt32($byteArray, 0) } else { 0 }
    
        $advancedPid = Get-StringFromBytes -array $byteArray -start $offsets.AdvancedPid.Offset -length $offsets.AdvancedPid.Length
        $activationId = Get-StringFromBytes -array $byteArray -start $offsets.ActivationId.Offset -length $offsets.ActivationId.Length
        $editionType = Get-StringFromBytes -array $byteArray -start $offsets.EditionType.Offset -length $offsets.EditionType.Length
        $editionId = Get-StringFromBytes -array $byteArray -start $offsets.EditionId.Offset -length $offsets.EditionId.Length
        $keyType = Get-StringFromBytes -array $byteArray -start $offsets.KeyType.Offset -length $offsets.KeyType.Length
        $eula = Get-StringFromBytes -array $byteArray -start $offsets.EULA.Offset -length $offsets.EULA.Length
    
        # Extract bCDKey array used for key retrieval
        $bCDKeyOffset = $offsets.bCDKey.Offset
        $bCDKeyLength = $offsets.bCDKey.Length
        $bCDKeyArray = $byteArray[$bCDKeyOffset..($bCDKeyOffset + $bCDKeyLength - 1)]
    
        # Extract MajorVersion and MinorVersion from byte array
        $majorVersion = [BitConverter]::ToUInt16($byteArray, $offsets.MajorVersion.Offset)
        $minorVersion = [BitConverter]::ToUInt16($byteArray, $offsets.MinorVersion.Offset)
    
        # Call to external helper to decode the Digital Product Key
        # You need to define this function based on your key decoding logic
        $digitalProductKey = Get-DigitalProductKey -bCDKeyArray $bCDKeyArray
    
        # Return a structured object
        return [PSCustomObject]@{
            UISize       = $uiSize
            MajorVersion = $majorVersion
            MinorVersion = $minorVersion
            AdvancedPID  = $advancedPid
            ActivationID = $activationId
            EditionType  = $editionType
            EditionID    = $editionId
            KeyType      = $keyType
            EULA         = $eula
            DigitalKey   = $digitalProductKey
        }
    }
    
    # INIT NTDLL Function
    $Global:type = Init-NTDLL
    $Global:PebPtr = $Global:type::RtlGetCurrentPeb()
    $Global:CurrentVersion = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
    $Global:OperatingSystem = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue
    $Global:ubr = try { Get-LatestUBR -AutoMode $true } catch { 0 }
    $Global:osVersion = Init-osVersion
    $Global:OperatingSystemInfo = [PSCustomObject]@{
        ProductType = try { $Global:OperatingSystem.OperatingSystemSKU } catch { $null };
        dwOSMajorVersion = $Global:osVersion.Major
        dwOSMinorVersion = $Global:osVersion.Minor
        dwSpMajorVersion = try {$Global:OperatingSystem.ServicePackMajorVersion} catch {0};
        dwSpMinorVersion = try {$Global:OperatingSystem.ServicePackMinorVersion} catch {0};
    }
    
    $Global:osVersion
    Get-NativeOSVersion
    Get-ProductID
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  2. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,462
    6,523
    150
    if we like to make it stylish version.
    PSCustomObject object ? sure.

    Code:
    
    #requires -Version 5.1
    #requires -RunAsAdministrator
    
    using namespace System.Runtime.InteropServices
    
    # $$$$$$$$$$$$$$$$$$$$$$$$ Read ME $$$$$$$$$$$$$$$$$$$$$$$$
    
    # KUSER_SHARED_DATA
    # https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
    
    # KUSER_SHARED_DATA structure (ntddk.h)
    # https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
    
    # The read-only user-mode address for the shared data is 0x7FFE0000, both in 32-bit and 64-bit Windows.
    # The only formal definition among headers in the Windows Driver Kit (WDK) or the Software Development Kit (SDK) is in assembly language headers: KS386.
    # INC from the WDK and KSAMD64.
    # INC from the SDK both define MM_SHARED_USER_DATA_VA for the user-mode address.
    
    # That they also define USER_SHARED_DATA for the kernel-mode address suggests that they too are intended for kernel-mode programming,
    # albeit of a sort that is at least aware of what address works for user-mode access.
    
    # Among relatively large structures,
    # the KUSER_SHARED_DATA is highly unusual for having exactly the same layout in 32-bit and 64-bit Windows.
    # This is because the one instance must be simultaneously accessible by both 32-bit and 64-bit code on 64-bit Windows,
    # and it’s desired that 32-bit user-mode code can run unchanged on both 32-bit and 64-bit Windows.
    
    # 0x026C,ULONG NtMajorVersion,4.0 and higher
    # 0x0270,ULONG NtMinorVersion,4.0 and higher
    # 0x0260,ULONG NtBuildNumber,10.0 and higher
    
    cls
    Write-Host
    
    $Global:osVersion = [PSCustomObject]@{
        Major   = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x26C))
        Minor   = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x270))
        Build   = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x260))
        UBR     = gpv 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name UBR -EA 0
        Version = 0x26C,0x270,0x260 | % { [Marshal]::ReadInt32(0x7FFE0000 + $_) }
    }
    
    "Microsoft Windows"
    "Major Version : $($osVersion.Major)"
    "Minor Version : $($osVersion.Minor)"
    "Build Number  : $($osVersion.Build)"
    "UBR           : $($osVersion.UBR)"
    
    Write-Host
    return
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  3. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,462
    6,523
    150
    #3 Dark Vador, May 26, 2025
    Last edited: May 27, 2025
    (OP)
    Darki have fail safe version,
    using Direct memory, fail,
    registry, fail API, fail .. well

    Code:
    #requires -Version 5.1
    #requires -RunAsAdministrator
    
    # RtlGetNtVersionNumbers Function
    # The RtlGetNtVersionNumbers function gets Windows version numbers directly from NTDLL.
    # https://www.geoffchappell.com/studies/windows/win32/ntdll/api/ldrinit/getntversionnumbers.htm
    
    # RtlGetVersion function (wdm.h)
    # https://learn.microsoft.com/en-us/windows/win32/devnotes/rtlgetversion
    # https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion
    
    # KUSER_SHARED_DATA
    # https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
    
    # KUSER_SHARED_DATA structure (ntddk.h)
    # https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
    
    # The read-only user-mode address for the shared data is 0x7FFE0000, both in 32-bit and 64-bit Windows.
    # The only formal definition among headers in the Windows Driver Kit (WDK) or the Software Development Kit (SDK) is in assembly language headers: KS386.
    # INC from the WDK and KSAMD64.
    # INC from the SDK both define MM_SHARED_USER_DATA_VA for the user-mode address.
    
    # That they also define USER_SHARED_DATA for the kernel-mode address suggests that they too are intended for kernel-mode programming,
    # albeit of a sort that is at least aware of what address works for user-mode access.
    
    # Among relatively large structures,
    # the KUSER_SHARED_DATA is highly unusual for having exactly the same layout in 32-bit and 64-bit Windows.
    # This is because the one instance must be simultaneously accessible by both 32-bit and 64-bit code on 64-bit Windows,
    # and it’s desired that 32-bit user-mode code can run unchanged on both 32-bit and 64-bit Windows.
    
    using namespace System.Reflection
    using namespace System.Reflection.Emit
    using namespace System.Runtime.InteropServices
    
    function Get-NativeOSVersion {
        
        # Pre-allocate memory
        $majorPtr        = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(4)
        $minorPtr        = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(4)
        $buildPtr        = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(4)
        $versionInfoPtr  = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(284)
        [System.Runtime.InteropServices.Marshal]::WriteInt32($versionInfoPtr, 0, 284)
    
        # Define dynamic type and methods before main try-finally
        $asmName = New-Object AssemblyName "NativeOSVersionAssembly"
        $asm     = [AppDomain]::CurrentDomain.DefineDynamicAssembly($asmName, [AssemblyBuilderAccess]::Run::Run)
        $mod     = $asm.DefineDynamicModule("NativeOSVersionModule")
        $tb      = $mod.DefineType("NativeMethods",
            [TypeAttributes]::Public -bor [TypeAttributes]::Abstract -bor [TypeAttributes]::Sealed)
    
        # RtlGetNtVersionNumbers
        $tb.DefinePInvokeMethod("RtlGetNtVersionNumbers", "ntdll.dll",
            [MethodAttributes]::Public -bor [MethodAttributes]::Static -bor [MethodAttributes]::PinvokeImpl,
            [CallingConventions]::Standard, [void], [Type[]]@([IntPtr], [IntPtr], [IntPtr]),
            [Runtime.InteropServices.CallingConvention]::Winapi, [Runtime.InteropServices.CharSet]::Unicode
        ).SetImplementationFlags([MethodImplAttributes]::PreserveSig)
    
        # RtlGetVersion
        $tb.DefinePInvokeMethod("RtlGetVersion", "ntdll.dll",
            [MethodAttributes]::Public -bor [MethodAttributes]::Static -bor [MethodAttributes]::PinvokeImpl,
            [CallingConventions]::Standard, [int], [Type[]]@([IntPtr]),
            [Runtime.InteropServices.CallingConvention]::Winapi, [Runtime.InteropServices.CharSet]::Unicode
        ).SetImplementationFlags([MethodImplAttributes]::PreserveSig)
    
        $type = $tb.CreateType()
    
        try {
            # First API: RtlGetNtVersionNumbers
            try {
                $type::RtlGetNtVersionNumbers($majorPtr, $minorPtr, $buildPtr)
                $major = [System.Runtime.InteropServices.Marshal]::ReadInt32($majorPtr)
                $minor = [System.Runtime.InteropServices.Marshal]::ReadInt32($minorPtr)
                $build = [System.Runtime.InteropServices.Marshal]::ReadInt32($buildPtr) -band 0xFFFF
    
                if ($major -eq 0 -and $minor -eq 0 -and $build -eq 0) { throw }
    
                return [PSCustomObject]@{
                    Major   = $major
                    Minor   = $minor
                    Build   = $build
                    UBR     = $Global:ubr
                    Version = @($major, $minor, $build)
                }
            } catch {}
    
            # Second API: RtlGetVersion
            try {
                $status = $type::RtlGetVersion($versionInfoPtr)
                if ($status -ne 0) { throw }
    
                $major = [System.Runtime.InteropServices.Marshal]::ReadInt32($versionInfoPtr, 4)
                $minor = [System.Runtime.InteropServices.Marshal]::ReadInt32($versionInfoPtr, 8)
                $build = [System.Runtime.InteropServices.Marshal]::ReadInt32($versionInfoPtr, 12)
    
                if ($major -eq 0 -and $minor -eq 0 -and $build -eq 0) { throw }
    
                return [PSCustomObject]@{
                    Major   = $major
                    Minor   = $minor
                    Build   = $build
                    UBR     = $Global:ubr
                    Version = @($major, $minor, $build)
                }
            } catch {}
        }
        finally {
            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($majorPtr)
            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($minorPtr)
            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($buildPtr)
            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($versionInfoPtr)
        }
    
        return $null
    }
    $Global:CurrentVersion = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
    $Global:ubr = try { Get-ItemPropertyValue $Global:CurrentVersion -Name UBR -ea 1 } catch { 0 }
    
    try {
        $Global:osVersion = [PSCustomObject]@{
            Major   = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x26C))
            Minor   = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x270))
            Build   = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x260))
            UBR     = $Global:ubr
            Version = 0x26C,0x270,0x260 | ForEach-Object { [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + $_)) }
        }
    }
    catch {
        
        # Fallback: API
        try {
            $Global:osVersion = Get-NativeOSVersion
            if ($null -eq $Global:osVersion) {
              Throw }
        }
        catch {
            
            # Fallback: REGISTRY
            try {
                $major = Get-ItemPropertyValue $Global:CurrentVersion -Name CurrentMajorVersionNumber -ea 1
                $minor = Get-ItemPropertyValue $Global:CurrentVersion -Name CurrentMinorVersionNumber -ea 1
                $build = Get-ItemPropertyValue $Global:CurrentVersion -Name CurrentBuildNumber -ea 1
    
                $Global:osVersion = [PSCustomObject]@{
                    Major   = [int]$major
                    Minor   = [int]$minor
                    Build   = [int]$build
                    UBR     = $Global:ubr
                    Version = @([int]$major, [int]$minor, [int]$build)
                }
            }
            catch {
                Write-Warning "Failed to retrieve OS version from all methods."
                $Global:osVersion = [PSCustomObject]@{
                    Major = 0
                    Minor = 0
                    Build = 0
                    UBR = 0
                    Version = @(0,0,0)
                }     
            }
        }
    }
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  4. abbodi1406

    abbodi1406 MDL KB0000001

    Feb 19, 2011
    17,547
    93,122
    340
    Why restrict to admin PS 5.1 though? :)
    it works fine in PS 2.0 (changing syntax) and/or non-admin

    you might consider using RtlGetNtVersionNumbers instead RtlGetVersion
     
  5. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,462
    6,523
    150
    Might be interesting
    Will sit in it tomorrow
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  6. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,462
    6,523
    150
    #6 Dark Vador, May 26, 2025
    Last edited: May 27, 2025
    (OP)
    Updated code.
    include abbodi1406 suggestion
    Also lower version to ps 3.0 & Up
    And direct memory access, check for wrong value (nt10 only)
    Also, using both ntdll api. so get build info
    Also, improve [int] check, to get right value (less tha nt10)
    Also, fix Get-NativeOSVersion, fill any intptr with Zero's,
    so, we can validate against real value

    Code:
    # RtlGetNtVersionNumbers Function
    # The RtlGetNtVersionNumbers function gets Windows version numbers directly from NTDLL.
    # https://www.geoffchappell.com/studies/windows/win32/ntdll/api/ldrinit/getntversionnumbers.htm
    
    # RtlGetVersion function (wdm.h)
    # https://learn.microsoft.com/en-us/windows/win32/devnotes/rtlgetversion
    # https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion
    
    # KUSER_SHARED_DATA
    # https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
    
    # KUSER_SHARED_DATA structure (ntddk.h)
    # https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
    
    # The read-only user-mode address for the shared data is 0x7FFE0000, both in 32-bit and 64-bit Windows.
    # The only formal definition among headers in the Windows Driver Kit (WDK) or the Software Development Kit (SDK) is in assembly language headers: KS386.
    # INC from the WDK and KSAMD64.
    # INC from the SDK both define MM_SHARED_USER_DATA_VA for the user-mode address.
    
    # That they also define USER_SHARED_DATA for the kernel-mode address suggests that they too are intended for kernel-mode programming,
    # albeit of a sort that is at least aware of what address works for user-mode access.
    
    # Among relatively large structures,
    # the KUSER_SHARED_DATA is highly unusual for having exactly the same layout in 32-bit and 64-bit Windows.
    # This is because the one instance must be simultaneously accessible by both 32-bit and 64-bit code on 64-bit Windows,
    # and it's desired that 32-bit user-mode code can run unchanged on both 32-bit and 64-bit Windows.
    
    using namespace System.IO
    using namespace System.Net
    using namespace System.Text
    using namespace System.Threading
    using namespace System.Reflection
    using namespace System.IO.Compression
    using namespace System.ServiceProcess
    using namespace System.Reflection.Emit
    using namespace System.Security.Principal
    using namespace System.Text.RegularExpressions
    using namespace System.Runtime.InteropServices
    
    # Check if $PSVersionTable or $PSVersionTable.PSVersion is null, or Major version is invalid
    if ($null -eq $PSVersionTable -or $null -eq $PSVersionTable.PSVersion -or $null -eq $PSVersionTable.PSVersion.Major) {
        cls
        Write-Host
        Write-Host "Unable to determine PowerShell version." -ForegroundColor Green
        Write-Host "This script requires PowerShell 3.0 or higher." -ForegroundColor Green
        Start-Sleep 3
        return
    }
    
    # Check if PowerShell version is lower than 3.0
    if ($PSVersionTable.PSVersion.Major -lt 3) {
        cls
        Write-Host
        Write-Host "This script requires PowerShell 3.0 or higher!" -ForegroundColor Green
        Write-Host "Windows 8 & Above are supported." -ForegroundColor Green
        Start-Sleep 3
        return
    }
    
    # Check if the current user is System or an Administrator
    $IsSystem = [WindowsIdentity]::GetCurrent().IsSystem
    $principal = [WindowsPrincipal]::new([WindowsIdentity]::GetCurrent())
    
    # If the user is neither System nor an Administrator, show a warning and stop the script
    if (!$IsSystem -and -not $principal.IsInRole([WindowsBuiltInRole]::Administrator)) {
        cls
        Write-Host
        Write-Host "This script must be run as Administrator or System!" -ForegroundColor Green
        Write-Host "Please run this script as Administrator." -ForegroundColor Green
        Write-Host "(Right-click and select 'Run as Administrator')" -ForegroundColor Green
        Write-Host
        Read-Host "Press Enter to exit..."
        return
    }
    
    function Get-NativeOSVersion {
        
        # Pre-allocate memory
        $majorPtr        = [Marshal]::AllocHGlobal(4)
        $minorPtr        = [Marshal]::AllocHGlobal(4)
        $buildPtr        = [Marshal]::AllocHGlobal(4)
        
        # Initialize the allocated memory with zeros (4 bytes for each DWORD)
        [Marshal]::WriteInt32($majorPtr, 0, 0)
        [Marshal]::WriteInt32($minorPtr, 0, 0)
        [Marshal]::WriteInt32($buildPtr, 0, 0)
    
        # Allocate memory for the OSVERSIONINFO structure (284 bytes)
        $versionInfoPtr = [Marshal]::AllocHGlobal(284)
    
        # Zero out the allocated memory to avoid garbage data
        [Marshal]::Copy([byte[]]@(0) * 284, 0, $versionInfoPtr, 284)
    
        # Write the size of the structure (284 bytes) to the beginning of the allocated memory
        [Marshal]::WriteInt32($versionInfoPtr, 0, 284)
    
        $Method = [PSCustomObject]@{
           attributes = [MethodAttributes]::Public -bor [MethodAttributes]::Static -bor [MethodAttributes]::PinvokeImpl
           CallingConventions = [CallingConventions]::Standard
           nativeCallConv = [CallingConvention]::Winapi
           nativeCharSet = [CharSet]::Unicode
           ImplAttributes = [MethodImplAttributes]::PreserveSig
           TypeAttributes = [TypeAttributes]::Public -bor [TypeAttributes]::Abstract -bor [TypeAttributes]::Sealed
        }
    
        # Define dynamic type and methods before main try-finally
        $asmName = New-Object AssemblyName "NativeOSVersionAssembly"
        $asm     = [AppDomain]::CurrentDomain.DefineDynamicAssembly($asmName, [AssemblyBuilderAccess]::Run::Run)
        $mod     = $asm.DefineDynamicModule("NativeOSVersionModule")
        $tb      = $mod.DefineType("NativeMethods", $Method.TypeAttributes)
    
        $api_builder = @(
            ("RtlGetVersion","ntdll.dll",[int], [IntPtr]),
            ("RtlGetNtVersionNumbers","ntdll.dll",[void], ([IntPtr], [IntPtr], [IntPtr])))
    
        ForEach ($API in $api_builder) {
            $tb.DefinePInvokeMethod($API[0], $API[1],
                $Method.attributes, $Method.CallingConventions,
                $API[2], $API[3], $Method.nativeCallConv, $Method.nativeCharSet
            ).SetImplementationFlags($Method.ImplAttributes)
        }
    
        $type = $tb.CreateType()
    
        try {
            # First API: RtlGetNtVersionNumbers
            try {
                $type::RtlGetNtVersionNumbers($majorPtr, $minorPtr, $buildPtr)
                $major = [Marshal]::ReadInt32($majorPtr)
                $minor = [Marshal]::ReadInt32($minorPtr)
                $build = [Marshal]::ReadInt32($buildPtr) -band 0xFFFF
    
                if ($major -eq 0 -and $minor -eq 0 -and $build -eq 0) { throw }
    
                return [PSCustomObject]@{
                    Major   = $major
                    Minor   = $minor
                    Build   = $build
                    UBR     = $Global:ubr
                    Version = @($major, $minor, $build)
                }
            } catch {}
    
            # Second API: RtlGetVersion
            try {
                $status = $type::RtlGetVersion($versionInfoPtr)
                if ($status -ne 0) { throw }
    
                $major = [Marshal]::ReadInt32($versionInfoPtr, 4)
                $minor = [Marshal]::ReadInt32($versionInfoPtr, 8)
                $build = [Marshal]::ReadInt32($versionInfoPtr, 12)
    
                if ($major -eq 0 -and $minor -eq 0 -and $build -eq 0) { throw }
    
                return [PSCustomObject]@{
                    Major   = $major
                    Minor   = $minor
                    Build   = $build
                    UBR     = $Global:ubr
                    Version = @($major, $minor, $build)
                }
            } catch {}
        }
        finally {
            [Marshal]::FreeHGlobal($majorPtr)
            [Marshal]::FreeHGlobal($minorPtr)
            [Marshal]::FreeHGlobal($buildPtr)
            [Marshal]::FreeHGlobal($versionInfoPtr)
        }
    
        return $null
    }
    $Global:CurrentVersion = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
    $Global:ubr = try { (Get-ItemProperty -Path $Global:CurrentVersion -Name UBR -ErrorAction SilentlyContinue).UBR } catch { 0 }
    
    try {
        # Initialize build version to $null
        $buildVersion = $null
        $parseSuccess = $false
    
        # ULONG NtBuildNumber; 10.0 and higher
        # Attempt to read the build version from system memory
        $buildPointer = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x260))
        $parseSuccess = [int]::TryParse($buildPointer, [ref]$buildVersion)
    
        # Check if parsing the build version failed
        if (-not $parseSuccess -or $buildVersion -eq $null) {
            throw "Failed to retrieve the build version from system memory."
        }
    
        # Ensure the build version is greater than or equal to Windows 10 (build 10240 or higher)
        if ($buildVersion -lt 10240) {
            throw "The operating system version is below Windows 10 (build 10240), which is not supported."
        }
    
        # Store base address for future use
        $baseAddress = 0x7FFE0000
    
        # Retrieve the OS version details
        $Global:osVersion = [PSCustomObject]@{
            Major   = [Marshal]::ReadInt32([IntPtr]($baseAddress + 0x26C))
            Minor   = [Marshal]::ReadInt32([IntPtr]($baseAddress + 0x270))
            Build   = $buildVersion
            UBR     = $Global:ubr
            Version = (0x26C, 0x270, 0x260) | ForEach-Object {
                [Marshal]::ReadInt32([IntPtr]($baseAddress + $_))
            }
        }
    }
    catch {
    
        # Fallback: API
        try {
            $Global:osVersion = Get-NativeOSVersion
            if ($null -eq $Global:osVersion) {
              Throw }
        }
        catch {
            
            # Fallback: REGISTRY
            try {
                $major = (Get-ItemProperty -Path $Global:CurrentVersion -Name CurrentMajorVersionNumber -ErrorAction SilentlyContinue).CurrentMajorVersionNumber
                $minor = (Get-ItemProperty -Path $Global:CurrentVersion -Name CurrentMinorVersionNumber -ErrorAction SilentlyContinue).CurrentMinorVersionNumber
                $build = (Get-ItemProperty -Path $Global:CurrentVersion -Name CurrentBuildNumber -ErrorAction SilentlyContinue).CurrentBuildNumber
    
    
                $Global:osVersion = [PSCustomObject]@{
                    Major   = [int]$major
                    Minor   = [int]$minor
                    Build   = [int]$build
                    UBR     = $Global:ubr
                    Version = @([int]$major, [int]$minor, [int]$build)
                }
            }
            catch {
                cls
                Write-Host
                write-host "Failed to retrieve OS version from all methods."
                Write-Host
                Start-Sleep 4
                return
            }
        }
    }
    
    $Global:osVersion
    Get-NativeOSVersion
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  7. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,462
    6,523
    150
    #7 Dark Vador, May 27, 2025
    Last edited: May 28, 2025
    (OP)
    Just figured out something
    If on case of not nt10.0, offset is reserved
    But we can read major value (nt4.0 & up)
    So major value less than 10, Don't read build offset
    B'Cause major is NT version!!
    So simple and so stupid at the same time :p

    anyway, fix main thread.

    Code:
    try {
        # 0x026C, ULONG NtMajorVersion; NT 4.0 and higher
        $NtMajorVersion = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x26C))
    
        # 0x0270, ULONG NtMinorVersion; NT 4.0 and higher  
        $NtMinorVersion = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x270))
    
        # 0x0260, ULONG NtBuildNumber; NT 10.0 & higher
        $NtBuildNumber = [int]0
        $BuildNumber   = [Marshal]::ReadInt32([IntPtr](0x7FFE0000 + 0x0260))
        [int]::TryParse($BuildNumber, [ref] $NtBuildNumber)
       
        if (($NtMajorVersion -lt 10) -or (
            $NtBuildNumber -lt 10240)) {
    
          # this offset for nt 10.0 & Up
          # NT 6.3 end in 9600,
          # nt 10.0 start with 10240 (RTM)
          throw
        }
    
        # Retrieve the OS version details
        $Global:osVersion = [PSCustomObject]@{
            Major   = $NtMajorVersion
            Minor   = $NtMinorVersion
            Build   = $NtBuildNumber
            UBR     = $Global:ubr
            Version = ($NtMajorVersion,$NtMinorVersion,$NtBuildNumber)
        }
    }
    catch {
    
        # Fallback: API
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  8. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,462
    6,523
    150
    it's confirmed
    under nt10.0, like w7,w8,w8.1, offset return value 0
    in that case, we use API, but for most system ->
    this offset will return build number.
    and it is probably the location, where the api take it info
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  9. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,462
    6,523
    150
    30/05/2025 ~ Changes
    Re-Build from scrath
    add new fail safe way, that work even on nt 4.0
    still read from memory, just from other memory [peb]
    nice catch from abbodi1406
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  10. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,462
    6,523
    150
    update the script,
    now with creative way to fetch ube value,
    very intersting way.
    i didn't see anyone think of it yet
    it take about 0.09 m's to get it -
    not from registry
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  11. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,462
    6,523
    150
    new way to get UBR, no enum 999999 File's
    target spefic simple regex --> *-edition*10.*.*.*
    in Manifests & Packages, Packages its better contail less files' (2K)
    compare to Manifests ---> like 20K files ?????
    i will give credit on first idea to abbodi1406, result is,
    enum on 20-30 file's only b'cause EnumerateFiles-> filter =>>
    low level ignore 9999999 file's

    what i get in return is this small list
    Code:
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-Package~31bf3856ad364e35~amd64~en-US~10.0.19041.5678.cat
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-Package~31bf3856ad364e35~amd64~en-US~10.0.19041.5678.mum
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-Package~31bf3856ad364e35~amd64~~10.0.19041.5854.cat
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-Package~31bf3856ad364e35~amd64~~10.0.19041.5854.mum
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-Removable-Package~31bf3856ad364e35~amd64~en-US~10.0.19041.3636.cat
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-Removable-Package~31bf3856ad364e35~amd64~en-US~10.0.19041.3636.mum
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-Removable-Package~31bf3856ad364e35~amd64~~10.0.19041.5794.cat
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-Removable-Package~31bf3856ad364e35~amd64~~10.0.19041.5794.mum
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-Removable-WOW64-Package~31bf3856ad364e35~amd64~en-US~10.0.19041.3636.cat
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-Removable-WOW64-Package~31bf3856ad364e35~amd64~en-US~10.0.19041.3636.mum
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-Removable-WOW64-Package~31bf3856ad364e35~amd64~~10.0.19041.5794.cat
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-Removable-WOW64-Package~31bf3856ad364e35~amd64~~10.0.19041.5794.mum
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-WOW64-Package~31bf3856ad364e35~amd64~en-US~10.0.19041.3636.cat
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-WOW64-Package~31bf3856ad364e35~amd64~en-US~10.0.19041.3636.mum
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-WOW64-Package~31bf3856ad364e35~amd64~~10.0.19041.5848.cat
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionPack-Professional-WOW64-Package~31bf3856ad364e35~amd64~~10.0.19041.5848.mum
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-Editions-EnterpriseSN-Package~31bf3856ad364e35~amd64~en-US~10.0.19041.5854.cat
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-Editions-EnterpriseSN-Package~31bf3856ad364e35~amd64~en-US~10.0.19041.5854.mum
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-Editions-EnterpriseSN-Package~31bf3856ad364e35~amd64~~10.0.19041.5854.cat
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-Editions-EnterpriseSN-Package~31bf3856ad364e35~amd64~~10.0.19041.5854.mum
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionSpecific-EnterpriseSN-Package~31bf3856ad364e35~amd64~en-US~10.0.19041.5678.cat
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionSpecific-EnterpriseSN-Package~31bf3856ad364e35~amd64~en-US~10.0.19041.5678.mum
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionSpecific-EnterpriseSN-Package~31bf3856ad364e35~amd64~~10.0.19041.5854.cat
    WARNING: C:\Windows\servicing\Packages\Microsoft-Windows-EditionSpecific-EnterpriseSN-Package~31bf3856ad364e35~amd64~~10.0.19041.5854.mum
    WARNING: C:\Windows\WinSxS\Manifests\amd64_microsoft-windows-edition-transmogrifier_31bf3856ad364e35_10.0.19041.5678_none_8a2c4126f07f6b63.manifest
    WARNING: C:\Windows\WinSxS\Manifests\amd64_microsoft-windows-editions-enterprisesn_31bf3856ad364e35_10.0.19041.5854_none_a714aa12e924ac74.manifest
    WARNING: C:\Windows\WinSxS\Manifests\wow64_microsoft-windows-edition-transmogrifier_31bf3856ad364e35_10.0.19041.5678_none_9480eb7924e02d5e.manifest
    WARNING: UBR VALUE: 5854
    WARNING: Get-LatestUBR completed in 32 ms
    5854
    
    and now our function return result in 10 m's. crazy.

    Code:
    function Get-LatestUBR {
        $UBR = 0
        $wildcardPattern = '*-edition*10.*.*.*'
        $regexVersion = [regex]'10\.0\.\d+\.(\d+)'
        $Manifestsfolder = 'C:\Windows\WinSxS\Manifests'
        $Packagessfolder = 'C:\Windows\servicing\Packages'
        $sw = [System.Diagnostics.Stopwatch]::StartNew()
    
        try {
            $files = [System.IO.Directory]::EnumerateFiles(
                $Packagessfolder, $wildcardPattern, [System.IO.SearchOption]::TopDirectoryOnly)
            foreach ($file in $files) {
                Write-Warning $file
                $match = $regexVersion.Match($file)
                if ($match.Success) {
                    $candidateUBR = [int]$match.Groups[1].Value
                    if ($candidateUBR -gt $UBR) {
                        $UBR = $candidateUBR
                    }
                }
            }
        }
        catch {
            Write-Warning "Failed to enumerate files"
        }
    
        # debug purpose only
        # $UBR = 0
    
        try {
            if ($UBR -eq 0) {
                $files = [System.IO.Directory]::EnumerateFiles(
                    $Manifestsfolder, $wildcardPattern, [System.IO.SearchOption]::TopDirectoryOnly)
                foreach ($file in $files) {
                    Write-Warning $file
                    $match = $regexVersion.Match($file)
                    if ($match.Success) {
                        $candidateUBR = [int]$match.Groups[1].Value
                        if ($candidateUBR -gt $UBR) {
                            $UBR = $candidateUBR
                        }
                    }
                }
            }
        }
        catch {
            Write-Warning "Failed to enumerate files"
        }
    
        if ($UBR -eq 0) {
            # Fallback to registry read
            try {
                $regPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
                $UBR = Get-ItemPropertyValue -Path $regPath -Name UBR -ErrorAction Stop
                Write-Warning "Fallback: Got UBR from registry: $UBR"
            }
            catch {
            }
        }
    
        $sw.Stop()
        Write-Warning "UBR VALUE: ${UBR}"
        Write-Warning "Get-LatestUBR completed in $($sw.ElapsedMilliseconds) ms"
    
        return $UBR
    }
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...