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" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime"
"strings" "strings"
"syscall" "syscall"
"time" "time"
@ -89,7 +90,7 @@ type ioCounters struct {
type processBasicInformation32 struct { type processBasicInformation32 struct {
Reserved1 uint32 Reserved1 uint32
PebBaseAddress uint32 PebBaseAddress uintptr
Reserved2 uint32 Reserved2 uint32
Reserved3 uint32 Reserved3 uint32
UniqueProcessId uint32 UniqueProcessId uint32
@ -98,7 +99,7 @@ type processBasicInformation32 struct {
type processBasicInformation64 struct { type processBasicInformation64 struct {
Reserved1 uint64 Reserved1 uint64
PebBaseAddress uint64 PebBaseAddress uintptr
Reserved2 uint64 Reserved2 uint64
Reserved3 uint64 Reserved3 uint64
UniqueProcessId uint64 UniqueProcessId uint64
@ -111,7 +112,7 @@ type processEnvironmentBlock32 struct {
Reserved2 uint8 Reserved2 uint8
Reserved3 [2]uint32 Reserved3 [2]uint32
Ldr uint32 Ldr uint32
ProcessParameters uint32 ProcessParameters uintptr
// More fields which we don't use so far // 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) _ [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 Reserved3 [2]uint64
Ldr uint64 Ldr uint64
ProcessParameters uint64 ProcessParameters uintptr
// More fields which we don't use so far // More fields which we don't use so far
} }
@ -135,18 +136,18 @@ type rtlUserProcessParameters32 struct {
StdErrorHandle uint32 StdErrorHandle uint32
CurrentDirectoryPathNameLength uint16 CurrentDirectoryPathNameLength uint16
_ uint16 // Max Length _ uint16 // Max Length
CurrentDirectoryPathAddress uint32 CurrentDirectoryPathAddress uintptr
CurrentDirectoryHandle uint32 CurrentDirectoryHandle uint32
DllPathNameLength uint16 DllPathNameLength uint16
_ uint16 // Max Length _ uint16 // Max Length
DllPathAddress uint32 DllPathAddress uintptr
ImagePathNameLength uint16 ImagePathNameLength uint16
_ uint16 // Max Length _ uint16 // Max Length
ImagePathAddress uint32 ImagePathAddress uintptr
CommandLineLength uint16 CommandLineLength uint16
_ uint16 // Max Length _ uint16 // Max Length
CommandLineAddress uint32 CommandLineAddress uintptr
EnvironmentAddress uint32 EnvironmentAddress uintptr
// More fields which we don't use so far // More fields which we don't use so far
} }
@ -160,21 +161,21 @@ type rtlUserProcessParameters64 struct {
CurrentDirectoryPathNameLength uint16 CurrentDirectoryPathNameLength uint16
_ uint16 // Max Length _ uint16 // Max Length
_ uint32 // Padding _ uint32 // Padding
CurrentDirectoryPathAddress uint64 CurrentDirectoryPathAddress uintptr
CurrentDirectoryHandle uint64 CurrentDirectoryHandle uint64
DllPathNameLength uint16 DllPathNameLength uint16
_ uint16 // Max Length _ uint16 // Max Length
_ uint32 // Padding _ uint32 // Padding
DllPathAddress uint64 DllPathAddress uintptr
ImagePathNameLength uint16 ImagePathNameLength uint16
_ uint16 // Max Length _ uint16 // Max Length
_ uint32 // Padding _ uint32 // Padding
ImagePathAddress uint64 ImagePathAddress uintptr
CommandLineLength uint16 CommandLineLength uint16
_ uint16 // Max Length _ uint16 // Max Length
_ uint32 // Padding _ uint32 // Padding
CommandLineAddress uint64 CommandLineAddress uintptr
EnvironmentAddress uint64 EnvironmentAddress uintptr
// More fields which we don't use so far // More fields which we don't use so far
} }
@ -403,13 +404,13 @@ func (p *Process) CwdWithContext(_ context.Context) (string, error) {
procIs32Bits := is32BitProcess(h) procIs32Bits := is32BitProcess(h)
if procIs32Bits { if procIs32Bits && !strings.Contains(runtime.GOARCH, "64") {
userProcParams, err := getUserProcessParams32(h) userProcParams, err := getUserProcessParams32(h)
if err != nil { if err != nil {
return "", err return "", err
} }
if userProcParams.CurrentDirectoryPathNameLength > 0 { 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) { if len(cwd) != int(userProcParams.CurrentDirectoryPathNameLength) {
return "", errors.New("cannot read current working directory") 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) { 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 { 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{}))) buf := readProcessMemory(syscall.Handle(handle), true, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock32{})))
if len(buf) != int(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])) peb := (*processEnvironmentBlock32)(unsafe.Pointer(&buf[0]))
userProcessAddress := uint64(peb.ProcessParameters) userProcessAddress := peb.ProcessParameters
buf = readProcessMemory(syscall.Handle(handle), true, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters32{}))) buf = readProcessMemory(syscall.Handle(handle), true, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters32{})))
if len(buf) != int(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 return *(*rtlUserProcessParameters32)(unsafe.Pointer(&buf[0])), nil
} }
func getUserProcessParams64(handle windows.Handle) (rtlUserProcessParameters64, error) { 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 { 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{}))) buf := readProcessMemory(syscall.Handle(handle), false, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock64{})))
if len(buf) != int(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])) peb := (*processEnvironmentBlock64)(unsafe.Pointer(&buf[0]))
userProcessAddress := peb.ProcessParameters userProcessAddress := peb.ProcessParameters
buf = readProcessMemory(syscall.Handle(handle), false, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters64{}))) buf = readProcessMemory(syscall.Handle(handle), false, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters64{})))
if len(buf) != int(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 return *(*rtlUserProcessParameters64)(unsafe.Pointer(&buf[0])), nil
} }
@ -1035,14 +1036,14 @@ func getProcessEnvironmentVariables(pid int32, ctx context.Context) ([]string, e
procIs32Bits := is32BitProcess(h) procIs32Bits := is32BitProcess(h)
var processParameterBlockAddress uint64 var processParameterBlockAddress uintptr
if procIs32Bits { if procIs32Bits && !strings.Contains(runtime.GOARCH, "64") {
peb, err := getUserProcessParams32(h) peb, err := getUserProcessParams32(h)
if err != nil { if err != nil {
return nil, err return nil, err
} }
processParameterBlockAddress = uint64(peb.EnvironmentAddress) processParameterBlockAddress = peb.EnvironmentAddress
} else { } else {
peb, err := getUserProcessParams64(h) peb, err := getUserProcessParams64(h)
if err != nil { if err != nil {
@ -1050,6 +1051,7 @@ func getProcessEnvironmentVariables(pid int32, ctx context.Context) ([]string, e
} }
processParameterBlockAddress = peb.EnvironmentAddress processParameterBlockAddress = peb.EnvironmentAddress
} }
envvarScanner := bufio.NewScanner(&processReader{ envvarScanner := bufio.NewScanner(&processReader{
processHandle: h, processHandle: h,
is32BitProcess: procIs32Bits, is32BitProcess: procIs32Bits,
@ -1094,7 +1096,7 @@ func getProcessEnvironmentVariables(pid int32, ctx context.Context) ([]string, e
type processReader struct { type processReader struct {
processHandle windows.Handle processHandle windows.Handle
is32BitProcess bool is32BitProcess bool
offset uint64 offset uintptr
} }
func (p *processReader) Read(buf []byte) (int, error) { func (p *processReader) Read(buf []byte) (int, error) {
@ -1103,7 +1105,7 @@ func (p *processReader) Read(buf []byte) (int, error) {
return 0, io.EOF return 0, io.EOF
} }
copy(buf, processMemory) copy(buf, processMemory)
p.offset += uint64(len(processMemory)) p.offset += uintptr(len(processMemory))
return len(processMemory), nil return len(processMemory), nil
} }
@ -1119,15 +1121,15 @@ func getProcessCommandLine(pid int32) (string, error) {
procIs32Bits := is32BitProcess(h) procIs32Bits := is32BitProcess(h)
if procIs32Bits { if procIs32Bits && !strings.Contains(runtime.GOARCH, "64") {
userProcParams, err := getUserProcessParams32(h) userProcParams, err := getUserProcessParams32(h)
if err != nil { if err != nil {
return "", err return "", err
} }
if userProcParams.CommandLineLength > 0 { 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) { 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 return convertUTF16ToString(cmdLine), nil
@ -1140,7 +1142,7 @@ func getProcessCommandLine(pid int32) (string, error) {
if userProcParams.CommandLineLength > 0 { if userProcParams.CommandLineLength > 0 {
cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CommandLineAddress, uint(userProcParams.CommandLineLength)) cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CommandLineAddress, uint(userProcParams.CommandLineLength))
if len(cmdLine) != int(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 return convertUTF16ToString(cmdLine), nil

@ -25,7 +25,8 @@ type PROCESS_MEMORY_COUNTERS struct {
PeakPagefileUsage uint32 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 { if is32BitProcess {
// we are on a 32-bit process reading an external 32-bit process // we are on a 32-bit process reading an external 32-bit process
var info processBasicInformation32 var info processBasicInformation32
@ -38,9 +39,9 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, er
uintptr(0), uintptr(0),
) )
if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS { if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return uint64(info.PebBaseAddress), nil return info.PebBaseAddress, nil, queryFrom64Bit
} else { } else {
return 0, windows.NTStatus(ret) return 0, windows.NTStatus(ret), queryFrom64Bit
} }
} else { } else {
// we are on a 32-bit process reading an external 64-bit process // 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), uintptr(0),
) )
if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS { if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return info.PebBaseAddress, nil return info.PebBaseAddress, nil, queryFrom64Bit
} else { } else {
return 0, windows.NTStatus(ret) return 0, windows.NTStatus(ret), queryFrom64Bit
} }
} else { } 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 { if is32BitProcess {
var read uint var read uint

@ -24,25 +24,10 @@ type PROCESS_MEMORY_COUNTERS struct {
PeakPagefileUsage uint64 PeakPagefileUsage uint64
} }
func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, error) { func queryPebAddress(procHandle syscall.Handle, _ bool) (uintptr, error, bool) {
if is32BitProcess { var queryFrom64Bit bool = true
// we are on a 64-bit process reading an external 32-bit process
var wow64 uint
ret, _, _ := common.ProcNtQueryInformationProcess.Call( // we are in a 64-bit process
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 var info processBasicInformation64
ret, _, _ := common.ProcNtQueryInformationProcess.Call( ret, _, _ := common.ProcNtQueryInformationProcess.Call(
@ -53,14 +38,13 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, er
uintptr(0), uintptr(0),
) )
if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS { if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return info.PebBaseAddress, nil return info.PebBaseAddress, nil, queryFrom64Bit
} else { } else {
return 0, windows.NTStatus(ret) 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 var read uint
buffer := make([]byte, size) buffer := make([]byte, size)

Loading…
Cancel
Save