A couple of years ago, on January 28th, 2004 to be exact, I was working another late night at the office when I got a visit from the General Manager of Microsoft.com. He was walking the halls desperately seeking someone to quickly develop a tool/utility to give to customers to help them determine if their personal computer was infected with a new virus. It was about 8 p.m., and there were very few people still at work that night. But I was, and agreed to take a crack at developing this tool. The Mydoom.B virus was running wild, fresh and free around the world that night, and I was going to help hunt it down.
I took me about two hours to deliver a solution. Following I describe what I did, how I did it, and my thinking at the time.
The goal of the tool would be to detect the presence of the virus by finding a specific file anywhere under a client machine’s Windows system folder. The first challenge was to decide what form the tool should take. What kind of executable, written in what language would be simple, quick, and portable?
Even though I was already in love with C# by then, I decided a VBS script file, written with Visual Basic Scripting Edition (VBScript), would be good for this. I was no scripting god, but I had done enough of it by then to make it feasible. The VBS file would be a small, portable, native Windows executable file with no support files and no installation required. The VBScript code could be readily ported to running within a web page, or even ported to a little Visual Basic (VB) application, if need be. In addition, VB and its dialects were very popular, making it easy for other developers to work with the code I was going to write, if required.
But how fast and simply could I get this rush job done?
The core function of this tool would be code that finds a given file name (“ctfmon.dll”) residing in any folder in and under the Windows system folder. The code would have to locate the system folder, and then search on the files in there. For many software developers, coding this sort of file hunt is a straightforward task.
Code that traverses an entire hierarchical folder tree, looking for a specific file name, typically employs recursion. The recursive algorithm will get a list of all of the files in a folder, operating on each of them. Then it gets a list of the subfolders, and calls itself to execute on each of those. If there are no subfolders found under a particular folder, that recursion “drill-down” branch terminates.
I could have gone this route, working out the code and carefully testing it. But instead, I thought it would be faster and safer to leverage a ready-made, built-in Windows operating system command: DIR! Yes, I am talking about the ancient DOS command, still in popular use today, that produces file and directory listings. With the simple inclusion of its “/s” switch, DIR will cover all subfolders – no fancy recursion required!
So I broke out my two DOS reference books from my bookshelf:
Running MS-DOS: Version 6.22
by Van Wolverton, August 1994
MS-DOS 6 Companion
by Joanne Woodcock, May 1993
and got to work. It was time to cook up a little “old school” magic.
Quickly came the challenge of working out how to invoke the DIR command from the VBScript code, and examining the output to see if it found any files. I also had to locate the Windows system folder and get the DIR command to operate on it. Here is the actual code I wrote for this task:
set fso = CreateObject("Scripting.FileSystemObject")
set wso = CreateObject("WScript.Shell")
set wse = wso.Environment("SYSTEM")
sTempDir = wse("TEMP")
sTempDir = wso.ExpandEnvironmentStrings(sTempDir)
sTempFileName = fso.GetTempName()
sTempFullPath = sTempDir & "\" & sTempFileName
sWinDir = wse("WINDIR")
sWinDir = wso.ExpandEnvironmentStrings(sWinDir)
wso.CurrentDirectory = sWinDir
sCmd = "cmd /c dir ctfmon.dll /b /s > " & chr(34) & sTempFullPath & chr(34)
wso.Run sCmd, 0, true
set ts = fso.OpenTextFile(sTempFullPath, 1)
on error resume next
sFileContents = ts.ReadAll
on error goto 0
if len(sFileContents) > 0 then
msgbox "Virus was found at:" & chr(10) & chr(10) & sFileContents
msgbox "Virus was not found"
set ts = Nothing
set wse = Nothing
set wso = Nothing
set fso = Nothing
I hope this code looks pretty short to you, considering what it accomplishes. Less code, less chance of flaws in the logic. Let’s review this run of VBScript. This code:
employs the Scripting.FileSystemObject and WScript.Shell objects, to assemble a location for writing a temporary working file, and to locate the Windows system folder. Coming up with this one statement:
took a big portion of the two hours, believe it or not. I was stumped for a while on how to get the DIR command to execute on a specified folder, and not the folder where the VBS file was residing. That code statement did the trick. The next two lines “do the heavy-lifting” in this little solution:
This is the formulation and execution of the full DIR command, passing several arguments, such as the name of the file the search for, and where to direct the output. For reasons that escape me, I had to prefix the DIR command with a CMD command to invoke the OS command shell. The “/b” switch asks for bare output – just the names of the files with no dates or sizes. The second argument on the wso.Run method – the “0” – hides the command shell UI.
The next run of code:
reads the contents of the DIR output file into a program variable. The error suppression comes into play when none of the target files are found, and the output file is empty. The final functional run:
tells the user if the virus was detected, accomplishing the goal of the tool.
Crude, but effective! I don’t code VBScript much anymore, but I am ready at moments' notice. Around here, the more languages and programming tricks you know the better equipped you are for putting out the fires and preventing them. Keeping Microsoft.com up takes everything you got.
The Microsoft.com operations blog has a fantastic post here describing how they put together a VBscript
Loved your script - stole bits of it for the creating a temp file as well as reading the shell output.
Just for your info, though, there's an easier way of specifying the directory: put it in the dir commmand itself. In this case, your command would be:
dir %windir%\regedit* /b /s > ....
Great idea! I also used a temp file creation part of the script for some other solution.