Skip to content

Instantly share code, notes, and snippets.

@jcefoli
Last active May 3, 2023 20:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcefoli/816fac3234531f842217dd292a5188ce to your computer and use it in GitHub Desktop.
Save jcefoli/816fac3234531f842217dd292a5188ce to your computer and use it in GitHub Desktop.
Script that creates a scheduled task to keep your RDP session alive to work around nonsensical GPOs that inhibit productivity
<#
.SYNOPSIS
Keeps you productive by spoofing activity to prevent GPO idle timeouts, RDP disconnects, sleep, etc.
.Description
This script creates a Scheduled Task that runs at login which uses Kernel SetThreadExecutionState to prevent GPOs
from disconnecting your RDP session. Will also prevent sleeping/screensavers/display timeouts
See the example below for a one liner that will download and execute this script directly from GitHub!
ASSUMPTIONS AND WARNINGS:
==============================
- This will most likely be frowned upon by your security team. Please do not break any rules/compliance
- I am not responsible for any security issues that arise from misuse of this script (session hijacking, etc). Always log off when idle
- If run on a laptop/desktop, it will use more energy and prevent screen timeouts / sleep! Be environmentally conscious!
.NOTES
Version: 1.0.0
Author: jcefoli
Creation Date: 1/5/2021
.EXAMPLE
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://gist.githubusercontent.com/jcefoli/816fac3234531f842217dd292a5188ce/raw/9a1368cbdeea3cbb2f6a554216b335b6c9dd62a0/keepaliveScheduler.ps1'))
#>
$dateStamp = Get-Date -Format "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffff"
$currentUserSID = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).User.Value
$schTaskXML = @"
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>CHANGEME</Date>
<Author>CHANGEME</Author>
<URI>\Keepalive</URI>
</RegistrationInfo>
<Triggers>
<LogonTrigger>
<Enabled>true</Enabled>
<UserId>CHANGEME</UserId>
</LogonTrigger>
<SessionStateChangeTrigger>
<Enabled>true</Enabled>
<StateChange>RemoteConnect</StateChange>
<UserId>CHANGEME</UserId>
</SessionStateChangeTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<UserId>CHANGEME_SID</UserId>
<LogonType>InteractiveToken</LogonType>
<RunLevel>LeastPrivilege</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>false</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession>
<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>powershell</Command>
<Arguments>-WindowStyle Hidden -File "C:\Users\$($env:USERNAME)\keepalive.ps1"</Arguments>
</Exec>
</Actions>
</Task>
"@
# https://gist.github.com/jcefoli/36ed07c08dc3795648b3f45185f721c5
$rdpKeepalivePS1 = @"
`$host.ui.RawUI.WindowTitle = "Idle Keepalive"
`$dotNetCode = @'
[DllImport("kernel32.dll", CharSet = CharSet.Auto,SetLastError = true)]
public static extern void SetThreadExecutionState(uint esFlags);
'@
`$ste = Add-Type -memberDefinition `$dotNetCode -name System -namespace Win32 -passThru
`$ES_CONTINUOUS = [uint32]"0x80000000" #Requests that the other EXECUTION_STATE flags set remain in effect until SetThreadExecutionState is called again with the ES_CONTINUOUS flag set and one of the other EXECUTION_STATE flags cleared.
`$ES_AWAYMODE_REQUIRED = [uint32]"0x00000040" #Requests Away Mode to be enabled.
`$ES_DISPLAY_REQUIRED = [uint32]"0x00000002" #Requests display availability (display idle timeout is prevented).
`$ES_SYSTEM_REQUIRED = [uint32]"0x00000001" #Requests system availability (sleep idle timeout is prevented).
Write-Verbose "Power Plan suspended with option: `$option"
`$ste::SetThreadExecutionState(`$ES_CONTINUOUS -bor `$ES_SYSTEM_REQUIRED -bor `$ES_DISPLAY_REQUIRED)
read-host "Keepalive active. Press any key to quit"
Write-Verbose "Power Plan suspension ended"
`$ste::SetThreadExecutionState(`$ES_CONTINUOUS)
#powercfg -requests
"@
# Replacements
$schTaskXML = $schTaskXML -replace "<Date>CHANGEME</Date>", "<Date>$dateStamp</Date>"
$schTaskXML = $schTaskXML -replace "<Author>CHANGEME</Author>", "<Author>$($env:USERDOMAIN)\$($env:USERNAME)</Author>"
$schTaskXML = $schTaskXML -replace "<UserId>CHANGEME</UserId>", "<UserId>$($env:USERDOMAIN)\$($env:USERNAME)</UserId>"
$schTaskXML = $schTaskXML -replace "<UserId>CHANGEME_SID</UserId>", "<UserId>$($currentUserSID)</UserId>"
# Write Necessary Files
$rdpKeepalivePS1 | Out-File -FilePath "C:\Users\$($env:USERNAME)\keepalive.ps1" -Encoding ascii
$schTaskXML | Out-File -FilePath "C:\Users\$($env:USERNAME)\keepalive.xml" -Encoding ascii
# Schedule the task
. schtasks /delete /tn "RDP Keepalive" /f
. schtasks /create /xml "$home\keepalive.xml" /tn "RDP Keepalive" /IT
# Cleanup
Remove-Item -Path "$home\keepalive.xml" -Force
@sapddic
Copy link

sapddic commented May 3, 2023

Hello Jcefoli - it worked great !! , thank you for amazing work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment