File not found when ExecuteGlobal after a ReadAll in VBScript - vbscript

this might be trivial but I'm writing a VBScript template and I wanted to add the possibility to inject VBScript code from the internet inside VBScript and run it.
The code originally was:
Sub WebImport(URL)
' Import the VBS code at a given URL.
' and run it globally pushing the functions into the Main
Dim Request
Set Request = createobject ("MSXML2.ServerXMLHTTP")
Request.Open "GET", URL, false
Request.Send
ExecuteGlobal Request.ResponseText
Set Request = Nothing
End Sub
I already wrote a function to import code, just like Python, and the principle is the same. It works flawlessly.
If the WebImport works as it should, I'll create a basic import that handles them in the same way.
I import the raw code from my own repository here.
It does everything correctly except executing the file globally in the Main scope, I tried with/without deleting the file, the result doesn't change at all, unfortunately...
Edit
This is the full script, it imports WebImport from scr/Functions and runs the code to inject the code from a given repo until it has to write a file, then 800a0046 vbscript at row 0, line 1 kicks in... I'm admin on the machine.
Option Explicit
' VBScript Main File Model
' Author:
' Fabio Craig Wimmer Florey (fabioflorey#hackermail.com)
'
' Reviewed By: Last Review:
' Fabio Craig Wimmer Florey (fabioflorey#hackermail.com) 2022-03-29
'
' Description:
' Main Subroutine
Import "src/Functions"
WebImport "https://raw.githubusercontent.com/nmoosaJHB/Docker-compose-SuiteCRM/714bada98abbb2ab497258c8ea9a726f234aaba3/public_html/vendor/gymadarasz/ace/demo/kitchen-sink/docs/vbscript.vbs"
Sub Main()
' Main Subroutine
MakeHelloWorldFile "Hello.txt"
End Sub
Sub Import(Filename)
' Import Code from VBS File, DO NOT DELETE
Dim Lib, Code, FSO
Set FSO = CreateObject("Scripting.FileSystemObject")
Set Lib = FSO.OpenTextFile(Filename & ".vbs")
Code = Lib.ReadAll
Lib.Close
ExecuteGlobal Code
Set Lib = Nothing
Set FSO = Nothing
End Sub
Call Main

The problem here is not the XHR call that works without needing to try outputting the response to a file. The issue is the FileSystemObject does not support relative paths you need to provide the relative path using code like;
Dim path: path = FSO.GetParentFolderName(WScript.ScriptFullName) & "\"
With the following changes to the Import() procedure the code will run WebImports() from the src\Functions.vbs file.
'Use the correct directory separator
Import "src\Functions"
'... other code excluded for clarity
Sub Import(Filename)
' Import Code from VBS File, DO NOT DELETE
Dim Lib, Code, FSO
Set FSO = CreateObject("Scripting.FileSystemObject")
'Get path based off the script
Dim path: path = FSO.GetParentFolderName(WScript.ScriptFullName) & "\"
'Append path to the filename
Set Lib = FSO.OpenTextFile(path & Filename & ".vbs")
Code = Lib.ReadAll
Lib.Close
ExecuteGlobal Code
Set Lib = Nothing
Set FSO = Nothing
End Sub

Related

Retrieving an argument of a VBScript

How do I pass and return arguments from a VBScript WITHOUT using cscript.exe?
For example, I want to call script2 from script1 that returns a value to script1 without any involvement of cscript.exe.
I have searched various answers but they somehow involve the usage of cscript.exe.
This script gets installed voices and sets the one provided in the file voice.txt.
Set WshShell = CreateObject("WScript.Shell")
WShShell.CurrentDirectory = "..\Confirmatory Texts"
Set FSO = CreateObject("Scripting.FileSystemObject")
If FSO.FileExists("voice.txt") Then
Set temp = FSO.OpenTextFile("voice.txt")
confirm_voice = temp.ReadLine()
temp.Close
Set Sapi = CreateObject("SAPI.SpVoice")
For Each Voice In Sapi.GetVoices
i = i + 1
Next
For loopvar = 0 To i-1
If loopvar = CInt(confirm_voice) Then
Set Sapi.Voice = Sapi.GetVoices.Item(loopvar)
End If
Next
Else
WScript.Echo "An Error Occured"
End If
If I call this script from another script, how can I make this script to return some value to the script that invoked it?
VBScript doesn't really provide call or import mechanisms for other VBScript files. The closest thing is to read the contents of the other file and run them via ExecuteGlobal.
Demonstration:
Put the following two files in the same directory and run script1.vbs. It will read the contents of script2.vbs and make the function Square available in the global scope by running the code via ExecuteGlobal. Once the function is available in the global scope you it can be used in the rest of the script.
script1.vbs:
Set fso = CreateObject("Scripting.FileSystemObject")
dir = fso.GetParentFolderName(WScript.ScriptFullName)
script = fso.BuildPath(dir, "script2.vbs")
ExecuteGlobal fso.OpenTextFile(script).ReadAll '"import" code into global scope
WScript.Echo Square(3)
script2.vbs:
Function Square(i)
Square = i*i
End Function

