Merge pull request #1097 from secDre4mer/master

feat: Add support for environment variable read
pull/1113/head
shirou 4 years ago committed by GitHub
commit 78e21dd887
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

4
Gopkg.lock generated

@ -65,7 +65,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:0afe79d034c63eea8c5977401f5fc135394751c1213ee6c991b0c6bec44f8f60" digest = "1:1672d2eb5799465379f6016fa509eadd72c69e3eb2bf3cdfea9222e64e163b94"
name = "golang.org/x/sys" name = "golang.org/x/sys"
packages = [ packages = [
"internal/unsafeheader", "internal/unsafeheader",
@ -75,7 +75,7 @@
"windows/svc/mgr", "windows/svc/mgr",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "e3ed0017c21142d3345308d92dcc7ad7020040c6" revision = "134d130e1a049f4194ae9920ac9f96455cde360d"
[[projects]] [[projects]]
branch = "v3" branch = "v3"

@ -542,3 +542,8 @@ func (p *Process) Kill() error {
func (p *Process) Username() (string, error) { func (p *Process) Username() (string, error) {
return p.UsernameWithContext(context.Background()) return p.UsernameWithContext(context.Background())
} }
// Environ returns the environment variables of the process.
func (p *Process) Environ() ([]string, error) {
return p.EnvironWithContext(context.Background())
}

@ -72,6 +72,10 @@ func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesS
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
return nil, common.ErrNotImplementedError
}
func parseKinfoProc(buf []byte) (KinfoProc, error) { func parseKinfoProc(buf []byte) (KinfoProc, error) {
var k KinfoProc var k KinfoProc
br := bytes.NewReader(buf) br := bytes.NewReader(buf)

@ -203,3 +203,7 @@ func (p *Process) KillWithContext(ctx context.Context) error {
func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { func (p *Process) UsernameWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
return nil, common.ErrNotImplementedError
}

@ -490,6 +490,17 @@ func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]M
return &ret, nil return &ret, nil
} }
func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
environPath := common.HostProc(strconv.Itoa(int(p.Pid)), "environ")
environContent, err := ioutil.ReadFile(environPath)
if err != nil {
return nil, err
}
return strings.Split(string(environContent), "\000"), nil
}
/** /**
** Internal functions ** Internal functions
**/ **/

@ -198,6 +198,10 @@ func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]M
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
return nil, common.ErrNotImplementedError
}
/** /**
** Internal functions ** Internal functions
**/ **/

