# encoding: ascii
# api: powershell
# title: Base64 file payloads
# description: Create/Insert/Extract Well formatted and indexed based64 payload of files
# version: 0.1
# type: function
# author: Nicolas Tremblay
# license: CC0
# function: Encode-FileToBase64Block
# x-poshcode-id: 4294
# x-derived-from-id: 4295
# x-archived: 2016-08-21T16:04:30
# x-published: 2013-07-07T15:44:00
#
# in your Power Shell scripts, multiple payload per file supported…
# Coming from the Unix world, I was looking for the UUencoded payload strategy 
# in Power shell, but based on some Google queries, it did not seem to be readily 
# available or well documented yet.
# It will even extract files inserted in the same script that is currently executing.
# To do so just use $($MyInvocation.InvocationName) as the source path.
# This is a working concept, but I admin not extremely tested yet.
# I did not put formal Power Shell help yet.
#

function Encode-FileToBase64Block {
    param ( [ValidateScript({Test-Path $_ -PathType 'Leaf'})][string]$SourcePath ) 
    $leafName = (Get-Item $SourcePath).Name 
    Write-Output "`n`n`n# BEGIN base64 encoding of file ${leafName}`n<# BASE64ENCODE FILE: |${leafName}|"
    [system.convert]::ToBase64String((Get-Content $SourcePath -Encoding Byte), "InsertLineBreaks") 
    Write-Output "#> #END BASE64ENCODE FILE: |${leafName}|`n"
}


function Get-Base64BlockSection { 
    param ( 
            [ValidateScript({Test-Path $_ -PathType 'Leaf'})][string]$SourcePath,
            [switch]$List, 
            [string]$Indexfile
          )
    $encodedStartLines = $(get-content $SourcePath | select-string -Pattern "^<# BASE64ENCODE FILE: ")
    $outputFilenames   = $encodedStartLines | % { $_.Line.Split("|")[1]}
    if ($outputFilenames.Count -gt 0)
    {
        $metadataBlock = @{}
        foreach ($item in $outputFilenames)
        {
            $metadataBlock[$item] = @{}
            $metadataBlock[$item]["StartLine"] = $(get-content $SourcePath | select-string -Pattern "^<# BASE64ENCODE FILE: \|${item}\|").LineNumber
            $metadataBlock[$item]["EndLine"]   = $(get-content $SourcePath | select-string -Pattern "^#> #END BASE64ENCODE FILE: \|${item}\|").LineNumber
        }
        if ($List.IsPresent) { return $metadataBlock.Keys | sort }
        if (!$Indexfile) 
        { 
            Write-Host "ERROR: No index specified for the '-Indexfile' parameter" -ForegroundColor Red
            Write-Host "Available indexe(s) found are: $($outputFilenames -join " , ")`n`n"
            throw "No index specified"
        }
        else
        {
            $extractor = $metadataBlock.Keys | where { $_ -eq $Indexfile}
            if ($extractor)
            {
                (Get-Content $SourcePath)[$($metadataBlock[$extractor].StartLine) .. $($metadataBlock[$extractor].EndLine - 2 )]
            }
            else
            {
                Write-Host "ERROR: No Base64 Block index '$Indexfile' was found in file: $SourcePath`n`n" -ForegroundColor Red
                throw "Invalid index"
            }
        }
    }
    else
    {
        if ($List.IsPresent) {return $null}
        Write-Host "ERROR: No Base64 Block indexes detected in file: ${SourcePath}`n`n" -ForegroundColor Red
        throw "No block index detected"
    }
}

function Invoke-base64Extraction {
    param ( 
            [ValidateScript({Test-Path $_ -PathType 'Leaf'})][string]$SourcePath,
            [switch]$All, 
            [string]$Indexfile
          )
    if ($All.IsPresent) 
    {
        $fileList =  Get-Base64BlockSection -SourcePath $SourcePath -List
        $fileList | % { Invoke-base64Extraction -SourcePath $SourcePath -Indexfile $_ }
        return
    }
    Split-Path -Parent $MyInvocation.InvocationName
    $outputContentPath = Join-Path $defaultpath $Indexfile
    [system.convert]::FromBase64String((Get-Base64BlockSection -SourcePath $SourcePath -Indexfile $Indexfile)) | Set-Content $outputContentPath -Encoding Byte
}

## Create a payload:
#Encode-FileToBase64Block -SourcePath <<insert file with payloads here>>

## List all indexes in a payload
#Get-Base64BlockSection -SourcePath <<insert file with payloads here>> -List

## Example, to extract all base64 payload to files 
#Invoke-base64Extraction -SourcePath <<insert file with payloads here>> -All

## Example, Extract just one file from a multi-payload file
#Invoke-base64Extraction -SourcePath <<insert file with payloads here>> -Indexfile <<Index: name of the file>>

#bug: Invoke-base64Extraction need a destination path option probably :-)
