mirror of
https://github.com/go-delve/delve.git
synced 2025-11-03 13:57:33 +08:00
proc: replace cgo with syscalls on windows
Unrelated to conversion, I have also changed (*Thread).readMemory to return only first count bytes of memory just as advised by ReadProcessMemory. Fixes #409 Fixes #412 Fixes #416
This commit is contained in:
@ -1,7 +1,5 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
// #include "windows.h"
|
|
||||||
import "C"
|
|
||||||
import (
|
import (
|
||||||
"debug/gosym"
|
"debug/gosym"
|
||||||
"debug/pe"
|
"debug/pe"
|
||||||
@ -30,7 +28,7 @@ const (
|
|||||||
|
|
||||||
// OSProcessDetails holds Windows specific information.
|
// OSProcessDetails holds Windows specific information.
|
||||||
type OSProcessDetails struct {
|
type OSProcessDetails struct {
|
||||||
hProcess sys.Handle
|
hProcess syscall.Handle
|
||||||
breakThread int
|
breakThread int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,17 +119,13 @@ func (dbp *Process) Kill() error {
|
|||||||
// TODO: Should not have to ignore failures here,
|
// TODO: Should not have to ignore failures here,
|
||||||
// but some tests appear to Kill twice causing
|
// but some tests appear to Kill twice causing
|
||||||
// this to fail on second attempt.
|
// this to fail on second attempt.
|
||||||
_ = C.TerminateProcess(C.HANDLE(dbp.os.hProcess), 1)
|
_ = syscall.TerminateProcess(dbp.os.hProcess, 1)
|
||||||
dbp.exited = true
|
dbp.exited = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) requestManualStop() error {
|
func (dbp *Process) requestManualStop() error {
|
||||||
res := C.DebugBreakProcess(C.HANDLE(dbp.os.hProcess))
|
return _DebugBreakProcess(dbp.os.hProcess)
|
||||||
if res == C.FALSE {
|
|
||||||
return fmt.Errorf("failed to break process %d", dbp.Pid)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) updateThreadList() error {
|
func (dbp *Process) updateThreadList() error {
|
||||||
@ -140,7 +134,7 @@ func (dbp *Process) updateThreadList() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) addThread(hThread sys.Handle, threadID int, attach bool) (*Thread, error) {
|
func (dbp *Process) addThread(hThread syscall.Handle, threadID int, attach bool) (*Thread, error) {
|
||||||
if thread, ok := dbp.Threads[threadID]; ok {
|
if thread, ok := dbp.Threads[threadID]; ok {
|
||||||
return thread, nil
|
return thread, nil
|
||||||
}
|
}
|
||||||
@ -341,76 +335,76 @@ func dwarfFromPE(f *pe.File) (*dwarf.Data, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) waitForDebugEvent() (threadID, exitCode int, err error) {
|
func (dbp *Process) waitForDebugEvent() (threadID, exitCode int, err error) {
|
||||||
var debugEvent C.DEBUG_EVENT
|
var debugEvent _DEBUG_EVENT
|
||||||
shouldExit := false
|
shouldExit := false
|
||||||
for {
|
for {
|
||||||
// Wait for a debug event...
|
// Wait for a debug event...
|
||||||
res := C.WaitForDebugEvent(&debugEvent, C.INFINITE)
|
err := _WaitForDebugEvent(&debugEvent, syscall.INFINITE)
|
||||||
if res == C.FALSE {
|
if err != nil {
|
||||||
return 0, 0, fmt.Errorf("could not WaitForDebugEvent")
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... handle each event kind ...
|
// ... handle each event kind ...
|
||||||
unionPtr := unsafe.Pointer(&debugEvent.u[0])
|
unionPtr := unsafe.Pointer(&debugEvent.U[0])
|
||||||
switch debugEvent.dwDebugEventCode {
|
switch debugEvent.DebugEventCode {
|
||||||
case C.CREATE_PROCESS_DEBUG_EVENT:
|
case _CREATE_PROCESS_DEBUG_EVENT:
|
||||||
debugInfo := (*C.CREATE_PROCESS_DEBUG_INFO)(unionPtr)
|
debugInfo := (*_CREATE_PROCESS_DEBUG_INFO)(unionPtr)
|
||||||
hFile := debugInfo.hFile
|
hFile := debugInfo.File
|
||||||
if hFile != C.HANDLE(uintptr(0)) /* NULL */ && hFile != C.HANDLE(uintptr(0xFFFFFFFFFFFFFFFF)) /* INVALID_HANDLE_VALUE */ {
|
if hFile != 0 && hFile != syscall.InvalidHandle {
|
||||||
res = C.CloseHandle(hFile)
|
err = syscall.CloseHandle(hFile)
|
||||||
if res == C.FALSE {
|
if err != nil {
|
||||||
return 0, 0, fmt.Errorf("could not close create process file handle")
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dbp.os.hProcess = sys.Handle(debugInfo.hProcess)
|
dbp.os.hProcess = debugInfo.Process
|
||||||
_, err = dbp.addThread(sys.Handle(debugInfo.hThread), int(debugEvent.dwThreadId), false)
|
_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case C.CREATE_THREAD_DEBUG_EVENT:
|
case _CREATE_THREAD_DEBUG_EVENT:
|
||||||
debugInfo := (*C.CREATE_THREAD_DEBUG_INFO)(unionPtr)
|
debugInfo := (*_CREATE_THREAD_DEBUG_INFO)(unionPtr)
|
||||||
_, err = dbp.addThread(sys.Handle(debugInfo.hThread), int(debugEvent.dwThreadId), false)
|
_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case C.EXIT_THREAD_DEBUG_EVENT:
|
case _EXIT_THREAD_DEBUG_EVENT:
|
||||||
delete(dbp.Threads, int(debugEvent.dwThreadId))
|
delete(dbp.Threads, int(debugEvent.ThreadId))
|
||||||
break
|
break
|
||||||
case C.OUTPUT_DEBUG_STRING_EVENT:
|
case _OUTPUT_DEBUG_STRING_EVENT:
|
||||||
//TODO: Handle debug output strings
|
//TODO: Handle debug output strings
|
||||||
break
|
break
|
||||||
case C.LOAD_DLL_DEBUG_EVENT:
|
case _LOAD_DLL_DEBUG_EVENT:
|
||||||
debugInfo := (*C.LOAD_DLL_DEBUG_INFO)(unionPtr)
|
debugInfo := (*_LOAD_DLL_DEBUG_INFO)(unionPtr)
|
||||||
hFile := debugInfo.hFile
|
hFile := debugInfo.File
|
||||||
if hFile != C.HANDLE(uintptr(0)) /* NULL */ && hFile != C.HANDLE(uintptr(0xFFFFFFFFFFFFFFFF)) /* INVALID_HANDLE_VALUE */ {
|
if hFile != 0 && hFile != syscall.InvalidHandle {
|
||||||
res = C.CloseHandle(hFile)
|
err = syscall.CloseHandle(hFile)
|
||||||
if res == C.FALSE {
|
if err != nil {
|
||||||
return 0, 0, fmt.Errorf("could not close DLL load file handle")
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case C.UNLOAD_DLL_DEBUG_EVENT:
|
case _UNLOAD_DLL_DEBUG_EVENT:
|
||||||
break
|
break
|
||||||
case C.RIP_EVENT:
|
case _RIP_EVENT:
|
||||||
break
|
break
|
||||||
case C.EXCEPTION_DEBUG_EVENT:
|
case _EXCEPTION_DEBUG_EVENT:
|
||||||
tid := int(debugEvent.dwThreadId)
|
tid := int(debugEvent.ThreadId)
|
||||||
dbp.os.breakThread = tid
|
dbp.os.breakThread = tid
|
||||||
return tid, 0, nil
|
return tid, 0, nil
|
||||||
case C.EXIT_PROCESS_DEBUG_EVENT:
|
case _EXIT_PROCESS_DEBUG_EVENT:
|
||||||
debugInfo := (*C.EXIT_PROCESS_DEBUG_INFO)(unionPtr)
|
debugInfo := (*_EXIT_PROCESS_DEBUG_INFO)(unionPtr)
|
||||||
exitCode = int(debugInfo.dwExitCode)
|
exitCode = int(debugInfo.ExitCode)
|
||||||
shouldExit = true
|
shouldExit = true
|
||||||
default:
|
default:
|
||||||
return 0, 0, fmt.Errorf("unknown debug event code: %d", debugEvent.dwDebugEventCode)
|
return 0, 0, fmt.Errorf("unknown debug event code: %d", debugEvent.DebugEventCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// .. and then continue unless we received an event that indicated we should break into debugger.
|
// .. and then continue unless we received an event that indicated we should break into debugger.
|
||||||
res = C.ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, C.DBG_CONTINUE)
|
err = _ContinueDebugEvent(debugEvent.ProcessId, debugEvent.ThreadId, _DBG_CONTINUE)
|
||||||
if res == C.WINBOOL(0) {
|
if err != nil {
|
||||||
return 0, 0, fmt.Errorf("could not ContinueDebugEvent")
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldExit {
|
if shouldExit {
|
||||||
|
|||||||
@ -1,12 +1,9 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
// #include <windows.h>
|
|
||||||
import "C"
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"rsc.io/x86/x86asm"
|
"rsc.io/x86/x86asm"
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -97,22 +94,17 @@ func (r *Regs) TLS() uint64 {
|
|||||||
|
|
||||||
// SetPC sets the RIP register to the value specified by `pc`.
|
// SetPC sets the RIP register to the value specified by `pc`.
|
||||||
func (r *Regs) SetPC(thread *Thread, pc uint64) error {
|
func (r *Regs) SetPC(thread *Thread, pc uint64) error {
|
||||||
var context C.CONTEXT
|
context := newCONTEXT()
|
||||||
context.ContextFlags = C.CONTEXT_ALL
|
context.ContextFlags = _CONTEXT_ALL
|
||||||
|
|
||||||
res := C.GetThreadContext(C.HANDLE(thread.os.hThread), &context)
|
err := _GetThreadContext(thread.os.hThread, context)
|
||||||
if res == C.FALSE {
|
if err != nil {
|
||||||
return fmt.Errorf("could not GetThreadContext")
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Rip = C.DWORD64(pc)
|
context.Rip = pc
|
||||||
|
|
||||||
res = C.SetThreadContext(C.HANDLE(thread.os.hThread), &context)
|
return _SetThreadContext(thread.os.hThread, context)
|
||||||
if res == C.FALSE {
|
|
||||||
return fmt.Errorf("could not SetThreadContext")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Regs) Get(n int) (uint64, error) {
|
func (r *Regs) Get(n int) (uint64, error) {
|
||||||
@ -273,18 +265,18 @@ func (r *Regs) Get(n int) (uint64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func registers(thread *Thread) (Registers, error) {
|
func registers(thread *Thread) (Registers, error) {
|
||||||
var context C.CONTEXT
|
context := newCONTEXT()
|
||||||
|
|
||||||
context.ContextFlags = C.CONTEXT_ALL
|
context.ContextFlags = _CONTEXT_ALL
|
||||||
res := C.GetThreadContext(C.HANDLE(thread.os.hThread), &context)
|
err := _GetThreadContext(thread.os.hThread, context)
|
||||||
if res == C.FALSE {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read ThreadContext")
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var threadInfo _THREAD_BASIC_INFORMATION
|
var threadInfo _THREAD_BASIC_INFORMATION
|
||||||
status := _NtQueryInformationThread(syscall.Handle(thread.os.hThread), _ThreadBasicInformation, uintptr(unsafe.Pointer(&threadInfo)), uint32(unsafe.Sizeof(threadInfo)), nil)
|
status := _NtQueryInformationThread(thread.os.hThread, _ThreadBasicInformation, uintptr(unsafe.Pointer(&threadInfo)), uint32(unsafe.Sizeof(threadInfo)), nil)
|
||||||
if !_NT_SUCCESS(status) {
|
if !_NT_SUCCESS(status) {
|
||||||
return nil, fmt.Errorf("failed to get thread_basic_information")
|
return nil, fmt.Errorf("NtQueryInformationThread failed: it returns 0x%x", status)
|
||||||
}
|
}
|
||||||
|
|
||||||
regs := &Regs{
|
regs := &Regs{
|
||||||
|
|||||||
@ -22,8 +22,53 @@ type _THREAD_BASIC_INFORMATION struct {
|
|||||||
BasePriority int32
|
BasePriority int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type _CREATE_PROCESS_DEBUG_INFO struct {
|
||||||
|
File syscall.Handle
|
||||||
|
Process syscall.Handle
|
||||||
|
Thread syscall.Handle
|
||||||
|
BaseOfImage uintptr
|
||||||
|
DebugInfoFileOffset uint32
|
||||||
|
DebugInfoSize uint32
|
||||||
|
ThreadLocalBase uintptr
|
||||||
|
StartAddress uintptr
|
||||||
|
ImageName uintptr
|
||||||
|
Unicode uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type _CREATE_THREAD_DEBUG_INFO struct {
|
||||||
|
Thread syscall.Handle
|
||||||
|
ThreadLocalBase uintptr
|
||||||
|
StartAddress uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type _EXIT_PROCESS_DEBUG_INFO struct {
|
||||||
|
ExitCode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type _LOAD_DLL_DEBUG_INFO struct {
|
||||||
|
File syscall.Handle
|
||||||
|
BaseOfDll uintptr
|
||||||
|
DebugInfoFileOffset uint32
|
||||||
|
DebugInfoSize uint32
|
||||||
|
ImageName uintptr
|
||||||
|
Unicode uint16
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_ThreadBasicInformation = 0
|
_ThreadBasicInformation = 0
|
||||||
|
|
||||||
|
_DBG_CONTINUE = 0x00010002
|
||||||
|
_DBG_EXCEPTION_NOT_HANDLED = 0x80010001
|
||||||
|
|
||||||
|
_EXCEPTION_DEBUG_EVENT = 1
|
||||||
|
_CREATE_THREAD_DEBUG_EVENT = 2
|
||||||
|
_CREATE_PROCESS_DEBUG_EVENT = 3
|
||||||
|
_EXIT_THREAD_DEBUG_EVENT = 4
|
||||||
|
_EXIT_PROCESS_DEBUG_EVENT = 5
|
||||||
|
_LOAD_DLL_DEBUG_EVENT = 6
|
||||||
|
_UNLOAD_DLL_DEBUG_EVENT = 7
|
||||||
|
_OUTPUT_DEBUG_STRING_EVENT = 8
|
||||||
|
_RIP_EVENT = 9
|
||||||
)
|
)
|
||||||
|
|
||||||
func _NT_SUCCESS(x _NTSTATUS) bool {
|
func _NT_SUCCESS(x _NTSTATUS) bool {
|
||||||
@ -31,3 +76,12 @@ func _NT_SUCCESS(x _NTSTATUS) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//sys _NtQueryInformationThread(threadHandle syscall.Handle, infoclass int32, info uintptr, infolen uint32, retlen *uint32) (status _NTSTATUS) = ntdll.NtQueryInformationThread
|
//sys _NtQueryInformationThread(threadHandle syscall.Handle, infoclass int32, info uintptr, infolen uint32, retlen *uint32) (status _NTSTATUS) = ntdll.NtQueryInformationThread
|
||||||
|
//sys _GetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) = kernel32.GetThreadContext
|
||||||
|
//sys _SetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) = kernel32.SetThreadContext
|
||||||
|
//sys _SuspendThread(threadid syscall.Handle) (prevsuspcount uint32, err error) [failretval==0xffffffff] = kernel32.SuspendThread
|
||||||
|
//sys _ResumeThread(threadid syscall.Handle) (prevsuspcount uint32, err error) [failretval==0xffffffff] = kernel32.ResumeThread
|
||||||
|
//sys _ContinueDebugEvent(processid uint32, threadid uint32, continuestatus uint32) (err error) = kernel32.ContinueDebugEvent
|
||||||
|
//sys _WriteProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, byteswritten *uintptr) (err error) = kernel32.WriteProcessMemory
|
||||||
|
//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
|
||||||
|
|||||||
95
proc/syscall_windows_amd64.go
Normal file
95
proc/syscall_windows_amd64.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package proc
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
const (
|
||||||
|
_CONTEXT_AMD64 = 0x100000
|
||||||
|
_CONTEXT_CONTROL = (_CONTEXT_AMD64 | 0x1)
|
||||||
|
_CONTEXT_INTEGER = (_CONTEXT_AMD64 | 0x2)
|
||||||
|
_CONTEXT_SEGMENTS = (_CONTEXT_AMD64 | 0x4)
|
||||||
|
_CONTEXT_FLOATING_POINT = (_CONTEXT_AMD64 | 0x8)
|
||||||
|
_CONTEXT_DEBUG_REGISTERS = (_CONTEXT_AMD64 | 0x10)
|
||||||
|
_CONTEXT_FULL = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_FLOATING_POINT)
|
||||||
|
_CONTEXT_ALL = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_SEGMENTS | _CONTEXT_FLOATING_POINT | _CONTEXT_DEBUG_REGISTERS)
|
||||||
|
_CONTEXT_EXCEPTION_ACTIVE = 0x8000000
|
||||||
|
_CONTEXT_SERVICE_ACTIVE = 0x10000000
|
||||||
|
_CONTEXT_EXCEPTION_REQUEST = 0x40000000
|
||||||
|
_CONTEXT_EXCEPTION_REPORTING = 0x80000000
|
||||||
|
)
|
||||||
|
|
||||||
|
type _M128A struct {
|
||||||
|
Low uint64
|
||||||
|
High int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type _CONTEXT struct {
|
||||||
|
P1Home uint64
|
||||||
|
P2Home uint64
|
||||||
|
P3Home uint64
|
||||||
|
P4Home uint64
|
||||||
|
P5Home uint64
|
||||||
|
P6Home uint64
|
||||||
|
|
||||||
|
ContextFlags uint32
|
||||||
|
MxCsr uint32
|
||||||
|
|
||||||
|
SegCs uint16
|
||||||
|
SegDs uint16
|
||||||
|
SegEs uint16
|
||||||
|
SegFs uint16
|
||||||
|
SegGs uint16
|
||||||
|
SegSs uint16
|
||||||
|
EFlags uint32
|
||||||
|
|
||||||
|
Dr0 uint64
|
||||||
|
Dr1 uint64
|
||||||
|
Dr2 uint64
|
||||||
|
Dr3 uint64
|
||||||
|
Dr6 uint64
|
||||||
|
Dr7 uint64
|
||||||
|
|
||||||
|
Rax uint64
|
||||||
|
Rcx uint64
|
||||||
|
Rdx uint64
|
||||||
|
Rbx uint64
|
||||||
|
Rsp uint64
|
||||||
|
Rbp uint64
|
||||||
|
Rsi uint64
|
||||||
|
Rdi uint64
|
||||||
|
R8 uint64
|
||||||
|
R9 uint64
|
||||||
|
R10 uint64
|
||||||
|
R11 uint64
|
||||||
|
R12 uint64
|
||||||
|
R13 uint64
|
||||||
|
R14 uint64
|
||||||
|
R15 uint64
|
||||||
|
|
||||||
|
Rip uint64
|
||||||
|
|
||||||
|
FltSave [512]byte
|
||||||
|
|
||||||
|
VectorRegister [26]_M128A
|
||||||
|
VectorControl uint64
|
||||||
|
|
||||||
|
DebugControl uint64
|
||||||
|
LastBranchToRip uint64
|
||||||
|
LastBranchFromRip uint64
|
||||||
|
LastExceptionToRip uint64
|
||||||
|
LastExceptionFromRip uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// newCONTEXT allocates Windows CONTEXT structure aligned to 16 bytes.
|
||||||
|
func newCONTEXT() *_CONTEXT {
|
||||||
|
var c *_CONTEXT
|
||||||
|
buf := make([]byte, unsafe.Sizeof(*c)+15)
|
||||||
|
return (*_CONTEXT)(unsafe.Pointer((uintptr(unsafe.Pointer(&buf[15]))) &^ 15))
|
||||||
|
}
|
||||||
|
|
||||||
|
type _DEBUG_EVENT struct {
|
||||||
|
DebugEventCode uint32
|
||||||
|
ProcessId uint32
|
||||||
|
ThreadId uint32
|
||||||
|
_ uint32 // to align Union properly
|
||||||
|
U [160]byte
|
||||||
|
}
|
||||||
@ -1,10 +1,8 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
// #include <windows.h>
|
|
||||||
import "C"
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"unsafe"
|
"syscall"
|
||||||
|
|
||||||
sys "golang.org/x/sys/windows"
|
sys "golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
@ -15,7 +13,7 @@ type WaitStatus sys.WaitStatus
|
|||||||
// OSSpecificDetails holds information specific to the Windows
|
// OSSpecificDetails holds information specific to the Windows
|
||||||
// operating system / kernel.
|
// operating system / kernel.
|
||||||
type OSSpecificDetails struct {
|
type OSSpecificDetails struct {
|
||||||
hThread sys.Handle
|
hThread syscall.Handle
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) halt() (err error) {
|
func (t *Thread) halt() (err error) {
|
||||||
@ -29,20 +27,20 @@ func (t *Thread) halt() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) singleStep() error {
|
func (t *Thread) singleStep() error {
|
||||||
var context C.CONTEXT
|
context := newCONTEXT()
|
||||||
context.ContextFlags = C.CONTEXT_ALL
|
context.ContextFlags = _CONTEXT_ALL
|
||||||
|
|
||||||
// Set the processor TRAP flag
|
// Set the processor TRAP flag
|
||||||
res := C.GetThreadContext(C.HANDLE(t.os.hThread), &context)
|
err := _GetThreadContext(t.os.hThread, context)
|
||||||
if res == C.FALSE {
|
if err != nil {
|
||||||
return fmt.Errorf("could not GetThreadContext")
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
context.EFlags |= 0x100
|
context.EFlags |= 0x100
|
||||||
|
|
||||||
res = C.SetThreadContext(C.HANDLE(t.os.hThread), &context)
|
err = _SetThreadContext(t.os.hThread, context)
|
||||||
if res == C.FALSE {
|
if err != nil {
|
||||||
return fmt.Errorf("could not SetThreadContext")
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suspend all threads except this one
|
// Suspend all threads except this one
|
||||||
@ -50,20 +48,21 @@ func (t *Thread) singleStep() error {
|
|||||||
if thread.ID == t.ID {
|
if thread.ID == t.ID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
res := C.SuspendThread(C.HANDLE(thread.os.hThread))
|
_, err := _SuspendThread(thread.os.hThread)
|
||||||
if res == C.DWORD(0xFFFFFFFF) {
|
if err != nil {
|
||||||
return fmt.Errorf("could not suspend thread: %d", thread.ID)
|
return fmt.Errorf("could not suspend thread: %d: %v", thread.ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue and wait for the step to complete
|
// Continue and wait for the step to complete
|
||||||
|
err = nil
|
||||||
t.dbp.execPtraceFunc(func() {
|
t.dbp.execPtraceFunc(func() {
|
||||||
res = C.ContinueDebugEvent(C.DWORD(t.dbp.Pid), C.DWORD(t.ID), C.DBG_CONTINUE)
|
err = _ContinueDebugEvent(uint32(t.dbp.Pid), uint32(t.ID), _DBG_CONTINUE)
|
||||||
})
|
})
|
||||||
if res == C.FALSE {
|
if err != nil {
|
||||||
return fmt.Errorf("could not ContinueDebugEvent.")
|
return err
|
||||||
}
|
}
|
||||||
_, err := t.dbp.trapWait(0)
|
_, err = t.dbp.trapWait(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -73,40 +72,32 @@ func (t *Thread) singleStep() error {
|
|||||||
if thread.ID == t.ID {
|
if thread.ID == t.ID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
res := C.ResumeThread(C.HANDLE(thread.os.hThread))
|
_, err := _ResumeThread(thread.os.hThread)
|
||||||
if res == C.DWORD(0xFFFFFFFF) {
|
if err != nil {
|
||||||
return fmt.Errorf("ould not resume thread: %d", thread.ID)
|
return fmt.Errorf("could not resume thread: %d: %v", thread.ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unset the processor TRAP flag
|
// Unset the processor TRAP flag
|
||||||
res = C.GetThreadContext(C.HANDLE(t.os.hThread), &context)
|
err = _GetThreadContext(t.os.hThread, context)
|
||||||
if res == C.FALSE {
|
if err != nil {
|
||||||
return fmt.Errorf("could not GetThreadContext")
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
context.EFlags &= ^C.DWORD(0x100)
|
context.EFlags &= ^uint32(0x100)
|
||||||
|
|
||||||
res = C.SetThreadContext(C.HANDLE(t.os.hThread), &context)
|
return _SetThreadContext(t.os.hThread, context)
|
||||||
if res == C.FALSE {
|
|
||||||
return fmt.Errorf("could not SetThreadContext")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) resume() error {
|
func (t *Thread) resume() error {
|
||||||
t.running = true
|
t.running = true
|
||||||
var res C.WINBOOL
|
var err error
|
||||||
t.dbp.execPtraceFunc(func() {
|
t.dbp.execPtraceFunc(func() {
|
||||||
//TODO: Note that we are ignoring the thread we were asked to continue and are continuing the
|
//TODO: Note that we are ignoring the thread we were asked to continue and are continuing the
|
||||||
//thread that we last broke on.
|
//thread that we last broke on.
|
||||||
res = C.ContinueDebugEvent(C.DWORD(t.dbp.Pid), C.DWORD(t.ID), C.DBG_CONTINUE)
|
err = _ContinueDebugEvent(uint32(t.dbp.Pid), uint32(t.ID), _DBG_CONTINUE)
|
||||||
})
|
})
|
||||||
if res == C.FALSE {
|
return err
|
||||||
return fmt.Errorf("could not ContinueDebugEvent.")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) blocked() bool {
|
func (t *Thread) blocked() bool {
|
||||||
@ -135,15 +126,10 @@ func (t *Thread) stopped() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
|
func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
|
||||||
var (
|
var count uintptr
|
||||||
vmData = C.LPCVOID(unsafe.Pointer(&data[0]))
|
err := _WriteProcessMemory(t.dbp.os.hProcess, addr, &data[0], uintptr(len(data)), &count)
|
||||||
vmAddr = C.LPVOID(addr)
|
if err != nil {
|
||||||
length = C.SIZE_T(len(data))
|
return 0, err
|
||||||
count C.SIZE_T
|
|
||||||
)
|
|
||||||
ret := C.WriteProcessMemory(C.HANDLE(t.dbp.os.hProcess), vmAddr, vmData, length, &count)
|
|
||||||
if ret == C.FALSE {
|
|
||||||
return int(count), fmt.Errorf("could not write memory")
|
|
||||||
}
|
}
|
||||||
return int(count), nil
|
return int(count), nil
|
||||||
}
|
}
|
||||||
@ -152,16 +138,11 @@ func (t *Thread) readMemory(addr uintptr, size int) ([]byte, error) {
|
|||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
var (
|
var count uintptr
|
||||||
buf = make([]byte, size)
|
buf := make([]byte, size)
|
||||||
vmData = C.LPVOID(unsafe.Pointer(&buf[0]))
|
err := _ReadProcessMemory(t.dbp.os.hProcess, addr, &buf[0], uintptr(size), &count)
|
||||||
vmAddr = C.LPCVOID(addr)
|
if err != nil {
|
||||||
length = C.SIZE_T(size)
|
return nil, err
|
||||||
count C.SIZE_T
|
|
||||||
)
|
|
||||||
ret := C.ReadProcessMemory(C.HANDLE(t.dbp.os.hProcess), vmAddr, vmData, length, &count)
|
|
||||||
if ret == C.FALSE {
|
|
||||||
return nil, fmt.Errorf("could not read memory")
|
|
||||||
}
|
}
|
||||||
return buf, nil
|
return buf[:count], nil
|
||||||
}
|
}
|
||||||
|
|||||||
122
proc/zsyscall_windows.go
Executable file → Normal file
122
proc/zsyscall_windows.go
Executable file → Normal file
@ -8,9 +8,19 @@ import "syscall"
|
|||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
var (
|
var (
|
||||||
modntdll = syscall.NewLazyDLL("ntdll.dll")
|
modntdll = syscall.NewLazyDLL("ntdll.dll")
|
||||||
|
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
|
||||||
procNtQueryInformationThread = modntdll.NewProc("NtQueryInformationThread")
|
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")
|
||||||
)
|
)
|
||||||
|
|
||||||
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) {
|
||||||
@ -18,3 +28,113 @@ func _NtQueryInformationThread(threadHandle syscall.Handle, infoclass int32, inf
|
|||||||
status = _NTSTATUS(r0)
|
status = _NTSTATUS(r0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _GetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procGetThreadContext.Addr(), 2, uintptr(thread), uintptr(unsafe.Pointer(context)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func _SetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procSetThreadContext.Addr(), 2, uintptr(thread), uintptr(unsafe.Pointer(context)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func _SuspendThread(threadid syscall.Handle) (prevsuspcount uint32, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procSuspendThread.Addr(), 1, uintptr(threadid), 0, 0)
|
||||||
|
prevsuspcount = uint32(r0)
|
||||||
|
if prevsuspcount == 0xffffffff {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func _ResumeThread(threadid syscall.Handle) (prevsuspcount uint32, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procResumeThread.Addr(), 1, uintptr(threadid), 0, 0)
|
||||||
|
prevsuspcount = uint32(r0)
|
||||||
|
if prevsuspcount == 0xffffffff {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func _ContinueDebugEvent(processid uint32, threadid uint32, continuestatus uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procContinueDebugEvent.Addr(), 3, uintptr(processid), uintptr(threadid), uintptr(continuestatus))
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func _WriteProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, byteswritten *uintptr) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procWriteProcessMemory.Addr(), 5, uintptr(process), uintptr(baseaddr), uintptr(unsafe.Pointer(buffer)), uintptr(size), uintptr(unsafe.Pointer(byteswritten)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func _ReadProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, bytesread *uintptr) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procReadProcessMemory.Addr(), 5, uintptr(process), uintptr(baseaddr), uintptr(unsafe.Pointer(buffer)), uintptr(size), uintptr(unsafe.Pointer(bytesread)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DebugBreakProcess(process syscall.Handle) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procDebugBreakProcess.Addr(), 1, uintptr(process), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func _WaitForDebugEvent(debugevent *_DEBUG_EVENT, milliseconds uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procWaitForDebugEvent.Addr(), 2, uintptr(unsafe.Pointer(debugevent)), uintptr(milliseconds), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user