NtVirtualMemoryFunctions.ps1

# Copyright 2021 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

<#
.SYNOPSIS
Allocates a new block of virtual memory.
.DESCRIPTION
This cmdlet allocates a new block of virtual memory in a specified process with specified set of protection. Returns the address.
.PARAMETER Size
The size of the allocated memory region.
.PARAMETER BaseAddress
Optional address to allocate the memory at. Can be 0 which requests the kernel to pick an address.
.PARAMETER Process
The process to allocate the memory in, defaults to current process.
.PARAMETER AllocationType
The type of allocation to make. Defaults to Reserve and Commit.
.PARAMETER Protection
The protection for the memory region. Defaults to ReadWrite.
.OUTPUTS
int64
.EXAMPLE
$addr = Add-NtVirtualMemory 0x10000
Allocate a block 0x10000 in size.
.EXAMPLE
$addr = Add-NtVirtualMemory 0x10000 -Process $process
Allocate a block 0x10000 in size in the specified process.
.EXAMPLE
$addr = Add-NtVirtualMemory 0x10000 -AllocationType Reserve
Reserve a block 0x10000 in size but don't yet commit it.
.EXAMPLE
$addr = Add-NtVirtualMemory 0x10000 -Protection ExecuteReadWrite
Allocate a block 0x10000 in size with Read, Write and Execution protection.
#>

function Add-NtVirtualMemory {
    param (
        [parameter(Mandatory, Position = 0)]
        [int64]$Size,
        [int64]$BaseAddress,
        [NtApiDotNet.NtProcess]$Process = [NtApiDotnet.NtProcess]::Current,
        [NtApiDotNet.MemoryAllocationType]$AllocationType = "Reserve, Commit",
        [NtApiDotNet.MemoryAllocationProtect]$Protection = "ReadWrite"
    )
    $Process.AllocateMemory($BaseAddress, $Size, $AllocationType, $Protection)
}

<#
.SYNOPSIS
Deallocates a block of virtual memory.
.DESCRIPTION
This cmdlet deallocates a block of virtual memory in a specified process.
.PARAMETER Size
The size of the region to decommit. Only valid when FreeType is Decommit.
.PARAMETER Address
The address to deallocate the memory at.
.PARAMETER Process
The process to deallocate the memory in, defaults to current process.
.PARAMETER MemoryType
The type of allocation operation to perform. Release frees the memory while
Decommit makes it inaccessible.
.OUTPUTS
None
.EXAMPLE
Remove-NtVirtualMemory $addr
Free block at $addr
.EXAMPLE
Remove-NtVirtualMemory $addr -Process $process
Free a block in the specified process.
.EXAMPLE
Remove-NtVirtualMemory $addr -Size 0x1000 -FreeType Decommit
Decommit a 4096 byte block at $addr
#>

function Remove-NtVirtualMemory {
    param (
        [parameter(Mandatory, Position = 0)]
        [int64]$Address,
        [int64]$Size,
        [NtApiDotNet.MemoryFreeType]$FreeType = "Release",
        [NtApiDotNet.NtProcess]$Process = [NtApiDotnet.NtProcess]::Current
    )
    $Process.FreeMemory($Address, $Size, $FreeType)
}

<#
.SYNOPSIS
Get information about a virtual memory region by address or for the entire process.
.DESCRIPTION
This cmdlet gets information about a virtual memory region or all regions in a process.
.PARAMETER Address
The address to get information about.
.PARAMETER Process
The process to query for memory information, defaults to current process.
.PARAMETER All
Show all memory regions.
.PARAMETER Name
Show only memory regions for the named mapped file.
.PARAMETER IncludeFree
When showing all memory regions specify to include free regions as well.
.OUTPUTS
NtApiDotNet.MemoryInformation
.EXAMPLE
Get-NtVirtualMemory $addr
Get the memory information for the specified address for the current process.
.EXAMPLE
Get-NtVirtualMemory $addr -Process $process
Get the memory information for the specified address in another process.
.EXAMPLE
Get-NtVirtualMemory
Get all memory information for the current process.
.EXAMPLE
Get-NtVirtualMemory -Process $process
Get all memory information in another process.
.EXAMPLE
Get-NtVirtualMemory -Process $process -IncludeFree
Get all memory information in another process including free regions.
.EXAMPLE
Get-NtVirtualMemory -Type Mapped
Get all mapped memory information for the current process.
.EXAMPLE
Get-NtVirtualMemory -Name file.exe
Get all mapped memory information where the mapped name is file.exe.
#>

function Get-NtVirtualMemory {
    [CmdletBinding(DefaultParameterSetName = "All")]
    param (
        [parameter(Mandatory, Position = 0, ParameterSetName = "FromAddress")]
        [int64]$Address,
        [NtApiDotNet.NtProcess]$Process = [NtApiDotnet.NtProcess]::Current,
        [parameter(ParameterSetName = "All")]
        [switch]$All,
        [parameter(ParameterSetName = "All")]
        [switch]$IncludeFree,
        [NtApiDotNet.MemoryType]$Type = "All",
        [parameter(ParameterSetName = "All")]
        [NtApiDotNet.MemoryState]$State = "Commit, Reserve",
        [parameter(ParameterSetName = "All")]
        [string]$Name
    )
    switch ($PsCmdlet.ParameterSetName) {
        "FromAddress" {
            $Process.QueryMemoryInformation($Address) | Write-Output
        }
        "All" {
            if ($IncludeFree) {
                $State = $State -bor "Free"
            }
            if ($Name -ne "") {
                $Process.QueryAllMemoryInformation($Type, $State) | Where-Object Name -eq $Name | Write-Output
            }
            else {
                $Process.QueryAllMemoryInformation($Type, $State) | Write-Output
            }
        }
    }
}

