Jump to content

PHP - Uncompress and Base64 Decode Download On-the-Fly


wraithdu
 Share

Recommended Posts

Why you might ask? Well it seems to be a popular practice for web hosts to scan their servers with multi-brand AV products, similar to VirusTotal and Jotti. Inevitably a good number of these crappy AV engines detect every AutoIt script as a false positive. This has resulted in my domain being suspended TWICE until I could work it out with support (kudos to Dreamhost on at least having decent support). Regardless it's a PITA and takes time. Not to mention I'm sure they keep track of this kind of thing, and eventually no amount of email persuasion is going to get me back online.

My first solutions was to pack all my downloads into password protected encrypted zip files. This kept the AV clowns away, but it's annoying for users and just plain looks bad. So I thought about it a bit more lately and came up with a solution. I have to give some credit to UEZ and his File to Base64 String Code Generator for the idea. I decided to store the files base64 encoded and gzip compressed, then use PHP to uncompress and decode them on the fly. This way the AV scanners just see some some gzip compressed text files, and users get the real deal on their end.

First I wrote a short script using Ward's base64 and Zlib machine code UDFs to create the files. It adds a dummy header to the file to confuse anything that might try to base64 decode it, and encodes the actual file size into the file name for the PHP script. Additionally Ward's code breaks the encoded string into 76 character lines for standards compliance, but it also makes it look more like a regular text file.

#NoTrayIcon
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
#AutoIt3Wrapper_res_requestedExecutionLevel=asInvoker
#AutoIt3Wrapper_Run_Obfuscator=y
#Obfuscator_Parameters=/so
#AutoIt3Wrapper_Run_After=del "%scriptdir%%scriptfile%_Obfuscated.au3"
#AutoIt3Wrapper_Change2CUI=y
#include <_Base64.au3>
#include <_ZLIB.au3>

_Main()

Func _Main()
    If $CmdLine[0] <> 1 Then Exit
    Local $SrcFile = $CmdLine[1]
    If $SrcFile = "" Or (Not FileExists($SrcFile)) Then Exit

    Local $BufferSize = 0x80000
    Local $SrcFileSize = FileGetSize($SrcFile)
    Local $SrcFileHandle = FileOpen($SrcFile, 16)
    Local $DestFile = $SrcFile & ".b64"
    Local $DestFileHandle = FileOpen($DestFile, 2 + 16)

    ConsoleWrite("Encoding..." & @CRLF & @CRLF & $SrcFile & @CRLF & "--> " & $DestFile & @CRLF & @CRLF)

    Local $timer = TimerInit()

    ; write header
    FileWrite($DestFileHandle, "thisismyboomstick" & @LF)

    Local $State = _Base64EncodeInit()
    For $i = 1 To Ceiling($SrcFileSize / $BufferSize)
        ConsoleWrite(".")
        FileWrite($DestFileHandle, _Base64EncodeData($State, FileRead($SrcFileHandle, $BufferSize)))
    Next
    ConsoleWrite(" Finishing..." & @CRLF & @CRLF)
    FileWrite($DestFileHandle, _Base64EncodeEnd($State))

    FileClose($SrcFileHandle)
    FileClose($DestFileHandle)

    $SrcFile = $DestFile
    $DestFile = $CmdLine[1] & "." & $SrcFileSize & ".b64gz"

    ConsoleWrite("Compressing..." & @CRLF & @CRLF & $SrcFile & @CRLF & "--> " & $DestFile & @CRLF & @CRLF)

    _ZLIB_GZFileCompress($SrcFile, $DestFile)

    ConsoleWrite("Done... " & Round(TimerDiff($timer) / 1000, 4) & " sec" & @CRLF & @CRLF)
EndFunc

I already had a PHP click counter / download script in place, so I modified the end of it to server these special files. The $url variable holds the relative or full URL of the target file. The PHP code must strip the dummy header and line endings from the uncompressed data before base64 decoding it.

<?php

if (file_exists($url)) {
    if (substr_compare($url, ".b64gz", -6, 6, true)) {
        // normal file
        /* Redirect to the link URL */
        Header('Location: '.$url);
    } else {
        // b64 encoded, gz compressed file
        $base = basename($url, ".b64gz");
        // get file size for Content-Length from name
        $size = end(explode(".", $base));
        // remove the size
        $fname = basename($base, ".".$size);

        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename='.$fname);
        header('Content-Length: '.$size);
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');

        ob_clean();
        flush();

        // i'm gonna put this right here... the gz file commands that make working with gzipped
        // data transparent to the user are freakin awesome

        // each line of 76 chars has a trailing LF that must be stripped
        // meaning we need a chunksize that is a multiple of 77
        // and the resulting stripped string must be Mod 4 and Mod 3 to decode
        // tl;dr, 4K ~ 4158, 8K ~ 8085
        $chunk = 8085;
        $head = strlen("somethingclevereatmen");

        $f = gzopen($url, 'rb');
        if (!$f) die();

        // strip leading header first
        gzread($f, $head);

        while (!feof($f)) {
            echo base64_decode(str_replace("n", "", gzread($f, $chunk)));
            ob_flush();
            flush();
        }

        gzclose($f);
    }
}

exit();
?>

Voila. Cheapo AV scanners are thusly defeated and end users are none the wiser. Bonus - the data files are kept in a secured directory where only PHP can access them, preventing direct downloads.

Edited by wraithdu
Link to comment
Share on other sites

Use the basic editor by selecting the 'toggle editing mode' button (top left hand corner.)

Nice article.

UDF List:

 
_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_ArrayFilter/_ArrayReduce_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 22/04/2018

Link to comment
Share on other sites

Use the basic editor by selecting the 'toggle editing mode' button (top left hand corner.)

Yup that works for creating the post, but the formatting gets blown away as soon as you hit the edit link to change anything. Doesn't matter if you turn off formatting or switch to the full editor :-( Edited by wraithdu
Link to comment
Share on other sites

I use Opera and always use the basic editor, even when editing a post.

UDF List:

 
_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_ArrayFilter/_ArrayReduce_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 22/04/2018

Link to comment
Share on other sites

I use Opera and always use the basic editor, even when editing a post.

Totally unrelated to my OP, but I solved the editor problem, it was a cookie issue. Signed out, removed AutoIt cookies, and now it remembers my 'Toggle editing mode' preference. Hooray.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...