Skip to main content

Any chance you can post screenshots of what the popup prompts look like?


image


any chance we could see the eval/rem code in a non-PDF format?


Sure. Sorry I didn’t post earlier. I have been very busy working with OAuth 2. 🙂

Hidden.vbs
CreateObject("Wscript.Shell").Run """" & WScript.Arguments(0) & """", 0, False

Runtoasthidden.cmd
powershell.exe -executionpolicy Unrestricted -file "C:\ProgramData\amagent\rebootNotification\RebootToastNotification.ps1"

Runreboothidden.cmd
powershell.exe -executionpolicy Unrestricted -file "C:\ProgramData\amagent\rebootNotification\RebootWithoutNotification.ps1"

Rebootwithoutnotification.ps1
Add-Type @’
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PInvoke.Win32 {

    public static class UserInput {

        [DllImport("user32.dll", SetLastError=false)]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

        [StructLayout(LayoutKind.Sequential)]
        private struct LASTINPUTINFO {
            public uint cbSize;
            public int dwTime;
        }

        public static DateTime LastInput {
            get {
                DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount);
                DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
                return lastInput;
            }
        }

        public static TimeSpan IdleTime {
            get {
                return DateTime.UtcNow.Subtract(LastInput);
            }
        }

        public static int LastInputTicks {
            get {
                LASTINPUTINFO lii = new LASTINPUTINFO();
                lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
                GetLastInputInfo(ref lii);
                return lii.dwTime;
            }
        }
    }
}
'@

Function IsAutomaticReboot
{
    <# 
    
    Two conditions must be met for automatic reboot
    1. The local time must be between $rebootStartTime and $rebootEndTime
    2. There has not been any user input for at least $idleTime minutes

    #>

    $boolTimeConditionMet = $False
    $minTime = Get-Date $rebootStartTime
    $maxTime = Get-Date $rebootEndTime
    $now = Get-Date
    if ($minTime.TimeOfDay -le $now.TimeOfDay -and $maxTime.TimeOfDay -ge $now.TimeOfDay) 
    {
        $boolTimeConditionMet = $True
    }

    $boolIdleConditionMet = $False
    $idleTime = [PInvoke.Win32.UserInput]::IdleTime
    if ($idleTime.Minutes -ge $minIdleTime)
    {
        $boolIdleConditionMet = $True
    }

    return ($boolIdleConditionMet -and $boolTimeConditionMet)
}

Function RebootComputer
{
    Restart-Computer -Force
}

#---------------------------------------------------------------------------------------
# These parameters control the time of day window within which automatic reboot will be initiated
[string]$rebootStartTime = "1am"
[string]$rebootEndTime = "5am"

# If there is no user input detected for $minIdleTime minutes within $rebootStarTime and $rebootEndTime, automatic reboot
# will be initiated
[int]$minIdleTime = 30

# Check if a reboot is pending
$sysInfo = New-Object -ComObject "Microsoft.Update.SystemInfo"
if($sysInfo.RebootRequired)
{
  if (IsAutomaticReboot)
  {
    RebootComputer
  }
}

Reboottoastnotification.ps1
Add-Type @’
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PInvoke.Win32 {

    public static class UserInput {

        [DllImport("user32.dll", SetLastError=false)]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

        [StructLayout(LayoutKind.Sequential)]
        private struct LASTINPUTINFO {
            public uint cbSize;
            public int dwTime;
        }

        public static DateTime LastInput {
            get {
                DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount);
                DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
                return lastInput;
            }
        }

        public static TimeSpan IdleTime {
            get {
                return DateTime.UtcNow.Subtract(LastInput);
            }
        }

        public static int LastInputTicks {
            get {
                LASTINPUTINFO lii = new LASTINPUTINFO();
                lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
                GetLastInputInfo(ref lii);
                return lii.dwTime;
            }
        }
    }
}
'@

Function SaveToastLogo
{
    param([string]$strImageFile)

    $Base64Image = "......replace with your own image....."
    if (!(Test-Path $strImageFile))
    {
      # Create an image file from base64 string and save to user temp location    
      [byte[]]$Bytes = [convert]::FromBase64String($Base64Image)
      [System.IO.File]::WriteAllBytes($strImageFile,$Bytes)
    }
}

