NtWindowFunctions.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
Get the names of the Windows Stations in the current Session.
.DESCRIPTION
This cmdlet queries the names of the Window Stations in the current Session.
.PARAMETER Current
Show the current Window Station name only.
.INPUTS
string
.OUTPUTS
None
#>

function Get-NtWindowStationName {
    Param(
        [Parameter()]
        [switch]$Current
    )

    if ($Current) {
        [NtApiDotNet.NtWindowStation]::Current.Name | Write-Output
    }
    else {
        [NtApiDotNet.NtWindowStation]::WindowStations | Write-Output
    }
}

<#
.SYNOPSIS
Gets the names of the Desktops from the specified Window Station.
.DESCRIPTION
This cmdlet queries the names of the Desktops from the specified Window Station.
By default will use the current process Window Station.
.PARAMETER WindowStation
The Window Station to query.
.PARAMETER Current
Specify to get the name of the current thread desktop.
.PARAMETER ThreadId
Specify to get the name of the desktop from a thread.
.INPUTS
string
.OUTPUTS
None
#>

function Get-NtDesktopName {
    [CmdletBinding(DefaultParameterSetName = "FromCurrentWindowStation")]
    Param(
        [Parameter(Position = 0, ParameterSetName = "FromWindowStation")]
        [NtApiDotNet.NtWindowStation]$WindowStation,
        [Parameter(ParameterSetName = "FromCurrentDesktop")]
        [switch]$Current,
        [Parameter(ParameterSetName = "FromThreadId")]
        [alias("tid")]
        [int]$ThreadId
    )

    switch ($PSCmdlet.ParameterSetName) {
        "FromCurrentWindowStation" {
            $winsta = [NtApiDotNet.NtWindowStation]::Current
            $winsta.Desktops | Write-Output
        }
        "FromWindowStation" {
            $WindowStation.Desktops | Write-Output
        }
        "FromCurrentDesktop" {
            [NtApiDotNet.NtDesktop]::Current.Name | Write-Output
        }
        "FromThreadId" {
            [NtApiDotNet.NtDesktop]::GetThreadDesktop($ThreadId).Name | Write-Output
        }
    }
}

<#
.SYNOPSIS
Gets the a list of Window handles.
.DESCRIPTION
This cmdlet queries the list of Window Handles based on a set of criteria such as Desktop or ThreadId.
.PARAMETER Desktop
The Desktop to query.
.PARAMETER Parent
Specify the parent Window if enumerating children.
.PARAMETER Children
Specify the get list of child windows.
.PARAMETER Immersive
Specify to get immersive Windows.
.PARAMETER ThreadId
Specify the thread ID for the Window.
.PARAMETER ProcessId
Specify the process ID for the Window.
.INPUTS
None
.OUTPUTS
NtApiDotNet.NtWindow
#>

function Get-NtWindow {
    [CmdletBinding()]
    Param(
        [NtApiDotNet.NtDesktop]$Desktop,
        [switch]$Children,
        [switch]$Immersive,
        [NtApiDotNet.NtWindow]$Parent = [NtApiDotNet.NtWindow]::Null,
        [alias("tid")]
        [int]$ThreadId,
        [alias("pid")]
        [int]$ProcessId
    )

    $ws = [NtApiDotNet.NtWindow]::GetWindows($Desktop, $Parent, $Children, !$Immersive, $ThreadId)
    if ($ProcessId -ne 0) {
         $ws = $ws | Where-Object ProcessId -eq $ProcessId
    }
    $ws | Write-Output
}

<#
.SYNOPSIS
Send a message to a Window handle.
.DESCRIPTION
This cmdlet sends a message to a window handle.
.PARAMETER Window
The Window to send to.
.PARAMETER Message
Specify the message to send.
.PARAMETER WParam
Specify the WPARAM value.
.PARAMETER LParam
Specify the LPARAM value.
.PARAMETER Wait
Specify to send the message and wait rather than post.
.PARAMETER Ansi
Specify to send the message as ANSI rather than Unicode.
.INPUTS
None
.OUTPUTS
System.IntPtr
#>

