Merge pull request #705 from Lomanic/issue586

[process][windows] Fix #586 use win32 API in process.Exe() instead of slow WMI call
pull/706/head
shirou 6 years ago committed by GitHub
commit 13375e2f9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,6 +4,9 @@ package common
import ( import (
"context" "context"
"path/filepath"
"strings"
"syscall"
"unsafe" "unsafe"
"github.com/StackExchange/wmi" "github.com/StackExchange/wmi"
@ -59,6 +62,8 @@ var (
PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData") PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData")
PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue") PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue")
PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery") PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery")
procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW")
) )
type FILETIME struct { type FILETIME struct {
@ -133,3 +138,23 @@ func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, con
return err return err
} }
} }
// Convert paths using native DOS format like:
// "\Device\HarddiskVolume1\Windows\systemew\file.txt"
// into:
// "C:\Windows\systemew\file.txt"
func ConvertDOSPath(p string) string {
rawDrive := strings.Join(strings.Split(p, `\`)[:3], `\`)
for d := 'A'; d <= 'Z'; d++ {
szDeviceName := string(d) + ":"
szTarget := make([]uint16, 512)
ret, _, _ := procQueryDosDeviceW.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(szDeviceName))),
uintptr(unsafe.Pointer(&szTarget[0])),
uintptr(len(szTarget)))
if ret != 0 && windows.UTF16ToString(szTarget[:]) == rawDrive {
return filepath.Join(szDeviceName, p[len(rawDrive):])
}
}
return p
}

@ -25,12 +25,15 @@ const (
) )
var ( var (
modpsapi = windows.NewLazySystemDLL("psapi.dll") modpsapi = windows.NewLazySystemDLL("psapi.dll")
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
procGetProcessImageFileNameW = modpsapi.NewProc("GetProcessImageFileNameW")
advapi32 = windows.NewLazySystemDLL("advapi32.dll") advapi32 = windows.NewLazySystemDLL("advapi32.dll")
procLookupPrivilegeValue = advapi32.NewProc("LookupPrivilegeValueW") procLookupPrivilegeValue = advapi32.NewProc("LookupPrivilegeValueW")
procAdjustTokenPrivileges = advapi32.NewProc("AdjustTokenPrivileges") procAdjustTokenPrivileges = advapi32.NewProc("AdjustTokenPrivileges")
procQueryFullProcessImageNameW = common.Modkernel32.NewProc("QueryFullProcessImageNameW")
) )
type SystemProcessInformation struct { type SystemProcessInformation struct {
@ -234,24 +237,31 @@ func (p *Process) Exe() (string, error) {
} }
func (p *Process) ExeWithContext(ctx context.Context) (string, error) { func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
if p.Pid != 0 { // 0 or null is the current process for CreateToolhelp32Snapshot // 0x1000 is PROCESS_QUERY_LIMITED_INFORMATION
snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPMODULE|w32.TH32CS_SNAPMODULE32, uint32(p.Pid)) c, err := syscall.OpenProcess(0x1000, false, uint32(p.Pid))
if snap != 0 { // don't report errors here, fallback to WMI instead if err != nil {
defer w32.CloseHandle(snap) return "", err
var me32 w32.MODULEENTRY32 }
me32.Size = uint32(unsafe.Sizeof(me32)) defer syscall.CloseHandle(c)
buf := make([]uint16, syscall.MAX_LONG_PATH)
if w32.Module32First(snap, &me32) { size := uint32(syscall.MAX_LONG_PATH)
szexepath := windows.UTF16ToString(me32.SzExePath[:]) if err := procQueryFullProcessImageNameW.Find(); err == nil { // Vista+
return szexepath, nil ret, _, err := procQueryFullProcessImageNameW.Call(
} uintptr(c),
uintptr(0),
uintptr(unsafe.Pointer(&buf[0])),
uintptr(unsafe.Pointer(&size)))
if ret == 0 {
return "", err
} }
return windows.UTF16ToString(buf[:]), nil
} }
dst, err := GetWin32ProcWithContext(ctx, p.Pid) // XP fallback
if err != nil { ret, _, err := procGetProcessImageFileNameW.Call(uintptr(c), uintptr(unsafe.Pointer(&buf[0])), uintptr(size))
return "", fmt.Errorf("could not get ExecutablePath: %s", err) if ret == 0 {
return "", err
} }
return *dst[0].ExecutablePath, nil return common.ConvertDOSPath(windows.UTF16ToString(buf[:])), nil
} }
func (p *Process) Cmdline() (string, error) { func (p *Process) Cmdline() (string, error) {

Loading…
Cancel
Save