Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Issue] - Health Checker do not show windows full version #2257

Open
iserrano76 opened this issue Dec 12, 2024 · 2 comments
Open

[Issue] - Health Checker do not show windows full version #2257

iserrano76 opened this issue Dec 12, 2024 · 2 comments

Comments

@iserrano76
Copy link
Contributor

Provide Version Number
Any version

Describe the issue
Health checker only show Windows Server version version but not the minor version
Image
you could have an old version, for example widows server 2022 RTM (from 2021) , old version before Exchange CU12 start to support Windows 2022.
This version do not work correctly with Exchange.
Because of that I think we need to show some kind of warning when you are running an old Windows version and recommend to update your windows server if it is not update in the last one or two years.

Expected behavior
show detailed windows version and show a warning if it is old.

@dpaulson45
Copy link
Member

https://smsagent.blog/2017/05/18/find-the-full-windows-build-number-with-powershell/

We can at least provide this build number. Would be nice to also get a time if possible.

@iserrano76
Copy link
Contributor Author

The best approach to check the version and the date should be to create a table with all windows versions that we have but could be a pain to maintain it.
https://support.microsoft.com/en-us/topic/windows-server-2022-update-history-e1caa597-00c5-4ab9-9f3e-8212fe80b2ee

Additionally we need a table for each version of windows server that is supported.

i was think in an alternative method.
Maybe we can just verify the signature of one of the most updated files and if the file was signed more than 2 years ago show a warning that the windows version looks old and you need to verify if you have updates.

Something like:

