Extract an EXE from the binary table and run it from a VBScript Custom Action

Somebody over at ITNinja recently asked how they could run an executable stored in the binary table, from a VBScript Custom Action. Here’s an example I knocked up which describes how to extract an EXE from the binary table and run it from a VBScript Custom Action. I basically streamed notepad.exe into the binary table, and gave it a name of ‘notepad’. In this Custom Action, I extract it to the %temp% folder and run it from there. Note that there is no cleanup of the extraction afterwards. Also note that because this uses the Session object, it can only be executed in the Immediate context. So more consideration would be required (and probably a separate Custom Action) to execute the extracted EXE in a deferred context.

Dim oFSO : Set oFSO = CreateObject("Scripting.FileSystemObject")
Dim tempFolder : tempFolder = oFSO.GetSpecialFolder(2) 
Dim outputFile : outputFile = tempFolder & "\notepad.exe"
extractFromBinary "notepad", outputFile
If oFSO.fileExists(outputFile) Then 
Dim objShell : Set objShell = CreateObject("WScript.Shell") 
objShell.Run (outputFile) 
Set objShell = Nothing
End If
Function extractFromBinary(ByVal binaryName, ByVal binaryOutputFile)
Const msiReadStreamInteger = 0 
Const msiReadStreamBytes = 1 
Const msiReadStreamAnsi = 2  
Const msiReadStreamDirect = 3
Dim binaryView : Set binaryView = Session.Database.OpenView("SELECT * FROM Binary WHERE Name = '" & binaryName & "'")  
binaryView.Execute
Dim binaryRecord : Set binaryRecord = binaryView.Fetch  
Dim binaryData : binaryData = binaryRecord.ReadStream(2, binaryRecord.DataSize(2), msiReadStreamAnsi)  
Set binaryRecord = Nothing  
Dim binaryStream : Set binaryStream = oFSO.CreateTextFile(binaryOutputFile, True)  
binaryStream.Write binaryData  
binaryStream.Close 
Set binaryStream = Nothing
End Function
Set oFSO = Nothing

Below is an updated version that extracts from the binary table using msiReadStreamDirect (as a binary string), converts the string to binary and outputs it using ADODB.Stream . It may resolve locale-specific issues with the aforementioned approach.

Dim oFSO : Set oFSO = CreateObject("Scripting.FileSystemObject")
Dim tempFolder : tempFolder = oFSO.GetSpecialFolder(2) 
Dim outputFile : outputFile = tempFolder & "\notepad.exe"
extractFromBinary "notepad", outputFile
If oFSO.fileExists(outputFile) Then 
Dim objShell : Set objShell = CreateObject("WScript.Shell") 
objShell.Run (outputFile) 
Set objShell = Nothing
End If
Function MultiByteToBinary(MultiByte)
'obtained from http://www.motobit.com
'MultiByteToBinary converts multibyte string To real binary data (VT_UI1 | VT_ARRAY)
'Using recordset
Dim RS, LMultiByte, Binary
Const adLongVarBinary = 205
Set RS = CreateObject("ADODB.Recordset")
LMultiByte = LenB(MultiByte)
If LMultiByte>0 Then
RS.Fields.Append "mBinary", adLongVarBinary, LMultiByte
RS.Open
RS.AddNew
RS("mBinary").AppendChunk MultiByte & ChrB(0)
RS.Update
Binary = RS("mBinary").GetChunk(LMultiByte)
End If
Set RS = Nothing
MultiByteToBinary = Binary
End Function
Function SaveBinaryData(FileName, ByteArray)
Const adTypeBinary = 1
Const adSaveCreateOverWrite = 2
'Create Stream object
Dim BinaryStream
Set BinaryStream = CreateObject("ADODB.Stream")
'Specify stream type - we want To save binary data.
BinaryStream.Type = adTypeBinary
'Open the stream And write binary data To the object
BinaryStream.Open
BinaryStream.Write ByteArray
'Save binary data To disk
BinaryStream.SaveToFile FileName, adSaveCreateOverWrite
Set BinaryStream = Nothing
End Function
Function extractFromBinary(ByVal binaryName, ByVal binaryOutputFile)
Const msiReadStreamInteger = 0 
Const msiReadStreamBytes = 1 
Const msiReadStreamAnsi = 2  
Const msiReadStreamDirect = 3
Dim binaryView : Set binaryView = Session.Database.OpenView("SELECT * FROM Binary WHERE Name = '" & binaryName & "'")  
binaryView.Execute
Dim binaryRecord : Set binaryRecord = binaryView.Fetch  
Dim binaryData : binaryData = binaryRecord.ReadStream(2, binaryRecord.DataSize(2), msiReadStreamDirect)  
Set binaryRecord = Nothing  
'convert to string of byte pairs to binary
binaryData = MultiByteToBinary(binaryData)
'save binary data
SaveBinaryData binaryOutputFile, binaryData
End Function
Set oFSO = Nothing

Finally, i verified the file imported into the Binary table against the file extracted from the Binary table using this simple command line:
fc.exe /b [full_path_file_1] [full_path_file_2]