//go:build windows package winpwn import ( "fmt" "unsafe" "golang.org/x/sys/windows" ) // guid mirrors the Win32 GUID struct layout. type guid struct { Data1 uint32 Data2 uint16 Data3 uint16 Data4 [8]byte } // WINTRUST_ACTION_GENERIC_VERIFY_V2, from wintrust.h. var wintrustActionGenericVerifyV2 = guid{ Data1: 0x00aac56b, Data2: 0xcd44, Data3: 0x11d0, Data4: [8]byte{0x8c, 0xc2, 0x00, 0xc0, 0x4f, 0xc2, 0x95, 0xee}, } // wintrustFileInfo mirrors WINTRUST_FILE_INFO (wintrust.h). type wintrustFileInfo struct { CbStruct uint32 PcwszFilePath *uint16 HFile windows.Handle PgKnownSubject *guid } // wintrustData mirrors WINTRUST_DATA (wintrust.h). The struct is normally a // union of pFile/pCatalog/pBlob/pSgnr/pCert at the PFile position; we only // ever populate the file-info variant. type wintrustData struct { CbStruct uint32 PPolicyCallbackData uintptr PSIPClientData uintptr DwUIChoice uint32 FdwRevocationChecks uint32 DwUnionChoice uint32 PFile *wintrustFileInfo DwStateAction uint32 HWVTStateData windows.Handle PwszURLReference *uint16 DwProvFlags uint32 DwUIContext uint32 PSignatureSettings uintptr } const ( wtdUINone = 2 wtdRevokeNone = 0 wtdChoiceFile = 1 wtdStateActionVerify = 1 wtdStateActionClose = 2 wtdSaferFlag = 0x00000100 wtdCacheOnlyURLRetrieval = 0x00001000 wtdDisableMD2MD4 = 0x00002000 ) var ( modWintrust = windows.NewLazySystemDLL("wintrust.dll") procWinVerifyTrust = modWintrust.NewProc("WinVerifyTrust") ) // VerifyAuthenticodeSignature asks the OS to validate the Authenticode // signature on path via WinVerifyTrust — a real cryptographic chain/hash // check, unlike CheckSecResult.AuthenticodeSigned which only checks whether // a signature directory is present in the PE at all. Revocation checking is // disabled, so this makes no network calls; it verifies the embedded // certificate chain and file hash only. // // Most Windows system binaries (System32) are catalog-signed (.cat files) // rather than embedded-signed and will report TRUST_E_NOSIGNATURE here even // though Windows itself trusts them — this function only validates an // Authenticode signature embedded directly in the PE. func VerifyAuthenticodeSignature(path string) (bool, error) { pathPtr, err := windows.UTF16PtrFromString(path) if err != nil { return false, err } fileInfo := wintrustFileInfo{ CbStruct: uint32(unsafe.Sizeof(wintrustFileInfo{})), PcwszFilePath: pathPtr, } data := wintrustData{ CbStruct: uint32(unsafe.Sizeof(wintrustData{})), DwUIChoice: wtdUINone, FdwRevocationChecks: wtdRevokeNone, DwUnionChoice: wtdChoiceFile, PFile: &fileInfo, DwStateAction: wtdStateActionVerify, DwProvFlags: wtdSaferFlag | wtdCacheOnlyURLRetrieval | wtdDisableMD2MD4, } ret, _, _ := procWinVerifyTrust.Call( 0, // hwnd: NULL, dwUIChoice already suppresses any UI uintptr(unsafe.Pointer(&wintrustActionGenericVerifyV2)), uintptr(unsafe.Pointer(&data)), ) status := uint32(ret) // WinVerifyTrust requires releasing the verification state it allocated, // regardless of the outcome above. data.DwStateAction = wtdStateActionClose procWinVerifyTrust.Call( 0, uintptr(unsafe.Pointer(&wintrustActionGenericVerifyV2)), uintptr(unsafe.Pointer(&data)), ) if status != 0 { return false, fmt.Errorf("WinVerifyTrust: signature not valid (status 0x%X)", status) } return true, nil }