Set package code of MSI using vbscript

I am changing product code, upgrade code and product name of MSI by editing MSI database.
With reference :- http://www.codeproject.com/Articles/383481/Editing-an-MSI-Database
I am able to change all parameters above but unable to change Package Code.
Suggest a way to change package code.
Found a way to do it with vbscript, just out of curiosity:
The "property #9" is the package code (revision number).
Set wi = CreateObject("WindowsInstaller.Installer")
Set summary = wi.SummaryInformation("your.msi", 2)
summary.Property(9) = "{PUT-NEW-GUID-HERE}"
summary.Persist
I'm guessing that the requirement here is to install the same MSI multiple times, which means they need to change that set of guids. However the more normal way to solve that problem is with MSINEWINSTANCE.
https://msdn.microsoft.com/en-us/library/aa370326(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/aa369528(v=vs.85).aspx
so that you are not changing the base MSI file every time.
Why do you even have the need to set the package code?
Its auto generated during each build.
Take a look at the documentation of the Package element:
http://wixtoolset.org/documentation/manual/v3/xsd/wix/package.html
"The package code GUID for a product or merge module. When compiling a product, this attribute should not be set in order to allow the package code to be generated for each build. When compiling a merge module, this attribute must be set to the modularization guid."
I needed it because MSI created cache in respective package code which restricts us to make another instance of application using MSI so I did this by
using (var database = new Database(#"D:\\Nirvana\\WorkingCopy\\trunk\\proj1\\installer.msi", DatabaseOpenMode.Direct))
{
database.SummaryInfo.RevisionNumber = "{" + Guid.NewGuid() + "}";
}
I extended the Nikolay script for generating a random GUID automatically. The script also support drag and drop and be called through arguments (so you can easily automate it through cscript) and it checks if the file is writable before creating Windows Installer object (if the file is locked by some application, like InstEd, it will throw an error).
Set objArgs = Wscript.Arguments
Set objFso = CreateObject("scripting.filesystemobject")
'iterate through all the arguments passed
' https://community.spiceworks.com/scripts/show/1653-drag-drop-vbscript-framework
For i = 0 to objArgs.count
on error resume next
'try and treat the argument like a folder
Set folder = objFso.GetFolder(objArgs(i))
'if we get an error, we know it is a file
If err.number <> 0 then
'this is not a folder, treat as file
ProcessFile(objArgs(i))
Else
'No error? This is a folder, process accordingly
For Each file In folder.Files
ProcessFile(file)
Next
End if
On Error Goto 0
Next
Function ProcessFile(sFilePath)
' http://www.wisesoft.co.uk/scripts/vbscript_file_modified_date.aspx
' Set objFile = objFSO.GetFile(sFilePath)
' MsgBox "Now processing file: " & CDATE( objFile.DateLastModified)
If Not IsWriteAccessible(sFilePath) Then WScript.Echo "Error persisting summary property stream" : Wscript.Quit 2
'Do something with the file here...
' https://stackoverflow.com/questions/31536349/set-package-code-of-msi-using-vbscript
Set installer = CreateObject("WindowsInstaller.Installer")
Set summary = installer.SummaryInformation(sFilePath, 2)
summary.Property(9) = CreateGuid()
summary.Persist
End Function
' https://stackoverflow.com/questions/968756/how-to-generate-a-guid-in-vbscript
Function CreateGuid()
CreateGuid = Left(CreateObject("Scriptlet.TypeLib").Guid,38)
End Function
' https://stackoverflow.com/questions/12300678/how-can-i-determine-if-a-file-is-locked-using-vbs
Function IsWriteAccessible(sFilePath)
' Strategy: Attempt to open the specified file in 'append' mode.
' Does not appear to change the 'modified' date on the file.
' Works with binary files as well as text files.
' Only 'ForAppending' is needed here. Define these constants
' outside of this function if you need them elsewhere in
' your source file.
Const ForReading = 1, ForWriting = 2, ForAppending = 8
IsWriteAccessible = False
Dim oFso : Set oFso = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
Dim nErr : nErr = 0
Dim sDesc : sDesc = ""
Dim oFile : Set oFile = oFso.OpenTextFile(sFilePath, ForAppending)
If Err.Number = 0 Then
oFile.Close
If Err Then
nErr = Err.Number
sDesc = Err.Description
Else
IsWriteAccessible = True
End if
Else
Select Case Err.Number
Case 70
' Permission denied because:
' - file is open by another process
' - read-only bit is set on file, *or*
' - NTFS Access Control List settings (ACLs) on file
' prevents access
Case Else
' 52 - Bad file name or number
' 53 - File not found
' 76 - Path not found
nErr = Err.Number
sDesc = Err.Description
End Select
End If
' The following two statements are superfluous. The VB6 garbage
' collector will free 'oFile' and 'oFso' when this function completes
' and they go out of scope. See Eric Lippert's article for more:
' http://blogs.msdn.com/b/ericlippert/archive/2004/04/28/when-are-you-required-to-set-objects-to-nothing.aspx
'Set oFile = Nothing
'Set oFso = Nothing
On Error GoTo 0
If nErr Then
Err.Raise nErr, , sDesc
End If
End Function

VB Script to delete certain files and if files are found copy other files to directory

I have a hard drive that is infected with a virus. The virus encrypts files and then asks for a ransom to unencrypt them. The files are HELP_DECRYPT.HTML, HELP_DECRYPT.PNG, HELP_DECRYPT.TXT and HELP_DECRYPT.URL.
There are thousands of infected files on the drive. I am trying to write a script to go through all the folders on the drive, and if it finds any of the malicious files it deletes them. I then want if to copy files from the backup drive in the same directory ie. if found in I\Folder\ if would get files from F\Folder\ .
In my case the infected drive is Y, and the backup drive is X.
I am relatively new to VBScripts and here is what I have so far:
set fso = CreateObject("Scripting.FileSystemObject")
ShowSubFolders FSO.GetFolder("Y:\"), 3
Sub ShowSubFolders(Folder, Depth)
If Depth > 0 then
For Each Subfolder in Folder.SubFolders
'Wscript.Echo Subfolder.Path
DeleteFiles(subFolder.path)
On Error Resume Next
ShowSubFolders Subfolder, Depth -1
Next
End if
End Sub
'deletes the malicious files and calls the copy function'
Function DeleteFiles(path)
'wscript.echo("in delete method")
set FSO2 = Createobject("Scripting.FileSystemObject")
set ofolder = createobject("Scripting.FileSystemObject")
set ofolder = FSO2.GetFolder(path)
if FSO2.FileExists("HELP_DECRYPT.URL") Then
ofolder.DeleteFile("HELP_DECRYPT.PNG")
ofolder.DeleteFile("HELP_DECRYPT.HTML")
ofolder.DeleteFile("HELP_DECRYPT.URL")
ofolder.DeleteFile("HELP_DECRYPT.TXT")
wscript.echo("DeletedFiles")
copyFiles(FSO.GetParentFolder)
end if
End Function
'copies files from the backup'
Function CopyFiles(from)
dim to1 'where we're copying to
to1=from 'where we're copying from
Call Replace (from, "Y:", "X:")
SET FSO3 = CreateObject("Scripting.FileSystemObject")
For Each file In from 'not sure about "file"
FSO3 = file
Call FSO3.CopyFile (from, to1, true)'copies file and overwrites if already there
Next
End Function
Here's what I would use:
Option Explicit
Dim FSO, badFiles
Set FSO = CreateObject("Scripting.FileSystemObject")
badFiles = Array("HELP_DECRYPT.PNG", "HELP_DECRYPT.URL", "HELP_DECRYPT.HTML", "HELP_DECRYPT.TXT")
Walk FSO.GetFolder("Y:\")
Sub Walk(folder)
Dim subFolder
For Each subFolder in folder.SubFolders
DeleteFiles subFolder, badFiles
RestoreFiles "X:", subFolder
Walk subFolder
Next
End Sub
Sub DeleteFiles(folder, filesToDelete)
Dim file
For Each file In filesToDelete
file = FSO.BuildPath(folder.Path, file)
If FSO.FileExists(file) Then FSO.DeleteFile file, True
Next
End Sub
Sub RestoreFiles(sourceRoot, destinationFolder)
Dim sourcePath, file
WScript.Echo "Restoring " & destinationFolder.Path & " ..."
sourcePath = Replace(destinationFolder.Path, destinationFolder.Drive, sourceRoot)
If FSO.FolderExists(sourcePath) Then
For Each file In FSO.GetFolder(sourcePath).Files
WScript.Echo file.Name
' maybe add a DateLastModified check here?
file.Copy FSO.BuildPath(destinationFolder.Path, file.Name), True
Next
Else
WScript.Echo "Warning! Folder not found: " & sourcePath
End If
End Sub
General tips for working with VBScript:
Always use Option Explicit
Avoid On Error Resume Next except in very closely confined situations. Simply suppressing any errors is never a good idea.
Run scripts like the above on the command line with cscript.exe so you can see the script's Echo output without having to click at 1000's of message boxes.
Use a global FSO object. No need to define a new one in every function
Try to be generic. Look how DeleteFiles() RestoreFiles() above are actually not at all tailored to your current problem. You might be able to re-use those functions in a different script without having to change them.

Error with Loop and recursive function

I am trying to create a VbScript file that will read a text file that has a list of folder names in it.
From these folder names I need to create a second text file that prints out all the files with a specific extension.
I have used this code to do the second part of the task
Option Explicit 'force all variables to be declared
Const ForWriting = 2
Dim objFSO 'File System Object
Set objFSO = CreateObject("Scripting.FileSystemObject")
Dim objTS 'Text Stream Object
Set objTS = objFSO.OpenTextFile("C:\Output.txt", ForWriting, True)
Call Recurse("C:\")
objTS.Close()
Sub Recurse(strFolderPath)
Dim objFolder
Set objFolder = objFSO.GetFolder(strFolderPath)
Dim objFile
Dim objSubFolder
For Each objFile In objFolder.Files
'only proceed if there is an extension on the file.
If (InStr(objFile.Name, ".") > 0) Then
'If the file's extension is "pfx", write the path to the output file.
If (LCase(Mid(objFile.Name, InStrRev(objFile.Name, "."))) = ".exe") Then _
objTS.WriteLine(objfile.Path)
End If
Next
For Each objSubFolder In objFolder.SubFolders
Call Recurse(objSubFolder.Path)
Next
End Sub
I have tried to put this in a loop but when I do I get a syntax error for this line Sub Recurse(strFolderPath)
Any help you can give me would be appreciated
One interpretation of
I have tried to put this in a loop but when I do I get a syntax error
for this line Sub Recurse(strFolderPath)
is that the structure of your resulting script looks like:
Do Until tsIn.AtEndOfStream
p = tsIn.ReadLine
Sub Recurse(p)
End Sub
Call Recurse(p)
Loop
output:
cscript 27537600-B.vbs
..\27537600-B.vbs(3, 4) Microsoft VBScript compilation error: Syntax error
VBScript does not allow nested Sub/Function definitions, especially in loops (you may get away with mixing simple statements and Sub/Function definitions in top-level code, but that's more a bug than a feature). If you re-structure the script like
Do Until tsIn.AtEndOfStream
p = tsIn.ReadLine
Call Recurse(p)
Loop
Sub Recurse(p)
End Sub
you won't get a syntax error on the Sub line.

VBScript -- Refer To Self In Program

Is there a way to refer to the current file running in VBScript? I could just use the name of the file, but it needs to be operable despite directory changes and renames. The purpose of this is to use the file in a file I/O operation. If not possible, are there any potential alternatives, such as making a file non-re-namable, or non-movable?
WScript.ScriptFullName gives you the full path to your running script. You can use the FileSystemObject to parse this path further, if you'd like. For example:
' Assuming the script is at c:\scripts\test.vbs
strFile = WScript.ScriptFullName
With CreateObject("Scripting.FileSystemObject")
MsgBox .GetDriveName(strFile) ' => c:
MsgBox .GetParentFolderName(strFile) ' => c:\scripts
MsgBox .GetFileName(strFile) ' => test.vbs
MsgBox .GetBaseName(strFile) ' => test
MsgBox .GetExtensionName(strFile) ' => vbs
End With
You can use WScript.ScriptFullName to access the full path of the running script at run time.
You can also use Wscript.ScriptName if you just want the script part
This link explains in more detail
I could just use the name of the file, but it needs to be operable
despite directory changes and renames.
It is true that Wscript.ScriptName & WScript.ScriptFullName can provide detailed information on the script that is running, but if you want the code to be executed from an external I/O file that you intend on changing. You can utilize the ExecuteGlobal statement which will allow you to move Subs and Functions into the vbscript namespace.
myFunctions.vbs
Function GetDate()
GetDate = DateValue(Now)
End Function
ExecuteInNameSpace.vbs:
Dim fsObj : Set fsObj = CreateObject("Scripting.FileSystemObject")
Dim vbsFile : Set vbsFile = fsObj.OpenTextFile("myfunctions.vbs", 1, False)
Dim myFunctionsStr : myFunctionsStr = vbsFile.ReadAll
vbsFile.Close
Set vbsFile = Nothing
Set fsObj = Nothing
ExecuteGlobal myFunctionsStr
Wscript.echo "Todays Date is: " & GetDate
By moving all the processing of the code to an external file, you can flexibly configure and change the file all you want in your processing.

Resources