@ -691,6 +691,60 @@ func Test_IsRunning(t *testing.T) {
} }
} }
func Test_Process_Environ(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("unable to create temp dir %v", err)
}
defer os.RemoveAll(tmpdir) // clean up
tmpfilepath := filepath.Join(tmpdir, "test.go")
tmpfile, err := os.Create(tmpfilepath)
if err != nil {
t.Fatalf("unable to create temp file %v", err)
}
tmpfilecontent := []byte("package main\nimport(\n\"time\"\n)\nfunc main(){\nfor range time.Tick(time.Second) {}\n}")
if _, err := tmpfile.Write(tmpfilecontent); err != nil {
tmpfile.Close()
t.Fatalf("unable to write temp file %v", err)
}
if err := tmpfile.Close(); err != nil {
t.Fatalf("unable to close temp file %v", err)
}
err = exec.Command("go", "build", "-o", tmpfile.Name()+".exe", tmpfile.Name()).Run()
if err != nil {
t.Fatalf("unable to build temp file %v", err)
}
cmd := exec.Command(tmpfile.Name() + ".exe")
cmd.Env = []string{"testkey=envvalue"}
assert.Nil(t, cmd.Start())
defer cmd.Process.Kill()
time.Sleep(100 * time.Millisecond)
p, err := NewProcess(int32(cmd.Process.Pid))
skipIfNotImplementedErr(t, err)
assert.Nil(t, err)
envs, err := p.Environ()
skipIfNotImplementedErr(t, err)
if err != nil {
t.Errorf("getting environ error %v", err)
}
var envvarFound bool
for _, envvar := range envs {
if envvar == "testkey=envvalue" {
envvarFound = true
break
}
}
if !envvarFound {
t.Error("environment variable not found")
}
}
func Test_AllProcesses_cmdLine(t *testing.T) { func Test_AllProcesses_cmdLine(t *testing.T) {
procs, err := Processes() procs, err := Processes()
if err == nil { if err == nil {
@ -709,6 +763,21 @@ func Test_AllProcesses_cmdLine(t *testing.T) {
} }
} }
func Test_AllProcesses_environ(t *testing.T) {
procs, err := Processes()
if err == nil {
for _, proc := range procs {
exeName, _ := proc.Exe()
environ, err := proc.Environ()
if err != nil {
environ = []string{"Error: " + err.Error() }
}
t.Logf("Process #%v: Name: %v / Environment Variables: %v\n", proc.Pid, exeName, environ)
}
}
}
func BenchmarkNewProcess(b *testing.B) { func BenchmarkNewProcess(b *testing.B) {
checkPid := os.Getpid() checkPid := os.Getpid()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

@ -3,17 +3,19 @@
package process package process
import ( import (
"bufio"
"context" "context"
"errors" "errors"
"fmt" "fmt"
"io"
"os" "os"
"strings" "strings"
"syscall" "syscall"
"unsafe" "unsafe"
cpu "github.com/shirou/gopsutil/cpu" "github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
net "github.com/shirou/gopsutil/net" "github.com/shirou/gopsutil/net"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@ -113,6 +115,55 @@ type processBasicInformation64 struct {
Reserved4 uint64 Reserved4 uint64
} }
type processEnvironmentBlock32 struct {
Reserved1 [2]uint8
BeingDebugged uint8
Reserved2 uint8
Reserved3 [2]uint32
Ldr uint32
ProcessParameters uint32
// More fields which we don't use so far
}
type processEnvironmentBlock64 struct {
Reserved1 [2]uint8
BeingDebugged uint8
Reserved2 uint8
_ [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
// More fields which we don't use so far
}
type rtlUserProcessParameters32 struct {
Reserved1 [16]uint8
Reserved2 [10]uint32
ImagePathNameLength uint16
_ uint16
ImagePathAddress uint32
CommandLineLength uint16
_ uint16
CommandLineAddress uint32
EnvironmentAddress uint32
// More fields which we don't use so far
}
type rtlUserProcessParameters64 struct {
Reserved1 [16]uint8
Reserved2 [10]uint64
ImagePathNameLength uint16
_ uint16 // Max Length
_ uint32 // Padding
ImagePathAddress uint64
CommandLineLength uint16
_ uint16 // Max Length
_ uint32 // Padding
CommandLineAddress uint64
EnvironmentAddress uint64
// More fields which we don't use so far
}
type winLUID struct { type winLUID struct {
LowPart winDWord LowPart winDWord
HighPart winLong HighPart winLong
@ -622,6 +673,14 @@ func (p *Process) KillWithContext(ctx context.Context) error {
return process.Kill() return process.Kill()
} }
func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
envVars, err := getProcessEnvironmentVariables(p.Pid, ctx)
if err != nil {
return nil, fmt.Errorf("could not get environment variables: %s", err)
}
return envVars, nil
}
// retrieve Ppid in a thread-safe manner // retrieve Ppid in a thread-safe manner
func (p *Process) getPpid() int32 { func (p *Process) getPpid() int32 {
p.parentMutex.RLock() p.parentMutex.RLock()
@ -748,39 +807,45 @@ func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) {
return times, err return times, err
} }
func is32BitProcess(procHandle syscall.Handle) bool { func getUserProcessParams32(handle windows.Handle) (rtlUserProcessParameters32, error) {
var wow64 uint pebAddress, err := queryPebAddress(syscall.Handle(handle), true)
if err != nil {
return rtlUserProcessParameters32{}, fmt.Errorf("cannot locate process PEB: %w", err)
}
ret, _, _ := common.ProcNtQueryInformationProcess.Call( buf := readProcessMemory(syscall.Handle(handle), true, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock32{})))
uintptr(procHandle), if len(buf) != int(unsafe.Sizeof(processEnvironmentBlock32{})) {
uintptr(common.ProcessWow64Information), return rtlUserProcessParameters32{}, fmt.Errorf("cannot read process PEB")
uintptr(unsafe.Pointer(&wow64)), }
uintptr(unsafe.Sizeof(wow64)), peb := (*processEnvironmentBlock32)(unsafe.Pointer(&buf[0]))
uintptr(0), userProcessAddress := uint64(peb.ProcessParameters)
) buf = readProcessMemory(syscall.Handle(handle), true, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters32{})))
if int(ret) >= 0 { if len(buf) != int(unsafe.Sizeof(rtlUserProcessParameters32{})) {
if wow64 != 0 { return rtlUserProcessParameters32{}, fmt.Errorf("cannot read user process parameters")
return true
}
} else {
//if the OS does not support the call, we fallback into the bitness of the app
if unsafe.Sizeof(wow64) == 4 {
return true
}
} }
return false return *(*rtlUserProcessParameters32)(unsafe.Pointer(&buf[0])), nil
} }
func getProcessCommandLine(pid int32) (string, error) { func getUserProcessParams64(handle windows.Handle) (rtlUserProcessParameters64, error) {
h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid)) pebAddress, err := queryPebAddress(syscall.Handle(handle), false)
if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
return "", nil
}
if err != nil { if err != nil {
return "", err return rtlUserProcessParameters64{}, fmt.Errorf("cannot locate process PEB: %w", err)
} }
defer syscall.CloseHandle(syscall.Handle(h))
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")
}
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)(unsafe.Pointer(&buf[0])), nil
}
func is32BitProcess(h windows.Handle) bool {
const ( const (
PROCESSOR_ARCHITECTURE_INTEL = 0 PROCESSOR_ARCHITECTURE_INTEL = 0
PROCESSOR_ARCHITECTURE_ARM = 5 PROCESSOR_ARCHITECTURE_ARM = 5
@ -789,86 +854,165 @@ func getProcessCommandLine(pid int32) (string, error) {
PROCESSOR_ARCHITECTURE_AMD64 = 9 PROCESSOR_ARCHITECTURE_AMD64 = 9
) )
procIs32Bits := true var procIs32Bits bool
switch processorArchitecture { switch processorArchitecture {
case PROCESSOR_ARCHITECTURE_INTEL: case PROCESSOR_ARCHITECTURE_INTEL:
fallthrough fallthrough
case PROCESSOR_ARCHITECTURE_ARM: case PROCESSOR_ARCHITECTURE_ARM:
procIs32Bits = true procIs32Bits = true
case PROCESSOR_ARCHITECTURE_ARM64: case PROCESSOR_ARCHITECTURE_ARM64:
fallthrough fallthrough
case PROCESSOR_ARCHITECTURE_IA64: case PROCESSOR_ARCHITECTURE_IA64:
fallthrough fallthrough
case PROCESSOR_ARCHITECTURE_AMD64: case PROCESSOR_ARCHITECTURE_AMD64:
procIs32Bits = is32BitProcess(syscall.Handle(h)) var wow64 uint
ret, _, _ := common.ProcNtQueryInformationProcess.Call(
uintptr(h),
uintptr(common.ProcessWow64Information),
uintptr(unsafe.Pointer(&wow64)),
uintptr(unsafe.Sizeof(wow64)),
uintptr(0),
)
if int(ret) >= 0 {
if wow64 != 0 {
procIs32Bits = true
}
} else {
//if the OS does not support the call, we fallback into the bitness of the app
if unsafe.Sizeof(wow64) == 4 {
procIs32Bits = true
}
}
default: default:
//for other unknown platforms, we rely on process platform //for other unknown platforms, we rely on process platform
if unsafe.Sizeof(processorArchitecture) == 8 { if unsafe.Sizeof(processorArchitecture) == 8 {
procIs32Bits = false procIs32Bits = false
} else {
procIs32Bits = true
} }
} }
return procIs32Bits
}
pebAddress := queryPebAddress(syscall.Handle(h), procIs32Bits) func getProcessEnvironmentVariables(pid int32, ctx context.Context) ([]string, error) {
if pebAddress == 0 { h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid))
return "", errors.New("cannot locate process PEB") if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
return nil, nil
}
if err != nil {
return nil, err
} }
defer syscall.CloseHandle(syscall.Handle(h))
procIs32Bits := is32BitProcess(h)
var processParameterBlockAddress uint64
if procIs32Bits { if procIs32Bits {
buf := readProcessMemory(syscall.Handle(h), procIs32Bits, pebAddress+uint64(16), 4) peb, err := getUserProcessParams32(h)
if len(buf) != 4 { if err != nil {
return "", errors.New("cannot locate process user parameters") return nil, err
} }
userProcParams := uint64(buf[0]) | (uint64(buf[1]) << 8) | (uint64(buf[2]) << 16) | (uint64(buf[3]) << 24) processParameterBlockAddress = uint64(peb.EnvironmentAddress)
} else {
//read CommandLine field from PRTL_USER_PROCESS_PARAMETERS peb, err := getUserProcessParams64(h)
remoteCmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams+uint64(64), 8) if err != nil {
if len(remoteCmdLine) != 8 { return nil, err
return "", errors.New("cannot read cmdline field") }
processParameterBlockAddress = peb.EnvironmentAddress
}
envvarScanner := bufio.NewScanner(&processReader{
processHandle: h,
is32BitProcess: procIs32Bits,
offset: processParameterBlockAddress,
})
envvarScanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error){
if atEOF && len(data) == 0 {
return 0, nil, nil
}
// Check for UTF-16 zero character
for i := 0; i < len(data) - 1; i+=2 {
if data[i] == 0 && data[i+1] == 0 {
return i+2, data[0:i], nil
}
}
if atEOF {
return len(data), data, nil
}
// Request more data
return 0, nil, nil
})
var envVars []string
for envvarScanner.Scan() {
entry := envvarScanner.Bytes()
if len(entry) == 0 {
break // Block is finished
} }
envVars = append(envVars, convertUTF16ToString(entry))
select {
case <-ctx.Done():
break
default:
continue
}
}
if err := envvarScanner.Err(); err != nil {
return nil, err
}
return envVars, nil
}
type processReader struct {
processHandle windows.Handle
is32BitProcess bool
offset uint64
}
func (p *processReader) Read(buf []byte) (int, error) {
processMemory := readProcessMemory(syscall.Handle(p.processHandle), p.is32BitProcess, p.offset, uint(len(buf)))
if len(processMemory) == 0 {
return 0, io.EOF
}
copy(buf, processMemory)
p.offset += uint64(len(processMemory))
return len(processMemory), nil
}
//remoteCmdLine is actually a UNICODE_STRING32 func getProcessCommandLine(pid int32) (string, error) {
//the first two bytes has the length h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid))
cmdLineLength := uint(remoteCmdLine[0]) | (uint(remoteCmdLine[1]) << 8) if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
if cmdLineLength > 0 { return "", nil
//and, at offset 4, is the pointer to the buffer }
bufferAddress := uint32(remoteCmdLine[4]) | (uint32(remoteCmdLine[5]) << 8) | if err != nil {
(uint32(remoteCmdLine[6]) << 16) | (uint32(remoteCmdLine[7]) << 24) return "", err
}
defer syscall.CloseHandle(syscall.Handle(h))
cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(bufferAddress), cmdLineLength) procIs32Bits := is32BitProcess(h)
if len(cmdLine) != int(cmdLineLength) {
if procIs32Bits {
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))
if len(cmdLine) != int(userProcParams.CommandLineLength) {
return "", errors.New("cannot read cmdline") return "", errors.New("cannot read cmdline")
} }
return convertUTF16ToString(cmdLine), nil return convertUTF16ToString(cmdLine), nil
} }
} else { } else {
buf := readProcessMemory(syscall.Handle(h), procIs32Bits, pebAddress+uint64(32), 8) userProcParams, err := getUserProcessParams64(h)
if len(buf) != 8 { if err != nil {
return "", errors.New("cannot locate process user parameters") return "", err
}
userProcParams := uint64(buf[0]) | (uint64(buf[1]) << 8) | (uint64(buf[2]) << 16) | (uint64(buf[3]) << 24) |
(uint64(buf[4]) << 32) | (uint64(buf[5]) << 40) | (uint64(buf[6]) << 48) | (uint64(buf[7]) << 56)
//read CommandLine field from PRTL_USER_PROCESS_PARAMETERS
remoteCmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams+uint64(112), 16)
if len(remoteCmdLine) != 16 {
return "", errors.New("cannot read cmdline field")
} }
if userProcParams.CommandLineLength > 0 {
//remoteCmdLine is actually a UNICODE_STRING64 cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CommandLineAddress, uint(userProcParams.CommandLineLength))
//the first two bytes has the length if len(cmdLine) != int(userProcParams.CommandLineLength) {
cmdLineLength := uint(remoteCmdLine[0]) | (uint(remoteCmdLine[1]) << 8)
if cmdLineLength > 0 {
//and, at offset 8, is the pointer to the buffer
bufferAddress := uint64(remoteCmdLine[8]) | (uint64(remoteCmdLine[9]) << 8) |
(uint64(remoteCmdLine[10]) << 16) | (uint64(remoteCmdLine[11]) << 24) |
(uint64(remoteCmdLine[12]) << 32) | (uint64(remoteCmdLine[13]) << 40) |
(uint64(remoteCmdLine[14]) << 48) | (uint64(remoteCmdLine[15]) << 56)
cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, bufferAddress, cmdLineLength)
if len(cmdLine) != int(cmdLineLength) {
return "", errors.New("cannot read cmdline") return "", errors.New("cannot read cmdline")
} }

