Insane Reality issue #8 - (c)opyright 1996 Immortal Riot/Genesis - REALITY.007 Article: Win-95 Viruses Author: DV8 [IRG] % Windows-95 Compatible Viruses by DV8 [IRG] % ______________________________________________ This article is excellent information for anyone interested in writing Win-95 viruses. It outlines many of the methods used in 'Mr Klunky' the worlds first VIABLE Windows'95 virus. Thanks again DV8. - _Sepultura_ ============================================================================= % Compatable, Resident Windows 95 Viruses. % - by DV8 This article assumes the reader is familiar with articles in VLAD-6 by Quantum and Qark - it would take unnecessary room to cover that ground again. When I started investigating Windows 95, a shade under a year ago, I decided to make a list of matters I felt were important in any virus running under that pseudo-OS or needed further consideration. The basic list isn't long... ù Full compatability. ù Stay in memory after program termination. ù What about Ring-0/Ring-3 incompatabilities? ù Insert itself into the '95 boot sequence. ù Other % Full Compatability % Compatability is the single most important thing any virus needs. How many old viruses can you name off the top of your head which failed to funtion properly once an undocumented change was made to an undocumented part of it's base operating system? This is what happened with Bizatch and will continue to happen with any virus that does not implement full OS compatability. Keeping compatability some very important questions arise. ù How do I call functions? ù How do I get loaded? ù How do I monitor file activity? ù Compatable Function Calling. Anyone who has read the VLAD-6 articles on Windows 95 will be familiar with this problem. Quantum's solution was to locate the internal KERNEL32.DLL entry point and call KERNEL32 functions by ordinal. However, a compatability problem arose for him. If a newer version of KERNEL32 was in use or if the ordinal of a function he used was changed his code was rendered unusable, either causing a fault (which Windows 95 should handle) or trashing the run-time and necessitating a reboot. Unfortunately each time MS releases an update to Win95 this is likely to happen. How can increase our compatability then? Also, how can we call functions in a different module (ie. not KERNEL32.DLL)? The answer becomes apparent when a few facts about Win95 are considered. ù There is no hardware protection of memory. This means that any valid memory address can be read or written with impunity. ù KERNEL32.DLL is always loaded at the same starting address. KERNEL32.DLL, regardless of version (language, revision, etc) is always loaded to the same starting address in memory, as long as that module is for Windows 95 (ie. the base load address is different under Windows NT). ù Loaded files are mapped into memory peicemeal. When a DLL is loaded it is opened and copied into memory, as a whole. Next certain references are resolved (like the actual address of data items). Finally control is passed to the initialisation portion of the DLL. ù DLL files are of the PE type format. Part of the PE format is an export table. '95 uses this to resolve what functions are in a module and where they are, as well as how to call them. All documented Win95 functions are called by name using this convention. ù There are functions to call other functions. The "GetModuleHandle" and "GetProcAddess" class of functions allow the exported entry point for any function in any module to be located, allowing compatable calling. Hence the method I present in this issue is comprised of the following steps. ù Locate KERNEL32.DLL's PE header in memory (starting at the flat address BFF70000h). ù Use the PE header info to locate KERNEL32's export section. ù Locate the export info for "GetModuleHandleA" and "GetProcAddress". ù Extract the correct entry point for those two functions. ù Use "GetModuleHandleA" and "GetProcAddress" to call any and all needed exported functions from any valid w95 module. Strengths of this approach. ù Robust and reliable. ù Allows calling of any ring 3 function normally available to a PE program. Inherant weaknesses of this approach. ù If MS should (*shudder*) move KERNEL32.DLL then this approach would meet the same fate as Bizatch. However this is extremely unlikely for a number of reasons, not the least of which is the fact they'd have to change absolute addresses in other modules :) You will agree that this approach is much more desirable than previously used approaches. However it is not perfect, since it does still rely on a constant value. Expect to see a 100% compatable approach in the next version of Mr Klunky, hopefully in IR-9 (if I get all the bugs ironed out). ù Compatable Residency. Although using an undocumented method one could probably go resident under w95, taking such an approach would be begging for compatability problems. So, how do we go resident under Windows 95? Well, you'll just have to look in the next section (Residency), won't you. ù Monitoring File Activity Compatably. As I said about going resident, you could probably achieve this using an undocumented approach - but probably shouldn't. Once again, read the "Residency" section for answers to this question. % Residency % Residency under Windows 95... and (as we now see) compatable residency at that! but how? First - what is a TSR virus, really? Most simply stated it is a type of driver, not (not always anyway) a hardware driver. It's more of a file driver. So we need to look at device drivers under Windows 95. ù The VxD. VxD stands for "Virtual something Driver" - remember highschool maths? "x" is the unknown quantity ;] A VxD has the LE executable format. Any w95 device or activity (including AV software, CD ROMs, etc) needs a way of operating at privelage level 0 (ring 0). This way it can have priority to do anything it needs to. In the case of a virus this means intercepting file activity. If we somehow can load our virus (compatably) as a ring 0 (VxD) process we can do vitually anything. As soon as we are a ring 0 process we cannot directly use ring 3 (normal program type) functions. We have to do everything through VxD calls. These are in fact int 20h instructions followed by an ordinal identifying which VxD call we are making. Luckily there are VxD calls that let us do just about anything that can be done in ring 3 - and then some. When a VxD is it's control dispatch is the key to it's execution. This dispatcher has control passed to it when any of several system events occur. To signify success it RETurns with the carry flag clear, or set on failure. It should default to success for unhandler events. Events we are concerned with follow. ù SYS_DYNAMIC_DEVICE_INIT So that we can do any setting up we need to when the VxD is loaded. This event only occurs if the device can be dynamically loaded (ie. loaded at any point other than system start). A virus needs this handler so it can go resident immediately after an infected program is run. ù SYS_DYNAMIC_DEVICE_EXIT So that we can deinitialise all necessary items when we are manually unloaded. This event only occurs if the device can be dynamically unloaded (ie. unloaded at any point other than system shutdown). A virus only needs this at the debugging level. One would not normally want their virus to be trivially unloadable. ù INIT_COMPLETE So that we can initialise anything we need when the system finishes booting. A virus that inserted itself into the w95 boot sequence will need this handler so that it can be properly installed in memory at the end of system boot. ù SYSTEM_EXIT So that we can deinitialise all necessary items when the system shuts down. A virus needs this handler so that it can let '95 shut down without generating errors (and thus signalling the user that something is wrong). Once we are loaded into ring 0 we need to monitor file activity. ù Monitoring File Access. A VxD call named "IFSMgr_InstallFileSystemApiHook" lets us insert our own file handler into the File System. A virus VxD simply uses this function to trap all file processing and filter out the desired items. The File System (FS) hook needs to follow C calling conventions to remain compatable with the rest of sub-systems. Certain parameters are passed to the FS hook on the stack and these are utilised to determine whether a call should be processed by the VxD. There are several traps and tricks one needs to take care of, most are learnt by experience. I have included the most important two. ù ALL file processing calls go through the file handler, including those made by the VxD itself. The VxD must take care not to allow processing of it's own calls (there is no "chaining" in the DOS sense) or this sequence of processing will be infinite and lead to a machine crash. ù Some VxD functions restore the stack, most do not. Be sure to always restore the stack manually, but only when a function does not do so itself. When deinitialising the VxD calls "IFSMgr_RemoveFileSystemApiHook" to remove it's File System hook. % Ring-0/Ring-3 Incompatabilities % I previously mentioned the incompatabilities between ring 0 and ring 3 processes. They simply are not (in normal circumstances) able to use each other's code. For compatability reasons this is COMPLETELY ruled out. However, here we find ourselves with a situation where we NEED code running at ring 3 (that is, attached to the host PE module) and code running at ring 0 (the file monitoring VxD). How do we resolve (once again in a compatable way) these needs and difficulties? My approach was to have the code that is patched to the infected program in the VxD file, as data. When the VxD infects a file it writes the PE patch code to the correct position and appends a copy of the VxD to the PE patch code. In it's turn, when the infected file is run control is passed to the PE patch code. This patch code creates the VxD file on disk and loads into memory as a driver. This allows two distinctly different ring levels of code to exist in a single module and yet to interact in a compatable fashion. % Boot Sequence % Infecting the Windows 95 startup sequence is almost laughable when a compatable approach has been used. Observing a few step will guarantee, in a tasefully compatable fashion, that our virus will be loaded whenever w95 starts. ù Create the VxD in a system directory. There are two default directories where system DLLs, VxD, EXEs, etc may be located, in a default installation of Windows 95 these are... C:\WINDOWS C:\WINDOWS\SYSTEM The "GetSystemDirectory" and "GetWindowsDirectory" ring 3 functions return these directories. The ring 0 "Get_Exec_Path" returns the fully qualified path to the file "VMM32.VXD", for example "C:\WINDOWS\SYSTEM\VMM32.VXD" (ie. in the system directory). Ring 0 VxD call "Get_Config_Directory" returns the directory the configuration files (eg "SYSTEM.INI") are stored in, for example "C:\WINDOWS\" (ie. the windows directory). Between the four of these functions the Windows or System directories can be derived, either from ring 0 or ring 3. ù Create an entry in the registry. Any registry key entry located in the "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VxD" area of the registry can be made to run a VxD automatically on system start. This key should have the following entries within it. ù Start A binary entry with the value 00. ù StaticVxD A string entry with the filename of the VxD. For example "MrKlunky.VxD". The module "ADVAPI32.DLL" contains ring 3 functions for creating, opening, adding, editing, deleting and closing registry keys and entries. The "RegCreateKey" class of functions allows keys to be opened or created. The "RegSetValue" class of fucntions allows entries to be created and set. The "RegCloseKey" function closes a handle to a registry key. % Other Concerns % There are a few of matters that concerned me or may concern the reader. ù Size. Size is not really a concern on Windows 95 machines for several reasons. There is a 4GB continuous addressing space, with a minimum actual RAM requirement of 8MB. Similaraly hard drive space on machines running '95 tend to be in the GB number range. Available HDD space is never constant on a machine running '95, there is so much swapping inevitably going on. The average user rarely, if ever, sees the size of programs (s)he is running, much less notices changes in those sizes! ù Viability. How viable is this approach? Well, hopefully I have already answered that ;] Remember that the example that comes with this is article is not really likely to survive "in the wild" and is purely for study and experimentation. ù Loading multiple copies of the VxD. I did discover something unsettling! Multiple copies of a VxD can be loaded into memory if one of several criteria is not met. The easy way (which I used) was Device ID numbers. Each VxD must have either a unique Device ID, or a Device ID set to Undefined_Device_ID. If the Device ID is Undefined_Device_ID, multiple copies of the VxD could conceivably be loaded simultaneously. So, I "steal" a device ID which (I believe) is unused. I have developed a method of preventing multiple-loads while using Undefined_Device_ID, more on that next time. % Conclusion % This issue also includes an example of a virus employing the techniques discussed here. Shudder in awe at the might of Mr Klunky! You should find the source and a debug script for MrKlunky.VxD (compiled directly from the source) and for a util (Load-MrK.COM) in this issue. The next version of Mr Klunky will incorporate the following... ù Absolutely NO absolute addresses. ù Uses Undefined_Device_ID without allowing multiple loads of the VxD. ù All new PE infection method. ù More things to make it viable "in the wild". ù Maybe a (non-destructive) activation routine. Anyone who wants to yap with me about w95 techniques, approaches or anything virus related (EXCEPT morality) contact me through any IR member. Goodbye! Enjoy! And remember - A Smurf a day keeps the doctor away! -DV8/IRG