Copy Link
Add to Bookmark
Report
Xine - issue #5 - Phile 113
Ú-----------------------------¿
| Xine - issue #5 - Phile 113 |
À-----------------------------Ù
Ú--------------¿
| VxD tutorial |
À--------------Á------------------------------------------------------------
This tutorial covers VxD related information relevant to viral technology.
Ú-------------¿
| 1. Hands on |
À-------------Ù
First, read the documents from the "Device Developer Kit", write some VxDs
and use its tools until you become familiar with it. There is no point in
rewriting those documents here. The DDK is available in the "Microsoft Deve-
loper Network".
Ú-----------------¿
| 2. Dropped VxDs |
À-----------------Ù
The main use for VxDs in virus writing so far was dropping a VxD in a Win-
dows directory and then making the necessary changes to load it next time
Windows restarts. There are some interesting viruses and tutorials that de-
monstrate how to do this in magazines such as Insane Reality and 29A.
Ú-------------------¿
| 2.1. Dynamic VxDs |
À-------------------Ù
There is no need to wait until Windows restarts to load the virus VxD. All
we have to do is make the VxD dynamically-loadable and then call it. This
requires a change in the control dispatcher and in the definition file.
An example follows:
BeginProc VXD_Control
Control_Dispatch Device_Init, VXD_Device_Init
Control_Dispatch System_Exit, VXD_System_Exit
clc
ret
EndProc VXD_Control
This control dispatcher should be changed to:
BeginProc VXD_Control
Control_Dispatch Sys_Dynamic_Device_Init, VXD_Device_Init
Control_Dispatch Sys_Dynamic_Device_Exit, VXD_System_Exit
Control_Dispatch W32_DeviceIoControl, VXD_DeviceIO
clc
ret
EndProc VXD_Control
Or (to be statically and dynamically-loadable):
BeginProc VXD_Control
Control_Dispatch Device_Init, VXD_Device_Init
Control_Dispatch System_Exit, VXD_System_Exit
Control_Dispatch Sys_Dynamic_Device_Init, VXD_Device_Init
Control_Dispatch Sys_Dynamic_Device_Exit, VXD_System_Exit
Control_Dispatch W32_DeviceIoControl, VXD_DeviceIO
clc
ret
EndProc VXD_Control
And the "VXD_DeviceIO" procedure should be:
BeginProc VXD_DeviceIO
xor eax,eax ;Indicate success
clc
ret
EndProc VXD_DeviceIO
The corresponding definition file:
"VXD GENERIC
SEGMENTS
_LPTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE..."
Should also be changed to:
"VXD GENERIC DYNAMIC
SEGMENTS
_LPTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE..."
We can then load (dynamically) the VxD from ring 3 using:
push 0
push FILE_FLAG_DELETE_ON_CLOSE
push 0
push 0
push 0
push 0
push offset Filename
call CreateFileA
...
Filename db '\\.\C:\WINDOWS\SYSTEM\VIRUS.VXD',0
In this example, the VxD file was "C:\WINDOWS\SYSTEM\VIRUS.VXD".
This sends a Sys_Dynamic_Device_Init and W32_DeviceIoControl message to the
VxD. There is no need to use the API function DeviceIoControl since the de-
vice already received the necessary messages. We can't close the file either
or the device would be unloaded, but it still must respond to unloading mes-
sages because Windows will send them when it terminates.
Ú---------------------¿
| 2.2. VxD optimizing |
À---------------------Ù
The main problem about viruses using this technique was the size of the
required VxD. There are 3 types of unused space in a VxD file (however, none
of them results in memory loss after the device initialization):
* The space between segments (objects). To fix this, we could:
þ Make sure we fill the segments to a multiple of "VXD page size".
þ Reduce the size of "VXD page size" using the assembler options (this
may impair system performance).
þ Use only one segment. Since the last segment is never padded, no space
would be wasted. One segment is enough for any type of virus. Checking
for a Duplicate_Device_ID in the real-mode init proc is redundant when
Undefined_Device_ID is specified as the device identifier. The real-mode
segment is obsolete and can be eliminated in such cases.
* The space between the end of the "Fixup Record Table" and the beginning of
the "Enumerated Data Pages", assuming that no imports or "Per-Page Check-
sum Table" are used. To fix this, use the VxDPack utility.
* The space between the end of the MZ header and the start of the LE header.
To fix this, use the VxDPack2 utility.
Ú------------------¿
| 3. VxD infection |
À------------------Ù
First, you must understand the internal structure of VxDs. They use the LE
format, derived from OS/2's LXEXE format. Unfortunately, none of the infor-
mation I've found about this format was coherent or consistent. I advise to
read the available documents and then use VxD analysis tools like W32Dasm,
DumpBIN (from the DDK), VXDasm, IDA, HIEW or DumpLX to verify or refute the
information from the docs.
When I was researching VxD infection, I found out that there already were
a VxD infector, called Navrhar, but further examination of this virus allows
to conclude that it can only infect some Windows VxDs and fails to infect
any other besides those, because it relies on too many false assumptions.
This virus only used this sort of VxD infection to gain control of ring 0,
in order to infect MS Word documents, making the use of VxDs, an option of
questionable usefulness. Anyway, Navrhar only "scratched the surface" of VxD
infection. To make it an effective tool for virus technology, we must go a
lot further and try to exhaust all possibilities of infection (including
Navrhar's).
Ú----------------¿
| 3.1. Some tips |
À----------------Ù
* Some .VXD files are not true VxD device drivers, but rather archived lib-
raries of several VXDs. Main examples are "VMM32.VXD" and "WIN386.EXE". To
extract VxDs contained in these archives, we can use DEVLIB (from the DDK)
or VxDLIB.
* Use Soft-ICE to debug VxDs:
þ To debug real-mode init procedures, make sure you reserve enough memory
for Windows to start using the /EXT switch.
þ To debug protected-mode, place a 0xCC (INT 3) byte in the code you want
to examine and set a breakpoint in the debugger. To debug static VxDs,
modify the file "WINICE.DAT".
* Special MS-DOS executables use this format: "SMARTDRV.EXE", "EMM386.EXE".
Ú--------------------¿
| 3.2. The LE format |
À--------------------Ù
This section clarifies important issues relative to the LE format.
Ú----------------------------¿
| 3.2.1. Some considerations |
À----------------------------Ù
* The "fixup section" consists of the data from the beginning of the "Fixup
Page Table" to the end of the "Fixup Record Table" whereas the loader sec-
tion consists of the data from the beginning of the "Object Table" to the
end of the "Module Directive Table".
* In unmodified VxDs, the order of the data within the file is the same as
the respective pointers declared in the LE header. The "Enumerated Data
Pages" (assumed to be consecutive within the file) are no exception and
should be considered as one big, variable table.
* The "Module Directive Table" contains variable pointers but is never used.
To simplify infection, the virus should only infect VxDs with no "Module
Directive Table".
* When a VxD is loaded, relocations are applied and only then the appropria-
te init procedures are called. This seems obvious, but since the "Device
Descriptor Block" (DDB) is always in the first object, it can (and proba-
bly will) have relocations within it. This means that in order to succes-
sfully manipulate DDB data, WE MUST PROCESS RELOCATIONS!
* To simplify the processing of relocations, the virus should only infect
VxDs with no imports. Since these are rarely used, infection will not be
considerably limited due to this.
* To store the virus or other necessary code, we should use the end of file
(like with regular MS-DOS executable files). Since there are no references
pointing there, the VxD will still run perfectly.
* To store code in the first object, we can use the DDB reserved fields.
Ú---------------------------------------¿
| 3.2.2. Resident and non-resident data |
À---------------------------------------Ù
It should be noted that all pointers from the "Object table offset" (first)
to the "Offset of Per-Page Checksum Table" are relative to the beginning of
the LE header (pointers to resident data) whereas the ones from the "Offset
of Enumerated Data Pages" to the "Offset of Windows Resources" (last), are
relative to the beginning of the EXE file (pointers to non-resident data).
Ú----------------¿
| 3.3. Real-mode |
À----------------Ù
Real-mode segments contain no relocations and can be freely patched.
The possible methods of infection are:
* Use blank spaces. There are very few and thus we can't rely on this.
* Use the padding at the end of the real-mode segment (when possible) that
contains the init proc to store the virus or a small stub that will load
the virus from the end of file. To gain execution control, we can save and
patch the original bytes of the init proc with a jump to our code and then
restore them before returning execution to the init proc.
* Overwrite the start of the original init proc with virus code or a small
stub that will load the virus from the end of file, saving the original
bytes of the init proc also at the end of file.
To demonstrate this type of infection, I wrote "DOS16/VxD.Opera IX".
Ú--------------------------¿
| 3.3.1. Allocating memory |
À--------------------------Ù
To allocate memory, we can use DOS functions but this way the memory will
be discarded after Windows initialization. To remain resident, we must use
MCBs/UMBs.
Ú--------------------¿
| 3.3.2. The INT 21h |
À--------------------Ù
The VMM has a nasty habit of unhooking the INT 21h. To successfully infect
files under Windows, we must rehook (if necessary) this interrupt.
Ú-----------------------¿
| 3.3.3. Reloading code |
À-----------------------Ù
To retrieve the name of the current VxD, we can use the name of the last
accessed file available in the SDA, but VMM opens the file in read mode (no
sharing), preventing us from opening the file. This means that in order to
reload code from the end of file, we must crack the DOS file sharing protec-
tion and manipulate the internal file structures directly. Fortunately, all
we need is the handle of the last accessed file, which is also available in
the SDA. Since we only want to read from the file, there is no need to close
that handle and reopen the file in read/write mode and thus we can use the
handle directly, just like if we opened the file ourselves.
Ú----------------------¿
| 3.3.4. Using the SDA |
À----------------------Ù
The "DOS Swappable Data Area" was completely overlooked by virus writers.
It consists of a "snapshot" of the current DOS file input/output state and
other critical data.
Ú--------------------------------¿
| 3.3.4.1. Obtaining the address |
À--------------------------------Ù
We could use the following:
mov ax,5d06h
int 21h ;Get address of "DOS Swappable Data Area"
;DS:SI now points to the SDA
But this call may fail (carry flag set). A much more robust approach is to
obtain the address of the "InDOS flag". Since this flag is at offset 0001h
within the SDA (in all DOS versions), we should use:
mov ah,34h
int 21h ;Get address of "InDOS flag"
;ES:BX now points to the "InDOS flag"
dec bx
;ES:BX now points to the SDA
Ú-----------------------------¿
| 3.3.4.2. Interesting fields |
À-----------------------------Ù
There are lots of fields in the SDA relevant for virus writing, such as:
* The current DTA and PSP.
* The first, best and last usable memory blocks.
* The current time and date (to check for a payload activation).
* The name and handle of the last accessed file (this is very useful to
crack the DOS file sharing protection in order to allow VxD real-mode inf-
ection). Study the "DOS16/VxD.Opera IX" virus for details.
* The DOS internal stacks (allows a new way of residency).
* Many more, just have a look at "Ralf Brown's Interrupt List".
Ú----------------¿
| 3.3.5. Stealth |
À----------------Ù
We can intercept and stealth during INT 21h functions.
Ú---------------------¿
| 3.3.6. Polymorphism |
À---------------------Ù
Use a regular 16-bit polymorphic engine.
Ú---------------------¿
| 3.3.7. Multipartite |
À---------------------Ù
Multipartite is very straightforward, have a look at "DOS16/VxD.Opera IX".
Ú----------------¿
| 3.3.8. Payload |
À----------------Ù
We can use the interrupts, like regular real-mode executables.
Ú---------------------------------------¿
| 3.4. From real-mode to protected-mode |
À---------------------------------------Ù
It is possible to reserve extended memory from real-mode using the service
LDRSRV_COPY_EXTENDED_MEMORY. Use real-mode infection techniques to do this
and then pass execution control to the protected-mode code, using the refe-
rence data, but call the original init proc first, which returns to the VMM
executing a retn (near). The VMM already places a retf (far) at the return
address, but for compatibility reasons, save the original byte, write a retf
there and restore it before returning execution to the VMM.
This is the simplest method of gaining control of ring 0, but only allows
to infect static VxDs with a real-mode procedure, which is a quite limited
infection range.
To demonstrate this type of infection, I wrote "VxD.Burzum".
Ú---------------------¿
| 3.5. Protected-mode |
À---------------------Ù
This is the orthodox and most effective way of gaining ring 0 control.
The only necessary segment for a VxD is the first one, which always has the
same characteristics (PRELOAD NONDISCARDABLE) and thus this is the only seg-
ment in which we can rely. Hence, the possible methods of infection are:
* Use blank spaces. These may contain relocations and thus we can't rely on
this.
* Use the padding at the end of the first segment to store the virus or a
small stub that will load the virus from the end of file.
* Overwrite the original DDB control procedure. It may contain relocations
and thus we can't rely on this either.
* Create a new segment with the same characteristics as the first one.
But wait a minute! We can't use nondiscardable segments for residency be-
cause this way we would be dependant of the success of the original DDB con-
trol procedure and the virus would be repeatedly loaded into memory, causing
significant memory loss. To solve this, we could deallocate memory when the
virus is being reloaded (this requires the manipulation of internal memory
structures, like the "Device_Location_List") or apply full file stealth.
However, a much more versatile approach is to create a new discardable seg-
ment, allocate memory and copy the virus there when necessary.
When loading static VxDs, discardable segments are only "dropped" when the
Init_Complete message is sent, which means that all virus segments (from
infected VxDs) are loaded into memory before they can be discarded, and may
prevent Windows from starting due to lack of memory. But the sum of the
"Virtual Size" of all resident sections from modules like "KERNEL32.DLL" and
"USER32.DLL" is still larger than the size of the virus segment, multiplied
by the maximum number of VxDs being loaded at startup. Which leads to an im-
mediate but interesting conclusion: If there isn't enough memory to load all
virus segments before they can be discarded, Windows wouldn't run anyway be-
cause there isn't enough memory to load crucial modules like "KERNEL32.DLL".
When loading dynamic VxDs, the virus segment is immediately discarded, re-
sulting again in no memory loss.
VxD segments are loaded at random places in memory. In order to pass exe-
cution control to the virus segment, we need to add a relocation.
To demonstrate this type of infection, I wrote "VxD.Abigor".
Ú----------------¿
| 3.5.1. Stealth |
À----------------Ù
Stealth in VxD viruses is a great idea. Since static VxDs are loaded befo-
re any Windows application (including anti-virus programs), AV researchers
will have to restructure their products in order to detect these viruses.
To make the virus less noticeable, we could use a discreet name for the
virus object (like ICOD) and increase the virtual size by a small amount.
To stealth file access, install a file hook.
Ú---------------------¿
| 3.5.2. Polymorphism |
À---------------------Ù
Use a regular 32-bit polymorphic engine. Be careful with relocations!
Ú---------------------¿
| 3.5.3. Multipartite |
À---------------------Ù
Multipartite is very straightforward, have a look at "Win95/VxD.Godgory".
Ú----------------¿
| 3.5.4. Payload |
À----------------Ù
We can use VxD services, like "SHELL_Message".