@ -3,10 +3,12 @@
package process package process
import ( import (
"errors"
"syscall" "syscall"
"unsafe" "unsafe"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/windows"
) )
type PROCESS_MEMORY_COUNTERS struct { type PROCESS_MEMORY_COUNTERS struct {
@ -22,7 +24,7 @@ type PROCESS_MEMORY_COUNTERS struct {
PeakPagefileUsage uint32 PeakPagefileUsage uint32
} }
func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 { func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, error) {
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
@ -34,8 +36,10 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 {
uintptr(unsafe.Sizeof(info)), uintptr(unsafe.Sizeof(info)),
uintptr(0), uintptr(0),
) )
if int(ret) >= 0 { if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return uint64(info.PebBaseAddress) return uint64(info.PebBaseAddress), nil
} else {
return 0, windows.NTStatus(ret)
} }
} 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
@ -49,14 +53,15 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 {
uintptr(unsafe.Sizeof(info)), uintptr(unsafe.Sizeof(info)),
uintptr(0), uintptr(0),
) )
if int(ret) >= 0 { if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return info.PebBaseAddress return info.PebBaseAddress, nil
} else {
return 0, windows.NTStatus(ret)
} }
} else {
return 0, errors.New("can't find API to query 64 bit process from 32 bit")
} }
} }
//return 0 on error
return 0
} }
func readProcessMemory(h syscall.Handle, is32BitProcess bool, address uint64, size uint) []byte { func readProcessMemory(h syscall.Handle, is32BitProcess bool, address uint64, size uint) []byte {

@ -7,6 +7,7 @@ import (
"unsafe" "unsafe"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/windows"
) )
type PROCESS_MEMORY_COUNTERS struct { type PROCESS_MEMORY_COUNTERS struct {
@ -22,7 +23,7 @@ type PROCESS_MEMORY_COUNTERS struct {
PeakPagefileUsage uint64 PeakPagefileUsage uint64
} }
func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 { func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, error) {
if is32BitProcess { if is32BitProcess {
//we are on a 64-bit process reading an external 32-bit process //we are on a 64-bit process reading an external 32-bit process
var wow64 uint var wow64 uint
@ -34,8 +35,10 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 {
uintptr(unsafe.Sizeof(wow64)), uintptr(unsafe.Sizeof(wow64)),
uintptr(0), uintptr(0),
) )
if int(ret) >= 0 { if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return uint64(wow64) return uint64(wow64), nil
} else {
return 0, windows.NTStatus(ret)
} }
} else { } else {
//we are on a 64-bit process reading an external 64-bit process //we are on a 64-bit process reading an external 64-bit process
@ -48,13 +51,12 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 {
uintptr(unsafe.Sizeof(info)), uintptr(unsafe.Sizeof(info)),
uintptr(0), uintptr(0),
) )
if int(ret) >= 0 { if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return info.PebBaseAddress return info.PebBaseAddress, nil
} else {
return 0, windows.NTStatus(ret)
} }
} }
//return 0 on error
return 0
} }
func readProcessMemory(procHandle syscall.Handle, _ bool, address uint64, size uint) []byte { func readProcessMemory(procHandle syscall.Handle, _ bool, address uint64, size uint) []byte {

@ -549,6 +549,11 @@ func (p *Process) Username() (string, error) {
return p.UsernameWithContext(context.Background()) return p.UsernameWithContext(context.Background())
} }
// Environ returns the environment variables of the process.
func (p *Process) Environ() ([]string, error) {
return p.EnvironWithContext(context.Background())
}
func convertStatusChar(letter string) string { func convertStatusChar(letter string) string {
switch letter { switch letter {
case "R": case "R":

@ -67,6 +67,10 @@ func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesS
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
return nil, common.ErrNotImplementedError
}
func parseKinfoProc(buf []byte) (KinfoProc, error) { func parseKinfoProc(buf []byte) (KinfoProc, error) {
var k KinfoProc var k KinfoProc
br := bytes.NewReader(buf) br := bytes.NewReader(buf)

@ -199,3 +199,7 @@ func (p *Process) KillWithContext(ctx context.Context) error {
func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { func (p *Process) UsernameWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
return nil, common.ErrNotImplementedError
}

@ -485,6 +485,17 @@ func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]M
return &ret, nil return &ret, nil
} }
func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
environPath := common.HostProc(strconv.Itoa(int(p.Pid)), "environ")
environContent, err := ioutil.ReadFile(environPath)
if err != nil {
return nil, err
}
return strings.Split(string(environContent), "\000"), nil
}
/** /**
** Internal functions ** Internal functions
**/ **/

@ -194,6 +194,10 @@ func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]M
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
return nil, common.ErrNotImplementedError
}
/** /**
** Internal functions ** Internal functions
**/ **/

@ -693,6 +693,60 @@ func Test_IsRunning(t *testing.T) {
} }
} }
func Test_Process_Environ(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("unable to create temp dir %v", err)
}
defer os.RemoveAll(tmpdir) // clean up
tmpfilepath := filepath.Join(tmpdir, "test.go")
tmpfile, err := os.Create(tmpfilepath)
if err != nil {
t.Fatalf("unable to create temp file %v", err)
}
tmpfilecontent := []byte("package main\nimport(\n\"time\"\n)\nfunc main(){\nfor range time.Tick(time.Second) {}\n}")
if _, err := tmpfile.Write(tmpfilecontent); err != nil {
tmpfile.Close()
t.Fatalf("unable to write temp file %v", err)
}
if err := tmpfile.Close(); err != nil {
t.Fatalf("unable to close temp file %v", err)
}
err = exec.Command("go", "build", "-o", tmpfile.Name()+".exe", tmpfile.Name()).Run()
if err != nil {
t.Fatalf("unable to build temp file %v", err)
}
cmd := exec.Command(tmpfile.Name() + ".exe")
cmd.Env = []string{"testkey=envvalue"}
assert.Nil(t, cmd.Start())
defer cmd.Process.Kill()
time.Sleep(100 * time.Millisecond)
p, err := NewProcess(int32(cmd.Process.Pid))
skipIfNotImplementedErr(t, err)
assert.Nil(t, err)
envs, err := p.Environ()
skipIfNotImplementedErr(t, err)
if err != nil {
t.Errorf("getting environ error %v", err)
}
var envvarFound bool
for _, envvar := range envs {
if envvar == "testkey=envvalue" {
envvarFound = true
break
}
}
if !envvarFound {
t.Error("environment variable not found")
}
}
func Test_AllProcesses_cmdLine(t *testing.T) { func Test_AllProcesses_cmdLine(t *testing.T) {
procs, err := Processes() procs, err := Processes()
if err == nil { if err == nil {
@ -711,6 +765,21 @@ func Test_AllProcesses_cmdLine(t *testing.T) {
} }
} }
func Test_AllProcesses_environ(t *testing.T) {
procs, err := Processes()
if err == nil {
for _, proc := range procs {
exeName, _ := proc.Exe()
environ, err := proc.Environ()
if err != nil {
environ = []string{"Error: " + err.Error() }
}
t.Logf("Process #%v: Name: %v / Environment Variables: %v\n", proc.Pid, exeName, environ)
}
}
}
func BenchmarkNewProcess(b *testing.B) { func BenchmarkNewProcess(b *testing.B) {
checkPid := os.Getpid() checkPid := os.Getpid()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

@ -3,17 +3,19 @@
package process package process
import ( import (
"bufio"
"context" "context"
"errors" "errors"
"fmt" "fmt"
"io"
"os" "os"
"strings" "strings"
"syscall" "syscall"
"unsafe" "unsafe"
cpu "github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/internal/common" "github.com/shirou/gopsutil/v3/internal/common"
net "github.com/shirou/gopsutil/v3/net" "github.com/shirou/gopsutil/v3/net"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@ -98,6 +100,55 @@ type processBasicInformation64 struct {
Reserved4 uint64 Reserved4 uint64
} }
type processEnvironmentBlock32 struct {
Reserved1 [2]uint8
BeingDebugged uint8
Reserved2 uint8
Reserved3 [2]uint32
Ldr uint32
ProcessParameters uint32
// More fields which we don't use so far
}
type processEnvironmentBlock64 struct {
Reserved1 [2]uint8
BeingDebugged uint8
Reserved2 uint8
_ [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
// More fields which we don't use so far
}
type rtlUserProcessParameters32 struct {
Reserved1 [16]uint8
Reserved2 [10]uint32
ImagePathNameLength uint16
_ uint16
ImagePathAddress uint32
CommandLineLength uint16
_ uint16
CommandLineAddress uint32
EnvironmentAddress uint32
// More fields which we don't use so far
}
type rtlUserProcessParameters64 struct {
Reserved1 [16]uint8
Reserved2 [10]uint64
ImagePathNameLength uint16
_ uint16 // Max Length
_ uint32 // Padding
ImagePathAddress uint64
CommandLineLength uint16
_ uint16 // Max Length
_ uint32 // Padding
CommandLineAddress uint64
EnvironmentAddress uint64
// More fields which we don't use so far
}
type winLUID struct { type winLUID struct {
LowPart winDWord LowPart winDWord
HighPart winLong HighPart winLong
@ -603,6 +654,14 @@ func (p *Process) KillWithContext(ctx context.Context) error {
return process.Kill() return process.Kill()
} }
func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
envVars, err := getProcessEnvironmentVariables(p.Pid, ctx)
if err != nil {
return nil, fmt.Errorf("could not get environment variables: %s", err)
}
return envVars, nil
}
// retrieve Ppid in a thread-safe manner // retrieve Ppid in a thread-safe manner
func (p *Process) getPpid() int32 { func (p *Process) getPpid() int32 {
p.parentMutex.RLock() p.parentMutex.RLock()
@ -729,39 +788,46 @@ func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) {
return times, err return times, err
} }
func is32BitProcess(procHandle syscall.Handle) bool {
var wow64 uint
ret, _, _ := common.ProcNtQueryInformationProcess.Call( func getUserProcessParams32(handle windows.Handle) (rtlUserProcessParameters32, error) {
uintptr(procHandle), pebAddress, err := queryPebAddress(syscall.Handle(handle), true)
uintptr(common.ProcessWow64Information), if err != nil {
uintptr(unsafe.Pointer(&wow64)), return rtlUserProcessParameters32{}, fmt.Errorf("cannot locate process PEB: %w", err)
uintptr(unsafe.Sizeof(wow64)),
uintptr(0),
)
if int(ret) >= 0 {
if wow64 != 0 {
return true
}
} else {
//if the OS does not support the call, we fallback into the bitness of the app
if unsafe.Sizeof(wow64) == 4 {
return true
}
} }
return false
}
func getProcessCommandLine(pid int32) (string, error) { buf := readProcessMemory(syscall.Handle(handle), true, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock32{})))
h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid)) if len(buf) != int(unsafe.Sizeof(processEnvironmentBlock32{})) {
if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER { return rtlUserProcessParameters32{}, fmt.Errorf("cannot read process PEB")
return "", nil
} }
peb := (*processEnvironmentBlock32)(unsafe.Pointer(&buf[0]))
userProcessAddress := uint64(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)(unsafe.Pointer(&buf[0])), nil
}
func getUserProcessParams64(handle windows.Handle) (rtlUserProcessParameters64, error) {
pebAddress, err := queryPebAddress(syscall.Handle(handle), false)
if err != nil { if err != nil {
return "", err return rtlUserProcessParameters64{}, fmt.Errorf("cannot locate process PEB: %w", err)
} }
defer syscall.CloseHandle(syscall.Handle(h))
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")
}
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)(unsafe.Pointer(&buf[0])), nil
}
func is32BitProcess(h windows.Handle) bool {
const ( const (
PROCESSOR_ARCHITECTURE_INTEL = 0 PROCESSOR_ARCHITECTURE_INTEL = 0
PROCESSOR_ARCHITECTURE_ARM = 5 PROCESSOR_ARCHITECTURE_ARM = 5
@ -770,86 +836,165 @@ func getProcessCommandLine(pid int32) (string, error) {
PROCESSOR_ARCHITECTURE_AMD64 = 9 PROCESSOR_ARCHITECTURE_AMD64 = 9
) )
procIs32Bits := true var procIs32Bits bool
switch processorArchitecture { switch processorArchitecture {
case PROCESSOR_ARCHITECTURE_INTEL: case PROCESSOR_ARCHITECTURE_INTEL:
fallthrough fallthrough
case PROCESSOR_ARCHITECTURE_ARM: case PROCESSOR_ARCHITECTURE_ARM:
procIs32Bits = true procIs32Bits = true
case PROCESSOR_ARCHITECTURE_ARM64: case PROCESSOR_ARCHITECTURE_ARM64:
fallthrough fallthrough
case PROCESSOR_ARCHITECTURE_IA64: case PROCESSOR_ARCHITECTURE_IA64:
fallthrough fallthrough
case PROCESSOR_ARCHITECTURE_AMD64: case PROCESSOR_ARCHITECTURE_AMD64:
procIs32Bits = is32BitProcess(syscall.Handle(h)) var wow64 uint
ret, _, _ := common.ProcNtQueryInformationProcess.Call(
uintptr(h),
uintptr(common.ProcessWow64Information),
uintptr(unsafe.Pointer(&wow64)),
uintptr(unsafe.Sizeof(wow64)),
uintptr(0),
)
if int(ret) >= 0 {
if wow64 != 0 {
procIs32Bits = true
}
} else {
//if the OS does not support the call, we fallback into the bitness of the app
if unsafe.Sizeof(wow64) == 4 {
procIs32Bits = true
}
}
default: default:
//for other unknown platforms, we rely on process platform //for other unknown platforms, we rely on process platform
if unsafe.Sizeof(processorArchitecture) == 8 { if unsafe.Sizeof(processorArchitecture) == 8 {
procIs32Bits = false procIs32Bits = false
} else {
procIs32Bits = true
} }
} }
return procIs32Bits
}
pebAddress := queryPebAddress(syscall.Handle(h), procIs32Bits) func getProcessEnvironmentVariables(pid int32, ctx context.Context) ([]string, error) {
if pebAddress == 0 { h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid))
return "", errors.New("cannot locate process PEB") if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
return nil, nil
}
if err != nil {
return nil, err
} }
defer syscall.CloseHandle(syscall.Handle(h))
procIs32Bits := is32BitProcess(h)
var processParameterBlockAddress uint64
if procIs32Bits { if procIs32Bits {
buf := readProcessMemory(syscall.Handle(h), procIs32Bits, pebAddress+uint64(16), 4) peb, err := getUserProcessParams32(h)
if len(buf) != 4 { if err != nil {
return "", errors.New("cannot locate process user parameters") return nil, err
} }
userProcParams := uint64(buf[0]) | (uint64(buf[1]) << 8) | (uint64(buf[2]) << 16) | (uint64(buf[3]) << 24) processParameterBlockAddress = uint64(peb.EnvironmentAddress)
} else {
//read CommandLine field from PRTL_USER_PROCESS_PARAMETERS peb, err := getUserProcessParams64(h)
remoteCmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams+uint64(64), 8) if err != nil {
if len(remoteCmdLine) != 8 { return nil, err
return "", errors.New("cannot read cmdline field") }
processParameterBlockAddress = peb.EnvironmentAddress
}
envvarScanner := bufio.NewScanner(&processReader{
processHandle: h,
is32BitProcess: procIs32Bits,
offset: processParameterBlockAddress,
})
envvarScanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error){
if atEOF && len(data) == 0 {
return 0, nil, nil
}
// Check for UTF-16 zero character
for i := 0; i < len(data) - 1; i+=2 {
if data[i] == 0 && data[i+1] == 0 {
return i+2, data[0:i], nil
}
}
if atEOF {
return len(data), data, nil
}
// Request more data
return 0, nil, nil
})
var envVars []string
for envvarScanner.Scan() {
entry := envvarScanner.Bytes()
if len(entry) == 0 {
break // Block is finished
} }
envVars = append(envVars, convertUTF16ToString(entry))
select {
case <-ctx.Done():
break
default:
continue
}
}
if err := envvarScanner.Err(); err != nil {
return nil, err
}
return envVars, nil
}
type processReader struct {
processHandle windows.Handle
is32BitProcess bool
offset uint64
}
func (p *processReader) Read(buf []byte) (int, error) {
processMemory := readProcessMemory(syscall.Handle(p.processHandle), p.is32BitProcess, p.offset, uint(len(buf)))
if len(processMemory) == 0 {
return 0, io.EOF
}
copy(buf, processMemory)
p.offset += uint64(len(processMemory))
return len(processMemory), nil
}
//remoteCmdLine is actually a UNICODE_STRING32 func getProcessCommandLine(pid int32) (string, error) {
//the first two bytes has the length h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid))
cmdLineLength := uint(remoteCmdLine[0]) | (uint(remoteCmdLine[1]) << 8) if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
if cmdLineLength > 0 { return "", nil
//and, at offset 4, is the pointer to the buffer }
bufferAddress := uint32(remoteCmdLine[4]) | (uint32(remoteCmdLine[5]) << 8) | if err != nil {
(uint32(remoteCmdLine[6]) << 16) | (uint32(remoteCmdLine[7]) << 24) return "", err
}
defer syscall.CloseHandle(syscall.Handle(h))
cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(bufferAddress), cmdLineLength) procIs32Bits := is32BitProcess(h)
if len(cmdLine) != int(cmdLineLength) {
if procIs32Bits {
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))
if len(cmdLine) != int(userProcParams.CommandLineLength) {
return "", errors.New("cannot read cmdline") return "", errors.New("cannot read cmdline")
} }
return convertUTF16ToString(cmdLine), nil return convertUTF16ToString(cmdLine), nil
} }
} else { } else {
buf := readProcessMemory(syscall.Handle(h), procIs32Bits, pebAddress+uint64(32), 8) userProcParams, err := getUserProcessParams64(h)
if len(buf) != 8 { if err != nil {
return "", errors.New("cannot locate process user parameters") return "", err
}
userProcParams := uint64(buf[0]) | (uint64(buf[1]) << 8) | (uint64(buf[2]) << 16) | (uint64(buf[3]) << 24) |
(uint64(buf[4]) << 32) | (uint64(buf[5]) << 40) | (uint64(buf[6]) << 48) | (uint64(buf[7]) << 56)
//read CommandLine field from PRTL_USER_PROCESS_PARAMETERS
remoteCmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams+uint64(112), 16)
if len(remoteCmdLine) != 16 {
return "", errors.New("cannot read cmdline field")
} }
if userProcParams.CommandLineLength > 0 {
//remoteCmdLine is actually a UNICODE_STRING64 cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CommandLineAddress, uint(userProcParams.CommandLineLength))
//the first two bytes has the length if len(cmdLine) != int(userProcParams.CommandLineLength) {
cmdLineLength := uint(remoteCmdLine[0]) | (uint(remoteCmdLine[1]) << 8)
if cmdLineLength > 0 {
//and, at offset 8, is the pointer to the buffer
bufferAddress := uint64(remoteCmdLine[8]) | (uint64(remoteCmdLine[9]) << 8) |
(uint64(remoteCmdLine[10]) << 16) | (uint64(remoteCmdLine[11]) << 24) |
(uint64(remoteCmdLine[12]) << 32) | (uint64(remoteCmdLine[13]) << 40) |
(uint64(remoteCmdLine[14]) << 48) | (uint64(remoteCmdLine[15]) << 56)
cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, bufferAddress, cmdLineLength)
if len(cmdLine) != int(cmdLineLength) {
return "", errors.New("cannot read cmdline") return "", errors.New("cannot read cmdline")
} }

