提交 f09ef23f 编写于 作者: A Alex Brainman

proc: implement attach on windows

Updates #363
上级 c3ade94b
...@@ -79,6 +79,11 @@ func New(pid int) *Process { ...@@ -79,6 +79,11 @@ func New(pid int) *Process {
ptraceChan: make(chan func()), ptraceChan: make(chan func()),
ptraceDoneChan: make(chan interface{}), ptraceDoneChan: make(chan interface{}),
} }
// TODO: find better way to determine proc arch (perhaps use executable file info)
switch runtime.GOARCH {
case "amd64":
dbp.arch = AMD64Arch()
}
go dbp.handlePtraceFuncs() go dbp.handlePtraceFuncs()
return dbp return dbp
} }
...@@ -704,11 +709,6 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e ...@@ -704,11 +709,6 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e
return nil, err return nil, err
} }
switch runtime.GOARCH {
case "amd64":
dbp.arch = AMD64Arch()
}
if err := dbp.updateThreadList(); err != nil { if err := dbp.updateThreadList(); err != nil {
return nil, err return nil, err
} }
......
...@@ -8,21 +8,15 @@ import ( ...@@ -8,21 +8,15 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime"
"sync" "sync"
"syscall" "syscall"
"unsafe" "unsafe"
sys "golang.org/x/sys/windows" sys "golang.org/x/sys/windows"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/frame" "github.com/derekparker/delve/dwarf/frame"
"github.com/derekparker/delve/dwarf/line" "github.com/derekparker/delve/dwarf/line"
) "golang.org/x/debug/dwarf"
const (
// DEBUGONLYTHISPROCESS tracks https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
DEBUGONLYTHISPROCESS = 0x00000002
) )
// OSProcessDetails holds Windows specific information. // OSProcessDetails holds Windows specific information.
...@@ -97,24 +91,25 @@ func Launch(cmd []string) (*Process, error) { ...@@ -97,24 +91,25 @@ func Launch(cmd []string) (*Process, error) {
si.StdOutput = sys.Handle(fd[1]) si.StdOutput = sys.Handle(fd[1])
si.StdErr = sys.Handle(fd[2]) si.StdErr = sys.Handle(fd[2])
pi := new(sys.ProcessInformation) pi := new(sys.ProcessInformation)
err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, DEBUGONLYTHISPROCESS, nil, nil, si, pi) err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, _DEBUG_ONLY_THIS_PROCESS, nil, nil, si, pi)
if err != nil { if err != nil {
return nil, err return nil, err
} }
sys.CloseHandle(sys.Handle(pi.Process)) sys.CloseHandle(sys.Handle(pi.Process))
sys.CloseHandle(sys.Handle(pi.Thread)) sys.CloseHandle(sys.Handle(pi.Thread))
dbp := New(int(pi.ProcessId)) return newDebugProcess(int(pi.ProcessId), argv0Go)
}
switch runtime.GOARCH {
case "amd64":
dbp.arch = AMD64Arch()
}
// Note - it should not actually be possible for the // newDebugProcess prepares process pid for debugging.
func newDebugProcess(pid int, exepath string) (*Process, error) {
dbp := New(pid)
// It should not actually be possible for the
// call to waitForDebugEvent to fail, since Windows // call to waitForDebugEvent to fail, since Windows
// will always fire a CreateProcess event immediately // will always fire a CREATE_PROCESS_DEBUG_EVENT event
// after launching under DEBUGONLYTHISPROCESS. // immediately after launching under DEBUG_ONLY_THIS_PROCESS.
// Attaching with DebugActiveProcess has similar effect.
var err error
var tid, exitCode int var tid, exitCode int
dbp.execPtraceFunc(func() { dbp.execPtraceFunc(func() {
tid, exitCode, err = dbp.waitForDebugEvent() tid, exitCode, err = dbp.waitForDebugEvent()
...@@ -126,13 +121,55 @@ func Launch(cmd []string) (*Process, error) { ...@@ -126,13 +121,55 @@ func Launch(cmd []string) (*Process, error) {
dbp.postExit() dbp.postExit()
return nil, ProcessExitedError{Pid: dbp.Pid, Status: exitCode} return nil, ProcessExitedError{Pid: dbp.Pid, Status: exitCode}
} }
return initializeDebugProcess(dbp, exepath, false)
}
// findExePath searches for process pid, and returns its executable path.
func findExePath(pid int) (string, error) {
// Original code suggested different approach (see below).
// Maybe it could be useful in the future.
//
// Find executable path from PID/handle on Windows:
// https://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
p, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid))
if err != nil {
return "", err
}
defer syscall.CloseHandle(p)
return initializeDebugProcess(dbp, argv0Go, false) n := uint32(128)
for {
buf := make([]uint16, int(n))
err = _QueryFullProcessImageName(p, 0, &buf[0], &n)
switch err {
case syscall.ERROR_INSUFFICIENT_BUFFER:
// try bigger buffer
n *= 2
// but stop if it gets too big
if n > 10000 {
return "", err
}
case nil:
return syscall.UTF16ToString(buf[:n]), nil
default:
return "", err
}
}
} }
// Attach to an existing process with the given PID. // Attach to an existing process with the given PID.
func Attach(pid int) (*Process, error) { func Attach(pid int) (*Process, error) {
return nil, fmt.Errorf("not implemented: Attach") // TODO: Probably should have SeDebugPrivilege before starting here.
err := _DebugActiveProcess(uint32(pid))
if err != nil {
return nil, err
}
exepath, err := findExePath(pid)
if err != nil {
return nil, err
}
return newDebugProcess(pid, exepath)
} }
// Kill kills the process. // Kill kills the process.
...@@ -316,11 +353,6 @@ func (dbp *Process) parseDebugLineInfo(exe *pe.File, wg *sync.WaitGroup) { ...@@ -316,11 +353,6 @@ func (dbp *Process) parseDebugLineInfo(exe *pe.File, wg *sync.WaitGroup) {
var UnsupportedArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported") var UnsupportedArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
func (dbp *Process) findExecutable(path string) (*pe.File, error) { func (dbp *Process) findExecutable(path string) (*pe.File, error) {
if path == "" {
// TODO: Find executable path from PID/handle on Windows:
// https://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
return nil, fmt.Errorf("not yet implemented")
}
peFile, err := openExecutablePath(path) peFile, err := openExecutablePath(path)
if err != nil { if err != nil {
return nil, err return nil, err
......
...@@ -69,6 +69,9 @@ const ( ...@@ -69,6 +69,9 @@ const (
_UNLOAD_DLL_DEBUG_EVENT = 7 _UNLOAD_DLL_DEBUG_EVENT = 7
_OUTPUT_DEBUG_STRING_EVENT = 8 _OUTPUT_DEBUG_STRING_EVENT = 8
_RIP_EVENT = 9 _RIP_EVENT = 9
// DEBUG_ONLY_THIS_PROCESS tracks https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
_DEBUG_ONLY_THIS_PROCESS = 0x00000002
) )
func _NT_SUCCESS(x _NTSTATUS) bool { func _NT_SUCCESS(x _NTSTATUS) bool {
...@@ -85,3 +88,5 @@ func _NT_SUCCESS(x _NTSTATUS) bool { ...@@ -85,3 +88,5 @@ func _NT_SUCCESS(x _NTSTATUS) bool {
//sys _ReadProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, bytesread *uintptr) (err error) = kernel32.ReadProcessMemory //sys _ReadProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, bytesread *uintptr) (err error) = kernel32.ReadProcessMemory
//sys _DebugBreakProcess(process syscall.Handle) (err error) = kernel32.DebugBreakProcess //sys _DebugBreakProcess(process syscall.Handle) (err error) = kernel32.DebugBreakProcess
//sys _WaitForDebugEvent(debugevent *_DEBUG_EVENT, milliseconds uint32) (err error) = kernel32.WaitForDebugEvent //sys _WaitForDebugEvent(debugevent *_DEBUG_EVENT, milliseconds uint32) (err error) = kernel32.WaitForDebugEvent
//sys _DebugActiveProcess(processid uint32) (err error) = kernel32.DebugActiveProcess
//sys _QueryFullProcessImageName(process syscall.Handle, flags uint32, exename *uint16, size *uint32) (err error) = kernel32.QueryFullProcessImageNameW
...@@ -2,8 +2,10 @@ ...@@ -2,8 +2,10 @@
package proc package proc
import "unsafe" import (
import "syscall" "syscall"
"unsafe"
)
var _ unsafe.Pointer var _ unsafe.Pointer
...@@ -11,16 +13,18 @@ var ( ...@@ -11,16 +13,18 @@ var (
modntdll = syscall.NewLazyDLL("ntdll.dll") modntdll = syscall.NewLazyDLL("ntdll.dll")
modkernel32 = syscall.NewLazyDLL("kernel32.dll") modkernel32 = syscall.NewLazyDLL("kernel32.dll")
procNtQueryInformationThread = modntdll.NewProc("NtQueryInformationThread") procNtQueryInformationThread = modntdll.NewProc("NtQueryInformationThread")
procGetThreadContext = modkernel32.NewProc("GetThreadContext") procGetThreadContext = modkernel32.NewProc("GetThreadContext")
procSetThreadContext = modkernel32.NewProc("SetThreadContext") procSetThreadContext = modkernel32.NewProc("SetThreadContext")
procSuspendThread = modkernel32.NewProc("SuspendThread") procSuspendThread = modkernel32.NewProc("SuspendThread")
procResumeThread = modkernel32.NewProc("ResumeThread") procResumeThread = modkernel32.NewProc("ResumeThread")
procContinueDebugEvent = modkernel32.NewProc("ContinueDebugEvent") procContinueDebugEvent = modkernel32.NewProc("ContinueDebugEvent")
procWriteProcessMemory = modkernel32.NewProc("WriteProcessMemory") procWriteProcessMemory = modkernel32.NewProc("WriteProcessMemory")
procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory") procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory")
procDebugBreakProcess = modkernel32.NewProc("DebugBreakProcess") procDebugBreakProcess = modkernel32.NewProc("DebugBreakProcess")
procWaitForDebugEvent = modkernel32.NewProc("WaitForDebugEvent") procWaitForDebugEvent = modkernel32.NewProc("WaitForDebugEvent")
procDebugActiveProcess = modkernel32.NewProc("DebugActiveProcess")
procQueryFullProcessImageNameW = modkernel32.NewProc("QueryFullProcessImageNameW")
) )
func _NtQueryInformationThread(threadHandle syscall.Handle, infoclass int32, info uintptr, infolen uint32, retlen *uint32) (status _NTSTATUS) { func _NtQueryInformationThread(threadHandle syscall.Handle, infoclass int32, info uintptr, infolen uint32, retlen *uint32) (status _NTSTATUS) {
...@@ -138,3 +142,27 @@ func _WaitForDebugEvent(debugevent *_DEBUG_EVENT, milliseconds uint32) (err erro ...@@ -138,3 +142,27 @@ func _WaitForDebugEvent(debugevent *_DEBUG_EVENT, milliseconds uint32) (err erro
} }
return return
} }
func _DebugActiveProcess(processid uint32) (err error) {
r1, _, e1 := syscall.Syscall(procDebugActiveProcess.Addr(), 1, uintptr(processid), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func _QueryFullProcessImageName(process syscall.Handle, flags uint32, exename *uint16, size *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procQueryFullProcessImageNameW.Addr(), 4, uintptr(process), uintptr(flags), uintptr(unsafe.Pointer(exename)), uintptr(unsafe.Pointer(size)), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册