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.
Comments
Anonymous
January 03, 2016
The code snippet does not render correctly in either IE or Chrome. Something ate all the newlines.Anonymous
January 03, 2016
Sorry about that. I fixed it and it looks better now. /JeffAnonymous
March 10, 2016
Thanks Jeff and thanks to blogs.msdn.com. Here is some more information for people like me.. I have commented in sysinternals forum and will add this too.. gnomicbits.blogspot.in/.../how-to-verify-pe-digital-signature.html I had to create a new blog for few reasons.. so that's the only page... Thanks to Microsoft for documenting those helper functions. Great help!!! -Sreejith. D. Menon (#iwork4Dell)