You've just written a wonderful script that you're about to send out into the world, but something makes you pause. Yes, your new script will save you, your users, and the company time, money, and effort. It will likely increase productivity and elevate your status as a scripting guru. Everything seems right with the world, but you can't quite shake the feeling that there's still a problem. Then it dawns on you: Although the script you just wrote can do a lot of good if used properly, someone could, through malice or ignorance, change your script and create havoc.
By their very nature, scripts are open to the world. Anyone can see all the fine work you've done in a text editor. This is both good and bad. On the good side, it's great to be able to share scripts among fellow IT professionals. It's a common practice to keep a repository of scripts handy so that people can combine snippets from various scripts in order to create a new tool. On the bad side, if you write a script that's intended for use by others, they might be curious and try to dissect your code. Even worse, it's quite easy for them to make changes. This situation might not only become a support nightmare but also has the potential to cause damage, depending on what the script was designed to do.
If you're going to distribute your scripts, you need to make sure they have some protection. Although you can use the typical approaches to protecting scripts, there's a better way.
The Typical Approaches
Typically, scriptwriters use one of three basic approaches to protect the scripts they intend to distribute. The first approach is to impose file-or folder-level security on the script. By allowing only read and execute permissions, you can effectively prevent anyone from modifying that file. The downside to this approach is that someone can still view the code and simply save or copy it to another location.
Another approach is to encode the script. For some time, Microsoft has offered the Script Encoder (http://www.microsoft.com/downloads/details.aspx?familyid=E7877F67-C447-4873-B1B0-21F0626A6329&displaylang=en). If you use the Script Encoder, your code will no longer be readable and any changes will render the script useless. However, the Script Encoder works with only certain types of scripts, such as VBScript, JScript, and HTML scripts. And in some cases, such as with VBScript scripts, it's easy to tell that the encoder has been used because the extension changes from .vbs to .vbe. A quick Internet search will reveal the methods needed to return the code to its original state. The third approach is to rewrite your script in a language that's compiled rather than interpreted, then compile the code into an executable. Although no one can view or change your code, you might have to totally rewrite your script to suit the compiled language. Plus, you might not have the resources or development environment to even attempt this option.
A Better Way
I created a tool, DotNetWrapper.vbs, that provides a better way to protect scripts. DotNetWrapper.vbs not only encodes a script but also compiles the script into an executable—without you having to rewrite a single line of code. Unlike the Script Encoder, DotNetWrapper.vbs doesn't limit you to certain types of scripts. You can use it with any script that you save as a text file and that will be launched by a scripting engine or from the command line. So, DotNetWrapper.vbs works with a wide variety of scripts, including .vbs, .js, .htm, .bat, .cmd, and .hta files.
DotNetWrapper.vbs uses the Visual Basic .NET command-line compiler (vbc.exe) that comes with the Microsoft .NET Framework. DotNetWrapper.vbs creates a Visual Basic .NET executable that runs, on the fly, the script you want to protect. DotNetWrapper.vbs does this by storing the content of the script in a variable inside the executable. The executable writes the script to a temporary file and calls the appropriate scripting engine or command interpreter to run it. After the script is launched, the executable deletes the temporary file.
By itself, this offers some protection. However, if you look at a .NET executable in a text editor, you'll notice that it includes some plain text. In particular, the source code of your script would still be viewable. It wouldn't be easily readable (there would be white spaces between each character and no line breaks), but it would be visible nonetheless.
To provide additional protection, the script includes the EncodeScript function, which applies a simple character rotation scheme. As Listing 1 shows, this function shifts alphabetic characters up by 128 places. Numeric and other non-alphabetic characters are left alone. A decoding scheme is built into the .NET executable.
Besides the benefits I already mentioned, wrapping your scripts inside a .NET executable comes with benefits inherent to an .exe file. You can add a custom icon for the executable. You can also use the RunAs tool to run the script you want to protect under an account that differs from the one you logged on with.
Although there are many benefits, DotNetWrapper.vbs has some limitations. You must run DotNetWrapper.vbs on a machine on which the .NET Framework is installed. In addition, you must run the executable on a local computer. It won't work if you run it from a network drive or Universal Naming Convention (UNC) path. Finally, because the script being protected is deleted after it's launched, any event that refers back to that script won't function properly. For example, suppose you use DotNetWrapper.vbs to protect an HTML Application (HTA) or HTML page. If you attempt to view the HTA's or HTML page's source code by selecting the Source option on the View menu in Microsoft Internet Explorer (IE), you'll get a blank screen. And any attempt to refresh the HTA or HTML page will cause it to hang. Note that you can use DotNetWrapper.vbs for scripts that require responses. For example, you can use DotNetWrapper.vbs to run scripts under WScript that include the InputBox function.
How to Use DotNetWrapper.vbs
To use DotNetWrapper.vbs, you must have Windows Script Host (WSH) 5.6 and .NET Framework 1.1 or later installed on the machine on which you plan to run it. To determine which version of the .NET Framework you have on your machine, you can look in the %systemroot%\Microsoft.NET\ Framework folder. Each version of the .NET Framework appears in a subfolder. I wrote the script to work with version 1.1.4322. If you have a later version, you need to change the information on the following line in DotNetWrapper.vbs to match your version:
dotNetVersion = "v1.1.4322"
Another change you might need to make concerns the code
This code, which inserts a 1-second pause, is used twice in DotNetWrapper.vbs. The first pause occurs between the closing of the temporary file and the execution of the script. The second pause occurs between the execution of the script and the deletion of the script. It might be necessary (although unlikely) to increase the length of the pauses based on the size of the script you're protecting. A 1-second pause works fine for the largest scripts (about 500 lines, or 14KB) I run with DotNetWrapper.vbs. If you do need to change it, simply change the 1000 value to, say, 2000, which would create a 2-second pause.
Other than these two possible changes, DotNetWrapper.vbs requires no modifications. You can download DotNetWrapper.vbs from the Windows Scripting Solutions Web site.
To run DotNetWrapper.vbs, use the syntax
DotNetWrapper.vbs Script_File Script_Type Script_Engine Output_File \[Icon_File\]
The Script_File argument specifies the pathname of the script you want to protect. If this argument (or any of the other arguments) includes spaces, enclose it in quotes (e.g., "C:\My Scripts\ myscript.vbs").
The Script_Type argument specifies the extension (e.g., .vbs) of the script you want to protect. Be sure to include the period (.) in the extension.
The Script_Engine argument specifies the scripting engine with which to launch the script you're protecting. Scripting engines that are in the computer's system path can be called by specifying the program name (e.g., specifying cscript.exe for CScript or wscript.exe for WScript). Otherwise, you need to specify the full pathname to the scripting engine. If you want the Windows command interpreter (cmd.exe) to run the script, specify
"%COMSPEC% /k". The /k option leaves the command-shell window visible for displaying information. If you want the script to run in a hidden window, you can specify "%COMSPEC% /c" instead.
The Output_File argument specifies the name you want to assign to the executable that the .NET Framework creates. Specify the full pathname (e.g., "C:\Programs\myscript.exe").
Unlike the other four arguments, the Icon_File argument is optional. If you specify a full pathname (C:\ Icons\mypicture.ico), an icon will be created for the .NET executable.
Protect Your Scripts
After you've written a wonderful script, stop and consider whether you need to protect it before you send it out into the world. If that script needs some protection, DotNetWrapper.vbs might be just what you need.