BGINFO - A Posh Recreation
Recently I have been building a lot of Windows Servers in different environments - one
Recently I have been building a lot of Windows Servers in different environments - one of the things I used to love to install as a system administrator was BGINFO. This helped me keep track of which server I was on. BGINFO was a stable tool for any system I built and administered for quite a while
https://learn.microsoft.com/en-us/sysinternals/downloads/bginfo
It would, as you expect, put information on the desktop image - this was very useful - made me less prone to mistakes. Mistakes make GrumpyAdmin very grumpy, and in my company, if people make a stupid mistake - they are Pizza Fined, which means they have to buy Pizza for the team! There is a reason; I am poor, overweight and looking at needing a heart bypass in the near future!!!
BGINFO helped ground an administrator into which system/server they were on!
However, BGINFO, an attack vector for particular environments, was banned and removed. MS has resolved the issues and is now safe and part of the Powertoys from Microsoft - However, it has now been delisted in most workplaces and environments I deal with.
https://www.securitynewspaper.com/2017/05/19/bypassing-application-whitelisting-bginfo/
Naturally, many replacements can be used - but the grumpy admin like to do his own thing and stuff and likes Powershell, and he thought to himself - can I do this in Powershell?
As I get easily distracted at work, I created a BGINFO PowerShell-type script.
I thought about it and the logical steps that I needed to take:
Get the information into variables - what variables would I want to get?, well things I feel are essential to know at a glance on a server is:
Hostname
The Operating System
The processor
Total Memory in GB
The IP address
System UPTIME
The domain name
The current logon user
Windows Defender last updated
Now as a PowerShell guru, we know how to collect these things.
Wow that works - So now I have this - I need a way for PowerShell to modify the desktop background.
This is where we can look at and use the C# abilities of Powershell.
https://learn.microsoft.com/en-us/dotnet/api/system.drawing.graphics?view=windowsdesktop-7.0
So as you can see, there are lots of capabilities to manipulate graphics - looking at the table of classes - there is something we can use to achieve our aim!
DRAWSTRING
Now that I know that, I can start to create a function that will open an image file and then write the strings from the variables on the image.
After some trial and error work, I developed the following code.
$font = "Arial"
$Size = 16
$OutputImagePath = "C:\GrumpyAdmin\new_background.jpg"
$BackGroundImagePath = "C:\GrumpyAdmin\background.jpg"
# Create a new Graphics object
$image = [System.Drawing.Image]::FromFile($BackgroundImagePath)
$graphic = [System.Drawing.Graphics]::FromImage($image)
$positionX = 10
$positionY = 10
# Draw the text
$position = New-Object System.Drawing.PointF($positionX, $positionY)
$graphic.DrawString($MachineName, $font, $brush, $position)
# Save the modified image
$image.Save($OutputImagePath)
Wow, that works! Amazing!
This proves the concept will work! So the next step is to get the code to write every variable on the desktop image!
Typically when dealing with Powershell and variables - it is much better to create what they call a HashMap
So here is a simple HashMap for my variables
$variables = @{
"Machine Name" = $machineName
"Operating System" = $operatingSystem
"Processor" = $processor
"Total Memory (GB)" = "{0:N2}" -f $totalMemory
"IP Address" = $ipAddress
"Uptime" = $uptime
"Defender Last Updated" = $defenderLastUpdated
"Domain Name" = $domainName
"Current Logon User" = $currentLogonUser
}
Then we need to loop through the Hashmap and put them on JPG image.
So this part of the problem is sorted - we can generate a JPG image with our variable information printed on it!
The next thing to do is to set that as the active background image.
Here is a quick Google, and I find some sample code that does the trick - why reinvent the wheel? Grumpy Admin does something very common: Swipping with Pride - I am not the smartest of the best in the world, why trouble myself with writing and figuring stuff out when you can Google and swipe with pride? :)
The main thing is that as long as you understand what the code you steal does and that you don't violate any licences, you are golden!
# Set the wallpaper path in the registry
Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name Wallpaper -Value $OutputImagePath
# Set the wallpaper style in the registry
Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name WallpaperStyle -Value 2
# Refresh the desktop
$user32Dll = Add-Type -MemberDefinition @"[DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni); "@ -Name "User32Dll" -Namespace "User32" -PassThru
$SPI_SETDESKWALLPAPER = 20
$SPIF_UPDATEINIFILE = 0x01
$SPIF_SENDCHANGE = 0x02
$result = $user32Dll::SystemParametersInfo($SPI_SETDESKWALLPAPER, 0, $OutputImagePath, $SPIF_UPDATEINIFILE -bor $SPIF_SENDCHANGE)
Combine the whole thing, and I have a working prototype PowerShell code! Yippy!
Now as always, I have a working code example. It is time to turn that into a total production standard code!
There are many function templates, and the standard is well documented - so if you are doing POSH code much, you should know this.
I am going to call this function - using the standard verb method.
Add-TextToImage
So variables are extracted, and a CmdLetBinding is created for each.
I used If statements used to toggle elements of the code on or off
Added comments to the code at various structural points
Here is the completed working function - Enjoy
function Add-TextToImage {
<#
.SYNOPSIS
Adds text to an image using machine information variables. Written by Grumpy Admin Version 0.01 28/06/2023
.DESCRIPTION
This function takes a background image and adds text to it using machine information variables, similar to the BGInfo application. The modified image is saved as a JPG.
.PARAMETER BackgroundImagePath
The path to the background image.
.PARAMETER OutputImagePath
The path to save the modified image.
.PARAMETER Font
The font to be used for the text. Default: "Arial".
.PARAMETER Size
The font size. Default: 14.
.PARAMETER AntiAlias
Specifies whether to enable anti-aliasing for the text. Default: $true.
.PARAMETER SetAsDesktopBackground
Specifies whether to set the modified image as the active desktop background. Default: $false.
.EXAMPLE
Add-TextToImage -BackgroundImagePath "C:\path\to\background.jpg" -OutputImagePath "C:\path\to\output.jpg" -Size 16 -AntiAlias $false -SetAsDesktopBackground
Adds text to the background image using machine information variables with a font size of 16, no anti-aliasing. Sets the modified image as the active desktop background.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0, HelpMessage = "Path to the background image.")]
[ValidateScript({ Test-Path $_ -PathType Leaf })]
[String]$BackgroundImagePath,
[Parameter(Mandatory = $true, Position = 1, HelpMessage = "Path to save the modified image.")]
[String]$OutputImagePath,
[Parameter(Position = 2, HelpMessage = "The font to be used for the text.")]
[String]$FontName = "Arial",
[Parameter(Position = 3, HelpMessage = "The font size.")]
[int]$Size = 16,
[Parameter(Position = 4, HelpMessage = "Specifies whether to enable anti-aliasing for the text.")]
[bool]$AntiAlias = $true,
[Parameter(Position = 5, HelpMessage = "Specifies whether to set the modified image as the active desktop background.")]
[switch]$SetAsDesktopBackground
)
# Get machine information
$machineName = $env:COMPUTERNAME
$operatingSystem = (Get-CimInstance -ClassName Win32_OperatingSystem).Caption
$processor = (Get-CimInstance -ClassName Win32_Processor).Name
$totalMemory = (Get-CimInstance -ClassName Win32_ComputerSystem).TotalPhysicalMemory / 1GB
$ipAddress = (Get-NetIPAddress | Where-Object { $_.AddressFamily -eq 'IPv4' -and $_.InterfaceAlias -ne 'Loopback' }).IPAddress
# Additional machine information variables
$LastBootUpTime = (Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime
$SystemUpTime = (Get-Date) - $LastBootUpTime
$Days = $SystemUpTime.Days
$Hours = $SystemUpTime.Hours
$Minutes = $SystemUpTime.Minutes
$Seconds = $SystemUpTime.Seconds
$uptime = "$Days days, $Hours hours, $Minutes minutes"
$defenderLastUpdated = (Get-MpComputerStatus).AntivirusSignatureLastUpdated
$domainName = (Get-CimInstance -ClassName Win32_ComputerSystem).Domain
$currentLogonUser = $env:USERNAME
# Create a hashtable to store the variable names and their corresponding values
$variables = @{
"Machine Name" = $machineName
"Operating System" = $operatingSystem
"Processor" = $processor
"Total Memory (GB)" = "{0:N2}" -f $totalMemory
"IP Address" = $ipAddress
"Uptime" = $uptime
"Defender Last Updated" = $defenderLastUpdated
"Domain Name" = $domainName
"Current Logon User" = $currentLogonUser
}
# Create a new Graphics object
$image = [System.Drawing.Image]::FromFile($BackgroundImagePath)
$graphic = [System.Drawing.Graphics]::FromImage($image)
# Set the font properties
$font = New-Object System.Drawing.Font($FontName, $Size, [System.Drawing.FontStyle]::Regular, [System.Drawing.GraphicsUnit]::Pixel)
$brush = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::White)
# Set anti-aliasing
if ($AntiAlias) {
$graphic.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
}
$positionX = 10
$positionY = 10
foreach ($variable in $variables.GetEnumerator()) {
$variableName = $variable.Key
$variableValue = $variable.Value
# Draw the text
$position = New-Object System.Drawing.PointF($positionX, $positionY)
$graphic.DrawString($variableName +":" + $variableValue, $font, $brush, $position)
# Increase the Y position for the next variable
$positionY += 20
}
# Save the modified image
$image.Save($OutputImagePath)
# Clean up
$graphic.Dispose()
$font.Dispose()
$brush.Dispose()
# Set the wallpaper path in the registry
Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name Wallpaper -Value $OutputImagePath
# Set the wallpaper style in the registry
Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name WallpaperStyle -Value 2
# Refresh the desktop
$user32Dll = Add-Type -MemberDefinition @"
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
"@ -Name "User32Dll" -Namespace "User32" -PassThru
$SPI_SETDESKWALLPAPER = 20
$SPIF_UPDATEINIFILE = 0x01
$SPIF_SENDCHANGE = 0x02
$result = $user32Dll::SystemParametersInfo($SPI_SETDESKWALLPAPER, 0, $OutputImagePath, $SPIF_UPDATEINIFILE -bor $SPIF_SENDCHANGE)
}