HOWTO: Verifying the Signature of a PE File

Share your advanced PureBasic knowledge/code with the community.
Flower
User
User
Posts: 22
Joined: Fri Jan 08, 2010 8:05 am
Location: United States

HOWTO: Verifying the Signature of a PE File

Post by Flower »

Use WinVerifyTrust API to verify the signature of a PE file.
Windows only, should be x64-compatible.

*** I ripped it out from a hobby project I was working on, so please let me know if it does not work for you.
Usage:
Replace "WinTrust.Lib" with the correct file path (SDK required, download it here if you don't have one: http://msdn.microsoft.com/en-us/windows/bb980924.aspx)
Or you can use the old OpenLibrary / GetFunction way, which do not need a lib file.

CheckFileTrust(FilePath.s)
[IN] FilePath the path of the file to verify
[RETURNS] #True if verified, #False if not verified or an error has been detected.

Code: Select all

;************************************************
; Partially translated from:
; http://msdn.microsoft.com/en-us/library/aa382384(VS.85).aspx
;
; // Copyright (c) Microsoft Corporation.  All rights reserved.
; // Example of verifying the embedded signature of a PE file by using 
; // the WinVerifyTrust function.
;************************************************

EnableExplicit

#WINTRUST_MAX_HEADER_BYTES_TO_MAP_DEFAULT = $00A00000
#WINTRUST_MAX_HASH_BYTES_TO_MAP_DEFAULT = $00100000
#WTD_UI_ALL = 1
#WTD_UI_NONE = 2
#WTD_UI_NOBAD = 3
#WTD_UI_NOGOOD = 4
#WTD_REVOKE_NONE = $00000000
#WTD_REVOKE_WHOLECHAIN = $00000001
#WTD_CHOICE_FILE = 1
#WTD_CHOICE_CATALOG = 2
#WTD_CHOICE_BLOB = 3
#WTD_CHOICE_SIGNER = 4
#WTD_CHOICE_CERT = 5
#WTD_STATEACTION_IGNORE = $00000000
#WTD_STATEACTION_VERIFY = $00000001
#WTD_STATEACTION_CLOSE = $00000002
#WTD_STATEACTION_AUTO_CACHE = $00000003
#WTD_STATEACTION_AUTO_CACHE_FLUSH = $00000004
#WTD_PROV_FLAGS_MASK = $0000FFFF
#WTD_USE_IE4_TRUST_FLAG = $00000001
#WTD_NO_IE4_CHAIN_FLAG = $00000002
#WTD_NO_POLICY_USAGE_FLAG = $00000004
#WTD_REVOCATION_CHECK_NONE = $00000010
#WTD_REVOCATION_CHECK_END_CERT = $00000020
#WTD_REVOCATION_CHECK_CHAIN = $00000040
#WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT = $00000080
#WTD_SAFER_FLAG = $00000100
#WTD_HASH_ONLY_FLAG = $00000200
#WTD_USE_DEFAULT_OSVER_CHECK = $00000400
#WTD_LIFETIME_SIGNING_FLAG = $00000800
#WTD_CACHE_ONLY_URL_RETRIEVAL = $00001000
#WTD_UICONTEXT_EXECUTE = 0
#WTD_UICONTEXT_INSTALL = 1
#WTCI_DONT_OPEN_STORES = $00000001
#WTCI_OPEN_ONLY_ROOT = $00000002
#WTCI_USE_LOCAL_MACHINE = $00000004
#WTPF_TRUSTTEST = $00000020
#WTPF_TESTCANBEVALID = $00000080
#WTPF_IGNOREEXPIRATION = $00000100
#WTPF_IGNOREREVOKATION = $00000200
#WTPF_OFFLINEOK_IND = $00000400
#WTPF_OFFLINEOK_COM = $00000800
#WTPF_OFFLINEOKNBU_IND = $00001000
#WTPF_OFFLINEOKNBU_COM = $00002000
#WTPF_VERIFY_V1_OFF = $00010000
#WTPF_IGNOREREVOCATIONONTS = $00020000
#WTPF_ALLOWONLYPERTRUST = $00040000
#TRUSTERROR_STEP_WVTPARAMS = 0
#TRUSTERROR_STEP_FILEIO = 2
#TRUSTERROR_STEP_SIP = 3
#TRUSTERROR_STEP_SIPSUBJINFO = 5
#TRUSTERROR_STEP_CATALOGFILE = 6
#TRUSTERROR_STEP_CERTSTORE = 7
#TRUSTERROR_STEP_MESSAGE = 8
#TRUSTERROR_STEP_MSG_SIGNERCOUNT = 9
#TRUSTERROR_STEP_MSG_INNERCNTTYPE = 10
#TRUSTERROR_STEP_MSG_INNERCNT = 11
#TRUSTERROR_STEP_MSG_STORE = 12
#TRUSTERROR_STEP_MSG_SIGNERINFO = 13
#TRUSTERROR_STEP_MSG_SIGNERCERT = 14
#TRUSTERROR_STEP_MSG_CERTCHAIN = 15
#TRUSTERROR_STEP_MSG_COUNTERSIGINFO = 16
#TRUSTERROR_STEP_MSG_COUNTERSIGCERT = 17
#TRUSTERROR_STEP_VERIFY_MSGHASH = 18
#TRUSTERROR_STEP_VERIFY_MSGINDIRECTDATA = 19
#TRUSTERROR_STEP_FINAL_WVTINIT = 30
#TRUSTERROR_STEP_FINAL_INITPROV = 31
#TRUSTERROR_STEP_FINAL_OBJPROV = 32
#TRUSTERROR_STEP_FINAL_SIGPROV = 33
#TRUSTERROR_STEP_FINAL_CERTPROV = 34
#TRUSTERROR_STEP_FINAL_CERTCHKPROV = 35
#TRUSTERROR_STEP_FINAL_POLICYPROV = 36
#TRUSTERROR_STEP_FINAL_UIPROV = 37
#TRUSTERROR_MAX_STEPS = 38
#CPD_USE_NT5_CHAIN_FLAG = $80000000
#CPD_REVOCATION_CHECK_NONE = $00010000
#CPD_REVOCATION_CHECK_END_CERT = $00020000
#CPD_REVOCATION_CHECK_CHAIN = $00040000
#CPD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT = $00080000
#CPD_UISTATE_MODE_PROMPT = $00000000
#CPD_UISTATE_MODE_BLOCK = $00000001
#CPD_UISTATE_MODE_ALLOW = $00000002
#CPD_UISTATE_MODE_MASK = $00000003
#CERT_CONFIDENCE_SIG = $10000000
#CERT_CONFIDENCE_TIME = $01000000
#CERT_CONFIDENCE_TIMENEST = $00100000
#CERT_CONFIDENCE_AUTHIDEXT = $00010000
#CERT_CONFIDENCE_HYGIENE = $00001000
#CERT_CONFIDENCE_HIGHEST = $11111000
#DWACTION_ALLOCANDFILL = 1
#DWACTION_FREE = 2
#szOID_TRUSTED_CODESIGNING_CA_LIST = "1.3.6.1.4.1.311.2.2.1"
#szOID_TRUSTED_CLIENT_AUTH_CA_LIST = "1.3.6.1.4.1.311.2.2.2"
#szOID_TRUSTED_SERVER_AUTH_CA_LIST = "1.3.6.1.4.1.311.2.2.3"
#SPC_TIME_STAMP_REQUEST_OBJID = "1.3.6.1.4.1.311.3.2.1"
#SPC_INDIRECT_DATA_OBJID = "1.3.6.1.4.1.311.2.1.4"
#SPC_SP_AGENCY_INFO_OBJID = "1.3.6.1.4.1.311.2.1.10"
#SPC_STATEMENT_TYPE_OBJID = "1.3.6.1.4.1.311.2.1.11"
#SPC_SP_OPUS_INFO_OBJID = "1.3.6.1.4.1.311.2.1.12"
#SPC_CERT_EXTENSIONS_OBJID = "1.3.6.1.4.1.311.2.1.14"
#SPC_PE_IMAGE_DATA_OBJID = "1.3.6.1.4.1.311.2.1.15"
#SPC_RAW_FILE_DATA_OBJID = "1.3.6.1.4.1.311.2.1.18"
#SPC_STRUCTURED_STORAGE_DATA_OBJID = "1.3.6.1.4.1.311.2.1.19"
#SPC_JAVA_CLASS_DATA_OBJID = "1.3.6.1.4.1.311.2.1.20"
#SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID = "1.3.6.1.4.1.311.2.1.21"
#SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID = "1.3.6.1.4.1.311.2.1.22"
#SPC_CAB_DATA_OBJID = "1.3.6.1.4.1.311.2.1.25"
#SPC_GLUE_RDN_OBJID = "1.3.6.1.4.1.311.2.1.25"
#SPC_MINIMAL_CRITERIA_OBJID = "1.3.6.1.4.1.311.2.1.26"
#SPC_FINANCIAL_CRITERIA_OBJID = "1.3.6.1.4.1.311.2.1.27"
#SPC_LINK_OBJID = "1.3.6.1.4.1.311.2.1.28"
#SPC_SIGINFO_OBJID = "1.3.6.1.4.1.311.2.1.30"
#SPC_PE_IMAGE_PAGE_HASHES_V1_OBJID = "1.3.6.1.4.1.311.2.3.1"
#SPC_PE_IMAGE_PAGE_HASHES_V2_OBJID = "1.3.6.1.4.1.311.2.3.2"
#CAT_NAMEVALUE_OBJID = "1.3.6.1.4.1.311.12.2.1"
#CAT_MEMBERINFO_OBJID = "1.3.6.1.4.1.311.12.2.2"
#SPC_SP_AGENCY_INFO_STRUCT =(2000)
#SPC_MINIMAL_CRITERIA_STRUCT =(2001)
#SPC_FINANCIAL_CRITERIA_STRUCT =(2002)
#SPC_INDIRECT_DATA_CONTENT_STRUCT =(2003)
#SPC_PE_IMAGE_DATA_STRUCT =(2004)
#SPC_LINK_STRUCT =(2005)
#SPC_STATEMENT_TYPE_STRUCT =(2006)
#SPC_SP_OPUS_INFO_STRUCT =(2007)
#SPC_CAB_DATA_STRUCT =(2008)
#SPC_JAVA_CLASS_DATA_STRUCT =(2009)
#SPC_SIGINFO_STRUCT =(2130)
#CAT_NAMEVALUE_STRUCT =(2221)
#CAT_MEMBERINFO_STRUCT =(2222)
#SPC_UUID_LENGTH = 16
#WIN_CERT_REVISION_1_0 =($0100)
#WIN_CERT_REVISION_2_0 =($0200)
#WIN_CERT_TYPE_X509 =($0001)
#WIN_CERT_TYPE_PKCS_SIGNED_DATA =($0002)
#WIN_CERT_TYPE_RESERVED_1 =($0003)
#WIN_CERT_TYPE_TS_STACK_SIGNED =($0004)
#WT_TRUSTDBDIALOG_NO_UI_FLAG = $00000001
#WT_TRUSTDBDIALOG_ONLY_PUB_TAB_FLAG = $00000002
#WT_TRUSTDBDIALOG_WRITE_LEGACY_REG_FLAG = $00000100
#WT_TRUSTDBDIALOG_WRITE_IEAK_STORE_FLAG = $00000200

Structure WINTRUST_DATA
      cbStruct.l
      *pPolicyCallbackData
      *pSIPClientData
      dwUIChoice.l
      fdwRevocationChecks.l
      dwUnionChoice.l
      *pPointer
      dwStateAction.l
      hWVTStateData.l
      *pwszURLReference
      dwProvFlags.l
      dwUIContext.l
EndStructure

Structure WINTRUST_FILE_INFO
      cbStruct.l
      *pcwszFilePath
      hFile.l
      *pgKnownSubject
EndStructure

Structure WINTRUST_CATALOG_INFO
      cbStruct.l
      dwCatalogVersion.l
      *pcwszCatalogFilePath
      *pcwszMemberTag
      *pcwszMemberFilePath
      hMemberFile.l
EndStructure

Structure CATALOG_INFO
      cbStruct.l
      wszCatalogFile.b[520]
EndStructure

#WTD_CHOICE_FILE = 1
#WTD_CHOICE_CATALOG = 2
#WTD_UI_NONE = 2
#WTD_REVOKE_NONE = 0
#WTD_STATEACTION_IGNORE = 0
#WTD_STATEACTION_VERIFY = 1
#WTD_SAFER_FLAG = 256

Import "WinTrust.Lib"
      CryptCATAdminAcquireContext.l(*phCatAdmin, *pgSubsystem, dwFlags.l)
      CryptCATAdminReleaseContext.l(hCatAdmin.l, dwFlags.l)
      CryptCATAdminCalcHashFromFileHandle.l(hFile.l, *pcbHash, *pbHash, dwFlags.l)
      CryptCATAdminEnumCatalogFromHash.l(hCatAdmin.l, *pbHash, cbHash.l, dwFlags.l, *phPrevCatInfo)
      CryptCATCatalogInfoFromContext.l(hCatInfo.l, *psCatInfo, dwFlags.l)
      CryptCATAdminReleaseCatalogContext.l(hCatAdmin.l, hCatInfo.l, dwFlags.l)
      WinVerifyTrust.l(HWND.l, *pgActionID, *pWVTData)
EndImport

Procedure CheckFileTrust(FilePath.s)
      Define bRet.l
      Define wd.WINTRUST_DATA
      Define wfi.WINTRUST_FILE_INFO
      Define wci.WINTRUST_CATALOG_INFO
      Define ci.CATALOG_INFO
      Define hCatAdmin.i
      Define bRet.l, hFile.l
      Define hr.l
      Define pszMemberTag.s
      Define dwCnt.l
      Dim byHash.b(99)
      Define dw.l
      Define hCatInfo.l
      
      ClearStructure(@wd, WINTRUST_DATA)
      ClearStructure(@wfi, WINTRUST_FILE_INFO)
      ClearStructure(@wci, WINTRUST_CATALOG_INFO)
      ClearStructure(@ci, CATALOG_INFO)
      
      bRet = #False
      hCatAdmin = #Null
      
      If(Not(CryptCATAdminAcquireContext(@hCatAdmin, #Null, 0)))
            ProcedureReturn #False
      EndIf
      
      ;********************
      ;Open file
      ;********************
      hFile = CreateFile_(@FilePath, #GENERIC_READ, #FILE_SHARE_READ, #Null, #OPEN_EXISTING, #FILE_ATTRIBUTE_NORMAL, #Null)
      
      If (hFile = #INVALID_HANDLE_VALUE)
            Debug ("CheckFileTrust(): CreateFile FAILED, GetLastError = " + Hex(GetLastError_()))
            CryptCATAdminReleaseContext(hCatAdmin, 0)
            ProcedureReturn #False
      EndIf
      
      dwCnt = 100
      CryptCATAdminCalcHashFromFileHandle(hFile, @dwCnt, @byHash(), 0)
      
      ;********************
      ;Close file
      ;********************
      CloseHandle_(hFile)
      
      ;********************
      ;Compute member tag
      ;********************
      For dw = 0 To dwCnt - 1
            pszMemberTag + Right("0" + Hex(byHash(dw)), 2)
      Next
      
      Debug ("CheckFileTrust(): MemberTag = " + pszMemberTag)
      
      hCatInfo = CryptCATAdminEnumCatalogFromHash(hCatAdmin, @byHash(), dwCnt, 0, #Null)
      
      If (hCatInfo = #Null)
            Debug ("CheckFileTrust(): CryptCATAdminEnumCatalogFromHash FAILED, start verifying embedded signature.")
            wd\cbStruct = SizeOf(wd)
            
            ; Use default code signing EKU.
            wd\pPolicyCallbackData = #Null
            
            ; No data to pass to SIP.
            wd\pSIPClientData = #Null
            
            ; Disable WVT UI.
            wd\dwUIChoice = #WTD_UI_NONE
            
            ; No revocation checking.
            wd\fdwRevocationChecks = #WTD_REVOKE_NONE
            
            ; Verify an embedded signature on a file.
            wd\dwUnionChoice = #WTD_CHOICE_FILE
            
            ; Default verification.
            wd\dwStateAction = #WTD_STATEACTION_IGNORE
            
            ; Not applicable for default verification of embedded signature.
            wd\hWVTStateData = #Null
            
            ; Not used.
            wd\pwszURLReference = #Null
            
            ; Default.
            wd\dwProvFlags = #WTD_SAFER_FLAG
            
            ; This is not applicable if there is no UI because it changes
            ; the UI to accommodate running applications instead of
            ; installing applications.
            wd\dwUIContext = #Null
            
            ; Set pFile.
            wfi\cbStruct = SizeOf(WINTRUST_FILE_INFO)
            wfi\pcwszFilePath = @FilePath
            wfi\hFile = #Null
            wfi\pgKnownSubject = #Null
            wd\pPointer = @wfi
      Else
            If(Not(CryptCATCatalogInfoFromContext(hCatInfo, @ci, 0)))
                  Debug ("CheckFileTrust(): CryptCATCatalogInfoFromContext FAILED, GetLastError = " + Hex(GetLastError_()))
            EndIf
            wci\cbStruct = SizeOf(wci)
            wci\pcwszCatalogFilePath = @ci\wszCatalogFile[0]
            wci\pcwszMemberFilePath = @FilePath
            wci\pcwszMemberTag = @pszMemberTag
            
            wd\cbStruct = SizeOf(wd)
            wd\dwUnionChoice = #WTD_CHOICE_CATALOG
            wd\pPointer = @wci
            wd\dwUIChoice = #WTD_UI_NONE
            wd\fdwRevocationChecks = #WTD_REVOKE_NONE
            wd\dwStateAction = #WTD_STATEACTION_VERIFY
            wd\dwProvFlags = 0
            wd\hWVTStateData = #Null
            wd\pwszURLReference = #Null
      EndIf
      
      Define action.GUID
      
      ;********************
      ; WINTRUST_ACTION_GENERIC_VERIFY_V2 (Authenticode)
      ;********************
      With action
            \Data1 = $AAC56B
            \Data2 = $CD44
            \Data3 = $11D0
            \Data4[0] = $8C
            \Data4[1] = $C2
            \Data4[2] = $00
            \Data4[3] = $C0
            \Data4[4] = $4F
            \Data4[5] = $C2
            \Data4[6] = $95
            \Data4[7] = $EE
      EndWith
      
      hr = WinVerifyTrust(#INVALID_HANDLE_VALUE, @action, @wd)
      
      If (hCatInfo <> #Null)
            CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0)
      EndIf
      
      CryptCATAdminReleaseContext(hCatAdmin, 0)
      
      If (hr = 0)
            wd\dwStateAction = #WTD_STATEACTION_CLOSE
            WinVerifyTrust(#INVALID_HANDLE_VALUE, @action, @wd)
            ProcedureReturn #True
      Else
            ProcedureReturn #False
      EndIf
      
EndProcedure
Last edited by Flower on Tue Sep 28, 2010 12:46 am, edited 2 times in total.
Registered PureBasic user since 4.50
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: HOWTO: Verifying the Signature of a PE File

Post by ts-soft »

Code: Select all

      If (hr = 0)
            ProcedureReturn #True
            wd\dwStateAction = #WTD_STATEACTION_CLOSE
            WinVerifyTrust(#INVALID_HANDLE_VALUE, @action, @wd)
the last two lines will never executed!

greetings
Thomas
Flower
User
User
Posts: 22
Joined: Fri Jan 08, 2010 8:05 am
Location: United States

Re: HOWTO: Verifying the Signature of a PE File

Post by Flower »

ts-soft wrote:

Code: Select all

      If (hr = 0)
            ProcedureReturn #True
            wd\dwStateAction = #WTD_STATEACTION_CLOSE
            WinVerifyTrust(#INVALID_HANDLE_VALUE, @action, @wd)
the last two lines will never executed!

greetings
Thomas
You're right, I corrected it. Thanks.
I was originally modifying a value in a structure passed in when it was used in my project. :mrgreen:
Registered PureBasic user since 4.50
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Re: HOWTO: Verifying the Signature of a PE File

Post by SFSxOI »

Does this work in PB 4.50? I get a bunch of polink errors involving these:

Code: Select all

CryptCATAdminAcquireContext.l(*phCatAdmin, *pgSubsystem, dwFlags.l)
CryptCATAdminReleaseContext.l(hCatAdmin.l, dwFlags.l)
CryptCATAdminCalcHashFromFileHandle.l(hFile.l, *pcbHash, *pbHash, dwFlags.l)
CryptCATAdminEnumCatalogFromHash.l(hCatAdmin.l, *pbHash, cbHash.l, dwFlags.l, *phPrevCatInfo)
CryptCATCatalogInfoFromContext.l(hCatInfo.l, *psCatInfo, dwFlags.l)
CryptCATAdminReleaseCatalogContext.l(hCatAdmin.l, hCatInfo.l, dwFlags.l)
The advantage of a 64 bit operating system over a 32 bit operating system comes down to only being twice the headache.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: HOWTO: Verifying the Signature of a PE File

Post by ts-soft »

@SFSxOI
with correct "WinTrust.Lib" no problem here.
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Re: HOWTO: Verifying the Signature of a PE File

Post by SFSxOI »

nevermind, I just read it again, ya need the SDK. DuH! :)
The advantage of a 64 bit operating system over a 32 bit operating system comes down to only being twice the headache.
jassing
Addict
Addict
Posts: 1885
Joined: Wed Feb 17, 2010 12:00 am

Re: HOWTO: Verifying the Signature of a PE File

Post by jassing »

Any idea how to get the publisher/owner of the signature?
Post Reply