function Test-FileSignature {
   param (
       [string]$FilePath
   )

   Add-Type -TypeDefinition @"
   using System;
   using System.Runtime.InteropServices;

   [StructLayout(LayoutKind.Sequential)]
   public struct FILETIME {
       public uint dwLowDateTime;
       public uint dwHighDateTime;
   }

   [StructLayout(LayoutKind.Sequential)]
   public struct SYSTEMTIME {
       public ushort wYear;
       public ushort wMonth;
       public ushort wDayOfWeek;
       public ushort wDay;
       public ushort wHour;
       public ushort wMinute;
       public ushort wSecond;
       public ushort wMilliseconds;
   }

   public class Win32 {
       [DllImport("kernel32.dll", SetLastError = true)]
       public static extern bool FileTimeToSystemTime(ref FILETIME fileTime, out SYSTEMTIME systemTime);
   }

   public class WinTrust {
       [DllImport("wintrust.dll", CharSet = CharSet.Unicode, SetLastError = true)]
       public static extern int WinVerifyTrust(IntPtr hwnd, ref Guid pgActionID, IntPtr pWVTData);

       [DllImport("wintrust.dll", CharSet = CharSet.Unicode, SetLastError = true)]
       public static extern IntPtr WTHelperProvDataFromStateData(IntPtr hWVTStateData);

       [DllImport("wintrust.dll", CharSet = CharSet.Unicode, SetLastError = true)]
       public static extern IntPtr WTHelperGetProvSignerFromChain(IntPtr pProvData, uint idxSigner, bool fCounterSigner, uint idxCounterSigner);

       public const int ERROR_SUCCESS = 0;
       public const int WTD_UI_NONE = 2;
       public const int WTD_REVOKE_NONE = 0;
       public const int WTD_CHOICE_FILE = 1;
       public const int WTD_STATEACTION_VERIFY = 1;
       public const int WTD_STATEACTION_CLOSE = 2;

       [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
       public struct WINTRUST_FILE_INFO {
           public uint cbStruct;
           public string pcwszFilePath;
           public IntPtr hFile;
           public IntPtr pgKnownSubject;
       }

       [StructLayout(LayoutKind.Sequential)]
       public struct WINTRUST_DATA {
           public uint cbStruct;
           public IntPtr pPolicyCallbackData;
           public IntPtr pSIPClientData;
           public uint dwUIChoice;
           public uint fdwRevocationChecks;
           public uint dwUnionChoice;
           public IntPtr pFile;
           public uint dwStateAction;
           public IntPtr hWVTStateData;
           public IntPtr pwszURLReference;
           public uint dwProvFlags;
           public uint dwUIContext;
       }

       [StructLayout(LayoutKind.Sequential)]
       public struct CRYPT_PROVIDER_SGNR {
           public uint cbStruct;
           public FILETIME sftVerifyAsOf;
           public uint csCertChain;
           public IntPtr pasCertChain;
           public uint dwSignerType;
           public IntPtr psSigner;
           public uint dwError;
           public uint csCounterSigners;
           public IntPtr pasCounterSigners;
           public IntPtr pChainContext;
       }
   }
"@

   # Initialize variables
   $WINTRUST_ACTION_GENERIC_VERIFY_V2 = [Guid]"00AAC56B-CD44-11d0-8CC2-00C04FC295EE"
   $fileInfo = New-Object WinTrust+WINTRUST_FILE_INFO
   $fileInfo.cbStruct = [System.Runtime.InteropServices.Marshal]::SizeOf($fileInfo)
   $fileInfo.pcwszFilePath = $FilePath

   $winTrustData = New-Object WinTrust+WINTRUST_DATA
   $winTrustData.cbStruct = [System.Runtime.InteropServices.Marshal]::SizeOf($winTrustData)
   $winTrustData.dwUIChoice = [WinTrust]::WTD_UI_NONE
   $winTrustData.fdwRevocationChecks = [WinTrust]::WTD_REVOKE_NONE
   $winTrustData.dwUnionChoice = [WinTrust]::WTD_CHOICE_FILE
   $winTrustData.dwStateAction = [WinTrust]::WTD_STATEACTION_VERIFY

   try {
       # Allocate and marshal memory
       $pFilePointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($fileInfo))
       [System.Runtime.InteropServices.Marshal]::StructureToPtr($fileInfo, $pFilePointer, $false)
       $winTrustData.pFile = $pFilePointer

       $pWinTrustDataPointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($winTrustData))
       [System.Runtime.InteropServices.Marshal]::StructureToPtr($winTrustData, $pWinTrustDataPointer, $false)

       # Call WinVerifyTrust
       $status = [WinTrust]::WinVerifyTrust([IntPtr]::Zero, [ref]$WINTRUST_ACTION_GENERIC_VERIFY_V2, $pWinTrustDataPointer)

       if ($status -eq [WinTrust]::ERROR_SUCCESS) {
           Write-Output "File signature is valid."

           # Retrieve provider data
           $winTrustData = [System.Runtime.InteropServices.Marshal]::PtrToStructure($pWinTrustDataPointer, [type]$winTrustData.GetType())
           $pProvData = [WinTrust]::WTHelperProvDataFromStateData($winTrustData.hWVTStateData)

           if ($pProvData -ne [IntPtr]::Zero) {
               # Retrieve signer information
               $pSigner = [WinTrust]::WTHelperGetProvSignerFromChain($pProvData, 0, $false, 0)
               if ($pSigner -ne [IntPtr]::Zero) {
                   Write-Host "Signer information retrieved."
                   $signer = New-Object WinTrust+CRYPT_PROVIDER_SGNR
                   $signer = [System.Runtime.InteropServices.Marshal]::PtrToStructure($pSigner, [type]$signer.GetType())

                   # Convert FILETIME to SYSTEMTIME
                   $st = New-Object SYSTEMTIME
                   $result = [Win32]::FileTimeToSystemTime([ref]$signer.sftVerifyAsOf, [ref]$st)
                   if ($result) {
                       $signDate = Get-Date -Year $st.wYear -Month $st.wMonth -Day $st.wDay -Hour $st.wHour -Minute $st.wMinute -Second $st.wSecond -Millisecond $st.wMilliseconds
                       Write-Host "Signing Time: $($signDate)"

                       # Date verification
                       if ($signDate -lt (Get-Date).AddYears(-2)) {
                           Write-Host "The file was signed more than 2 years ago. Please update."
                       } elseif ($signDate -gt (Get-Date).AddDays(-1)) {
                           Write-Host "Unexpected recent signature date."
                       } else {
                           Write-Host "The file was signed less than 2 years ago and appears current."
                       }
                   } else {
                       Write-Host "Could not convert signing time."
                   }
               } else {
                   Write-Host "No signer information available."
               }
           }
       } else {
           Write-Host "File signature verification failed. Status Code: $status"
       }
   } catch {
       Write-Error "An error occurred during verification: $_"
   } finally {
       # Free allocated memory
       if ($pFilePointer) {
           [System.Runtime.InteropServices.Marshal]::DestroyStructure($pFilePointer, [WinTrust+WINTRUST_FILE_INFO])
           [System.Runtime.InteropServices.Marshal]::FreeHGlobal($pFilePointer)
       }
       if ($pWinTrustDataPointer) {
           [System.Runtime.InteropServices.Marshal]::DestroyStructure($pWinTrustDataPointer, [WinTrust+WINTRUST_DATA])
           [System.Runtime.InteropServices.Marshal]::FreeHGlobal($pWinTrustDataPointer)
       }
   }
}

$FilePath = "C:\Windows\System32\ntdll.dll"
Test-FileSignature -FilePath $FilePath

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants