126 lines
3.5 KiB
Go
126 lines
3.5 KiB
Go
//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
|
|
}
|