I am trying to port specific functionality of PSHunt (https://github.com/Infocyte/PSHunt/blob/master/Surveys/Survey.ps1) to Go. Specifically, I am trying to use VirtualQueryEx to iterate through the pages of a process's memory, as seen in this Powershell snippit:
# Get handle to the process
$hProcess = $Kernel32::OpenProcess(0x400, $False, $ProcessID) # PROCESS_QUERY_INFORMATION (0x00000400)
if (-not $hProcess) {
throw "Unable to get a process handle for process ID: $ProcessID"
}
$MemoryInfo = New-Object $MEMORY_BASIC_INFORMATION
$BytesRead = $Kernel32::VirtualQueryEx($hProcess, $ModuleBaseAddress, [Ref] $MemoryInfo, $PageSize)
$null = $Kernel32::CloseHandle($hProcess)
Note that the code above is called from a different function via the following: $MemoryInfo = Get-VirtualMemoryInfo -ProcessID $ProcessID -ModuleBaseAddress ([IntPtr]::Zero) -PageSize $SysInfo.PageSize
My implementation in Go looks like this:
var systemInfo SYSTEM_INFO
getSystemInfo :=
kernel32dll.NewProc("GetSystemInfo")
_, _, err = getSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
openProcess := kernel32dll.NewProc("OpenProcess")
hProcess, _, err := openProcess.Call(uintptr(0x410), uintptr(0), uintptr(processId))
fmt.Println("Message from OpenProcess:",err.Error())
defer windows.CloseHandle(windows.Handle(hProcess))
var memoryBasicInformation MEMORY_BASIC_INFORMATION
dll := syscall.MustLoadDLL("kernel32.dll")
defer dll.Release()
virtualQueryEx := dll.MustFindProc("VirtualQueryEx")
bytesRead, _, err := virtualQueryEx.Call((uintptr)(unsafe.Pointer(hProcess)), (uintptr)(unsafe.Pointer(????)), (uintptr)(unsafe.Pointer(&memoryBasicInformation)), (uintptr)(unsafe.Pointer(&systemInfo.PageSize)))
fmt.Println("Bytes read:",bytesRead)
fmt.Println("Message from VirtualQueryEx:", err.Error())
No matter what I do, VirtualQueryEx returns "Invalid access to memory location.". I cannot figure out what value to pass as the base address of the process (indicated by "????" above). The Microsoft documentation says this parameter is optional, but if I leave it blank, I get an error about improper command length.
As I mentioned, my goal is to start at the base of the process and scan through the entire thing via a loop, which would occur after this initial call to VirtualQueryEx.
For reference, I am using the go syscall library (in this case; although I've also tried the sys/windows library to no avail.)
Please let me know if I can clarify anything.
Each process has its own address space. If you access memory addresses that do not belong to it, then may cause "Invalid access to memory location".
my goal is to start at the base of the process and scan through the entire thing via a loop
First, set the "lpAddress"(I mean "????") start from zero.(Usually you will get the offset between the process base address and the zero address)
Then, check the value of memoryBasicInformation.AllocationProtect
and memoryBasicInformation.Protect
to determine whether there are access rights.
Iterate the "lpAddress" in the loop with
lpAddress = memoryBasicInformation.BaseAddress + memoryBasicInformation.RegionSize
then continue to use VirtualQueryEx()
. When lpAddress specifies an address above the highest memory address accessible to the process, the function fails with ERROR_INVALID_PARAMETER
, and break the loop.