pkeyconfig Info Reader +Plus+ [v16.6]

Discussion in 'MDL Projects and Applications' started by Dark Vador, Apr 22, 2024.

  1. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,593
    6,777
    150
    #101 Dark Vador, May 30, 2025
    Last edited: May 30, 2025
    (OP)
    Code:
    # ***************************** #
    #  v12.6.0 compiled at 30-05-25 #
    # ***************************** #
    
    # Add hwid + kms38 Support
    # Rebuild way of fetch system data
    # add few `using namespace`, and remove repeated code
    # Update\fix, function: Parse-DigitalProductId,Parse-DigitalProductId4,Get-ProductID
    
    {Major.Minor} Version -> new
    AdvancedPID -> fixed
    some offset was mis calculated (wchar x2)
    also, remove some un-know offset, re-built again all offset + length
    also, improve Get-ProductID function code

    upload_2025-5-30_11-21-58.png
     
    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,593
    6,777
    150
    #102 Dark Vador, May 31, 2025
    Last edited: May 31, 2025
    (OP)
    Code:
    # ***************************** #
    #  v12.8.0 compiled at 31-05-25 #
    # ***************************** #
    
    # Add hwid + kms38 Support
    # Rebuild way of fetch system data
    # Add few `using namespace`, and remove repeated code
    # Update\fix, function: Parse-DigitalProductId,Parse-DigitalProductId4,Get-ProductID
    # New Function added, Get-LatestUBR [v2]
    
    Add new way to get ubr value, via creative way, not registry
    B`cause no api was found, so. found a creative way.

    Update it again,
    it seem that regex is faster than manual extract ubr
    40'ms, like half of a 1's. not seem much, but older weak system ? ...
    i made test script. check what is faster.
    Code:
    Regex Mode
    
    Auto Mode --> UseWinSxS
    Ubr value: 5854
    Time elapsed: 00:00:00.051
    Auto Mode --> UseWinSxS
    
    Manual Mode --> UseWinSxS
    Ubr value: 5854
    Time elapsed: 00:00:00.082
    Manual Mode --> UseWinSxS
    
    Auto Mode --> UsePackages
    Ubr value: 5854
    Time elapsed: 00:00:00.071
    Auto Mode --> UsePackages
    
    Manual Mode --> UsePackages
    Ubr value: 5854
    Time elapsed: 00:00:00.077
    Manual Mode --> UsePackages
    
    Function Mode
    
    Auto Mode --> UseWinSxS
    Ubr value: 5854
    Time elapsed: 00:00:00.094
    Auto Mode --> UseWinSxS
    
    Manual Mode --> UseWinSxS
    Ubr value: 5854
    Time elapsed: 00:00:00.100
    Manual Mode --> UseWinSxS
    
    Auto Mode --> UsePackages
    Ubr value: 5854
    Time elapsed: 00:00:00.102
    Auto Mode --> UsePackages
    
    Manual Mode --> UsePackages
    Ubr value: 5854
    Time elapsed: 00:00:00.088
    Manual Mode --> UsePackages
    
    Get-LatestUBR Function [v2]
    Code:
    function Get-LatestUBR {
        param (
            [bool]$AutoMode = $false,
            [switch]$UseWinSxS,
            [switch]$UsePackages
        )
    
    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-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'
    
            # Directly 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
    }
    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('*_10.*_none_*')) {
                $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('*~10.*')) {
                $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
    }
    
    # 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'
    
    # 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
    }
    
     
    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,593
    6,777
    150
    #103 Dark Vador, Jun 3, 2025
    Last edited: Jun 3, 2025
    (OP)
    v13.4.0 [compiled at 03-06-25]
    Code:
    # Add hwid + kms38 Support
    # Rebuild way of fetch system data
    # Add few `using namespace`, and remove repeated code
    # Update\fix, function: Parse-DigitalProductId,Parse-DigitalProductId4,Get-ProductID
    # Rebild Get-LatestUBR from scratch, avoid enum 99999999 trillion files
    # Add ZeroCid replacement method for insider build etc etc
    # Add alternative method to get key if GetRandom Function fail
    # Fix bug in productID function
    # Add Insider Check, and case of ZeroCid + Insider, well, Warning messege
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  4. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,593
    6,777
    150
    #104 Dark Vador, Jun 7, 2025
    Last edited: Jun 7, 2025
    (OP)
    Code:
    # ***************************** #
    #  v13.5.0 compiled at 08-06-25 #
    # ***************************** #
    
    * Add hwid + kms38 Support
    * Rebuild way of fetch system data
    * Add few `using namespace`, and remove repeated code
    * Update\fix, function: Parse-DigitalProductId,Parse-DigitalProductId4,Get-ProductID
    * Rebild Get-LatestUBR from scratch, avoid enum 99999999 trillion files
    * Add ZeroCid replacement method for insider build etc etc
    * Add alternative method to get key if GetRandom Function fail
    * Fix bug in productID function
    * Add Insider Check, and case of ZeroCid + Insider, well, Warning messege
    * Add support to remove **specific license, or install specifc license
      using low level api from hell
    upload_2025-6-7_21-34-51.png

    upload_2025-6-7_21-35-27.png

    upload_2025-6-7_21-35-37.png
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  5. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,593
    6,777
    150
    #105 Dark Vador, Jun 9, 2025
    Last edited: Jun 9, 2025
    (OP)
    v13.6.0 [compiled at 09-06-25]
    Code:
    * Add hwid + kms38 Support
    * Rebuild way of fetch system data
    * Add few `using namespace`, and remove repeated code
    * Update\fix, function: Parse-DigitalProductId,Parse-DigitalProductId4,Get-ProductID
    * Rebild Get-LatestUBR from scratch, avoid enum 99999999 trillion files
    * Add ZeroCid replacement method for insider build etc etc
    * Add alternative method to get key if GetRandom Function fail
    * Fix bug in productID function
    * Add Insider Check, and case of ZeroCid + Insider, well, Warning messege
    * Add support to remove **specific license, or install specifc license
      using low level api from hell
    * Api & Function Update, to make it work better
    
    so main change it,
    replace all DLL calls from slc.dll to spcc.dll {who doe's the actualy job !}
    and update Retrieve-SKUInfo function
    which is importand helper to deal with SKU -> Any. [specific needed guid]
    was re desgined, add few fail safe method , add try-catch
    Code:
    Main Function, GetSLIDList. {don't support SL_ID_LICENSE_FILE for specific Sku)}
    Backup Function, SLGetProductSkuInformation with some un-document Parameters
    some, From License data blob, some from service himself (un-document) like fileId, pkeyId
    
    "fileId",                                   # SL_ID_LICENSE_FILE
    "pkeyId",                                   # SL_ID_PKEY
    "productSkuId",                             # SL_ID_PRODUCT_SKU
    "licenseId", "privateCertificateId"         # SL_ID_LICENSE
    "applicationId",                            # SL_ID_APPLICATION
    
    and another backup {of backup} function, SLGetInstalledProductKeyIds,
    to get SL_ID_PKEY only
    Code:
    <#
    .SYNOPSIS
    Function Retrieve-SKUInfo retrieves related licensing IDs for a given SKU GUID.
    
    Specific SKUs require particular IDs:
    - The SKU for SLUninstallLicense requires the ID_LICENSE_FILE GUID.
    - The SKU for SLUninstallProofOfPurchase requires the ID_PKEY GUID.
    
    Optional Pointer: Handle to the Software Licensing Service (SLC).
    Optional eReturnIdType: Type of ID to return (e.g., SL_ID_APPLICATION, SL_ID_PKEY, etc.).
    #>
    function Retrieve-SKUInfo {
        param(
            [Parameter(Mandatory = $true)]
            [ValidatePattern('^[{]?[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}[}]?$')]
            [string]$SkuId,
    
            [Parameter(Mandatory = $false)]
            [ValidateSet("SL_ID_APPLICATION", "SL_ID_PRODUCT_SKU", "SL_ID_LICENSE", "SL_ID_PKEY", "SL_ID_ALL_LICENSES", "SL_ID_ALL_LICENSE_FILES", "SL_ID_LICENSE_FILE")]
            [string]$eReturnIdType,
    
            [Parameter(Mandatory=$false)]
            [Intptr]$hSLC = [IntPtr]::Zero
        )
    
        function Get-IDs {
            param (
                [string]$returnType,
                [Intptr]$hSLC
            )
            try {
                return Get-SLIDList -eQueryIdType SL_ID_PRODUCT_SKU -eReturnIdType $returnType -pQueryId $SkuId -hSLC $hSLC
            } catch {
                Write-Warning "Get-SLIDList -eQueryIdType SL_ID_PRODUCT_SKU -eReturnIdType $returnType -pQueryId $SkuId -hSLC $hSLC"
                return $null
            }
        }
    
        $product = [Guid]$SkuId
    
        if (-not $hSLC -or $hSLC -eq [IntPtr]::Zero -or $hSLC -eq 0) {
            $hSLC = if ($global:hSLC_ -and $global:hSLC_ -ne [IntPtr]::Zero -and $global:hSLC_ -ne 0) {
                $global:hSLC_
            } else {
                Manage-SLHandle
            }
        }
    
        try {
            $closeHandle = $true
            if (-not $hSLC -or $hSLC -eq [IntPtr]::Zero -or $hSLC -eq 0) {
                $hr = $Global:SLC::SLOpen([ref]$hSLC)
                if ($hr -ne 0) {
                    throw "SLOpen failed: HRESULT 0x{0:X8}" -f $hr
                }
            } else {
                $closeHandle = $false
            }
        }
        catch {
            return $null
        }
    
        try {
            # [SL_ID_LICENSE_FILE] Case
            [Guid]$fileId = try {
                [Guid]::Parse((Get-LicenseDetails -ActConfigId $product -pwszValueName fileId -hSLC $hSLC).Trim().Substring(0,36))
            }
            catch {
                [GUID]::Empty
            }
    
            # [SL_ID_LICENSE] Case **Alternative**
            [Guid]$licenseId = try {
                [Guid]::Parse((Get-LicenseDetails -ActConfigId $product -pwszValueName licenseId -hSLC $hSLC).Trim().Substring(0,36))
            } catch {
                [Guid]::Empty
            }
    
            [Guid]$privateCertificateId = try {
                [Guid]::Parse((Get-LicenseDetails -ActConfigId $product -pwszValueName privateCertificateId -hSLC $hSLC).Trim().Substring(0,36))
            } catch {
                [Guid]::Empty
            }
    
            # [SL_ID_APPLICATION] Case **Alternative**
            [Guid]$applicationId = try {
                [Guid]::Parse((Get-LicenseDetails -ActConfigId $product -pwszValueName applicationId -hSLC $hSLC).Trim().Substring(0,36))
            } catch {
                [Guid]::Empty
            }
    
            # [SL_ID_PKEY] Case **Alternative**
            [Guid]$pkId = try {
                [Guid]::Parse((Get-LicenseDetails -ActConfigId $product -pwszValueName pkeyId -hSLC $hSLC).Trim().Substring(0,36))
            } catch {
                [Guid]::Empty
            }
    
            [uint32]$countRef = 0
            [IntPtr]$ppKeyIds = [intPtr]::Zero
            [GUID]$Product_SKU_ID = [GUID]::Empty
            [uint32]$hresults = $Global:SLC::SLGetInstalledProductKeyIds($hSLC, [ref]$product, [ref]$countRef, [ref]$ppKeyIds)
            if ($hresults -and ($hresults -eq 0)) {
                if ($countRef -gt 0 -and (
                    $ppKeyIds -ne [IntPtr]::Zero)) {
                        if ($ppKeyIds.ToInt64() -gt 0) {
                            try {
                                $buffer = New-Object byte[] 16
                                [Marshal]::Copy($ppKeyIds, $buffer, 0, 16)
                                $Product_SKU_ID = [Guid]::new($buffer)
                            }
                            catch {
                                $Product_SKU_ID
                            }
                    }
                }
            }
    
            # -------------------------------------------------
    
            if (-not $eReturnIdType) {
                $SKU_DATA = [pscustomobject]@{
                    ID_SKU          = $SkuId
                    ID_APPLICATION  = if ($applicationId -and $applicationId -ne [Guid]::Empty) { $applicationId } else { try { Get-IDs SL_ID_APPLICATION -hSLC $hSLC } catch { [Guid]::Empty } }
                    ID_PKEY         = if ($pkId -and $pkId -ne [Guid]::Empty) { $pkId } elseif ($Product_SKU_ID -and $Product_SKU_ID -ne [Guid]::Empty) { $Product_SKU_ID } else { try { Get-IDs SL_ID_PKEY -hSLC $hSLC } catch { [Guid]::Empty } }
                    ID_LICENSE_FILE = if ($fileId -and $fileId -ne [Guid]::Empty) { $fileId } else { try { Get-IDs SL_ID_LICENSE_FILE -hSLC $hSLC } catch { [Guid]::Empty } }
                    ID_LICENSE      = if (($licenseId -and $privateCertificateId) -and ($licenseId -ne [Guid]::Empty -and $privateCertificateId -ne [Guid]::Empty)) { @($licenseId, $privateCertificateId) } else { try { Get-IDs SL_ID_LICENSE -hSLC $hSLC } catch { [Guid]::Empty } }
                }
                return $SKU_DATA
            }
    
            switch ($eReturnIdType) {
                "SL_ID_APPLICATION" {
                    if ($applicationId -and $applicationId -ne [Guid]::Empty) {
                        return $applicationId
                    }
                    try { return Get-IDs SL_ID_APPLICATION -hSLC $hSLC } catch {}
                    return [Guid]::Empty
                }
    
                "SL_ID_PRODUCT_SKU" {
                    return $SkuId
                }
    
                "SL_ID_LICENSE" {
                    if ($licenseId -and $privateCertificateId -and $licenseId -ne [Guid]::Empty -and $privateCertificateId -ne [Guid]::Empty) {
                        return @($licenseId, $privateCertificateId)
                    }
                    try { return Get-IDs SL_ID_LICENSE -hSLC $hSLC } catch {}
                    return [Guid]::Empty
                }
    
                "SL_ID_PKEY" {
                    if ($pkId -and $pkId -ne [Guid]::Empty) {
                        return $pkId
                    }
                    if ($Product_SKU_ID -and $Product_SKU_ID -ne [Guid]::Empty) {
                        return $Product_SKU_ID
                    }
                    try { return Get-IDs SL_ID_PKEY -hSLC $hSLC } catch {}
                    return [Guid]::Empty
                }
    
                "SL_ID_ALL_LICENSES" {
                    try { return Get-IDs SL_ID_ALL_LICENSES -hSLC $hSLC } catch {}
                    return [Guid]::Empty
                }
    
                "SL_ID_ALL_LICENSE_FILES" {
                    try { return Get-IDs SL_ID_ALL_LICENSE_FILES -hSLC $hSLC } catch {}
                    return [Guid]::Empty
                }
    
                "SL_ID_LICENSE_FILE" {
                    if ($fileId -and $fileId -ne [Guid]::Empty) {
                        return $fileId
                    }
                    #try { return Get-IDs SL_ID_LICENSE_FILE -hSLC $hSLC } catch {}
                    return [Guid]::Empty
                }
                default {
                    return [Guid]::Empty
                }
            }
        }
        finally {
    
            if ($null -ne $ppKeyIds -and (
                $ppKeyIds -ne [IntPtr]::Zero) -and (
                    $ppKeyIds -ne 0)) {
                        $null = $Global:kernel32::LocalFree($ppKeyIds)
            }
    
            if ($null -ne $hSLC -and (
                $hSLC -ne [IntPtr]::Zero) -and (
                    $hSLC -ne 0) -and (
                        $closeHandle)) {
                            Write-Warning "Consider Open handle Using Manage-SLHandle"
                            $null = $Global:SLC::SLClose($hSLC)
            }
        }
    }
     
    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,593
    6,777
    150
    #106 Dark Vador, Jun 9, 2025
    Last edited: Jun 9, 2025
    (OP)
    New version
    [per user request]

    Code:
    # ***************************** #
    #  v14.0.1 compiled at 09-06-25 #
    # ***************************** #
    <#
    * license remove failure [fixed]
    * Add Option for custom Specifc activation key
    * fix another bug, early return, cause results to fail
    #>
    
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    as per request, will be added soon.
    only if key match SKU ! Else, try use Genuine one, Or generate One.
    how it done, decode Key using KeyInfo Libary -> extract Pkeyconfig -> find a match for SKU-REF pair
    match, ok, not match, use genuine or random. so, i build new function, for that.
    Sku -> Ref, Ref -> Sku, CD-KEY -> Both :)

    new Function.
    Code:
    <#
    .SYNOPSIS
    Function Retrieve-SKUInfo retrieves related licensing IDs for a given SKU GUID.
    Convert option, CD-KEY->Ref/ID, Ref->SKU, SKU->Ref
    #>
    function Retrieve-ProductKeyInfo {
        param (
            [ValidateScript({ $_ -ne $null -and $_ -ne [guid]::Empty })]
            [guid]$SkuId,
    
            [ValidateScript({ $_ -ne $null -and $_ -gt 0 })]
            [int]$RefGroupId,
    
            [ValidateScript({ $_ -match '^(?i)[A-Z0-9]{5}(-[A-Z0-9]{5}){4}$' })]
            [string]$CdKey
        )
    
        function Get-PKeyEntry {
            param (
                [guid]$FilterSkuId,
                [int32]$FilterRefGroupId
            )
    
            $paths = @(
                "C:\Windows\System32\spp\tokens\pkeyconfig\pkeyconfig.xrm-ms",
                "C:\Windows\System32\spp\tokens\pkeyconfig\pkeyconfig-csvlk.xrm-ms",
                "C:\Windows\System32\spp\tokens\pkeyconfig\pkeyconfig-downlevel.xrm-ms",,
                "C:\Program Files\Microsoft Office\root\Licenses16\pkeyconfig-office.xrm-ms"
            )
    
            foreach ($path in $paths) {
                if (Test-Path -Path $path) {
                    $entries = Extract-Base64Xml -FilePath $path
                    foreach ($entry in $entries) {
                        if ($FilterSkuId -and $entry.ActConfigId -eq "{$FilterSkuId}") {
                            return $entry
                        }
                        elseif ($FilterRefGroupId -and $entry.RefGroupId -eq $FilterRefGroupId) {
                            return $entry
                        }
                    }
                }
            }
    
            return $null
        }
    
        # Validate only one parameter
        $paramsProvided = @($SkuId, $RefGroupId, $CdKey) | Where-Object { $_ }
        if ($paramsProvided.Count -ne 1) {
            Write-Warning "Please specify exactly one of -SkuId, -RefGroupId, or -CdKey"
            return $null
        }
    
        # SkuId to RefGroupId
        if ($SkuId) {
            $entry = Get-PKeyEntry -FilterSkuId $SkuId
            if ($entry) {
                return $entry.RefGroupId
            } else {
                Write-Warning "RefGroupId not found for SkuId: $SkuId"
                return $null
            }
        }
    
        # RefGroupId to SkuId
        elseif ($RefGroupId) {
            $entry = Get-PKeyEntry -FilterRefGroupId $RefGroupId
            if ($entry) {
                return [guid]($entry.ActConfigId -replace '[{}]', '')
            } else {
                Write-Warning "ActConfigId not found for RefGroupId: $RefGroupId"
                return $null
            }
        }
    
        # CdKey to RefGroupId to SkuId
        elseif ($CdKey) {
            try {
                $decoded = KeyDecode -key0 $CdKey.Substring(0,29)
                $refGroupFromKey = [int]$decoded[2].Value
    
                $entry = Get-PKeyEntry -FilterRefGroupId $refGroupFromKey
                if ($entry) {
                    return [PSCustomObject]@{
                        RefGroupId = $refGroupFromKey
                        SkuId      = [guid]($entry.ActConfigId -replace '[{}]', '')
                    }
                } else {
                    Write-Warning "SKU not found for RefGroupId $refGroupFromKey extracted from CD Key"
                    return $null
                }
            } catch {
                Write-Warning "Failed to decode CD Key: $_"
                return $null
            }
        }
    }
    
    upload_2025-6-9_19-16-17.png

    upload_2025-6-9_19-13-42.png

    upload_2025-6-9_19-15-59.png
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  7. Low Level Perform

    Low Level Perform MDL Member

    Jul 21, 2024
    100
    95
    10
    #107 Low Level Perform, Jun 9, 2025
    Last edited: Jun 9, 2025
    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,593
    6,777
    150
    #108 Dark Vador, Jun 9, 2025
    Last edited: Jun 9, 2025
    (OP)
    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,593
    6,777
    150
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  10. Low Level Perform

    Low Level Perform MDL Member

    Jul 21, 2024
    100
    95
    10
    You've fixed it, thanks
     
    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,593
    6,777
    150
    #111 Dark Vador, Jun 10, 2025
    Last edited: Jun 11, 2025
    (OP)
    Code:
    # ***************************** #
    #  v14.2.0 compiled at 11-06-25 #
    # ***************************** #
    
    <#
    * license remove failure [fixed]
    * Add Option for custom Specifc activation key
    * Update Get-LicenseDetails Function
    * build main enum parser function,
      to mange enum results
    * Improve slc libary, to re-use logic if possible
      reduce loading times of remove windows form
    * Close Handle Before & after, Add -or Remove license
    #>
    
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Code:
    # ***************************** #
    #  v14.1.0 compiled at 10-06-25 #
    # ***************************** #
    <#
    * license remove failure [fixed]
    * Add Option for custom Specifc activation key
    * Update Get-LicenseDetails Function
    * build main enum parser function,
      to mange enum results
    #>
    
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    OK .. let's see how this goe's
    Edit. yep, Solved my problem.
    include in latest version
    Code:
    typedef enum _tagSLDATATYPE {
    
    SL_DATA_NONE = REG_NONE,      // 0
    SL_DATA_SZ = REG_SZ,          // 1
    SL_DATA_DWORD = REG_DWORD,    // 4
    SL_DATA_BINARY = REG_BINARY,  // 3
    SL_DATA_MULTI_SZ,             // 7
    SL_DATA_SUM = 100             // 100
    
    } SLDATATYPE;
    
    #define REG_NONE        0    /* no type */
    #define REG_SZ            1    /* string type (ASCII) */
    #define REG_EXPAND_SZ    2    /* string, includes %ENVVAR% (expanded by caller) (ASCII) */
    #define REG_BINARY        3    /* binary format, callerspecific */
    #define REG_DWORD        4    /* DWORD in little endian format */
    #define REG_DWORD_LITTLE_ENDIAN    4    /* DWORD in little endian format */
    #define REG_DWORD_BIG_ENDIAN    5    /* DWORD in big endian format  */
    #define REG_LINK        6    /* symbolic link (UNICODE) */
    #define REG_MULTI_SZ    7    /* multiple strings, delimited by \0, terminated by \0\0 (ASCII) */
    #define REG_RESOURCE_LIST    8    /* resource list? huh? */
    #define REG_FULL_RESOURCE_DESCRIPTOR    9    /* full resource descriptor? huh? */
    #define REG_RESOURCE_REQUIREMENTS_LIST    10
    #define REG_QWORD        11    /* QWORD in little endian format */
    #>
    
    # Define the enum-like values in PowerShell
    $SLDATATYPE = @{
        SL_DATA_NONE       = 0   # REG_NONE
        SL_DATA_SZ         = 1   # REG_SZ
        SL_DATA_DWORD      = 4   # REG_DWORD
        SL_DATA_BINARY     = 3   # REG_BINARY
        SL_DATA_MULTI_SZ   = 7   # REG_MULTI_SZ
        SL_DATA_SUM        = 100 # Custom value
    }
    function Parse-RegistryData {
        param (
            [Parameter(Mandatory=$true)]
            [int]$dataType,   # Data type (e.g., $SLDATATYPE.SL_DATA_NONE, $SLDATATYPE.SL_DATA_SZ, etc.)
    
            [Parameter(Mandatory=$true)]
            [IntPtr]$ptr,     # Pointer to the data (e.g., registry value pointer)
    
            [Parameter(Mandatory=$true)]
            [int]$valueSize,  # Size of the data (in bytes)
    
            [Parameter(Mandatory=$false)]
            [string]$valueName # Optional, for special cases (e.g., ProductSkuId)
        )
    
        $result = $null  # Default result to null
    
        switch ($dataType) {
            $SLDATATYPE.SL_DATA_NONE {
                $result = $null
            }
    
            $SLDATATYPE.SL_DATA_SZ { # SL_DATA_SZ = Unicode string
                # PtrToStringUni expects length in characters, valueSize is in bytes, so divide by 2
                $result = [Marshal]::PtrToStringUni($ptr, $valueSize / 2)
            }
    
            $SLDATATYPE.SL_DATA_DWORD { # SL_DATA_DWORD = DWORD (4 bytes)
                if ($valueSize -ne 4) {
                    $result = $null  # Unexpected size, return null
                }
                else {
                    $result = [Marshal]::ReadInt32($ptr)
                }
            }
    
            $SLDATATYPE.SL_DATA_BINARY { # SL_DATA_BINARY = Binary blob
                if ($valueName -eq 'ProductSkuId' -and $valueSize -eq 16) {
                    # If it's ProductSkuId and the buffer is 16 bytes, treat it as a GUID
                    $bytes = New-Object byte[] 16
                    [Marshal]::Copy($ptr, $bytes, 0, 16)
                    $result = [Guid]::new($bytes)
                }
                else {
                    # Otherwise, just copy the binary data
                    $result = New-Object byte[] $valueSize
                    [Marshal]::Copy($ptr, $result, 0, $valueSize)
                }
            }
    
            $SLDATATYPE.SL_DATA_MULTI_SZ { # SL_DATA_MULTI_SZ = Multi-string
                $raw = [Marshal]::PtrToStringUni($ptr, $valueSize / 2)
                $result = $raw -split "`0" | Where-Object { $_ -ne '' }
            }
    
            $SLDATATYPE.SL_DATA_SUM { # SL_DATA_SUM = Custom (100)
                # Handle this case accordingly (based on your logic)
                $result = $null
            }
    
            default {
                # Return null for any unsupported data types
                $result = $null
            }
        }
    
        return $result
    }
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  12. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,593
    6,777
    150
    #112 Dark Vador, Jun 11, 2025
    Last edited: Jun 11, 2025
    (OP)
    uplaod new version.
    remove all wmi calls's, using low level api
    rebuild activation form, kms38 was fixed (bug)
    fetch table information become very fast.
    activation too

    Code:
    # ***************************** #
    #  v14.5.0 compiled at 12-06-25 #
    # ***************************** #
    
    <#
    * license remove failure [fixed]
    * Add Option for custom Specifc activation key
    * Update Get-LicenseDetails Function
    * build main enum parser function,
      to mange enum results
    * Improve slc libary, to re-use logic if possible
      reduce loading times of remove windows form
    * Close Handle Before & after, Add -or Remove license
    * Rebuild HWID + KMS38, using low level API, ONLY
      enum, activation, etc, etc, now very fast.
    * Fix problem when pointer was not initilized
    #>
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  13. pm67310

    pm67310 MDL Guru

    Sep 6, 2011
    3,625
    2,862
    120
    Tested custom key not working with this version and output status are broken
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  14. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,593
    6,777
    150
    #114 Dark Vador, Jun 12, 2025
    Last edited: Jun 12, 2025
    (OP)
    I will check it today
    Did you check that key fir to sku ?

    Information will be fixed next time
    This Information was cause of failure last time
    It trip and cause catch *

    Also found out that
    Use genuine keys was cause of failure
    They did fit the sku

    There is the console window l
    Where you can see latest action

    Edit.
    found the problem with user key.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  15. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,593
    6,777
    150
    #115 Dark Vador, Jun 12, 2025
    Last edited: Jun 12, 2025
    (OP)

    Attached Files:

    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  16. pm67310

    pm67310 MDL Guru

    Sep 6, 2011
    3,625
    2,862
    120
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  17. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,593
    6,777
    150
    New version.
    Code:
    # ***************************** #
    #  v15.0.0 compiled at 14-06-25 #
    # ***************************** #
    
    * Core libraries have been upgraded to improve performance, compatibility, and security.
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  18. haber123

    haber123 MDL Member

    Nov 5, 2009
    108
    44
    10
    Thank you. I very much like this, and how rapidly its evolving.
     
  19. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,593
    6,777
    150
    #119 Dark Vador, Jun 15, 2025
    Last edited: Jun 15, 2025
    (OP)
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  20. abbodi1406

    abbodi1406 MDL KB0000001

    Feb 19, 2011
    17,587
    93,415
    340
    :rolleyes:
    Code:
    for /L %# in (0,1,1000) do @(>>keys.txt KeyInfo64 139b %# 0)
    findstr /b "NBBBB" keys.txt >>BBBs.txt