Encode / Decode .EXE into Base64
Asked Answered
C

3

48

I have a .NET exe file that I'd like to encode into a Base-64 string, and then at a later point decode into a .exe file from the Base64 string, using Powershell.

What I have so far produces a .exe file, however, the file isn't recognizable to windows as an application that can run, and is always a different length than the file that I'm passing into the encoding script.

I think I may be using the wrong encoding here, but I'm not sure.

Encode script:

Function Get-FileName($initialDirectory)
{   
 [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $initialDirectory
$OpenFileDialog.filter = "All files (*.*)| *.*"
$OpenFileDialog.ShowDialog() | Out-Null
$FileName = $OpenFileDialog.filename
$FileName

} #end function Get-FileName

$FileName = Get-FileName

$Data = get-content $FileName
$Bytes = [System.Text.Encoding]::Unicode.GetBytes($Data)
$EncodedData = [Convert]::ToBase64String($Bytes)

Decode Script:

$Data = get-content $FileName
$Bytes = [System.Text.Encoding]::UTF8.GetBytes($Data)
$EncodedData = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($Bytes))

$EncodedData | Out-File ( $FileName )
Cass answered 4/3, 2017 at 5:33 Comment(0)
S
81

The problem was caused by:

  1. Get-Content without -raw splits the file into an array of lines thus destroying the code
  2. Text.Encoding interprets the binary code as text thus destroying the code
  3. Out-File is for text data, not binary code

The correct approach is to use IO.File ReadAllBytes:

$base64string = [Convert]::ToBase64String([IO.File]::ReadAllBytes($FileName))

and WriteAllBytes to decode:

[IO.File]::WriteAllBytes($FileName, [Convert]::FromBase64String($base64string))
Szabadka answered 4/3, 2017 at 6:38 Comment(8)
Thanks, this makes sense. I'll try this today and will report back results.Cass
Just to clarify, may I still use out-File on the $base64String you referenced above, after it's been converted?Cass
@schizoid04, yeah, $base64String is plain ASCII text so you can use the standard text-based commands.Szabadka
Quick note if you're trying to use this as a handy one-liner. I was trying to use the relative filename (I was already cd'd to my desktop) but the subcommand ReadAllBytes(.\file.png) was expecting "file.png" to be in %userprofile%.Carilyn
I know I'm biased, using C#, but shouldn't this NOT WORK? I thought loading, converting, then saving it as exe again would break it...? Or is this some other method?Vaccine
@Vaccine an exe is just a file, not some magical thing, so naturally the answer works as Convert API is lossless when used with ReadAllBytes + WriteAllBytes that read/write the data as is without altering it.Szabadka
Ohhhh... :) I thought he meant he would, say, put "TvqAAAA==" into the code, then save it :D now I understand what he wants :)Vaccine
any reason not to use Get-Content -Raw -AsByteStream ?Microstructure
B
60

Just to add an alternative for people looking to do a similar task: Windows comes with certutil.exe (a tool to manipulate certificates) which can base64 encode and decode files.

certutil -encode test.exe test.txt
certutil -decode test.txt test.exe
Brochette answered 4/3, 2017 at 14:9 Comment(5)
So, I assume, then, that for encode, it's [File to be encoded] and then [file for resulting base64] ? (As far as syntax goes on that command?)Cass
Do you know which versions this comes with? (All?) Are there any recent versions (7 and higher) for workstations (non-server OS) that might not contain this tool? The solution will be executed by an ITSM tool run on an end-users workstation.Cass
I think all currently used Windows versions have this. But do your own research if you want to be sure.Brochette
I believe certutil.exe dates all the way back to Windows 2000, though it has gone through some changes since then.Fief
Glorious thank you - is there a way to decode this output on a mac or linux box ? My OpenSSL commands fail on keys.Gershom
P
12

This is a purely PowerShell version of Swonkie's answer which, despite working quite well if you have access to the utility, isn't a PowerShell answer - which is what I needed.

$SourceFile    = "C:\Src\OriginalBinaryFile.dll"
$B64File       = "C:\Src\DllAsB64.txt"
$Reconstituted = "C:\Src\ReConstituted.dll"

[IO.File]::WriteAllBytes($B64File,[char[]][Convert]::ToBase64String([IO.File]::ReadAllBytes($SourceFile)))

[IO.File]::WriteAllBytes($Reconstituted, [Convert]::FromBase64String([char[]][IO.File]::ReadAllBytes($B64File)))

As a side note. If the DllAsB64.txt is created by certutil, it will be wrapped by these lines.

-----BEGIN CERTIFICATE-----

-----END CERTIFICATE-----

After removing these lines the PowerShell command above will decode it. Certutil ignores them so it will decode its own output or the PowerShell output.

Panga answered 13/3, 2020 at 13:55 Comment(2)
How is this any different from the accepted answer (by wOxxOm)?Coverdale
People will be drawn by the question title rather than OP's problem. wOxxOm's answer helps the OP but doesn't present a complete working solution that one, coming here due to the title, could simply grab and run with. One could argue that the CertUtil answer doesn't even belong here but it's a good answer to the title question despite ignoring the underlying problem the OP had. I wanted a PowerShell based answer that was as clear and succinct as the certutil answer for those uninterested in the details of the problem in the OP's code.Panga

© 2022 - 2024 — McMap. All rights reserved.