Find Hidden Message Embedded in Image

Summary: Learn how to find a hidden message embedded in an image.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have an extra post from Shane Nelson with a recap of an interesting challenge presented to us at our Charlotte PowerShell User Group.

Shane is an associate Windows system administrator for a large-scale healthcare provider in the southeast U.S. He has worked in IT for since 2008. Along with his team, he helps manage and support Windows servers and Microsoft Exchange.

Take it away, Shane…

Windows PowerShell MVP, Jim Christopher, presented us with a challenge: Find the hidden message that he embedded in the image he provided. The image was a 640×480 seemingly black rectangle. And because most of us in the group aren’t Windows PowerShell veterans like the Scripting Guy, Jim gave us two clues:

1. He showed us how to load an image into Windows PowerShell:

$image = [System.Drawing.Image]::FromFile(‘C:\powershell\stegan1.png’)

2. He hinted that when we are look for a hidden message in an image, we need to examine the bits.

What are the “bits” of an image? The pixels!

So we needed to find a way to examine all the pixels in an image. Fortunately, our $image variable came with a GetPixel method, as seen from its Get-Member output.
When we called the GetPixel method, for instance on the first pixel (0,0), we saw its red, green, and blue values.
PS C:\powershell> $image.GetPixel(0,0)

R             : 0

G             : 0

B             : 0

A             : 255

IsKnownColor  : False

IsEmpty       : False

IsNamedColor  : False

IsSystemColor : False

Name          : ff000000

In this case, they were all zero, which indicates that this pixel was black. Now we needed to iterate through every pixel to see what was different. We did this by iterating through each pixel in a row and then iterating through each row (and in turn through each of its pixels).

for ($h=0;$h -lt $image.Height;$h++)


   for ($w=0;$w -lt $image.Width;$w++)





Here the inner for loop iterates through the objects in a row while the outer for loop iterates through the rows.

When we watched this output for a time, we noticed that some of the pixels had R=1 instead of R=0. This didn’t help us much, however, because the GetPixel output merely showed us screens and screens of the RGB values of each pixel. We wanted to see what was different.

We decided to create a map of the red pixels (the ones that were changed) so that we could let our human eyes notice any differences.

$pixelmap = @()

for ($h=0;$h -lt $image.Height;$h++)


   $line = “”

   for ($w=0;$w -lt $image.Width;$w++)


       $line += ($image.GetPixel($w,$h)).R


   $pixelmap += $line


Here we created a pixel map by concatenating each red value in a row to the $line variable and then concatenating each $line value to the $pixelmap variable.

Redirecting our pixel map to a text document with $pixelmap > pixelmap.txt, we were then able to open the text containing all of the R values, zoom out as far as we could, and see the hidden message:

Image of message

Happy Holidays, scripters!


Thanks, Shane!

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy