Compatible, Resident Windows 95 Viruses
by DV8/IRG

    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 100compatable 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