Using WinVerifyTrust API to verify the embedded signature of a file or fall back to catalog signature verification

 

Writing today to discuss an interesting issue that our Microsoft developer support received recently on usage of the WinVerifyTrust API. The support case was opened to report a memory leak was being observed when calling the WinVerifyTrust API. Specifically, the leak was observed when doing verification of signatures of executable code using the WinVerifyTrust API with the dwStateAction flags set to WTD_STATEACTION_VERIFY. From debugging and reviewing the code further, a call was missing to free the memory allocated by the WinVerifyTrust call. Specifically, another call to WinVerifyTrust needs to be made after the allocation with setting the dwStateAction flag to WTD_STATEACTION_CLOSE.

 

Below is a code snippet that was posted from the community over on the sysinternals site that shows how to call the API and free the memory accordingly.

 

#include "windows.h"

#include <Softpub.h>

#include <wincrypt.h>

#include <wintrust.h>

#include <mscat.h>

#include <wchar.h>

#pragma comment(lib, "wintrust")

 

BOOLEAN IsFileDigitallySigned(PWCHAR FilePath)

{

       //Author: AD, 2009

       PVOID Context;

       HANDLE FileHandle;

       DWORD HashSize = 0;

       PBYTE Buffer;

       PVOID CatalogContext;

       CATALOG_INFO InfoStruct;

       WINTRUST_DATA WintrustStructure;

       WINTRUST_CATALOG_INFO WintrustCatalogStructure;

       WINTRUST_FILE_INFO WintrustFileStructure;

       PWCHAR MemberTag;

       BOOLEAN ReturnFlag = FALSE;

       ULONG ReturnVal;

       GUID ActionGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;

 

       //Zero our structures.

       memset(&InfoStruct, 0, sizeof(CATALOG_INFO));

       InfoStruct.cbStruct = sizeof(CATALOG_INFO);

       memset(&WintrustCatalogStructure, 0, sizeof(WINTRUST_CATALOG_INFO));

       WintrustCatalogStructure.cbStruct = sizeof(WINTRUST_CATALOG_INFO);

       memset(&WintrustFileStructure, 0, sizeof(WINTRUST_FILE_INFO));

       WintrustFileStructure.cbStruct = sizeof(WINTRUST_FILE_INFO);

 

       //Get a context for signature verification.

       if (!CryptCATAdminAcquireContext(&Context, NULL, 0))

       {

              return FALSE;

       }

 

       //Open file.

       FileHandle = CreateFileW(FilePath, GENERIC_READ, 7, NULL, OPEN_EXISTING, 0, NULL);

       if (INVALID_HANDLE_VALUE == FileHandle)

       {

              CryptCATAdminReleaseContext(Context, 0);

              return FALSE;

       }

 

       //Get the size we need for our hash.

       CryptCATAdminCalcHashFromFileHandle(FileHandle, &HashSize, NULL, 0);

       if (HashSize == 0)

       {

              //0-sized has means error!

              CryptCATAdminReleaseContext(Context, 0);

              CloseHandle(FileHandle); return FALSE;

       }

 

       //Allocate memory.

 

       Buffer = (PBYTE)calloc(HashSize, 1);

 

       //Actually calculate the hash

       if (!CryptCATAdminCalcHashFromFileHandle(FileHandle, &HashSize, Buffer, 0))

       {

              CryptCATAdminReleaseContext(Context, 0);

              free(Buffer); CloseHandle(FileHandle);

              return FALSE;

       }

 

       //Convert the hash to a string.

       MemberTag = (PWCHAR)calloc((HashSize * 2) + 1, sizeof(WCHAR));

       for (unsigned int i = 0; i < HashSize; i++)

       {

              swprintf(&MemberTag[i * 2], L"%02X", Buffer[i]);

       }

 

       //Get catalog for our context.

       CatalogContext = CryptCATAdminEnumCatalogFromHash(Context, Buffer, HashSize, 0, NULL);

 

       if (CatalogContext)

       {

              //If we couldn't get information

              if (!CryptCATCatalogInfoFromContext(CatalogContext, &InfoStruct, 0))

              {

                     //Release the context and set the context to null so it gets picked up below.

                     CryptCATAdminReleaseCatalogContext(Context, CatalogContext, 0); CatalogContext = NULL;

              }

       }

 

       //If we have a valid context, we got our info.

       //Otherwise, we attempt to verify the internal signature.

 

       if (!CatalogContext)

       {

              WintrustFileStructure.cbStruct = sizeof(WINTRUST_FILE_INFO);

              WintrustFileStructure.pcwszFilePath = FilePath;

              WintrustFileStructure.hFile = NULL;

              WintrustFileStructure.pgKnownSubject = NULL;

              WintrustStructure.cbStruct = sizeof(WINTRUST_DATA);

              WintrustStructure.dwUnionChoice = WTD_CHOICE_FILE;

              WintrustStructure.pFile = &WintrustFileStructure;

              WintrustStructure.dwUIChoice = WTD_UI_NONE;

              WintrustStructure.fdwRevocationChecks = WTD_REVOKE_NONE;

              WintrustStructure.dwStateAction = WTD_STATEACTION_IGNORE;

              WintrustStructure.dwProvFlags = WTD_SAFER_FLAG;

              WintrustStructure.hWVTStateData = NULL;

              WintrustStructure.pwszURLReference = NULL;

       }

       else

       {

              //If we get here, we have catalog info! Verify it.

              WintrustStructure.cbStruct = sizeof(WINTRUST_DATA);

              WintrustStructure.pPolicyCallbackData = 0;

              WintrustStructure.pSIPClientData = 0;

              WintrustStructure.dwUIChoice = WTD_UI_NONE;

              WintrustStructure.fdwRevocationChecks = WTD_REVOKE_NONE;

              WintrustStructure.dwUnionChoice = WTD_CHOICE_CATALOG;

              WintrustStructure.pCatalog = &WintrustCatalogStructure;

              WintrustStructure.dwStateAction = WTD_STATEACTION_VERIFY;

              WintrustStructure.hWVTStateData = NULL;

              WintrustStructure.pwszURLReference = NULL;

              WintrustStructure.dwProvFlags = 0;

              WintrustStructure.dwUIContext = WTD_UICONTEXT_EXECUTE;

 

              //Fill in catalog info structure.

              WintrustCatalogStructure.cbStruct = sizeof(WINTRUST_CATALOG_INFO);

              WintrustCatalogStructure.dwCatalogVersion = 0;

              WintrustCatalogStructure.pcwszCatalogFilePath = InfoStruct.wszCatalogFile;

              WintrustCatalogStructure.pcwszMemberTag = MemberTag;

              WintrustCatalogStructure.pcwszMemberFilePath = FilePath;

              WintrustCatalogStructure.hMemberFile = NULL;

       }

 

       //Call our verification function.

       ReturnVal = WinVerifyTrust(0, &ActionGuid, &WintrustStructure);

 

       //Check return.

       ReturnFlag = SUCCEEDED(ReturnVal);

 

       //Free context.

       if (CatalogContext) CryptCATAdminReleaseCatalogContext(Context, CatalogContext, 0);

       //If we successfully verified, we need to free.

       if (ReturnFlag)

       {

              WintrustStructure.dwStateAction = WTD_STATEACTION_CLOSE;

              WinVerifyTrust(0, &ActionGuid, &WintrustStructure);

       }

 

       //Free memory.

       free(MemberTag);

       free(Buffer);

       CloseHandle(FileHandle);

       CryptCATAdminReleaseContext(Context, 0);

       return ReturnFlag;

For more information, visit these resources:

    https://msdn.microsoft.com/en-us/library/windows/desktop/aa388205(v=vs.85).aspx

    https://forum.sysinternals.com/howto-verify-the-digital-signature-of-a-file_topic19247.html

 

 

Thanks,

Nathan

Source tweaked by Jeff Lambert

Follow us on Twitter, www.twitter.com/WindowsSDK.