Release 1.7.1: download-only updates and remove netsh/startup network probes for AV FPs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Navis
2026-07-28 09:17:16 +03:00
co-authored by Cursor
parent 29643e2157
commit cd0b3f4707
21 changed files with 216 additions and 275 deletions
+12 -8
View File
@@ -208,13 +208,17 @@ https://evilfox.win/
## Антивирусы (ложные срабатывания)
Некоторые AV (Kaspersky, Microsoft Wacatac.B!ml, Ikarus Trojan.WinGo.Agent и др.) часто помечают **любые** Go-бинарники с сетью и сменой системного прокси.
Некоторые AV (Bkav, Microsoft Wacapew/Wacatac, Ikarus Trojan.WinGo.Agent, Google, Trapmine) часто помечают **любые** неподписанные Go-бинарники с сетью и сменой системного прокси.
В 1.7.0 уменьшили «подозрительные» паттерны:
- нет скрытой загрузки cores при старте;
- нет `.bat`-самозамены с `del /f` + hidden detached `cmd`;
- обновление через `Navis-pending.exe` + явный флаг;
- Windows application manifest (`asInvoker`);
- metadata компании EvilFox / Product Navis.
В 1.7.1 дополнительно:
- обновление только скачивается в `Downloads` (без самозамены exe);
- нет автопроверки обновлений и тихой загрузки cores при старте;
- нет вызова `netsh winhttp`;
- Windows manifest + metadata EvilFox.
Если Defender/KAV ругается: добавьте `Navis.exe` в исключения или отправьте файл как False Positive вендору. Надёжнее всего — подпись Authenticode (code signing certificate).
Если всё ещё ругается — отправьте файл как False Positive:
- Microsoft: https://www.microsoft.com/wdsi/filesubmission
- Google: https://safebrowsing.google.com/safebrowsing/report_error/
- VirusTotal community vote: «False positive»
Полностью убрать ML-детекты обычно можно только с **Authenticode** (code signing).
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.7.0.0"
version="1.7.1.0"
processorArchitecture="*"
name="EvilFox.Navis"
type="win32"/>
+2 -31
View File
@@ -57,10 +57,6 @@ type uiState struct {
}
func main() {
if update.MaybeFinishUpdate(os.Args[1:]) {
return
}
log.SetFlags(log.LstdFlags | log.Lshortfile)
cfgPath, err := config.LocateConfig()
@@ -138,8 +134,6 @@ func main() {
mustBind(w, "saveHy2", a.saveHy2)
mustBind(w, "importSubscription", a.importSubscription)
go a.autoCheckUpdate()
w.SetHtml(appui.IndexHTML)
w.Run()
}
@@ -329,33 +323,10 @@ func (a *app) checkUpdate() (update.Status, error) {
return st, nil
}
func (a *app) applyUpdate() error {
func (a *app) applyUpdate() (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
latest, err := update.Apply(ctx, update.DefaultManifestURL)
if err != nil {
return err
}
_ = a.mgr.Disconnect()
go func() {
time.Sleep(400 * time.Millisecond)
if a.webview != nil {
a.webview.Terminate()
}
os.Exit(0)
}()
_ = latest
return nil
}
func (a *app) autoCheckUpdate() {
time.Sleep(2 * time.Second)
_, _ = a.checkUpdate()
ticker := time.NewTicker(6 * time.Hour)
defer ticker.Stop()
for range ticker.C {
_, _ = a.checkUpdate()
}
return update.Apply(ctx, update.DefaultManifestURL)
}
func openURL(raw string) error {
+2 -2
View File
@@ -287,11 +287,11 @@ func runApplyUpdate(args []string) int {
_ = args
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
defer cancel()
latest, err := update.Apply(ctx, update.DefaultManifestURL)
msg, err := update.Apply(ctx, update.DefaultManifestURL)
if err != nil {
fmt.Fprintf(os.Stderr, "apply-update: %v\n", err)
return 1
}
fmt.Printf("updating to %s — exit the app to finish\n", latest)
fmt.Println(msg)
return 0
}
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+10 -10
View File
@@ -1,38 +1,38 @@
{
"version": "1.7.0",
"notes": "Меньше ложных срабатываний AV: без bat-апдейтера и тихой загрузки cores; Windows manifest; metadata EvilFox",
"version": "1.7.1",
"notes": "AV: обновление только в Downloads без самозамены; без netsh и автопроверки при старте",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe",
"sha256": "8bb8bab990292be66f169dc0a23f0aaff45cf349b6c6b5cfbef763f55cfe0d4d",
"sha256": "e08425b2eddd17a71a6e083990313811df9a1f3a55586c407798bfeeb82fd68b",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe",
"sha256": "8bb8bab990292be66f169dc0a23f0aaff45cf349b6c6b5cfbef763f55cfe0d4d",
"sha256": "e08425b2eddd17a71a6e083990313811df9a1f3a55586c407798bfeeb82fd68b",
"os": "windows",
"arch": "amd64"
},
"darwin-arm64": {
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis",
"sha256": "3e544461c1ce8ec3b508639cb549a759b773da38517c98c9ca634faaf09a601c",
"sha256": "3cb42ab226b0263c8b6566888d80c6e7a6982d8cda992d112989c7f5c8a99bab",
"os": "darwin",
"arch": "arm64",
"dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg",
"dmg_sha256": "39e88c985867ce6ef37deb00d87e1c6669cfbc8b9f8f0589d11a3f32036a5258",
"dmg_sha256": "65b8fe8c0d294ee923d59125f3e39c37f7c8a201aa6fd4661d080da59928c2e4",
"zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.app.zip",
"zip_sha256": "213fd7563e068f60a1ae2504c6e48171b9f9e3f2fe3c14ecad75ed809fa05d06"
"zip_sha256": "6685b1f8be9f8aa87613f35b8f8a86a00a6fb667c2f7bdc7116e6908c0cb1513"
},
"darwin-amd64": {
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis",
"sha256": "f40a711108e78bfa1a944a1d4946549aba32cb5e00ef9035be352810c700807c",
"sha256": "7bffda217e178224f3a68fed4d5fff4e37b4705b7c8291b84f1d58151ef43b9d",
"os": "darwin",
"arch": "amd64",
"dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg",
"dmg_sha256": "c70fe7feb000eec34c0fe4ef2d05565dd0285c0c4f18a264ecad540d2c0878db",
"dmg_sha256": "959526963936ad22d3c3607fe18295018362ec0d5fedcdf7561edf5d71c3b827",
"zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.app.zip",
"zip_sha256": "5b3e3fea5aef4ac962c18a190c1943c7cfd5357ea233abbdec1d26007e1e6eb4"
"zip_sha256": "079c2af3fe5de6b9e73bad15a15bddfc93970d27be68b6620b5be3729163fc15"
}
}
}
+10 -10
View File
@@ -1,38 +1,38 @@
{
"version": "1.7.0",
"notes": "Меньше ложных срабатываний AV: без bat-апдейтера и тихой загрузки cores; Windows manifest; metadata EvilFox",
"version": "1.7.1",
"notes": "AV: обновление только в Downloads без самозамены; без netsh и автопроверки при старте",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe",
"sha256": "8bb8bab990292be66f169dc0a23f0aaff45cf349b6c6b5cfbef763f55cfe0d4d",
"sha256": "e08425b2eddd17a71a6e083990313811df9a1f3a55586c407798bfeeb82fd68b",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe",
"sha256": "8bb8bab990292be66f169dc0a23f0aaff45cf349b6c6b5cfbef763f55cfe0d4d",
"sha256": "e08425b2eddd17a71a6e083990313811df9a1f3a55586c407798bfeeb82fd68b",
"os": "windows",
"arch": "amd64"
},
"darwin-arm64": {
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis",
"sha256": "3e544461c1ce8ec3b508639cb549a759b773da38517c98c9ca634faaf09a601c",
"sha256": "3cb42ab226b0263c8b6566888d80c6e7a6982d8cda992d112989c7f5c8a99bab",
"os": "darwin",
"arch": "arm64",
"dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg",
"dmg_sha256": "39e88c985867ce6ef37deb00d87e1c6669cfbc8b9f8f0589d11a3f32036a5258",
"dmg_sha256": "65b8fe8c0d294ee923d59125f3e39c37f7c8a201aa6fd4661d080da59928c2e4",
"zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.app.zip",
"zip_sha256": "213fd7563e068f60a1ae2504c6e48171b9f9e3f2fe3c14ecad75ed809fa05d06"
"zip_sha256": "6685b1f8be9f8aa87613f35b8f8a86a00a6fb667c2f7bdc7116e6908c0cb1513"
},
"darwin-amd64": {
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis",
"sha256": "f40a711108e78bfa1a944a1d4946549aba32cb5e00ef9035be352810c700807c",
"sha256": "7bffda217e178224f3a68fed4d5fff4e37b4705b7c8291b84f1d58151ef43b9d",
"os": "darwin",
"arch": "amd64",
"dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg",
"dmg_sha256": "c70fe7feb000eec34c0fe4ef2d05565dd0285c0c4f18a264ecad540d2c0878db",
"dmg_sha256": "959526963936ad22d3c3607fe18295018362ec0d5fedcdf7561edf5d71c3b827",
"zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.app.zip",
"zip_sha256": "5b3e3fea5aef4ac962c18a190c1943c7cfd5357ea233abbdec1d26007e1e6eb4"
"zip_sha256": "079c2af3fe5de6b9e73bad15a15bddfc93970d27be68b6620b5be3729163fc15"
}
}
}
+2 -2
View File
@@ -842,8 +842,8 @@
updateBtn.addEventListener("click", () => withBusy(async () => {
try {
setMeta("Скачивание обновления…");
await applyUpdate();
setMeta("Обновление установлено, перезапуск…", "ok");
const msg = await applyUpdate();
setMeta(String(msg || "Готово"), "ok");
} catch (e) { setMeta(String(e), "err"); }
}));
+1 -19
View File
@@ -4,9 +4,7 @@ package sysproxy
import (
"fmt"
"os/exec"
"strings"
"syscall"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
@@ -87,7 +85,7 @@ func (c *windowsController) Enable(httpHostPort string) error {
if err := notifyInternetSettingsChanged(); err != nil {
return err
}
_ = setWinHTTPProxy(httpHostPort)
// Skip netsh winhttp — spawning netsh is a common AV heuristic; WinINET covers browsers.
c.enabled = true
return nil
@@ -120,7 +118,6 @@ func (c *windowsController) Disable() error {
}
_ = notifyInternetSettingsChanged()
_ = resetWinHTTPProxy()
c.enabled = false
return nil
@@ -139,18 +136,3 @@ func notifyInternetSettingsChanged() error {
}
return nil
}
func setWinHTTPProxy(httpHostPort string) error {
cmd := exec.Command("netsh", "winhttp", "set", "proxy", httpHostPort,
"bypass-list=localhost;127.*;10.*;192.168.*;<local>")
const createNoWindow = 0x08000000
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: createNoWindow}
return cmd.Run()
}
func resetWinHTTPProxy() error {
cmd := exec.Command("netsh", "winhttp", "reset", "proxy")
const createNoWindow = 0x08000000
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: createNoWindow}
return cmd.Run()
}
+1 -50
View File
@@ -9,7 +9,6 @@ import (
"io"
"net/http"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
@@ -17,7 +16,7 @@ import (
)
// CurrentVersion is the shipped client version.
const CurrentVersion = "1.7.0"
const CurrentVersion = "1.7.1"
// DefaultManifestURL is the update feed (hosted in the project git repo).
const DefaultManifestURL = "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/update.json"
@@ -226,51 +225,3 @@ func fileSHA256(path string) (string, error) {
}
return hex.EncodeToString(h.Sum(nil)), nil
}
func prepareDownload(ctx context.Context, manifestURL string) (latest, url, sha, exePath, tmpPath string, err error) {
st, err := Check(ctx, manifestURL)
if err != nil {
return "", "", "", "", "", err
}
if !st.Available {
if st.Error != "" {
return "", "", "", "", "", fmt.Errorf("%s", st.Error)
}
return "", "", "", "", "", fmt.Errorf("обновление не найдено")
}
if !allowedDownloadURL(st.URL) {
return "", "", "", "", "", fmt.Errorf("URL обновления должен быть на git.evilfox.cc или files.de4ima.uk")
}
exe, err := os.Executable()
if err != nil {
return "", "", "", "", "", err
}
exe, err = filepath.Abs(exe)
if err != nil {
return "", "", "", "", "", err
}
tmp := exe + ".new"
if err := downloadFile(ctx, st.URL, tmp); err != nil {
return "", "", "", "", "", err
}
wantSHA := st.SHA256
if wantSHA == "" {
if man, err := fetchManifest(ctx, manifestURL); err == nil {
if _, sha2, ok := man.AssetFor(PlatformKey()); ok {
wantSHA = sha2
}
}
}
if wantSHA != "" {
sum, err := fileSHA256(tmp)
if err != nil {
_ = os.Remove(tmp)
return "", "", "", "", "", err
}
if !strings.EqualFold(sum, wantSHA) {
_ = os.Remove(tmp)
return "", "", "", "", "", fmt.Errorf("checksum mismatch")
}
}
return st.Latest, st.URL, wantSHA, exe, tmp, nil
}
+72 -27
View File
@@ -8,43 +8,88 @@ import (
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"runtime"
)
// Apply downloads the new binary and schedules replacement after exit (macOS).
// Apply downloads the new binary into ~/Downloads (no silent self-replace).
func Apply(ctx context.Context, manifestURL string) (string, error) {
latest, _, _, exe, tmp, err := prepareDownload(ctx, manifestURL)
st, err := Check(ctx, manifestURL)
if err != nil {
return "", err
}
_ = os.Chmod(tmp, 0o755)
dir := filepath.Dir(exe)
scriptPath := filepath.Join(dir, "navis-update.sh")
pid := os.Getpid()
if !st.Available {
if st.Error != "" {
return "", fmt.Errorf("%s", st.Error)
}
return "", fmt.Errorf("обновление не найдено")
}
if !allowedDownloadURL(st.URL) {
return "", fmt.Errorf("URL обновления должен быть на git.evilfox.cc или files.de4ima.uk")
}
script := "#!/bin/bash\n" +
"set -e\n" +
"EXE=" + shellQuote(exe) + "\n" +
"NEW=" + shellQuote(tmp) + "\n" +
"PID=" + strconv.Itoa(pid) + "\n" +
"while kill -0 \"$PID\" 2>/dev/null; do sleep 0.4; done\n" +
"sleep 0.5\n" +
"mv -f \"$NEW\" \"$EXE\"\n" +
"chmod +x \"$EXE\"\n" +
"rm -f " + shellQuote(scriptPath) + "\n"
if err := os.WriteFile(scriptPath, []byte(script), 0o755); err != nil {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
cmd := exec.Command("/bin/bash", scriptPath)
cmd.Dir = dir
if err := cmd.Start(); err != nil {
return "", fmt.Errorf("start updater: %w", err)
dir := filepath.Join(home, "Downloads")
_ = os.MkdirAll(dir, 0o755)
dest := filepath.Join(dir, fmt.Sprintf("Navis-%s-darwin-%s", sanitizeDarwinVer(st.Latest), runtime.GOARCH))
tmp := dest + ".part"
_ = os.Remove(tmp)
_ = os.Remove(dest)
if err := downloadFile(ctx, st.URL, tmp); err != nil {
return "", err
}
return latest, nil
if st.SHA256 != "" {
sum, err := fileSHA256(tmp)
if err != nil {
_ = os.Remove(tmp)
return "", err
}
if sum != "" && !eqFold(sum, st.SHA256) {
_ = os.Remove(tmp)
return "", fmt.Errorf("checksum mismatch")
}
}
if err := os.Rename(tmp, dest); err != nil {
_ = os.Remove(tmp)
return "", err
}
_ = os.Chmod(dest, 0o755)
_ = exec.Command("open", "-R", dest).Start()
return fmt.Sprintf("Скачано v%s:\n%s\nЗакройте Navis и запустите новый файл.", st.Latest, dest), nil
}
func shellQuote(s string) string {
return "'" + strings.ReplaceAll(s, "'", `'\''`) + "'"
func sanitizeDarwinVer(v string) string {
out := make([]rune, 0, len(v))
for _, r := range v {
switch {
case r >= '0' && r <= '9', r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r == '.', r == '-':
out = append(out, r)
}
}
if len(out) == 0 {
return "update"
}
return string(out)
}
func eqFold(a, b string) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
ca, cb := a[i], b[i]
if ca >= 'A' && ca <= 'F' {
ca += 'a' - 'A'
}
if cb >= 'A' && cb <= 'F' {
cb += 'a' - 'A'
}
if ca != cb {
return false
}
}
return true
}
-9
View File
@@ -1,9 +0,0 @@
//go:build !windows
package update
// MaybeFinishUpdate is Windows-only.
func MaybeFinishUpdate(args []string) bool {
_ = args
return false
}
+92 -95
View File
@@ -5,116 +5,113 @@ package update
import (
"context"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strconv"
"syscall"
"time"
"golang.org/x/sys/windows"
)
const finishUpdateFlag = "--navis-finish-update"
// Apply downloads the new exe and starts it to replace the running binary after exit.
// Avoids writing .bat self-replacer scripts (common AV false-positive pattern).
// Apply downloads the new build into the user Downloads folder.
// It does NOT replace the running exe or spawn a self-updater (AV false-positive pattern).
func Apply(ctx context.Context, manifestURL string) (string, error) {
latest, _, _, exe, tmp, err := prepareDownload(ctx, manifestURL)
st, err := Check(ctx, manifestURL)
if err != nil {
return "", err
}
pending := filepath.Join(filepath.Dir(exe), "Navis-pending.exe")
_ = os.Remove(pending)
if err := os.Rename(tmp, pending); err != nil {
if err2 := copyFile(tmp, pending); err2 != nil {
_ = os.Remove(tmp)
return "", err2
if !st.Available {
if st.Error != "" {
return "", fmt.Errorf("%s", st.Error)
}
_ = os.Remove(tmp)
return "", fmt.Errorf("обновление не найдено")
}
_ = os.Chmod(pending, 0o755)
cmd := exec.Command(pending, finishUpdateFlag, strconv.Itoa(os.Getpid()), exe)
cmd.Dir = filepath.Dir(exe)
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: false}
if err := cmd.Start(); err != nil {
return "", fmt.Errorf("start updater: %w", err)
}
return latest, nil
if !allowedDownloadURL(st.URL) {
return "", fmt.Errorf("URL обновления должен быть на git.evilfox.cc или files.de4ima.uk")
}
// MaybeFinishUpdate handles: Navis-pending.exe --navis-finish-update <pid> <targetExe>
// Returns true if this process was the updater and should exit.
func MaybeFinishUpdate(args []string) bool {
if len(args) < 3 || args[0] != finishUpdateFlag {
dir, err := downloadsDir()
if err != nil {
return "", err
}
_ = os.MkdirAll(dir, 0o755)
dest := filepath.Join(dir, fmt.Sprintf("Navis-%s-windows-amd64.exe", sanitizeFileVer(st.Latest)))
tmp := dest + ".part"
_ = os.Remove(tmp)
_ = os.Remove(dest)
if err := downloadFile(ctx, st.URL, tmp); err != nil {
return "", err
}
if st.SHA256 != "" {
sum, err := fileSHA256(tmp)
if err != nil {
_ = os.Remove(tmp)
return "", err
}
if !equalFoldHex(sum, st.SHA256) {
_ = os.Remove(tmp)
return "", fmt.Errorf("checksum mismatch")
}
}
if err := os.Rename(tmp, dest); err != nil {
_ = os.Remove(tmp)
return "", err
}
_ = revealInExplorer(dest)
return fmt.Sprintf("Скачано v%s:\n%s\nЗакройте Navis и запустите новый файл.", st.Latest, dest), nil
}
func downloadsDir() (string, error) {
if u := os.Getenv("USERPROFILE"); u != "" {
return filepath.Join(u, "Downloads"), nil
}
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, "Downloads"), nil
}
func revealInExplorer(path string) error {
path, err := filepath.Abs(path)
if err != nil {
return err
}
cmd := exec.Command("explorer.exe", "/select,", path)
const createNoWindow = 0x08000000
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: createNoWindow}
return cmd.Start()
}
func sanitizeFileVer(v string) string {
out := make([]rune, 0, len(v))
for _, r := range v {
switch {
case r >= '0' && r <= '9', r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r == '.', r == '-':
out = append(out, r)
}
}
if len(out) == 0 {
return "update"
}
return string(out)
}
func equalFoldHex(a, b string) bool {
if len(a) != len(b) {
return false
}
pid, err := strconv.Atoi(args[1])
if err != nil || pid <= 0 {
for i := 0; i < len(a); i++ {
ca, cb := a[i], b[i]
if ca >= 'A' && ca <= 'F' {
ca += 'a' - 'A'
}
if cb >= 'A' && cb <= 'F' {
cb += 'a' - 'A'
}
if ca != cb {
return false
}
}
return true
}
target := args[2]
self, err := os.Executable()
if err != nil {
return true
}
self, _ = filepath.Abs(self)
target, _ = filepath.Abs(target)
waitPIDExit(uint32(pid), 90*time.Second)
time.Sleep(500 * time.Millisecond)
backup := target + ".bak"
_ = os.Remove(backup)
if err := os.Rename(target, backup); err != nil {
// Still locked — wait a bit more.
time.Sleep(2 * time.Second)
_ = os.Rename(target, backup)
}
if err := copyFile(self, target); err != nil {
_ = os.Rename(backup, target)
return true
}
_ = os.Chmod(target, 0o755)
_ = os.Remove(backup)
_ = os.Remove(self)
cmd := exec.Command(target)
cmd.Dir = filepath.Dir(target)
_ = cmd.Start()
return true
}
func waitPIDExit(pid uint32, timeout time.Duration) {
const synchronize = 0x00100000
h, err := windows.OpenProcess(synchronize, false, pid)
if err != nil {
time.Sleep(1500 * time.Millisecond)
return
}
defer windows.CloseHandle(h)
ms := uint32(timeout / time.Millisecond)
if ms == 0 {
ms = 1000
}
_, _ = windows.WaitForSingleObject(h, ms)
}
func copyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o755)
if err != nil {
return err
}
defer out.Close()
if _, err := io.Copy(out, in); err != nil {
return err
}
return out.Close()
}
+10 -10
View File
@@ -1,38 +1,38 @@
{
"version": "1.7.0",
"notes": "Меньше ложных срабатываний AV: без bat-апдейтера и тихой загрузки cores; Windows manifest; metadata EvilFox",
"version": "1.7.1",
"notes": "AV: обновление только в Downloads без самозамены; без netsh и автопроверки при старте",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe",
"sha256": "8bb8bab990292be66f169dc0a23f0aaff45cf349b6c6b5cfbef763f55cfe0d4d",
"sha256": "e08425b2eddd17a71a6e083990313811df9a1f3a55586c407798bfeeb82fd68b",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe",
"sha256": "8bb8bab990292be66f169dc0a23f0aaff45cf349b6c6b5cfbef763f55cfe0d4d",
"sha256": "e08425b2eddd17a71a6e083990313811df9a1f3a55586c407798bfeeb82fd68b",
"os": "windows",
"arch": "amd64"
},
"darwin-arm64": {
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis",
"sha256": "3e544461c1ce8ec3b508639cb549a759b773da38517c98c9ca634faaf09a601c",
"sha256": "3cb42ab226b0263c8b6566888d80c6e7a6982d8cda992d112989c7f5c8a99bab",
"os": "darwin",
"arch": "arm64",
"dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg",
"dmg_sha256": "39e88c985867ce6ef37deb00d87e1c6669cfbc8b9f8f0589d11a3f32036a5258",
"dmg_sha256": "65b8fe8c0d294ee923d59125f3e39c37f7c8a201aa6fd4661d080da59928c2e4",
"zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.app.zip",
"zip_sha256": "213fd7563e068f60a1ae2504c6e48171b9f9e3f2fe3c14ecad75ed809fa05d06"
"zip_sha256": "6685b1f8be9f8aa87613f35b8f8a86a00a6fb667c2f7bdc7116e6908c0cb1513"
},
"darwin-amd64": {
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis",
"sha256": "f40a711108e78bfa1a944a1d4946549aba32cb5e00ef9035be352810c700807c",
"sha256": "7bffda217e178224f3a68fed4d5fff4e37b4705b7c8291b84f1d58151ef43b9d",
"os": "darwin",
"arch": "amd64",
"dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg",
"dmg_sha256": "c70fe7feb000eec34c0fe4ef2d05565dd0285c0c4f18a264ecad540d2c0878db",
"dmg_sha256": "959526963936ad22d3c3607fe18295018362ec0d5fedcdf7561edf5d71c3b827",
"zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.app.zip",
"zip_sha256": "5b3e3fea5aef4ac962c18a190c1943c7cfd5357ea233abbdec1d26007e1e6eb4"
"zip_sha256": "079c2af3fe5de6b9e73bad15a15bddfc93970d27be68b6620b5be3729163fc15"
}
}
}
+4 -4
View File
@@ -1,7 +1,7 @@
{
"FixedFileInfo": {
"FileVersion": { "Major": 1, "Minor": 7, "Patch": 0, "Build": 0 },
"ProductVersion": { "Major": 1, "Minor": 7, "Patch": 0, "Build": 0 },
"FileVersion": { "Major": 1, "Minor": 7, "Patch": 1, "Build": 0 },
"ProductVersion": { "Major": 1, "Minor": 7, "Patch": 1, "Build": 0 },
"FileFlagsMask": "3f",
"FileFlags": "00",
"FileOS": "40004",
@@ -11,12 +11,12 @@
"StringFileInfo": {
"CompanyName": "EvilFox",
"FileDescription": "Navis VPN client (NaiveProxy / Hysteria 2)",
"FileVersion": "1.7.0.0",
"FileVersion": "1.7.1.0",
"InternalName": "Navis",
"LegalCopyright": "Copyright (c) EvilFox",
"OriginalFilename": "Navis.exe",
"ProductName": "Navis",
"ProductVersion": "1.7.0.0",
"ProductVersion": "1.7.1.0",
"Comments": "Open-source VPN/proxy client. https://evilfox.win/"
},
"VarFileInfo": {