Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Significantly reworked Windows architecture handling in Process module #1434

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 36 additions & 34 deletions process/process_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"syscall"
"time"
Expand Down Expand Up @@ -89,7 +90,7 @@ type ioCounters struct {

type processBasicInformation32 struct {
Reserved1 uint32
PebBaseAddress uint32
PebBaseAddress uintptr
Reserved2 uint32
Reserved3 uint32
UniqueProcessId uint32
Expand All @@ -98,7 +99,7 @@ type processBasicInformation32 struct {

type processBasicInformation64 struct {
Reserved1 uint64
PebBaseAddress uint64
PebBaseAddress uintptr
Reserved2 uint64
Reserved3 uint64
UniqueProcessId uint64
Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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
}

Expand Down Expand Up @@ -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")
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -1035,21 +1036,22 @@ 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 {
return nil, err
}
processParameterBlockAddress = peb.EnvironmentAddress
}

envvarScanner := bufio.NewScanner(&processReader{
processHandle: h,
is32BitProcess: procIs32Bits,
Expand Down Expand Up @@ -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) {
Expand All @@ -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
}

Expand All @@ -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
Expand All @@ -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
Expand Down
15 changes: 8 additions & 7 deletions process/process_windows_32bit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down
48 changes: 16 additions & 32 deletions process/process_windows_64bit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down