PowerShell: Embed binary data in your script

When writing automation scripts or modules, you might find that you frequently reference external binary data.

Binary data? Well, that accounts for all data!” you might say.

Yes, that’s true. But I’m talking about binary data as opposed to files containing simple ASCII or UTF-8 data. Maybe there’s some better terminology to describe that, but hey it works for now. Binary data could include things such as:

  • Word documents
  • Executable (Portable Executable format)
  • Code libraries (DLLs)
  • Registry files
  • etc.

In the case of executables, oftentimes they provide useful functionality that would take many lines of PowerShell code to replicate. Some developers, for better or for worse, elect to use these utilities instead of going through the effort of writing the necessary code to handle the function natively in PowerShell. This creates an additional dependency when porting the PowerShell code, as the author must be sure to include the utility with their code, or otherwise ensure (via documentation, for example) that the target user will already have it available.

Wouldn’t it be nice if you didn’t have to depend on the user having some executable pre-installed, just to get your script to work, though? Unfortunately the little topic of “software licensing” can sometimes prevent redistribution of software that you are not given explicit permission to copy, however there are also many cases where this is allowed (eg. open-source projects). The work-around in cases where redistribution is not allowed, is to either direct the user where to download the software from, or automate it for them.

In scenarios where redistributing software is allowed, you can opt to include the software side-by-side with your script, in the same folder on the filesystem, include it as part of a program installer, OR you could embed the binary data inside of your PowerShell script. That way, if someone ever got hold of your script file without the other surround files, it will still work!

The best way I can summarize this would be to call it “portable.” Your script can be copied from computer to computer, from user to user, and you don’t have to worry about it breaking because the dependencies weren’t included in the copy (without explicitly modifying the script code).

So, how do you do it? How do you embed the binaries into your script? Although it might sound complicated, it’s surprisingly easy to achieve. In short, binary data (a bunch of 1s and 0s) can be converted into a different representation. This alternate representation is called a Base64 string, which is an ASCII representation of the binary data in question. By embedding the data as a string inside of our script, we can easily write it out to a file on-demand and execute it when necessary.

The process we will follow to convert binary data into a string will look like the following:

  1. Obtain the binary data as an array of [Byte] objects (a [Byte[]])
  2. Convert the [Byte[]] into a Base64 string

Let’s write a simple function that converts a file path (input) to a Base64 string.

[cc height=”-1″ lang=”powershell”]
function Convert-BinaryToString {
[CmdletBinding()]
param (
[string] $FilePath
)

try {
$ByteArray = [System.IO.File]::ReadAllBytes($FilePath);
}
catch {
throw “Failed to read file. Please ensure that you have permission to the file, and that the file path is correct.”;
}

if ($ByteArray) {
$Base64String = [System.Convert]::ToBase64String($ByteArray);
}
else {
throw ‘$ByteArray is $null.’;
}

Write-Output -InputObject $Base64String;
}

$Output = Convert-BinaryToString -FilePath C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe;
[/cc]

This function simply reads in an array of bytes, converts the byte array to a Base64 string, and spits the string out to the PowerShell pipeline. Now we can simply call it and assign the output to a variable, as shown in the last line. Once it is in a variable, we can pipe the variable to clip.exe to copy the variable’s contents to the clipboard. Assigning the clipboard content to a variable is as simple as defining the variable, and pasting the content:

[cc lang=”powershell”]
$PowerShellExecutable = ‘TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAA4fug4AtAnN……’;
[/cc]

Now that we have a Base64 string embedded in our script, let’s convert it back to a file (which ultimately is an array of bytes).

[cc height=”-1″ lang=”powershell”]
function Convert-StringToBinary {
[CmdletBinding()]
param (
[string] $InputString
, [string] $FilePath = (‘{0}\{1}’ -f $env:TEMP, [System.Guid]::NewGuid().ToString())
)

try {
if ($InputString.Length -ge 1) {
$ByteArray = [System.Convert]::FromBase64String($InputString);
[System.IO.File]::WriteAllBytes($FilePath, $ByteArray);
}
}
catch {
throw (‘Failed to create file from Base64 string: {0}’ -f $FilePath);
}

Write-Output -InputObject (Get-Item -Path $FilePath);
}

$TargetFile = Convert-StringToBinary -InputString $PowerShellExecutable -FilePath C:\temp\powershell.exe;
[/cc]

This function takes the Base64 string and a target FilePath as inputs, converts the Base64 string to a Byte array, writes the Byte array to a file on the hard drive, and then returns the file that was created. The last line shows an example of how to call the function.

Once the file has been created, we can call it:

[cc lang=”powershell”]
Start-Process -FilePath $TargetFile.FullName;
[/cc]

And there you have it! We have discussed how to embed binary content inside of a PowerShell script as a Base64 string, and how to convert it back into a file. If you want to validate / compare the hashes of the original source file, and the target file, you can use a handy utility like HashTab.