Significantly reworked Windows architecture handling in Process

- Fixed several issues around struct and pointer sizing for 64-bit queries
  - Process handling now functions as expected on ARM64 Windows hosts
pull/1434/head
Sharon 2 years ago
parent c0365978d8
commit 67b6689799
No known key found for this signature in database
GPG Key ID: D4825F411DC21E5D

@ -12,6 +12,7 @@ import (
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"syscall"
"time"
@ -89,7 +90,7 @@ type ioCounters struct {
type processBasicInformation32 struct {
Reserved1 uint32
PebBaseAddress uint32
PebBaseAddress uintptr
Reserved2 uint32
Reserved3 uint32
UniqueProcessId uint32
@ -98,7 +99,7 @@ type processBasicInformation32 struct {
type processBasicInformation64 struct {
Reserved1 uint64
PebBaseAddress uint64
PebBaseAddress uintptr
Reserved2 uint64
Reserved3 uint64
UniqueProcessId uint64
@ -111,7 +112,7 @@ type processEnvironmentBlock32 struct {
Reserved2 uint8
Reserved3 [2]uint32
Ldr uint32
ProcessParameters uint32
ProcessParameters uintptr
// More fields which we don't use so far
}
@ -122,7 +123,7 @@ type processEnvironmentBlock64 struct {
_ [4]uint8 // padding, since we are 64 bit, the next pointer is 64 bit aligned (when compiling for 32 bit, this is not the case without manual padding)
Reserved3 [2]uint64
Ldr uint64
ProcessParameters uint64
ProcessParameters uintptr
// More fields which we don't use so far
}
@ -135,18 +136,18 @@ type rtlUserProcessParameters32 struct {
StdErrorHandle uint32
CurrentDirectoryPathNameLength uint16
_ uint16 // Max Length
CurrentDirectoryPathAddress uint32
CurrentDirectoryPathAddress uintptr
CurrentDirectoryHandle uint32
DllPathNameLength uint16
_ uint16 // Max Length
DllPathAddress uint32
DllPathAddress uintptr
ImagePathNameLength uint16
_ uint16 // Max Length
ImagePathAddress uint32
ImagePathAddress uintptr
CommandLineLength uint16
_ uint16 // Max Length
CommandLineAddress uint32
EnvironmentAddress uint32
CommandLineAddress uintptr
EnvironmentAddress uintptr
// More fields which we don't use so far
}
@ -160,21 +161,21 @@ type rtlUserProcessParameters64 struct {
CurrentDirectoryPathNameLength uint16
_ uint16 // Max Length
_ uint32 // Padding
CurrentDirectoryPathAddress uint64
CurrentDirectoryPathAddress uintptr
CurrentDirectoryHandle uint64
DllPathNameLength uint16
_ uint16 // Max Length
_ uint32 // Padding
DllPathAddress uint64
DllPathAddress uintptr
ImagePathNameLength uint16
_ uint16 // Max Length
_ uint32 // Padding
ImagePathAddress uint64
ImagePathAddress uintptr
CommandLineLength uint16
_ uint16 // Max Length
_ uint32 // Padding
CommandLineAddress uint64
EnvironmentAddress uint64
CommandLineAddress uintptr
EnvironmentAddress uintptr
// More fields which we don't use so far
}
@ -403,13 +404,13 @@ func (p *Process) CwdWithContext(_ context.Context) (string, error) {
procIs32Bits := is32BitProcess(h)
if procIs32Bits {
if procIs32Bits && !strings.Contains(runtime.GOARCH, "64") {
userProcParams, err := getUserProcessParams32(h)
if err != nil {
return "", err
}
if userProcParams.CurrentDirectoryPathNameLength > 0 {
cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(userProcParams.CurrentDirectoryPathAddress), uint(userProcParams.CurrentDirectoryPathNameLength))
cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CurrentDirectoryPathAddress, uint(userProcParams.CurrentDirectoryPathNameLength))
if len(cwd) != int(userProcParams.CurrentDirectoryPathNameLength) {
return "", errors.New("cannot read current working directory")
}
@ -941,39 +942,39 @@ func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) {
}
func getUserProcessParams32(handle windows.Handle) (rtlUserProcessParameters32, error) {
pebAddress, err := queryPebAddress(syscall.Handle(handle), true)
pebAddress, err, queryFrom64Bit := queryPebAddress(syscall.Handle(handle), true)
if err != nil {
return rtlUserProcessParameters32{}, fmt.Errorf("cannot locate process PEB: %w", err)
return rtlUserProcessParameters32{}, fmt.Errorf("cannot locate process PEB for 64-bit-initiator: %t, 32-bit-proc: %t : %w", queryFrom64Bit, true, err)
}
buf := readProcessMemory(syscall.Handle(handle), true, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock32{})))
if len(buf) != int(unsafe.Sizeof(processEnvironmentBlock32{})) {
return rtlUserProcessParameters32{}, fmt.Errorf("cannot read process PEB")
return rtlUserProcessParameters32{}, fmt.Errorf("cannot read process PEB for 64-bit-initiator: %t, 32-bit-proc: %t", queryFrom64Bit, true)
}
peb := (*processEnvironmentBlock32)(unsafe.Pointer(&buf[0]))
userProcessAddress := uint64(peb.ProcessParameters)
userProcessAddress := peb.ProcessParameters
buf = readProcessMemory(syscall.Handle(handle), true, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters32{})))
if len(buf) != int(unsafe.Sizeof(rtlUserProcessParameters32{})) {
return rtlUserProcessParameters32{}, fmt.Errorf("cannot read user process parameters")
return rtlUserProcessParameters32{}, fmt.Errorf("cannot read user process parameters for 64-bit-initiator: %t, 32-bit-proc: %t", queryFrom64Bit, true)
}
return *(*rtlUserProcessParameters32)(unsafe.Pointer(&buf[0])), nil
}
func getUserProcessParams64(handle windows.Handle) (rtlUserProcessParameters64, error) {
pebAddress, err := queryPebAddress(syscall.Handle(handle), false)
pebAddress, err, queryFrom64Bit := queryPebAddress(syscall.Handle(handle), false)
if err != nil {
return rtlUserProcessParameters64{}, fmt.Errorf("cannot locate process PEB: %w", err)
return rtlUserProcessParameters64{}, fmt.Errorf("cannot locate process PEB for 64-bit-initiator: %t, 32-bit-proc: %t : %w", queryFrom64Bit, false, err)
}
buf := readProcessMemory(syscall.Handle(handle), false, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock64{})))
if len(buf) != int(unsafe.Sizeof(processEnvironmentBlock64{})) {
return rtlUserProcessParameters64{}, fmt.Errorf("cannot read process PEB")
return rtlUserProcessParameters64{}, fmt.Errorf("cannot read process PEB for 64-bit-initiator: %t, 32-bit-proc: %t", queryFrom64Bit, false)
}
peb := (*processEnvironmentBlock64)(unsafe.Pointer(&buf[0]))
userProcessAddress := peb.ProcessParameters
buf = readProcessMemory(syscall.Handle(handle), false, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters64{})))
if len(buf) != int(unsafe.Sizeof(rtlUserProcessParameters64{})) {
return rtlUserProcessParameters64{}, fmt.Errorf("cannot read user process parameters")
return rtlUserProcessParameters64{}, fmt.Errorf("cannot read user process parameters for 64-bit-initiator: %t, 32-bit-proc: %t", queryFrom64Bit, false)
}
return *(*rtlUserProcessParameters64)(unsafe.Pointer(&buf[0])), nil
}
@ -1035,14 +1036,14 @@ func getProcessEnvironmentVariables(pid int32, ctx context.Context) ([]string, e
procIs32Bits := is32BitProcess(h)
var processParameterBlockAddress uint64
var processParameterBlockAddress uintptr
if procIs32Bits {
if procIs32Bits && !strings.Contains(runtime.GOARCH, "64") {
peb, err := getUserProcessParams32(h)
if err != nil {
return nil, err
}
processParameterBlockAddress = uint64(peb.EnvironmentAddress)
processParameterBlockAddress = peb.EnvironmentAddress
} else {
peb, err := getUserProcessParams64(h)
if err != nil {
@ -1050,6 +1051,7 @@ func getProcessEnvironmentVariables(pid int32, ctx context.Context) ([]string, e
}
processParameterBlockAddress = peb.EnvironmentAddress
}
envvarScanner := bufio.NewScanner(&processReader{
processHandle: h,
is32BitProcess: procIs32Bits,
@ -1094,7 +1096,7 @@ func getProcessEnvironmentVariables(pid int32, ctx context.Context) ([]string, e
type processReader struct {
processHandle windows.Handle
is32BitProcess bool
offset uint64
offset uintptr
}
func (p *processReader) Read(buf []byte) (int, error) {
@ -1103,7 +1105,7 @@ func (p *processReader) Read(buf []byte) (int, error) {
return 0, io.EOF
}
copy(buf, processMemory)
p.offset += uint64(len(processMemory))
p.offset += uintptr(len(processMemory))
return len(processMemory), nil
}
@ -1119,15 +1121,15 @@ func getProcessCommandLine(pid int32) (string, error) {
procIs32Bits := is32BitProcess(h)
if procIs32Bits {
if procIs32Bits && !strings.Contains(runtime.GOARCH, "64") {
userProcParams, err := getUserProcessParams32(h)
if err != nil {
return "", err
}
if userProcParams.CommandLineLength > 0 {
cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(userProcParams.CommandLineAddress), uint(userProcParams.CommandLineLength))
cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CommandLineAddress, uint(userProcParams.CommandLineLength))
if len(cmdLine) != int(userProcParams.CommandLineLength) {
return "", errors.New("cannot read cmdline")
return "", fmt.Errorf("cannot read cmdline for 32-bit-proc: %t, len: %d and %d", procIs32Bits, len(cmdLine), int(userProcParams.CommandLineLength))
}
return convertUTF16ToString(cmdLine), nil
@ -1140,7 +1142,7 @@ func getProcessCommandLine(pid int32) (string, error) {
if userProcParams.CommandLineLength > 0 {
cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CommandLineAddress, uint(userProcParams.CommandLineLength))
if len(cmdLine) != int(userProcParams.CommandLineLength) {
return "", errors.New("cannot read cmdline")
return "", fmt.Errorf("cannot read cmdline for 32-bit-proc: %t, len: %d and %d", procIs32Bits, len(cmdLine), int(userProcParams.CommandLineLength))
}
return convertUTF16ToString(cmdLine), nil

@ -25,7 +25,8 @@ type PROCESS_MEMORY_COUNTERS struct {
PeakPagefileUsage uint32
}
func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, error) {
func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uintptr, error, bool) {
var queryFrom64Bit bool
if is32BitProcess {
// we are on a 32-bit process reading an external 32-bit process
var info processBasicInformation32
@ -38,9 +39,9 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, er
uintptr(0),
)
if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return uint64(info.PebBaseAddress), nil
return info.PebBaseAddress, nil, queryFrom64Bit
} else {
return 0, windows.NTStatus(ret)
return 0, windows.NTStatus(ret), queryFrom64Bit
}
} else {
// we are on a 32-bit process reading an external 64-bit process
@ -55,17 +56,17 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, er
uintptr(0),
)
if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return info.PebBaseAddress, nil
return info.PebBaseAddress, nil, queryFrom64Bit
} else {
return 0, windows.NTStatus(ret)
return 0, windows.NTStatus(ret), queryFrom64Bit
}
} else {
return 0, errors.New("can't find API to query 64 bit process from 32 bit")
return 0, errors.New("can't find API to query 64 bit process from 32 bit"), queryFrom64Bit
}
}
}
func readProcessMemory(h syscall.Handle, is32BitProcess bool, address uint64, size uint) []byte {
func readProcessMemory(h syscall.Handle, is32BitProcess bool, address uintptr, size uint) []byte {
if is32BitProcess {
var read uint

@ -24,43 +24,27 @@ type PROCESS_MEMORY_COUNTERS struct {
PeakPagefileUsage uint64
}
func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, error) {
if is32BitProcess {
// we are on a 64-bit process reading an external 32-bit process
var wow64 uint
func queryPebAddress(procHandle syscall.Handle, _ bool) (uintptr, error, bool) {
var queryFrom64Bit bool = true
ret, _, _ := common.ProcNtQueryInformationProcess.Call(
uintptr(procHandle),
uintptr(common.ProcessWow64Information),
uintptr(unsafe.Pointer(&wow64)),
uintptr(unsafe.Sizeof(wow64)),
uintptr(0),
)
if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return uint64(wow64), nil
} else {
return 0, windows.NTStatus(ret)
}
} else {
// we are on a 64-bit process reading an external 64-bit process
var info processBasicInformation64
// we are in a 64-bit process
var info processBasicInformation64
ret, _, _ := common.ProcNtQueryInformationProcess.Call(
uintptr(procHandle),
uintptr(common.ProcessBasicInformation),
uintptr(unsafe.Pointer(&info)),
uintptr(unsafe.Sizeof(info)),
uintptr(0),
)
if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return info.PebBaseAddress, nil
} else {
return 0, windows.NTStatus(ret)
}
ret, _, _ := common.ProcNtQueryInformationProcess.Call(
uintptr(procHandle),
uintptr(common.ProcessBasicInformation),
uintptr(unsafe.Pointer(&info)),
uintptr(unsafe.Sizeof(info)),
uintptr(0),
)
if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return info.PebBaseAddress, nil, queryFrom64Bit
} else {
return 0, windows.NTStatus(ret), queryFrom64Bit
}
}
func readProcessMemory(procHandle syscall.Handle, _ bool, address uint64, size uint) []byte {
func readProcessMemory(procHandle syscall.Handle, _ bool, address uintptr, size uint) []byte {
var read uint
buffer := make([]byte, size)

Loading…
Cancel
Save