Function ClearToastNotification
{
    param([string]$toastAppID)
    [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
    [Windows.UI.Notifications.ToastNotification, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
    [Windows.UI.Notifications.ToastNotificationManager]::History.Clear($appID)
}

Function ShowToastNotification
{
    param([string]$rebootStartTime, [string]$rebootEndTime)

[xml]$Toast = @"
<toast scenario="reminder">
  <visual>
      <binding template="ToastGeneric">
       <text>BW IT Notice</text>
        <text>Your computer was recently patched. Please restart manually, or automatic restart will occur between $rebootStartTime and $rebootEndTime.</text>
        <image placement="appLogoOverride" hint-crop="circle" src="$ImageFile"/>
        <group>
         <subgroup>
          <text hint-style="captionSubtle">If you need assistance, contact Help Desk.</text>
         </subgroup>
        </group>
      </binding>
  </visual>
  <actions>
          <action activationType="system"
                  arguments="dismiss" 
                  content="Dismiss"
                  />
  </actions>
  <audio src="ms-winsoundevent:Notification.Looping.Alarm3"/>
</toast>
"@

    $Load = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
    $Load = [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime]

    # Load the notification into the required format
    $ToastXml = New-Object -TypeName Windows.Data.Xml.Dom.XmlDocument
    $ToastXml.LoadXml($Toast.OuterXml)

    [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($appID).Show($ToastXml)
}

Function IsAutomaticReboot
{
    <# 
    
    Two conditions must be met for automatic reboot
    1. The local time must be between $rebootStartTime and $rebootEndTime
    2. There has not been any user input for at least $idleTime minutes

    #>

    $boolTimeConditionMet = $False
    $minTime = Get-Date $rebootStartTime
    $maxTime = Get-Date $rebootEndTime
    $now = Get-Date
    if ($minTime.TimeOfDay -le $now.TimeOfDay -and $maxTime.TimeOfDay -ge $now.TimeOfDay) 
    {
        $boolTimeConditionMet = $True
    }

    $boolIdleConditionMet = $False
    $idleTime = [PInvoke.Win32.UserInput]::IdleTime
    if ($idleTime.Minutes -ge $minIdleTime)
    {
        $boolIdleConditionMet = $True
    }

    return ($boolIdleConditionMet -and $boolTimeConditionMet)
}

Function RebootComputer
{
    Restart-Computer -Force
}

Function IsTimeForReminder
{
    param([int]$intMinutesBetweenReminders, [string]$registryPath)

    $doReminder = $False
    $intLastReminder = GetMinutesSinceLastReminder($registryPath)
    if ($intLastReminder -ge $intMinutesBetweenReminders)
    {        
        $doReminder = $True
        UpdateReminderTracker($registryPath)
    }
    return $doReminder
}

Function GetMinutesSinceLastReminder
{
    param([string]$registryPath)
    
    $minutesSinceLastReminder = 999999
    $boolReturnVal = VerifyReminderTracker($registryPath)
    if ($boolReturnVal -eq $False)
    {
        $lastReminder = (Get-ItemProperty -Path $registryPath -Name "LastNotified").LastNotified
        $lastReminder = [datetime]$lastReminder

        $tempDate = New-TimeSpan -Start $lastReminder -End (Get-Date)
        $minutesSinceLastReminder = $tempDate.TotalMinutes
    }
    return ($minutesSinceLastReminder)
}

Function UpdateReminderTracker
{
    param([string]$registryPath)

    VerifyReminderTracker($registryPath)
    $now = [string](Get-Date)
    Set-ItemProperty -Path $registryPath -Name "LastNotified" -Value $now | Out-Null
}

Function VerifyReminderTracker
{
   param([string]$registryPath)

   $boolFirstTimeRunning = $False
   InitializeReminderTracker($registryPath)
   $lastReminder = (Get-ItemProperty -Path $registryPath -Name "LastNotified").LastNotified
   if (!([string]$lastReminder -as [DateTime]))
   {
        $now = [string](Get-Date)
        Set-ItemProperty -Path $registryPath -Name "LastNotified" -Value $now | Out-Null
        $boolFirstTimeRunning = $True
   }
   return $boolFirstTimeRunning
}

#--- Never call this function directly
Function InitializeReminderTracker
{    
    param([string]$registryPath)

    $Now = (Get-Date)
    $Now = $Now.AddMonths(-3)
    $Now = [string]$now
    if (!(Test-Path $registryPath))
    {
        New-Item -Path $registryPath -Force | Out-Null
        New-ItemProperty -Path $registryPath -Name "LastNotified" -Value $Now -PropertyType STRING -Force | Out-Null
    }

    try
    {
        $lastReminder = (Get-ItemProperty -Path $registryPath -Name "LastNotified" -ErrorAction Stop).LastNotified
    }
    catch
    {
        New-ItemProperty -Path $registryPath -Name "LastNotified" -Value $Now -PropertyType STRING -Force | Out-Null
    }
}

#---------------------------------------------------------------------------------------
[string]$appID = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe"

# Note: These next two variables work because this script is run as a scheduled task configured to execute as the currently logged on user.
# This is needed so that user is able to see the toast notification. If the task runs under the SYSTEM context, the toast notification
# will not show.
[string]$ImageFile = "$env:TEMP\BWToastLogo.png"
[string]$NotificationRegistryPath = "HKCU:\Software\BW\RebootNotification"        

# These parameters control the time of day window within which automatic reboot will be initiated
[string]$rebootStartTime = "1am"
[string]$rebootEndTime = "5am"

# If there is no user input detected for $minIdleTime minutes within $rebootStarTime and $rebootEndTime, automatic reboot
# will be initiated
[int]$minIdleTime = 30

# How many times per day to show reboot reminder toast notification
[int]$reminderTimesPerDay = 1

# Dump the base-64 image into a file on disk. This image is used in the toast notification.
# If the image is missing, the toast notification will not show.
SaveToastLogo($ImageFile)

# Clear any previous toast notifications generated by Powershell. This will ensure we don't have notifications that are stacked (they show one at a time
# immediately after you dismiss one). This may confuse / irritate a user.
ClearToastNotification($appID)

# Check registry key exists. Create if it doesn't exist.
VerifyReminderTracker($NotificationRegistryPath)
[int]$minutesBetweenReminders = [int]((24 / $reminderTimesPerDay) * 60)

# Check if a reboot is pending
$sysInfo = New-Object -ComObject "Microsoft.Update.SystemInfo"
if($sysInfo.RebootRequired)
{
  if (IsAutomaticReboot)
  {
    RebootComputer
  }
  else
  {
    if (IsTimeForReminder $minutesBetweenReminders $NotificationRegistryPath)
    {
       ShowToastNotification $rebootStartTime $rebootEndTime
    }
  }
}

you da man!