Files
sing-box/experimental/boxdd/update_executable.go
2026-07-14 21:33:15 +08:00

78 lines
2.2 KiB
Go

//go:build windows
package main
import (
"debug/pe"
"encoding/binary"
"os"
E "github.com/sagernet/sing/common/exceptions"
)
const (
peSecurityDirectoryIndex = 4
nsisFirstHeaderSize = 28
nsisSignature = 0xDEADBEEF
nsisIdentity = "NullsoftInst"
nsisMaximumAlignmentTail = 8
)
func validateNSISExecutable(path string) error {
executable, err := os.Open(path)
if err != nil {
return err
}
defer executable.Close()
information, err := executable.Stat()
if err != nil {
return err
}
peFile, err := pe.NewFile(executable)
if err != nil {
return err
}
defer peFile.Close()
var overlayOffset uint64
for _, section := range peFile.Sections {
sectionEnd := uint64(section.Offset) + uint64(section.Size)
if sectionEnd > overlayOffset {
overlayOffset = sectionEnd
}
}
contentEnd := uint64(information.Size())
var securityOffset uint64
switch optionalHeader := peFile.OptionalHeader.(type) {
case *pe.OptionalHeader32:
securityOffset = uint64(optionalHeader.DataDirectory[peSecurityDirectoryIndex].VirtualAddress)
case *pe.OptionalHeader64:
securityOffset = uint64(optionalHeader.DataDirectory[peSecurityDirectoryIndex].VirtualAddress)
}
if securityOffset != 0 {
if securityOffset > contentEnd {
return E.New("invalid Authenticode directory offset")
}
contentEnd = securityOffset
}
if overlayOffset+nsisFirstHeaderSize > contentEnd {
return E.New("missing NSIS first header")
}
firstHeader := make([]byte, nsisFirstHeaderSize)
_, err = executable.ReadAt(firstHeader, int64(overlayOffset))
if err != nil {
return err
}
if binary.LittleEndian.Uint32(firstHeader[4:8]) != nsisSignature || string(firstHeader[8:20]) != nsisIdentity {
return E.New("invalid NSIS first header signature")
}
headerSize := uint64(binary.LittleEndian.Uint32(firstHeader[20:24]))
archiveSize := uint64(binary.LittleEndian.Uint32(firstHeader[24:28]))
if headerSize < nsisFirstHeaderSize || archiveSize < headerSize || overlayOffset+archiveSize > contentEnd {
return E.New("invalid NSIS archive size")
}
if contentEnd-(overlayOffset+archiveSize) > nsisMaximumAlignmentTail {
return E.New("unexpected data after NSIS archive")
}
return nil
}