shadow1ng/fscan · error

local_pe_not_found

Error message

local_pe_not_found

What it means

WinServicePlugin.Scan stats the configured PE path to ensure the local file exists before comparing it with installed service binaries. If os.Stat fails, the plugin returns this localized 'local PE not found' error including the configured path. Service enumeration is skipped entirely.

Source

Thrown at plugins/local/winservice.go:34

)

type WinServicePlugin struct {
	plugins.BasePlugin
}

func NewWinServicePlugin() *WinServicePlugin {
	return &WinServicePlugin{
		BasePlugin: plugins.NewBasePlugin("winservice"),
	}
}

func (p *WinServicePlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result {
	pePath := session.Config.WinPEFile
	if pePath == "" {
		return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.GetText("local_pe_not_specified"))}
	}
	if _, err := os.Stat(pePath); err != nil {
		return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.Tr("local_pe_not_found", pePath))}
	}

	absPath, _ := filepath.Abs(pePath)
	baseName := strings.TrimSuffix(filepath.Base(absPath), filepath.Ext(absPath))

	services := []struct {
		name    string
		display string
		start   string
	}{
		{fmt.Sprintf("WinDefendUpdate_%s", baseName), "Windows Defender Update Service", "auto"},
		{fmt.Sprintf("SysHealthMon_%s", baseName), "System Health Monitor", "delayed-auto"},
	}

	var output strings.Builder
	var successCount int

	for _, svc := range services {

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Verify and correct the path stored in Config.WinPEFile.
  2. Use an absolute path and check existence (os.Stat) before invoking the plugin.
  3. Restore the file or re-point the config to a copy that exists locally.
  4. Check read permissions on the file's directory.

Example fix

// before
session.Config.WinPEFile = "./agent.exe" // not in CWD
// after
pe := "/home/analyst/samples/agent.exe"
if _, err := os.Stat(pe); err != nil { log.Fatal(err) }
session.Config.WinPEFile = pe
Defensive patterns

Strategy: validation

Validate before calling

pePath := cfg.WinPEFile
if abs, err := filepath.Abs(pePath); err == nil { pePath = abs }
if _, err := os.Stat(pePath); err != nil {
    return fmt.Errorf("service PE sample missing: %w", err)
}

Try / catch

res := plugin.Scan(ctx, host, session)
if !res.Success {
    if _, statErr := os.Stat(session.Config.WinPEFile); statErr != nil {
        // file vanished; re-acquire sample and retry
    }
}

Prevention

When it happens

Trigger: Config.WinPEFile non-empty but the path does not resolve: nonexistent file, relative path against an unexpected CWD, removed/quarantined sample, or unreadable directory.

Common situations: Path typo; the sample was deleted after the config was written; running the tool under a different user/CWD; pointing to a network share that is offline.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/48a1f817b9d124b9. Report an issue: GitHub.