//go:build windows package sboxrun import ( "crypto/sha256" "encoding/hex" "fmt" "os" "path/filepath" "strings" ) // AssetPaths records where the binaries landed after install. type AssetPaths struct { SingBoxExe string WintunDLL string WorkDir string // %PROGRAMDATA%\Drover\sboxrun ConfigPath string // \config.json LogPath string // \sing-box.log } // InstallAssets extracts sing-box.exe + wintun.dll into // %PROGRAMDATA%\Drover\sboxrun\ (creating the directory if needed) // and verifies SHA256. Idempotent — second runs skip if existing // files match the embedded SHAs. func InstallAssets() (*AssetPaths, error) { pd := os.Getenv("ProgramData") if pd == "" { return nil, fmt.Errorf("ProgramData environment variable is not set") } dir := filepath.Join(pd, "Drover", "sboxrun") if err := os.MkdirAll(dir, 0755); err != nil { return nil, fmt.Errorf("create %s: %w", dir, err) } exePath := filepath.Join(dir, "sing-box.exe") dllPath := filepath.Join(dir, "wintun.dll") if err := writeIfDifferent(exePath, singBoxExe, SingBoxSHA256); err != nil { return nil, fmt.Errorf("install sing-box.exe: %w", err) } if err := writeIfDifferent(dllPath, wintunDLL, WintunSHA256); err != nil { return nil, fmt.Errorf("install wintun.dll: %w", err) } return &AssetPaths{ SingBoxExe: exePath, WintunDLL: dllPath, WorkDir: dir, ConfigPath: filepath.Join(dir, "config.json"), LogPath: filepath.Join(dir, "sing-box.log"), }, nil } func writeIfDifferent(path string, content []byte, expectedSHA string) error { if existing, err := os.ReadFile(path); err == nil { if strings.EqualFold(sha256Hex(existing), expectedSHA) { return nil } } tmp := path + ".new" if err := os.WriteFile(tmp, content, 0644); err != nil { return err } if err := os.Rename(tmp, path); err != nil { _ = os.Remove(tmp) return err } got, err := os.ReadFile(path) if err != nil { return err } if !strings.EqualFold(sha256Hex(got), expectedSHA) { return fmt.Errorf("SHA256 mismatch after write at %s; antivirus may have tampered with the file. Add %%PROGRAMDATA%%\\Drover\\ to AV exclusions", path) } return nil } func sha256Hex(b []byte) string { h := sha256.Sum256(b) return hex.EncodeToString(h[:]) }