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

proc: implement attach on windows

Updates #363
上级 c3ade94b
......@@ -79,6 +79,11 @@ func New(pid int) *Process {
ptraceChan: make(chan func()),
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()
return dbp
}
......@@ -704,11 +709,6 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e
return nil, err
}
switch runtime.GOARCH {
case "amd64":
dbp.arch = AMD64Arch()
}
if err := dbp.updateThreadList(); err != nil {
return nil, err
}
......
......@@ -8,21 +8,15 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"sync"
"syscall"
"unsafe"
sys "golang.org/x/sys/windows"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/frame"
"github.com/derekparker/delve/dwarf/line"
)
const (
// DEBUGONLYTHISPROCESS tracks https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
DEBUGONLYTHISPROCESS = 0x00000002
"golang.org/x/debug/dwarf"
)
// OSProcessDetails holds Windows specific information.
......@@ -97,24 +91,25 @@ func Launch(cmd []string) (*Process, error) {
si.StdOutput = sys.Handle(fd[1])
si.StdErr = sys.Handle(fd[2])
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 {
return nil, err
}
sys.CloseHandle(sys.Handle(pi.Process))
sys.CloseHandle(sys.Handle(pi.Thread))
dbp := New(int(pi.ProcessId))
switch runtime.GOARCH {
case "amd64":
dbp.arch = AMD64Arch()
}
return newDebugProcess(int(pi.ProcessId), argv0Go)
}
// 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
// will always fire a CreateProcess event immediately
// after launching under DEBUGONLYTHISPROCESS.
// will always fire a CREATE_PROCESS_DEBUG_EVENT event
// immediately after launching under DEBUG_ONLY_THIS_PROCESS.
// Attaching with DebugActiveProcess has similar effect.
var err error
var tid, exitCode int
dbp.execPtraceFunc(func() {
tid, exitCode, err = dbp.waitForDebugEvent()
......@@ -126,13 +121,55 @@ func Launch(cmd []string) (*Process, error) {
dbp.postExit()
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.
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.
......@@ -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")
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)
if err != nil {
return nil, err
......
......@@ -69,6 +69,9 @@ const (
_UNLOAD_DLL_DEBUG_EVENT = 7
_OUTPUT_DEBUG_STRING_EVENT = 8
_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 {
......@@ -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 _DebugBreakProcess(process syscall.Handle) (err error) = kernel32.DebugBreakProcess
//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 @@
package proc
import "unsafe"
import "syscall"
import (
"syscall"
"unsafe"
)
var _ unsafe.Pointer
......@@ -11,16 +13,18 @@ var (
modntdll = syscall.NewLazyDLL("ntdll.dll")
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
procNtQueryInformationThread = modntdll.NewProc("NtQueryInformationThread")
procGetThreadContext = modkernel32.NewProc("GetThreadContext")
procSetThreadContext = modkernel32.NewProc("SetThreadContext")
procSuspendThread = modkernel32.NewProc("SuspendThread")
procResumeThread = modkernel32.NewProc("ResumeThread")
procContinueDebugEvent = modkernel32.NewProc("ContinueDebugEvent")
procWriteProcessMemory = modkernel32.NewProc("WriteProcessMemory")
procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory")
procDebugBreakProcess = modkernel32.NewProc("DebugBreakProcess")
procWaitForDebugEvent = modkernel32.NewProc("WaitForDebugEvent")
procNtQueryInformationThread = modntdll.NewProc("NtQueryInformationThread")
procGetThreadContext = modkernel32.NewProc("GetThreadContext")
procSetThreadContext = modkernel32.NewProc("SetThreadContext")
procSuspendThread = modkernel32.NewProc("SuspendThread")
procResumeThread = modkernel32.NewProc("ResumeThread")
procContinueDebugEvent = modkernel32.NewProc("ContinueDebugEvent")
procWriteProcessMemory = modkernel32.NewProc("WriteProcessMemory")
procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory")
procDebugBreakProcess = modkernel32.NewProc("DebugBreakProcess")
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) {
......@@ -138,3 +142,27 @@ func _WaitForDebugEvent(debugevent *_DEBUG_EVENT, milliseconds uint32) (err erro
}
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.
先完成此消息的编辑!
想要评论请 注册