Incrustar VBScript en PowerShell Script: un archivo
Recientemente aprendí mucho jugando con secuencias de comandos híbridas de VBScript + batch y, si bien fue un gran aprendizaje y funciona, es hora de aprender PowerShell más a fondo. Pero, mi parte favorita de la solución Batch / VBScript es que puedo crear un solo script.cmd
archivo para distribuir.
¿Existe algún tipo de solución con PowerShell / VBScript? Idealmente, creo que preferiría un .ps1
script con VBScript incrustado, pero interesado en conocer mis opciones.
Parece haber cierta confusión con respecto al objetivo.
- Un solo archivo (esta es la parte más importante)
- Extensión ya sea
.ps1
o.vbs
- Ambos
POWERSHELL
yVBScript
dentro de un solo archivo - Prima:
- No escribir en un archivo externo
- Prefacio de cada línea
- Tener que escapar de caracteres especiales en el código
- Codificación de secciones enteras del script (operaciones intensivas de CPU de sobrecarga)
Aquí hay un ejemplo teórico:
script.{ps1/vbs}
:
<!-- : Begin PS1 script
$strString = "Hello PowerShell" write-host $strString
cscript //nologo "%~f0?.wsf" //job:HELLOWORLD
exit /b
PAUSE
----- Begin wsf script --->
<package>
<job id="HELLOWORLD">
<script language="VBScript">
MsgBox "Hello World VBS"
</script>
</job>
<job id="VBS">
<script language="VBScript">
'Second Script!
</script>
</job>
</package>
Algo como esto ->
https://stackoverflow.com/a/9074483/5079799
<!-- : Begin batch script
@ECHO OFF
CLS
cscript //nologo "%~f0?.wsf" //job:HELLOWORLD
exit /b
PAUSE
----- Begin wsf script --->
<package>
<job id="HELLOWORLD">
<script language="VBScript">
MsgBox "Hello World"
</script>
</job>
<job id="VBS">
<script language="VBScript">
'Second Script!
</script>
</job>
</package>
Respuestas
Cree el script VBS como de costumbre. Guárdelo en alguna ubicación y luego conviértalo en Base64. La codificación de bytes se utiliza para que esto funcione también en archivos binarios y supere los problemas de codificación de caracteres. Al igual que,
$Content = Get-Content -Path C:\temp\myScript.vbs -Encoding Byte $Base64 = [System.Convert]::ToBase64String($Content) $Base64 | Out-File c:\temp\myScript.b64
Luego, en su secuencia de comandos de Powershell, incluya la versión codificada de la secuencia de comandos de VBS. Convierta Base64 nuevamente en una cadena y escríbalo en un archivo. Finalmente, llame cscript
para ejecutar el .vbs
.
$Base64 = "ZgB1AG4AYwB0AGkAbwBuACAAR..." $Content = [System.Convert]::FromBase64String($Base64) Set-Content -Path $env:temp\myScript.vbs -Value $Content -Encoding Byte & cscript /nologo $env:temp\myScript.vbs
Otra opción es incrustar el VBScript en una cadena aquí como esta,
# Paste the VBS in a here string
$Content = @' dim foo ... '@ Set-Content -Path $env:temp\myScript.vbs -Value $Content & cscript /nologo $env:temp\myScript.vbs
Quizás, ¿te refieres a crear un .ps1
archivo de script y ejecutarlo desde vbscript?
Si es así, aquí hay un ejemplo llamado Compress_Archive_by_Extension.vbs
Observación: Compress-Archive
solo está disponible con PS v4
Option Explicit
Dim Title,ArrExt,Ext
Title = "Compress Archive With Powreshell And Vbscript by Hackoo 2020"
REM We define an array of extensions for archiving !
ArrExt = Array("vbs","vbe","cmd","bat","ps1","js","jse","lnk")
REM Looping thru extensions defined from our array in order to zip and archive them,
REM so you can add or remove what you want as extension in the array above !
For each Ext in ArrExt
Call Compress_Archive("%Temp%\*."& Ext,"Temp_Archive_"& Ext)
Call Compress_Archive("%AppData%\*."& Ext,"AppData_Archive_"& Ext)
Call Compress_Archive("%LocalAppData%\*."& Ext,"LocalAppData_Archive_"& Ext)
Call Compress_Archive("%ProgramData%\Microsoft\Windows\Start Menu\Programs\Startup\*."& Ext,"ProgramData_Archive_"& Ext)
Call Compress_Archive("%UserProfile%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\*."& Ext,"UserProfile_Archive_"& Ext)
Next
MsgBox "Archive Script is completed !",vbInformation,Title
'---------------------------------------------------------------------
REM https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.archive/compress-archive?view=powershell-5.1&redirectedfrom=MSDN
Sub Compress_Archive(Source,Destination)
Const ForWriting = 2
Dim fs,Ws,ts,Ret,PSFile,ByPassPSFile
Set fs = CreateObject("Scripting.FileSystemObject")
Set Ws = WScript.CreateObject("WScript.Shell")
Source = Ws.ExpandEnvironmentStrings(Source)
Destination = Ws.ExpandEnvironmentStrings(Destination)
PSFile = Ws.ExpandEnvironmentStrings("%Temp%") & fs.GetTempName & ".ps1"
ByPassPSFile = "PowerShell -ExecutionPolicy bypass -noprofile -file "
Set ts = fs.OpenTextFile(PSFile,ForWriting,True)
ts.WriteLine "Compress-Archive -Path " & DblQuote(Source) &_
" -Update -CompressionLevel Optimal -DestinationPath "& DblQuote(Destination)
ts.Close
Ret = Ws.run(ByPassPSFile & PSFile,0,True)
If fs.FileExists(PSFile) Then fs.DeleteFile(PSFile)
End Sub
'---------------------------------------------------------------------
Function DblQuote(Str)
DblQuote = Chr(34) & Str & Chr(34)
End Function
'---------------------------------------------------------------------
Segundo ejemplo: para descargar una imagen del sitio:Download_File.vbs
Option Explicit
Dim URL,Ws,ByPassPSFile,PSFile,MyCmd,Result
URL = "https://cdn2.unrealengine.com/Fortnite%2FBoogieDown_GIF-1f2be97208316867da7d3cf5217c2486da3c2fe6.gif"
Set Ws = CreateObject("wscript.Shell")
PSFile = Left(Wscript.ScriptFullName, InstrRev(Wscript.ScriptFullName, ".")) & "ps1"
ByPassPSFile = "cmd /C PowerShell.exe -ExecutionPolicy bypass -noprofile -file "
MyCmd = "$source = " & DblQuote(URL) & VbCrlF MyCmd = MyCmd & "$Filename = [System.IO.Path]::GetFileName($source)" & VbCrlF MyCmd = MyCmd & "$dest = " & DblQuote("$env:temp\$Filename") & VbCrlF
MyCmd = MyCmd & "$wc = New-Object System.Net.WebClient" & VbCrlF MyCmd = MyCmd & "$wc.DownloadFile($source,$dest)" & VbCrlF
MyCmd = MyCmd & "Start-Process $dest"
Call WriteMyPSFile(MyCmd)
Result = Ws.run(ByPassPSFile & PSFile,0,True)
'----------------------------------------------------------------------------------------
Sub WriteMyPSFile(strText)
Dim fs,ts,PSFile
Const ForWriting = 2
PSFile = Left(Wscript.ScriptFullName, InstrRev(Wscript.ScriptFullName, ".")) & "ps1"
Set fs = CreateObject("Scripting.FileSystemObject")
Set ts = fs.OpenTextFile(PSFile,ForWriting,True)
ts.WriteLine strText
ts.Close
End Sub
'----------------------------------------------------------------------------------------
Function DblQuote(Str)
DblQuote = Chr(34) & Str & Chr(34)
End Function
'----------------------------------------------------------------------------------------
EDITAR: 21/08/2020 @ 20:45
Aquí hay un "pseudo-híbrido" porque usa un archivo temporal para ser ejecutado: inspirado en la respuesta de @vonPryz.
Puede guardarlo como Test.ps1
y ejecutarlo desde PowerShell ISE
$VBS_Content = @'
Dim http, WAN_IP
Set http = CreateObject( "MSXML2.ServerXmlHttp" )
http.Open "GET", "http://icanhazip.com", False
http.Send
WAN_IP = http.responseText
wscript.echo "WAN_IP : " & WAN_IP
'@
Set-Content -Path $env:temp\myScript.vbs -Value $VBS_Content
& wscript.exe $env:temp\myScript.vbs $url = "https://externals.lesechos.fr/medias/2019/04/26/2262811_pourquoi-salto-le-futur-netflix-francais-devra-seuropeaniser-195514-1.jpg"
#https://stackoverflow.com/questions/35813186/extract-the-filename-from-a-path
$output = $env:temp + "\" + $url.Split("/")[-1] $start_time = Get-Date
Try {$wb = (New-Object System.Net.WebClient).DownloadFile($url,$output)} Catch { Write-Host "Error from $url ! " -ForegroundColor Red -BackgroundColor Yellow
Write-Host "Message: [$($_.Exception.Message)"] -ForegroundColor Red -BackgroundColor Yellow
}
Write-Output "Running Script Time taken is : $((Get-Date).Subtract($start_time).Seconds) second(s)"
Start-process $output
Otro ejemplo sencillo:
$VBS_Content = @'
MsgBox "This a simple MsgBox from Vbscript"
'@
$TmpVBS="$env:temp\myScript.vbs"
SC $TmpVBS $VBS_Content
wscript.exe $TmpVBS
Echo 'Hello World from Powershell !'
Aquí está mi respuesta final, no he probado con nada súper complicado, así que no estoy seguro de cómo manejaría cosas como caracteres especiales ...
#https://stackoverflow.com/questions/63514534/embed-vbscript-in-powershell-script-one-file
#######################Begin VBS1#######################
###JOB_A START###
$VBS_Content_Job_A = @' MsgBox "This a simple MsgBox from Vbscript (Job_A)" '@ ###JOB_A END### ###JOB_B START### $VBS_Content_Job_B = @'
MsgBox "This a simple MsgBox from Vbscript (Job_B)"
'@
###JOB_B END###
#######################Begin PS1#######################
ECHO 'Hello World from Powershell !'
PAUSE
ECHO "Running VBS Now"
PAUSE
###VBS CALL START###
$VBSJob=$VBS_Content_Job_A
$TmpVBS="$env:temp\myScript.vbs"
Remove-Item $TmpVBS -ErrorAction SilentlyContinue SC $TmpVBS $VBSJob cscript //nologo $TmpVBS
Remove-Item $TmpVBS -ErrorAction SilentlyContinue ###VBS CALL END### ECHO "Some More PowerShell" PAUSE ECHO "I need anoter VBS Script" PAUSE ###VBS CALL START### $VBSJob=$VBS_Content_Job_B $TmpVBS="$env:temp\myScript.vbs" Remove-Item $TmpVBS -ErrorAction SilentlyContinue
Set-Content -Path $TmpVBS -Value $VBSJob
cscript //nologo $TmpVBS Remove-Item $TmpVBS -ErrorAction SilentlyContinue
###VBS CALL END###
ECHO "All Done!"
PAUSE
Puede incrustar código VB.NET en el código de PowerShell con TypeDefinition:
$code = @" Imports System Namespace MyNameSpace Public Class Responder Public Shared Sub StaticRespond() Console.WriteLine("Static Response") End Sub Public Sub Respond() Console.WriteLine("Instance Respond") End Sub End Class End Namespace "@ # Check the type has not been previously added within the session, otherwise an exception is raised if (-not ([System.Management.Automation.PSTypeName]'MyNameSpace.Responder').Type) { Add-Type -TypeDefinition $code -Language VisualBasic;
}
[MyNameSpace.Responder]::StaticRespond();
$instance = New-Object MyNameSpace.Responder; $instance.Respond();
No es exactamente vbscript, pero es una buena solución.