ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ 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".