Files
go_pwner/authenticode_windows.go
T
2026-07-18 21:37:15 +03:00

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
}