@ -3,9 +3,12 @@
package process package process
import ( import (
"errors"
"syscall" "syscall"
"unsafe" "unsafe"
"golang.org/x/sys/windows"
"github.com/shirou/gopsutil/v3/internal/common" "github.com/shirou/gopsutil/v3/internal/common"
) )
@ -22,7 +25,7 @@ type PROCESS_MEMORY_COUNTERS struct {
PeakPagefileUsage uint32 PeakPagefileUsage uint32
} }
func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 { func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, error) {
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
@ -34,8 +37,10 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 {
uintptr(unsafe.Sizeof(info)), uintptr(unsafe.Sizeof(info)),
uintptr(0), uintptr(0),
) )
if int(ret) >= 0 { if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return uint64(info.PebBaseAddress) return uint64(info.PebBaseAddress), nil
} else {
return 0, windows.NTStatus(ret)
} }
} 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
@ -49,14 +54,15 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 {
uintptr(unsafe.Sizeof(info)), uintptr(unsafe.Sizeof(info)),
uintptr(0), uintptr(0),
) )
if int(ret) >= 0 { if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return info.PebBaseAddress return info.PebBaseAddress, nil
} else {
return 0, windows.NTStatus(ret)
} }
} else {
return 0, errors.New("can't find API to query 64 bit process from 32 bit")
} }
} }
//return 0 on error
return 0
} }
func readProcessMemory(h syscall.Handle, is32BitProcess bool, address uint64, size uint) []byte { func readProcessMemory(h syscall.Handle, is32BitProcess bool, address uint64, size uint) []byte {

@ -7,6 +7,7 @@ import (
"unsafe" "unsafe"
"github.com/shirou/gopsutil/v3/internal/common" "github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/windows"
) )
type PROCESS_MEMORY_COUNTERS struct { type PROCESS_MEMORY_COUNTERS struct {
@ -22,7 +23,7 @@ type PROCESS_MEMORY_COUNTERS struct {
PeakPagefileUsage uint64 PeakPagefileUsage uint64
} }
func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 { func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, error) {
if is32BitProcess { if is32BitProcess {
//we are on a 64-bit process reading an external 32-bit process //we are on a 64-bit process reading an external 32-bit process
var wow64 uint var wow64 uint
@ -34,8 +35,10 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 {
uintptr(unsafe.Sizeof(wow64)), uintptr(unsafe.Sizeof(wow64)),
uintptr(0), uintptr(0),
) )
if int(ret) >= 0 { if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return uint64(wow64) return uint64(wow64), nil
} else {
return 0, windows.NTStatus(ret)
} }
} else { } else {
//we are on a 64-bit process reading an external 64-bit process //we are on a 64-bit process reading an external 64-bit process
@ -48,13 +51,12 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 {
uintptr(unsafe.Sizeof(info)), uintptr(unsafe.Sizeof(info)),
uintptr(0), uintptr(0),
) )
if int(ret) >= 0 { if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS {
return info.PebBaseAddress return info.PebBaseAddress, nil
} else {
return 0, windows.NTStatus(ret)
} }
} }
//return 0 on error
return 0
} }
func readProcessMemory(procHandle syscall.Handle, _ bool, address uint64, size uint) []byte { func readProcessMemory(procHandle syscall.Handle, _ bool, address uint64, size uint) []byte {

Loading…
Cancel
Save