function Send-NtWindowMessage {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory, Position = 0, ValueFromPipeline)]
        [NtApiDotNet.NtWindow[]]$Window,
        [Parameter(Mandatory, Position = 1)]
        [int]$Message,
        [Parameter(Position = 2)]
        [IntPtr]$WParam = [IntPtr]::Zero,
        [Parameter(Position = 3)]
        [IntPtr]$LParam = [IntPtr]::Zero,
        [switch]$Wait,
        [switch]$Ansi
    )

    PROCESS {
        foreach($w in $Window) {
            if ($Wait) {
                if ($Ansi) {
                    $w.SendMessageAnsi($Message, $WParam, $LParam) | Write-Output
                } else {
                    $w.SendMessage($Message, $WParam, $LParam) | Write-Output
                }
            } else {
                if ($Ansi) {
                    $w.PostMessageAnsi($Message, $WParam, $LParam)
                } else {
                    $w.PostMessage($Message, $WParam, $LParam)
                }
            }
        }
    }
}

<#
.SYNOPSIS
Get an ATOM object.
.DESCRIPTION
This cmdlet gets all ATOM objects or by name or atom.
.PARAMETER Atom
Specify the ATOM to get.
.PARAMETER Name
Specify the name of the ATOM to get.
.PARAMETER User
Specify to get a user atom rather than a global.
.INPUTS
None
.OUTPUTS
NtApiDotNet.NtAtom
#>

function Get-NtAtom {
    [CmdletBinding(DefaultParameterSetName = "All")]
    Param(
        [Parameter(Mandatory, ParameterSetName = "FromAtom")]
        [uint16]$Atom,
        [Parameter(Mandatory, Position = 0, ParameterSetName = "FromName")]
        [string]$Name,
        [Parameter(ParameterSetName = "All")]
        [Parameter(ParameterSetName = "FromAtom")]
        [switch]$User
    )

    switch ($PSCmdlet.ParameterSetName) {
        "All" { [NtApiDotNet.NtAtom]::GetAtoms(!$User) | Write-Output }
        "FromAtom" { [NtApiDotNet.NtAtom]::Open($Atom, $true, !$User, $true).Result | Write-Output }
        "FromName" { [NtApiDotNet.NtAtom]::Find($Name) | Write-Output }
    }
}

<#
.SYNOPSIS
Add a ATOM object.
.DESCRIPTION
This cmdlet adds an ATOM objects.
.PARAMETER Name
Specify the name of the ATOM to add.
.PARAMETER Flags
Specify the flags for the ATOM.
.INPUTS
None
.OUTPUTS
NtApiDotNet.NtAtom
#>

function Add-NtAtom {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory, Position = 0)]
        [string]$Name,
        [NtApiDotNet.AddAtomFlags]$Flags = 0
    )

    [NtApiDotNet.NtAtom]::Add($Name, $Flags) | Write-Output
}

<#
.SYNOPSIS
Removes an ATOM object.
.DESCRIPTION
This cmdlet removes an ATOM object by name or atom.
.PARAMETER Object
Specify the NtAtom object to remove.
.PARAMETER Atom
Specify the ATOM to remove.
.PARAMETER Name
Specify the name of the ATOM to remove.
.INPUTS
None
.OUTPUTS
None
#>

function Remove-NtAtom {
    [CmdletBinding(DefaultParameterSetName = "All")]
    Param(
        [Parameter(Position = 0, Mandatory, ParameterSetName = "FromObject")]
        [NtApiDotNet.NtAtom]$Object,
        [Parameter(Mandatory, ParameterSetName = "FromAtom")]
        [uint16]$Atom,
        [Parameter(Mandatory, Position = 0, ParameterSetName = "FromName")]
        [string]$Name
    )

    $obj = switch ($PSCmdlet.ParameterSetName) {
        "FromObject" { $Object }
        "FromAtom" { Get-NtAtom -Atom $Atom }
        "FromName" { Get-NtATom -Name $Name }
    }

    if ($null -ne $obj) {
        $obj.Delete()
    }
}