github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/proc/native/proc_windows.go (about) 1 package native 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 "syscall" 8 "unicode/utf16" 9 "unsafe" 10 11 sys "golang.org/x/sys/windows" 12 13 "github.com/go-delve/delve/pkg/logflags" 14 "github.com/go-delve/delve/pkg/proc" 15 "github.com/go-delve/delve/pkg/proc/internal/ebpf" 16 ) 17 18 // osProcessDetails holds Windows specific information. 19 type osProcessDetails struct { 20 hProcess syscall.Handle 21 breakThread int 22 entryPoint uint64 23 running bool 24 } 25 26 func (os *osProcessDetails) Close() {} 27 28 // Launch creates and begins debugging a new process. 29 func Launch(cmd []string, wd string, flags proc.LaunchFlags, _ []string, _ string, stdinPath string, stdoutOR proc.OutputRedirect, stderrOR proc.OutputRedirect) (*proc.TargetGroup, error) { 30 argv0Go := cmd[0] 31 32 env := proc.DisableAsyncPreemptEnv() 33 34 stdin, stdout, stderr, closefn, err := openRedirects(stdinPath, stdoutOR, stderrOR, true) 35 if err != nil { 36 return nil, err 37 } 38 39 creationFlags := uint32(_DEBUG_ONLY_THIS_PROCESS) 40 if flags&proc.LaunchForeground == 0 { 41 creationFlags |= syscall.CREATE_NEW_PROCESS_GROUP 42 } 43 44 var p *os.Process 45 dbp := newProcess(0) 46 dbp.execPtraceFunc(func() { 47 attr := &os.ProcAttr{ 48 Dir: wd, 49 Files: []*os.File{stdin, stdout, stderr}, 50 Sys: &syscall.SysProcAttr{ 51 CreationFlags: creationFlags, 52 }, 53 Env: env, 54 } 55 p, err = os.StartProcess(argv0Go, cmd, attr) 56 }) 57 closefn() 58 if err != nil { 59 return nil, err 60 } 61 defer p.Release() 62 63 dbp.pid = p.Pid 64 dbp.childProcess = true 65 66 tgt, err := dbp.initialize(argv0Go, []string{}) 67 if err != nil { 68 detachWithoutGroup(dbp, true) 69 return nil, err 70 } 71 return tgt, nil 72 } 73 74 func initialize(dbp *nativeProcess) (string, error) { 75 // It should not actually be possible for the 76 // call to waitForDebugEvent to fail, since Windows 77 // will always fire a CREATE_PROCESS_DEBUG_EVENT event 78 // immediately after launching under DEBUG_ONLY_THIS_PROCESS. 79 // Attaching with DebugActiveProcess has similar effect. 80 var err error 81 var tid, exitCode int 82 dbp.execPtraceFunc(func() { 83 tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking) 84 }) 85 if err != nil { 86 return "", err 87 } 88 if tid == 0 { 89 dbp.postExit() 90 return "", proc.ErrProcessExited{Pid: dbp.pid, Status: exitCode} 91 } 92 93 cmdline := getCmdLine(dbp.os.hProcess) 94 95 // Suspend all threads so that the call to _ContinueDebugEvent will 96 // not resume the target. 97 for _, thread := range dbp.threads { 98 if !thread.os.dbgUiRemoteBreakIn { 99 _, err := _SuspendThread(thread.os.hThread) 100 if err != nil { 101 return "", err 102 } 103 } 104 } 105 106 dbp.execPtraceFunc(func() { 107 err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE) 108 }) 109 return cmdline, err 110 } 111 112 // findExePath searches for process pid, and returns its executable path 113 func findExePath(pid int) (string, error) { 114 // Original code suggested different approach (see below). 115 // Maybe it could be useful in the future. 116 // 117 // Find executable path from PID/handle on Windows: 118 // https://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx 119 120 p, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid)) 121 if err != nil { 122 return "", err 123 } 124 defer syscall.CloseHandle(p) 125 126 n := uint32(128) 127 for { 128 buf := make([]uint16, int(n)) 129 err = _QueryFullProcessImageName(p, 0, &buf[0], &n) 130 switch err { 131 case syscall.ERROR_INSUFFICIENT_BUFFER: 132 // try bigger buffer 133 n *= 2 134 // but stop if it gets too big 135 if n > 10000 { 136 return "", err 137 } 138 case nil: 139 return syscall.UTF16ToString(buf[:n]), nil 140 default: 141 return "", err 142 } 143 } 144 } 145 146 var debugPrivilegeRequested = false 147 148 // Attach to an existing process with the given PID. 149 func Attach(pid int, waitFor *proc.WaitFor, _ []string) (*proc.TargetGroup, error) { 150 var aperr error 151 if !debugPrivilegeRequested { 152 debugPrivilegeRequested = true 153 // The following call will only work if the user is an administrator 154 // has the "Debug Programs" privilege in Local security settings. 155 // Since this privilege is not needed to debug processes owned by the 156 // current user, do not complain about this unless attach actually fails. 157 aperr = acquireDebugPrivilege() 158 } 159 160 if waitFor.Valid() { 161 var err error 162 pid, err = WaitFor(waitFor) 163 if err != nil { 164 return nil, err 165 } 166 } 167 168 dbp := newProcess(pid) 169 var err error 170 dbp.execPtraceFunc(func() { 171 // TODO: Probably should have SeDebugPrivilege before starting here. 172 err = _DebugActiveProcess(uint32(pid)) 173 }) 174 if err != nil { 175 if aperr != nil { 176 return nil, fmt.Errorf("%v also %v", err, aperr) 177 } 178 return nil, err 179 } 180 exepath, err := findExePath(pid) 181 if err != nil { 182 return nil, err 183 } 184 tgt, err := dbp.initialize(exepath, []string{}) 185 if err != nil { 186 detachWithoutGroup(dbp, true) 187 return nil, err 188 } 189 return tgt, nil 190 } 191 192 // acquireDebugPrivilege acquires the debug privilege which is needed to 193 // debug other user's processes. 194 // See: 195 // 196 // - https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debug-privilege 197 // - https://github.com/go-delve/delve/issues/3136 198 func acquireDebugPrivilege() error { 199 var token sys.Token 200 err := sys.OpenProcessToken(sys.CurrentProcess(), sys.TOKEN_QUERY|sys.TOKEN_ADJUST_PRIVILEGES, &token) 201 if err != nil { 202 return fmt.Errorf("could not acquire debug privilege (OpenCurrentProcessToken): %v", err) 203 } 204 defer token.Close() 205 206 privName, _ := sys.UTF16FromString("SeDebugPrivilege") 207 var luid sys.LUID 208 err = sys.LookupPrivilegeValue(nil, &privName[0], &luid) 209 if err != nil { 210 return fmt.Errorf("could not acquire debug privilege (LookupPrivilegeValue): %v", err) 211 } 212 213 var tp sys.Tokenprivileges 214 tp.PrivilegeCount = 1 215 tp.Privileges[0].Luid = luid 216 tp.Privileges[0].Attributes = sys.SE_PRIVILEGE_ENABLED 217 218 err = sys.AdjustTokenPrivileges(token, false, &tp, 0, nil, nil) 219 if err != nil { 220 return fmt.Errorf("could not acquire debug privilege (AdjustTokenPrivileges): %v", err) 221 } 222 223 return nil 224 } 225 226 func waitForSearchProcess(pfx string, seen map[int]struct{}) (int, error) { 227 log := logflags.DebuggerLogger() 228 handle, err := sys.CreateToolhelp32Snapshot(sys.TH32CS_SNAPPROCESS, 0) 229 if err != nil { 230 return 0, fmt.Errorf("could not get process list: %v", err) 231 } 232 defer sys.CloseHandle(handle) 233 234 var entry sys.ProcessEntry32 235 entry.Size = uint32(unsafe.Sizeof(entry)) 236 err = sys.Process32First(handle, &entry) 237 if err != nil { 238 return 0, fmt.Errorf("could not get process list: %v", err) 239 } 240 241 for err = sys.Process32First(handle, &entry); err == nil; err = sys.Process32Next(handle, &entry) { 242 if _, isseen := seen[int(entry.ProcessID)]; isseen { 243 continue 244 } 245 seen[int(entry.ProcessID)] = struct{}{} 246 247 hProcess, err := sys.OpenProcess(sys.PROCESS_QUERY_INFORMATION|sys.PROCESS_VM_READ, false, entry.ProcessID) 248 if err != nil { 249 continue 250 } 251 cmdline := getCmdLine(syscall.Handle(hProcess)) 252 sys.CloseHandle(hProcess) 253 254 log.Debugf("waitfor: new process %q", cmdline) 255 if strings.HasPrefix(cmdline, pfx) { 256 return int(entry.ProcessID), nil 257 } 258 } 259 260 return 0, nil 261 } 262 263 // kill kills the process. 264 func (procgrp *processGroup) kill(dbp *nativeProcess) error { 265 if dbp.exited { 266 return nil 267 } 268 269 p, err := os.FindProcess(dbp.pid) 270 if err != nil { 271 return err 272 } 273 defer p.Release() 274 275 // TODO: Should not have to ignore failures here, 276 // but some tests appear to Kill twice causing 277 // this to fail on second attempt. 278 _ = syscall.TerminateProcess(dbp.os.hProcess, 1) 279 280 dbp.execPtraceFunc(func() { 281 dbp.waitForDebugEvent(waitBlocking | waitDontHandleExceptions) 282 }) 283 284 p.Wait() 285 286 dbp.postExit() 287 return nil 288 } 289 290 func (dbp *nativeProcess) requestManualStop() error { 291 if !dbp.os.running { 292 return nil 293 } 294 dbp.os.running = false 295 return _DebugBreakProcess(dbp.os.hProcess) 296 } 297 298 func (dbp *nativeProcess) updateThreadList() error { 299 // We ignore this request since threads are being 300 // tracked as they are created/killed in waitForDebugEvent. 301 return nil 302 } 303 304 func (dbp *nativeProcess) addThread(hThread syscall.Handle, threadID int, attach, suspendNewThreads bool, dbgUiRemoteBreakIn bool) (*nativeThread, error) { 305 if thread, ok := dbp.threads[threadID]; ok { 306 return thread, nil 307 } 308 thread := &nativeThread{ 309 ID: threadID, 310 dbp: dbp, 311 os: new(osSpecificDetails), 312 } 313 thread.os.dbgUiRemoteBreakIn = dbgUiRemoteBreakIn 314 thread.os.hThread = hThread 315 dbp.threads[threadID] = thread 316 if dbp.memthread == nil { 317 dbp.memthread = dbp.threads[threadID] 318 } 319 if suspendNewThreads && !dbgUiRemoteBreakIn { 320 _, err := _SuspendThread(thread.os.hThread) 321 if err != nil { 322 return nil, err 323 } 324 } 325 326 for _, bp := range dbp.Breakpoints().M { 327 if bp.WatchType != 0 { 328 err := thread.writeHardwareBreakpoint(bp.Addr, bp.WatchType, bp.HWBreakIndex) 329 if err != nil { 330 return nil, err 331 } 332 } 333 } 334 335 return thread, nil 336 } 337 338 type waitForDebugEventFlags int 339 340 const ( 341 waitBlocking waitForDebugEventFlags = 1 << iota 342 waitSuspendNewThreads 343 waitDontHandleExceptions 344 ) 345 346 const _MS_VC_EXCEPTION = 0x406D1388 // part of VisualC protocol to set thread names 347 348 func (dbp *nativeProcess) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, exitCode int, err error) { 349 var debugEvent _DEBUG_EVENT 350 shouldExit := false 351 for { 352 continueStatus := uint32(_DBG_CONTINUE) 353 var milliseconds uint32 = 0 354 if flags&waitBlocking != 0 { 355 milliseconds = syscall.INFINITE 356 } 357 // Wait for a debug event... 358 err := _WaitForDebugEvent(&debugEvent, milliseconds) 359 if err != nil { 360 return 0, 0, err 361 } 362 363 // ... handle each event kind ... 364 unionPtr := unsafe.Pointer(&debugEvent.U[0]) 365 switch debugEvent.DebugEventCode { 366 case _CREATE_PROCESS_DEBUG_EVENT: 367 debugInfo := (*_CREATE_PROCESS_DEBUG_INFO)(unionPtr) 368 hFile := debugInfo.File 369 if hFile != 0 && hFile != syscall.InvalidHandle { 370 err = syscall.CloseHandle(hFile) 371 if err != nil { 372 return 0, 0, err 373 } 374 } 375 dbp.os.entryPoint = uint64(debugInfo.BaseOfImage) 376 dbp.os.hProcess = debugInfo.Process 377 _, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, 378 flags&waitSuspendNewThreads != 0, debugInfo.StartAddress == dbgUiRemoteBreakin.Addr()) 379 if err != nil { 380 return 0, 0, err 381 } 382 break 383 case _CREATE_THREAD_DEBUG_EVENT: 384 debugInfo := (*_CREATE_THREAD_DEBUG_INFO)(unionPtr) 385 _, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, 386 flags&waitSuspendNewThreads != 0, debugInfo.StartAddress == dbgUiRemoteBreakin.Addr()) 387 if err != nil { 388 return 0, 0, err 389 } 390 break 391 case _EXIT_THREAD_DEBUG_EVENT: 392 delete(dbp.threads, int(debugEvent.ThreadId)) 393 break 394 case _OUTPUT_DEBUG_STRING_EVENT: 395 //TODO: Handle debug output strings 396 break 397 case _LOAD_DLL_DEBUG_EVENT: 398 debugInfo := (*_LOAD_DLL_DEBUG_INFO)(unionPtr) 399 hFile := debugInfo.File 400 if hFile != 0 && hFile != syscall.InvalidHandle { 401 err = syscall.CloseHandle(hFile) 402 if err != nil { 403 return 0, 0, err 404 } 405 } 406 break 407 case _UNLOAD_DLL_DEBUG_EVENT: 408 break 409 case _RIP_EVENT: 410 break 411 case _EXCEPTION_DEBUG_EVENT: 412 if flags&waitDontHandleExceptions != 0 { 413 continueStatus = _DBG_EXCEPTION_NOT_HANDLED 414 break 415 } 416 exception := (*_EXCEPTION_DEBUG_INFO)(unionPtr) 417 tid := int(debugEvent.ThreadId) 418 419 switch code := exception.ExceptionRecord.ExceptionCode; code { 420 case _EXCEPTION_BREAKPOINT: 421 422 // check if the exception address really is a breakpoint instruction, if 423 // it isn't we already removed that breakpoint and we can't deal with 424 // this exception anymore. 425 atbp := true 426 if thread, found := dbp.threads[tid]; found { 427 data := make([]byte, dbp.bi.Arch.BreakpointSize()) 428 if _, err := thread.ReadMemory(data, uint64(exception.ExceptionRecord.ExceptionAddress)); err == nil { 429 instr := dbp.bi.Arch.BreakpointInstruction() 430 for i := range instr { 431 if data[i] != instr[i] { 432 atbp = false 433 break 434 } 435 } 436 } 437 if !atbp { 438 thread.setPC(uint64(exception.ExceptionRecord.ExceptionAddress)) 439 } 440 } 441 442 if atbp { 443 dbp.os.breakThread = tid 444 if th := dbp.threads[tid]; th != nil { 445 th.os.setbp = true 446 } 447 return tid, 0, nil 448 } else { 449 continueStatus = _DBG_CONTINUE 450 } 451 case _EXCEPTION_SINGLE_STEP: 452 dbp.os.breakThread = tid 453 return tid, 0, nil 454 case _MS_VC_EXCEPTION: 455 // This exception is sent to set the thread name in VisualC, we should 456 // mask it or it might crash the program. 457 continueStatus = _DBG_CONTINUE 458 default: 459 continueStatus = _DBG_EXCEPTION_NOT_HANDLED 460 } 461 case _EXIT_PROCESS_DEBUG_EVENT: 462 debugInfo := (*_EXIT_PROCESS_DEBUG_INFO)(unionPtr) 463 exitCode = int(debugInfo.ExitCode) 464 shouldExit = true 465 default: 466 return 0, 0, fmt.Errorf("unknown debug event code: %d", debugEvent.DebugEventCode) 467 } 468 469 // .. and then continue unless we received an event that indicated we should break into debugger. 470 err = _ContinueDebugEvent(debugEvent.ProcessId, debugEvent.ThreadId, continueStatus) 471 if err != nil { 472 return 0, 0, err 473 } 474 475 if shouldExit { 476 return 0, exitCode, nil 477 } 478 } 479 } 480 481 func trapWait(procgrp *processGroup, pid int) (*nativeThread, error) { 482 dbp := procgrp.procs[0] 483 var err error 484 var tid, exitCode int 485 dbp.execPtraceFunc(func() { 486 tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking) 487 }) 488 if err != nil { 489 return nil, err 490 } 491 if tid == 0 { 492 dbp.postExit() 493 return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: exitCode} 494 } 495 th := dbp.threads[tid] 496 return th, nil 497 } 498 499 func (dbp *nativeProcess) exitGuard(err error) error { 500 return err 501 } 502 503 func (procgrp *processGroup) resume() error { 504 dbp := procgrp.procs[0] 505 for _, thread := range dbp.threads { 506 if thread.CurrentBreakpoint.Breakpoint != nil { 507 if err := thread.StepInstruction(); err != nil { 508 return err 509 } 510 thread.CurrentBreakpoint.Clear() 511 } 512 } 513 514 for _, thread := range dbp.threads { 515 _, err := _ResumeThread(thread.os.hThread) 516 if err != nil { 517 return err 518 } 519 } 520 dbp.os.running = true 521 522 return nil 523 } 524 525 // stop stops all running threads threads and sets breakpoints 526 func (procgrp *processGroup) stop(cctx *proc.ContinueOnceContext, trapthread *nativeThread) (*nativeThread, error) { 527 dbp := procgrp.procs[0] 528 if dbp.exited { 529 return nil, proc.ErrProcessExited{Pid: dbp.pid} 530 } 531 532 dbp.os.running = false 533 for _, th := range dbp.threads { 534 th.os.setbp = false 535 } 536 trapthread.os.setbp = true 537 538 // While the debug event that stopped the target was being propagated 539 // other target threads could generate other debug events. 540 // After this function we need to know about all the threads 541 // stopped on a breakpoint. To do that we first suspend all target 542 // threads and then repeatedly call _ContinueDebugEvent followed by 543 // waitForDebugEvent in non-blocking mode. 544 // We need to explicitly call SuspendThread because otherwise the 545 // call to _ContinueDebugEvent will resume execution of some of the 546 // target threads. 547 548 err := trapthread.SetCurrentBreakpoint(true) 549 if err != nil { 550 return nil, err 551 } 552 553 context := newContext() 554 555 for _, thread := range dbp.threads { 556 thread.os.delayErr = nil 557 if !thread.os.dbgUiRemoteBreakIn { 558 // Wait before reporting the error, the thread could be removed when we 559 // call waitForDebugEvent in the next loop. 560 _, thread.os.delayErr = _SuspendThread(thread.os.hThread) 561 if thread.os.delayErr == nil { 562 // This call will block until the thread has stopped. 563 _ = thread.getContext(context) 564 } 565 } 566 } 567 568 for { 569 var err error 570 var tid int 571 dbp.execPtraceFunc(func() { 572 err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE) 573 if err == nil { 574 tid, _, _ = dbp.waitForDebugEvent(waitSuspendNewThreads) 575 } 576 }) 577 if err != nil { 578 return nil, err 579 } 580 if tid == 0 { 581 break 582 } 583 err = dbp.threads[tid].SetCurrentBreakpoint(true) 584 if err != nil { 585 return nil, err 586 } 587 } 588 589 // Check if trapthread still exist, if the process is dying it could have 590 // been removed while we were stopping the other threads. 591 trapthreadFound := false 592 for _, thread := range dbp.threads { 593 if thread.ID == trapthread.ID { 594 trapthreadFound = true 595 } 596 if thread.os.delayErr != nil && thread.os.delayErr != syscall.Errno(0x5) { 597 // Do not report Access is denied error, it is caused by the thread 598 // having already died but we haven't been notified about it yet. 599 return nil, thread.os.delayErr 600 } 601 } 602 603 if !trapthreadFound { 604 wasDbgUiRemoteBreakIn := trapthread.os.dbgUiRemoteBreakIn 605 // trapthread exited during stop, pick another one 606 trapthread = nil 607 for _, thread := range dbp.threads { 608 if thread.CurrentBreakpoint.Breakpoint != nil && thread.os.delayErr == nil { 609 trapthread = thread 610 break 611 } 612 } 613 if trapthread == nil && wasDbgUiRemoteBreakIn { 614 // If this was triggered by a manual stop request we should stop 615 // regardless, pick a thread. 616 for _, thread := range dbp.threads { 617 return thread, nil 618 } 619 } 620 } 621 622 return trapthread, nil 623 } 624 625 func (dbp *nativeProcess) detach(kill bool) error { 626 if !kill { 627 //TODO(aarzilli): when debug.Target exist Detach should be moved to 628 // debug.Target and the call to RestoreAsyncPreempt should be moved there. 629 for _, thread := range dbp.threads { 630 _, err := _ResumeThread(thread.os.hThread) 631 if err != nil { 632 return err 633 } 634 } 635 } 636 return _DebugActiveProcessStop(uint32(dbp.pid)) 637 } 638 639 func (dbp *nativeProcess) EntryPoint() (uint64, error) { 640 return dbp.os.entryPoint, nil 641 } 642 643 func (dbp *nativeProcess) SupportsBPF() bool { 644 return false 645 } 646 647 func (dbp *nativeProcess) SetUProbe(fnName string, goidOffset int64, args []ebpf.UProbeArgMap) error { 648 return nil 649 } 650 651 func (dbp *nativeProcess) GetBufferedTracepoints() []ebpf.RawUProbeParams { 652 return nil 653 } 654 655 type _PROCESS_BASIC_INFORMATION struct { 656 ExitStatus sys.NTStatus 657 PebBaseAddress uintptr 658 AffinityMask uintptr 659 BasePriority int32 660 UniqueProcessId uintptr 661 InheritedFromUniqueProcessId uintptr 662 } 663 664 type _PEB struct { 665 reserved1 [2]byte 666 BeingDebugged byte 667 BitField byte 668 reserved3 uintptr 669 ImageBaseAddress uintptr 670 Ldr uintptr 671 ProcessParameters uintptr 672 reserved4 [3]uintptr 673 AtlThunkSListPtr uintptr 674 reserved5 uintptr 675 reserved6 uint32 676 reserved7 uintptr 677 reserved8 uint32 678 AtlThunkSListPtr32 uint32 679 reserved9 [45]uintptr 680 reserved10 [96]byte 681 PostProcessInitRoutine uintptr 682 reserved11 [128]byte 683 reserved12 [1]uintptr 684 SessionId uint32 685 } 686 687 type _RTL_USER_PROCESS_PARAMETERS struct { 688 MaximumLength, Length uint32 689 690 Flags, DebugFlags uint32 691 692 ConsoleHandle sys.Handle 693 ConsoleFlags uint32 694 StandardInput, StandardOutput, StandardError sys.Handle 695 696 CurrentDirectory struct { 697 DosPath _NTUnicodeString 698 Handle sys.Handle 699 } 700 701 DllPath _NTUnicodeString 702 ImagePathName _NTUnicodeString 703 CommandLine _NTUnicodeString 704 Environment unsafe.Pointer 705 706 StartingX, StartingY, CountX, CountY, CountCharsX, CountCharsY, FillAttribute uint32 707 708 WindowFlags, ShowWindowFlags uint32 709 WindowTitle, DesktopInfo, ShellInfo, RuntimeData _NTUnicodeString 710 CurrentDirectories [32]struct { 711 Flags uint16 712 Length uint16 713 TimeStamp uint32 714 DosPath _NTString 715 } 716 717 EnvironmentSize, EnvironmentVersion uintptr 718 719 PackageDependencyData uintptr 720 ProcessGroupId uint32 721 LoaderThreads uint32 722 723 RedirectionDllName _NTUnicodeString 724 HeapPartitionName _NTUnicodeString 725 DefaultThreadpoolCpuSetMasks uintptr 726 DefaultThreadpoolCpuSetMaskCount uint32 727 } 728 729 type _NTString struct { 730 Length uint16 731 MaximumLength uint16 732 Buffer uintptr 733 } 734 735 type _NTUnicodeString struct { 736 Length uint16 737 MaximumLength uint16 738 Buffer uintptr 739 } 740 741 func getCmdLine(hProcess syscall.Handle) string { 742 logger := logflags.DebuggerLogger() 743 var info _PROCESS_BASIC_INFORMATION 744 err := sys.NtQueryInformationProcess(sys.Handle(hProcess), sys.ProcessBasicInformation, unsafe.Pointer(&info), uint32(unsafe.Sizeof(info)), nil) 745 if err != nil { 746 logger.Errorf("NtQueryInformationProcess: %v", err) 747 return "" 748 } 749 var peb _PEB 750 err = _ReadProcessMemory(hProcess, info.PebBaseAddress, (*byte)(unsafe.Pointer(&peb)), unsafe.Sizeof(peb), nil) 751 if err != nil { 752 logger.Errorf("Reading PEB: %v", err) 753 return "" 754 } 755 var upp _RTL_USER_PROCESS_PARAMETERS 756 err = _ReadProcessMemory(hProcess, peb.ProcessParameters, (*byte)(unsafe.Pointer(&upp)), unsafe.Sizeof(upp), nil) 757 if err != nil { 758 logger.Errorf("Reading ProcessParameters: %v", err) 759 return "" 760 } 761 if upp.CommandLine.Length%2 != 0 { 762 logger.Errorf("CommandLine length not a multiple of 2") 763 return "" 764 } 765 buf := make([]byte, upp.CommandLine.Length) 766 err = _ReadProcessMemory(hProcess, upp.CommandLine.Buffer, &buf[0], uintptr(len(buf)), nil) 767 if err != nil { 768 logger.Errorf("Reading CommandLine: %v", err) 769 return "" 770 } 771 utf16buf := make([]uint16, len(buf)/2) 772 for i := 0; i < len(buf); i += 2 { 773 utf16buf[i/2] = uint16(buf[i+1])<<8 + uint16(buf[i]) 774 } 775 return string(utf16.Decode(utf16buf)) 776 } 777 778 func killProcess(pid int) error { 779 p, err := os.FindProcess(pid) 780 if err != nil { 781 return err 782 } 783 defer p.Release() 784 785 return p.Kill() 786 }