Files
MyGoNavi/internal/db/driver_agent_binary_check.go
Syngnat e3515b9eb2 🐛 fix(windows): 修复闪退与驱动代理安装失败
- 修复 WebView2 zoom factor 跨线程调用风险,切回窗口线程执行并增加 recover 与超时保护
- 完善 Redis 命令结果 JSON-safe 兜底,避免复杂返回值格式化触发程序崩溃
- 调整 Windows driver-agent 校验逻辑,仅读取 PE Machine 字段判断架构兼容性
- 避免 COFF string table EOF 被误判为无效 Windows 可执行文件,修复驱动在线安装和本地导入失败
- 补充窗口缩放、Redis 返回值和驱动代理 PE 校验回归测试
2026-05-18 10:28:18 +08:00

148 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package db
import (
"encoding/binary"
"fmt"
"io"
"os"
"runtime"
"strings"
)
const (
peMachineI386 uint16 = 0x014c
peMachineAmd64 uint16 = 0x8664
peMachineArm64 uint16 = 0xaa64
peDOSHeaderMinSize = 0x40
peHeaderOffsetAddr = 0x3c
peSignatureSize = 4
peCOFFHeaderSize = 20
)
func windowsMachineLabel(machine uint16) string {
switch machine {
case peMachineI386:
return "windows-386"
case peMachineAmd64:
return "windows-amd64"
case peMachineArm64:
return "windows-arm64"
default:
return fmt.Sprintf("windows-unknown(0x%04x)", machine)
}
}
func expectedWindowsMachineForGoArch(goarch string) (uint16, string, bool) {
switch strings.ToLower(strings.TrimSpace(goarch)) {
case "386":
return peMachineI386, "windows-386", true
case "amd64":
return peMachineAmd64, "windows-amd64", true
case "arm64":
return peMachineArm64, "windows-arm64", true
default:
return 0, "", false
}
}
func validateWindowsExecutableMachine(pathText string) error {
return validateWindowsExecutableMachineForArch(pathText, runtime.GOARCH)
}
func validateWindowsExecutableMachineForArch(pathText string, goarch string) error {
machine, err := readWindowsExecutableMachine(pathText)
if err != nil {
return fmt.Errorf("无法识别为有效的 Windows 可执行文件:%w", err)
}
expectedMachine, expectedLabel, ok := expectedWindowsMachineForGoArch(goarch)
if !ok {
return nil
}
if machine != expectedMachine {
return fmt.Errorf("可执行文件架构不兼容(文件=%s当前进程=%s", windowsMachineLabel(machine), expectedLabel)
}
return nil
}
func readWindowsExecutableMachine(pathText string) (uint16, error) {
file, err := os.Open(pathText)
if err != nil {
return 0, err
}
defer file.Close()
info, statErr := file.Stat()
if statErr != nil {
return 0, statErr
}
if info.IsDir() {
return 0, fmt.Errorf("路径是目录")
}
if info.Size() < peDOSHeaderMinSize {
return 0, fmt.Errorf("文件头不完整")
}
var dosMagic [2]byte
if err := readWindowsPEBytes(file, 0, dosMagic[:]); err != nil {
return 0, fmt.Errorf("读取 DOS 头失败:%w", err)
}
if dosMagic[0] != 'M' || dosMagic[1] != 'Z' {
return 0, fmt.Errorf("缺少 MZ 头")
}
var offsetBytes [4]byte
if err := readWindowsPEBytes(file, peHeaderOffsetAddr, offsetBytes[:]); err != nil {
return 0, fmt.Errorf("读取 PE 头偏移失败:%w", err)
}
peOffset := int64(binary.LittleEndian.Uint32(offsetBytes[:]))
if peOffset < peDOSHeaderMinSize {
return 0, fmt.Errorf("PE 头偏移异常")
}
if peOffset+peSignatureSize+peCOFFHeaderSize > info.Size() {
return 0, fmt.Errorf("PE 头不完整")
}
var signature [4]byte
if err := readWindowsPEBytes(file, peOffset, signature[:]); err != nil {
return 0, fmt.Errorf("读取 PE 签名失败:%w", err)
}
if signature[0] != 'P' || signature[1] != 'E' || signature[2] != 0 || signature[3] != 0 {
return 0, fmt.Errorf("缺少 PE 签名")
}
var machineBytes [2]byte
if err := readWindowsPEBytes(file, peOffset+peSignatureSize, machineBytes[:]); err != nil {
return 0, fmt.Errorf("读取 PE 架构失败:%w", err)
}
return binary.LittleEndian.Uint16(machineBytes[:]), nil
}
func readWindowsPEBytes(reader io.ReaderAt, offset int64, target []byte) error {
if len(target) == 0 {
return nil
}
_, err := reader.ReadAt(target, offset)
if err == io.EOF {
return io.ErrUnexpectedEOF
}
return err
}
// ValidateOptionalDriverAgentExecutable 校验可选驱动代理二进制是否可在当前进程中执行。
// 当前主要用于 Windows 下的 PE 架构兼容性校验,避免升级后复用到错误架构的旧代理。
func ValidateOptionalDriverAgentExecutable(driverType string, executablePath string) error {
pathText := strings.TrimSpace(executablePath)
if pathText == "" {
return fmt.Errorf("%s 驱动代理路径为空", driverDisplayName(driverType))
}
if runtime.GOOS != "windows" {
return nil
}
if err := validateWindowsExecutableMachine(pathText); err != nil {
return fmt.Errorf("%s 驱动代理不可用:%w", driverDisplayName(driverType), err)
}
return nil
}