<#
.SYNOPSIS
Set protection flags for a virtual memory region.
.DESCRIPTION
This cmdlet sets protection flags for a region of virtual memory in the current process or another specified process.
.PARAMETER Address
The address location to set the memory protection.
.PARAMETER Size
The size of the memory region to set.
.PARAMETER Process
The process to set the memory in, defaults to current process.
.PARAMETER Protection
Specify the new protection for the memory region.
.OUTPUTS
NtApiDotNet.MemoryAllocationProtect - The previous memory protection setting.
.EXAMPLE
Set-NtVirtualMemory $addr 0x1000 ExecuteRead
Sets the protection of a memory region to ExecuteRead.
#>

function Set-NtVirtualMemory {
    [CmdletBinding()]
    param (
        [parameter(Mandatory, Position = 0)]
        [int64]$Address,
        [parameter(Mandatory, Position = 1)]
        [int64]$Size,
        [parameter(Mandatory, Position = 2)]
        [NtApiDotNet.MemoryAllocationProtect]$Protection,
        [NtApiDotNet.NtProcess]$Process = [NtApiDotnet.NtProcess]::Current
    )
    $Process.ProtectMemory($Address, $Size, $Protection)
}

<#
.SYNOPSIS
Reads bytes from a virtual memory region.
.DESCRIPTION
This cmdlet reads the bytes from a region of virtual memory in the current process or another specified process.
.PARAMETER Address
The address location to read.
.PARAMETER Size
The size of the memory to read. This is the maximum, if the memory address is invalid the returned buffer can be smaller.
.PARAMETER Process
The process to read from, defaults to current process.
.PARAMETER ReadAll
Specify to ensure you read all the requested memory from the process.
.PARAMETER Mapping
Specify a mapped section object.
.PARAMETER Offset
Specify the offset into the mapped section.
.OUTPUTS
byte[] - The array of read bytes. The size of the output might be smaller than the requested size.
.EXAMPLE
Read-NtVirtualMemory $addr 0x1000
Read up to 4096 from $addr.
.EXAMPLE
Read-NtVirtualMemory $addr 0x1000 -Process $process
Read up to 4096 from $addr in another process.
.EXAMPLE
Read-NtVirtualMemory $addr 0x1000 -ReadAll
Read up to 4096 from $addr, fail if can't read all the bytes.
.EXAMPLE
Read-NtVirtualMemory $map -Offset 100 -Size 512
Read up to 512 bytes from offset 100 into a mapped file.
#>

function Read-NtVirtualMemory {
    [CmdletBinding(DefaultParameterSetName="FromAddress")]
    param (
        [parameter(Mandatory, Position = 0, ParameterSetName="FromAddress")]
        [int64]$Address,
        [parameter(Mandatory, Position = 0, ParameterSetName="FromMapping")]
        [NtApiDotNet.NtMappedSection]$Mapping,
        [parameter(ParameterSetName="FromMapping")]
        [int64]$Offset = 0,
        [parameter(Mandatory, Position = 1)]
        [int]$Size,
        [parameter(ParameterSetName="FromAddress")]
        [NtApiDotNet.NtProcess]$Process = [NtApiDotnet.NtProcess]::Current,
        [switch]$ReadAll
    )

    if ($PSCmdlet.ParameterSetName -eq "FromMapping") {
        $Address = $Mapping.BaseAddress + $Offset
        $Process = $Mapping.Process
    }
    $Process.ReadMemory($Address, $Size, $ReadAll)
}

<#
.SYNOPSIS
Writes bytes to a virtual memory region.
.DESCRIPTION
This cmdlet writes bytes to a region of virtual memory in the current process or another specified process.
.PARAMETER Address
The address location to write.
.PARAMETER Data
The data buffer to write.
.PARAMETER Process
The process to write to, defaults to current process.
.PARAMETER Mapping
Specify a mapped section object.
.PARAMETER Offset
Specify the offset into the mapped section.
.PARAMETER Win32
Specify to use the Win32 WriteProcessMemory API which will automatically change page permissions.
.OUTPUTS
int - The length of bytes successfully written.
.EXAMPLE
Write-NtVirtualMemory $addr 0, 1, 2, 3, 4
Write 5 bytes to $addr
.EXAMPLE
Write-NtVirtualMemory $addr 0, 1, 2, 3, 4 -Process $process
Write 5 bytes to $addr in another process.
.EXAMPLE
Write-NtVirtualMemory $map -Offset 100 -Data 0, 1, 2, 3, 4
Write 5 bytes to a mapping at offset 100.
#>

function Write-NtVirtualMemory {
    [CmdletBinding(DefaultParameterSetName="FromAddress")]
    param (
        [parameter(Mandatory, Position = 0, ParameterSetName="FromAddress")]
        [int64]$Address,
        [parameter(Mandatory, Position = 0, ParameterSetName="FromMapping")]
        [NtApiDotNet.NtMappedSection]$Mapping,
        [parameter(ParameterSetName="FromMapping")]
        [int64]$Offset = 0,
        [parameter(Mandatory, Position = 1)]
        [byte[]]$Data,
        [parameter(ParameterSetName="FromAddress")]
        [NtApiDotNet.NtProcess]$Process = [NtApiDotnet.NtProcess]::Current,
        [switch]$Win32
    )

    if ($PSCmdlet.ParameterSetName -eq "FromMapping") {
        $Address = $Mapping.BaseAddress + $Offset
        $Process = $Mapping.Process
    }

    if ($Win32) {
        [NtApiDotNet.Win32.Memory.Win32MemoryUtils]::WriteMemory($Process, $Address, $Data)
    } else {
        $Process.WriteMemory($Address, $Data)
    }
}