Igor Daniloff
VIRUS BULLETIN
December 1997
A while ago I was emailed a number of complex polymorphic viruses by a mathematics student at Moscow State University. The family was later named RDA.Fighter, after the virus writer's own phrase 'Random Decoding Algorithm'. Although these viruses were zoo specimens, the 7408-byte variant has been distributed via a pirated copy of Dr. Web v2.12 on a BBS. In this way, the virus writer introduced his 'masterpiece' to the world. Leaving this point to his conscience, I will only say that the RDA.Fighters are indeed interesting to the specialists. They deploy new and unconventional techniques, showing the author's prime objective of making these viruses almost undetectable and thus incurable.
RDA.Fighter.5871, 5969, 7802, and 7868 are advanced, TSR, polymorphic parasites designed to infect COM and EXE files. RDA.Fighter.7408 is a multi-partite virus that infects COM and EXE files and hard disk MBRs. Along with versions 7802 and 7868, it hides the size increment of infected files. In addition to the random decoding algorithm, RDA.Fighter tends to use algorithms drawn from other viruses. For example, the polymorphic engine (APE) is taken from the Phantom1 virus, the self-restoring Hamming code from the Doodle family, and the MBR infection mechanism (for RDA.Fighter.7408) from both SVC and One_Half.
The outer layers of decryptors are more or less standard polymorphic decryptors, except for a few peculiarities. The decryptor is full of junk and register-twiddling commands. In most polymorphic viruses, such commands are often pure garbage - not so in this case, as the decryptor actually depends on the result of the operations.
The decryptor also contains lnt 21h calls to 'non-critical' functions (values returned in the AX register do not interfere with the decoding process). Functions used include AH=30h (get DOS version), AH=35h (get interrupt vector), AH=48h (allocate memory) and many others.
Special mention must be made of the polymorphism in the RDA.Fighter.7408, 7802, and 7868 variants, because they use up to sixteen levels of decryptors which decode each other in turn. This makes detection rather tedious and timeconsuming. Another factor which adds to the annoyance is that the decryptors are often backwards-decrypting. That is, you cannot set a breakpoint and are forced to single-step or run through the decryptor, unless you have one of the more powerful debuggers such as Soft-Ice.
The secondary decryption process is one of the most interesting features with these viruses, which I will describe in some detail. Following their initial decryption, RDA.Fighter.5871, 5969, and 7408 install their own Int 0lh handlers for single-step execution of instructions. After the virus sets the trap (trace) flag, the virus' Int 0lh handler is called for every instruction executed.
Then follow some nasty instructions, which will never be executed directly - in fact, they would probably cause a system lockup if they were. These instructions can be run and debugged only when the virus' Int 01h handler is active in memory. Needless to say, this makes debugging with a standard debugger difficult. It is particularly hazardous to debug RDA.Fighter.5969 and 7408 because in subsequent activities they continue switching the trap flag off and on for dynamic correction of their own CRCs.
These two variants also contain some code in the decryptor that overwrites two sectors - one in each of two randomly-selected cylinders of head zero. This code will be run if there is an attempt to set a breakpoint in the code - the Int 0lh lookahead will see the CCh and direct program flow to the payload. In RDA.Fighter.5871, the equivalent code is slightly different, and due to a bug it will never run when the trap flag is set.
When Int 01h is called (on each instruction when the trap flag is on), the virus' handler deletes the stack contents, then checks the computer type from the byte at the BIOS address FFFF:000E. If this value is FCh, the computer is an AT, and the virus takes the real time from the CMOS buffer. If not, it takes the time from the system clock via Int 21h AH=2Ch. This is the main time reference used to create a random 16-bit number for decrypting the next fragment of code. The first 'nicety' of these viruses is hidden in this decoding routine.
After a lot of multiplications, divisions, and subtractions of a 32-bit random number, a 16-bit random number is left in the AX register. Masking this number with 1Eh gives a random pointer into a table containing instructions to be used in the decryption. There are two tables, each containing sixteen decrypting commands, of which a few are repeated. We will call them the primary and mirror tables, and they are presented below.
Primary table | Mirror table |
---|---|
XOR [BX], DX ROR [BX], CL ROL [BX], CL SUB [BX], DX ADD [BX], DX NEG [BX] NOT [BX] INC [BX] DEC [BX] ROR [BX], 1 ROL [BX], 1 XOR [BX], DX SUB [BX], DX ADD [BX], DX MEG [BX] NOT [BX] | NOT [BX] NEG [BX] SUB [BX], DX ADD [BX], DX XOR [BX], DX ROR [BX], 1 ROL [BX], 1 INC [BX] DEC [BX] NOT [BX] NEG [BX] SUB [BX], DX ADD [BX], DX ROR [BX], CL ROL [BX], CL XOR [BX], DX |
Decryption at this stage takes place in 16-iteration cycles. On the basis of the values stored in AX, a decrypting command is chosen from the primary table depending on the DI register contents. For example, if a bit is set at one particular bit position in AX, then the command residing in that position in the table is copied from the primary table to the decryptor. An initial constant, which in a decrypting cycle is dynamically varied according to a given law, is also copied there. This means that on each decrypt cycle AX will vary and so will the structure of the decryptor.
On termination of a decrypting cycle, the value of DI is set to the next command in the primary table. This operation is then carried out for all set bits in the 16-bit number. If no bit is set in any position, the decrypting cycle is skipped and the value of DI is increased to the next command. If the DI pointer overshoots the last command, its value is set to the first command in the table.
When this operation is completed, the virus begins to check whether all smart combinations have been deciphered, in an equally smart way. It computes the decrypted code fragment's CRC as a function of the fragment that was not encrypted. The CRC computation pattern is 'fan-shaped' from the decryption boundary and upwards, and a 48-bit number is computed downwards. CRCs are computed using all possible transfer commands ADD, XOR, etc.
RDA.Fighter.5969 and 7408 dynamically apply additional CRC corrections via Int 0lh. RDA.Fighter.7408 has four further forms of CRC check, which vary from copy to copy. The computed CRC is stored in the AX, BP and DX registers. BP is summed with the number stored in the virus body and corrected for finding the base point of the virus in memory. The offset of BP is compared with the value of DX. For correct decoding, the CRC storage offset must lie in the previously-encrypted fragment.
If the value of the CRC does not match that of DX, the decryption attempt is 'undone' using the complementary commands from the mirror table, thus reversing the procedure. This resets the incorrectly-decrypted fragment so the virus can repeat its attempt at 'correct' decryption, starting with the determination of a random number in AX using the primary table commands.
The last two modifications of RDA.Fighter.7802 and 7868 apply the same random decoding algorithm as before, but with vital changes. The virus writer named this improved mechanism the Random Decoding Algorithm Engine (RDAE). After a maximum of sixteen APE-generators, these viruses have a polymorphic random decoding mechanism. In other words, the commands that implement random decoding alternate with garbage commands which in no way affect the decoding process. All jumps, references, and offsets used in RDA decoding are corrected by the virus while this code is being generated. The primary table and its corresponding mirror table will be different in files infected by the 7802 and 7868 variants.
Detection of RDA.Fighter.7802 and 7868 is indeed a Herculean task. It is even more difficult to cure infected files, because full and correct decoding with a random key depends on the code implementation of the actual RDA decryptor created for that infection.
These viruses deciphered their own code within seven seconds on a test 386DX-33 MHz machine using 'RandomDecodingAlgoritm' (note the author's type). Surprisingly, on a Pentium 100 MHz machine, the tests took longer. I found the main determinant of decryption speed was not the random sequence of base values used in the encryptor, but a pseudo-random factor more dependent on processor speed (where randomness decreases as speed increases). I have RDA.Fighter replicants that refuse to decipher themselves on some processor types. My company's scanner, Dr. Web, does not use the system clock for determining random base values and thus deciphers the RDA viruses in 40-400 test cycles.
Once fully decrypted, the virus checks the word at 0000:00C5h (the Int 31h handler address in the Interrupt Table). If it contains the characters 'SF', and the byte at 0000:00C7h corresponds with the virus' version number (10h for RDA.Fighter.5871, 0Bh for 5969, 14h for 7408, 1Fh for 7802, and 20h for 7868), the virus assumes that it is already resident, and hands control to the host application.
If not, the virus traces Int 13h to its original handler. The Int 21h handler is found by searching the DOS segment for 9090E8h (NOP; NOP; CALL) or FA80FC6Ch (CLI, CMP AH, 6Ch) - code commonly found at the start of Int 21h.
When the virus finds the Int 13h original handler, it reads the second sector of the first hard disk into memory, writes the word 'SF' at the buffer start, and writes this buffer back to the second sector via the original Int 13h. Finally, this sector is again read via Int 13h, compared with what should have been written, the original is restored and the sector written back to the disk in its original form. If the comparison reveals a mismatch, the virus assumes that the disk is cached and does not intercept Int 13h during infection.
To go resident, the virus reduces the memory allotted to the infected program (Int 21h AH=4Ah), intercepts Int 21h, starts the infected program (Int 21h AX=4B00h), and removes itself from memory (Int 21h AH=49h). It gets the return code (Int 21h AH=4Dh), fetches the memory block it had taken from DOS, and completes the process (Int 21h AX=4C00) by installing its resident copy in memory.
The resident copy applies a powerful, noise-proof algorithm (Hamming code) for restoring its code, making debug breakpoints useless. Furthermore, as a 'trick' to mislead the researcher, the virus introduces an instruction for exiting from interrupt (IRET) in its Int 21h handlers. However, RDA.Fighter corrects the handler using its noise-proof restoration algorithm. In testing, the virus restored sixteen and more sequentially changed bytes in the virus body!
RDA.Fighter.7408, 7802, and 7868 control the find file functions (Int 21h AH=11h, 12h, 4Eh, 4Fh) to hide the size increment of infected files.
The resident part of the virus controls the DOS File Open (AH=3Dh) and Load/Execute program (AX=4B00h) functions. RDA.Fighter.5969 controls the file rename function (AH=5611) as well. When these functions are called, RDA.Fighter tries to infect the files indicated by these functions.
RDA.Fighter infects COM and EXE files 014096 bytes or larger, but avoids files matching *ES?.* (AIDSTEST.EXE). *WE?.* (WEB.EXE), or *AN?.* (COMMAND.COM). Files with the system attribute are not infected, and a prospect's file format is checked by the file extension and the 'MZ' signature of EXE files. While infecting flies, the virus determines the drive letter from the filename (Int 2Fh AX=121Ah) and the free space on the disk (Int 21h AH=36h). The virus uses the seconds value in the host's time-stamp as an infection flag (RDA.Fighter.5871 - 32 seconds; 5969 - 22; 7408 - 40; and 5969 and 7868 - 2).
During infection, the virus installs its own Critical Error handler (Int 24h), and Disk I/O handler (Int 13h). If there is no disk cache program, it also installs handlers for Int 0lh (single step execution of commands), Int 03h (breakpoint) and Int 2Ah (Microsoft Network) - these handlers are all a single IRET (Return from Interrupt).
Initially, the virus code occupies about 600 bytes. In infected files, along with the garbage commands, it takes up about 1500 to 4000 bytes.
It should also be noted that the virus encrypts a piece of the host program at a random offset. When loading an infected program, the virus decodes and restores this encrypted fragment. The offset and code fragment to encrypt is randomly chosen, in much the same way as it determines the random AX in the decryptor. This procedure was probably designed to prevent anti-virus tools employing generic disinfection methods (AdinfExt, TbClean and others) from restoring infected files.
RDA.Fighter.7408 also infects the MBR of hard disks. Its infection algorithm closely resembles that of the viruses SVC.4641, 4644, and 4677. RDA.Fighter.7408 replaces the first three bytes of the boot-loader in the MBR with JUMP NEAR PTR and writes 33h virus bytes in the zero-byte region. The 'tail' of the virus is written in fifteen sectors, beginning from the second sector of track zero, head zero.
The different variants contain various internal texts, and the following are occasionally displayed:
RDA.Fighter.7408: Stealth Fighter 2.0 : New Aggression. RDA.Fighter.7802: Stealth Fighter DEMO Part (3.1): Enemy Unknown. RDA.Fighter.7868: Stealth,Fighter DEMO Part (3.2); Next mutation 06/09/95.
Analysing RDA.Fighter was quite a challenge, but I succeeded in adding full detection and disinfection to Dr. Web.
Aliases | APE.RDA |
---|---|
Variants | RDA.Fighter.5871, 5969, 7408, 7802 and 7868 |
Type | Memory resident, polymorphic file infector. RDA.Flghter.7408 is multi-partite. |
Infection | COM and EXE files. RDA.Fighter.7408 MBR also. |
Self-recognition in Files | Seconds field of file time-stamp. See text for details. |
Self-recognition in Memory | The string 'SF' at address 0000:00C5. |
Hex Pattern in Files | None possible. |
Hex Pattern in Memory | RDA.Fighter.5871: 50FE C43D 004C 740B 80FC 3E74 0658 2EFF 2EEF 17E8 E50F 2EC7 RDA.Fighter.5969: 50FE C43D 004C 7410 80FC 3E74 0B80 FC57 7406 582E FF2E 5118 RDA.Flghter.7408: 50FE C43D EEEF 7505 EBDE 0158 CF3D 004C 745B 80FC 3E74 5680 RDA.Fighter.7802: 50FE C43D EEEF 7505 E88B 0158 CF3D 004C 745B 80FC 3E74 5680 RDA.Fighter.7868: 50FE C43D EEEF 7505 E88E 0158 CF3D 004C 745B 80FC 3E74 5680 |
Intercepts | lnt 21h for infection. |
Payload | Displays messages (see text for details). |
Removal | Using a clean system, identify and restore infected files. |