Wednesday, July 21, 2021

Move-Mouse -- Keep your screen awake, session from dying, etc.

This was a fun project I did awhile ago.  I work in industries that typically have higher IT security standards.  However, much of that is bureaucracy and the org just wants to check a box -- as in this case.

The org I wrote this for had a security policy that required a screen idle timeout and session-logoff after 15 minutes.  That's good so that people don't stay logged into something in perpetuity; however, it becomes a pain in the neck for any process/operation that takes longer than 15 mins (e.g. hours).  

So when I offered to solve the problem, they got approval from their security team and I provided a mouse-mover in PowerShell.

This was an interesting exercise as it really forced me to get a better understanding of both screen positioning and actions that interact with Windows built-in idle-tracking.  

As an example of one of my failed experiments, I could not locate any .NET class that would interact with Windows idle-tracking.  It moved the mouse or entered keys successfully but still allowed the session to be terminated by the security policy.  

So I went a little deeper and decided to use Platform Invocation (P/Invoke) and while that did work to successfully move the mouse and do it in a way that also was Windows-idle-timeout-aware, it added a slightly new layer of complexity.  In the end, that didn't matter but it's worth noting.

System.Windows.Forms.Cursor represents a position that manipulates pixel position.

user32.dll::Mouse_Event.MoveTo() represents a point on a 65535 x 65535 grid called a 'mickey'. 

So... if you choose to emit the mouse position but set your $XY to something really small, you won't actually see the mouse move and the numbers reported to the screen won't reflect any change.

Again, this is because if you use the default of moving 1-mickey, the movement is too small to register from the .NET Cursor class.  

The logic employed oscillates the mouse back and forth.  Initial implementations didn't negate the previous movement so overtime, the mouse would drift into oblivion.  ;)

Examples:


## Move the mouse to keep the screen awake:
Move-Mouse -XY 1 -Secs 1 -LoopInfinite $true -DisplayPosition $true

## Move the mouse once:
Move-Mouse -XY 1 -Secs 1 -DisplayPosition $true

## Get frustrated!!
## Remember that CTRL-C is your friend.  ;)
Move-Mouse -XY 100 -Secs 1 -LoopInfinite $true -DisplayPosition $true


Function Move-Mouse {
param (
    ## Declare variables
    [uint16] $XY=1,  ## Mouse Position provided to both x and y axis
    [int32] $Secs = 5, ## Number of seconds to sleep between mouse movements when LoopInfinite is defined
    [boolean] $LoopInfinite = $false,  ## Determines whether to loop infinitely or not
    [boolean] $DisplayPosition = $false  ## Determines whether to write the mouse's pixel location to the screen.
)

begin {

    ## Use a .NET type defintion to access P/Invoke for the appropriate DLL and function.
    $typedef = @"
using System.Runtime.InteropServices;

namespace PoSh
{
    public static class Mouse
    {
        [DllImport("user32.dll")]
        static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);

        private const int MOUSEEVENTF_MOVE = 0x0001;

        public static void MoveTo(int x, int y)
        {
            mouse_event(MOUSEEVENTF_MOVE, x, y, 0, 0);
        }
    }
}
"@
    ## Load the type defintion into memory
    Add-Type -TypeDefinition $typedef

}

process {

    ## Determine if we want to loop infinitely, default is false
    if ($LoopInfinite) {
        
        $i = 1
        while ($true) {
            ## Write the pixel location to screen
            if ($DisplayPosition) { Write-Host "$([System.Windows.Forms.Cursor]::Position.X),$([System.Windows.Forms.Cursor]::Position.Y)" }
            
            ## Use modulo to alternate the mouse movement back and forth, default is a relative 1,1 and then -1,-1
            if (($i % 2) -eq 0) {
                [PoSh.Mouse]::MoveTo($XY, $XY)
                $i++
            } else {
                [PoSh.Mouse]::MoveTo(-$XY, -$XY)
                $i--
            }

            Start-Sleep -Seconds $Secs
        }
    } else {
        if ($DisplayPosition) { Write-Host "$([System.Windows.Forms.Cursor]::Position.X),$([System.Windows.Forms.Cursor]::Position.Y)" }
    
        [PoSh.Mouse]::MoveTo($XY, $XY)
    }
}

}

No comments:

Post a Comment