mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-22 19:36:11 +00:00
328 lines
14 KiB
NASM
328 lines
14 KiB
NASM
INTERRUPTS SEGMENT AT 0H
|
|
ORG 9H*4 ;holds the address of its service routine
|
|
KEYBOARD_INT LABEL DWORD
|
|
ORG 21H*4 ;This is to use INT 21H
|
|
INT_21H LABEL DWORD ;which is the DOS function call interrupt
|
|
INTERRUPTS ENDS
|
|
|
|
ROM_BIOS_DATA SEGMENT AT 40H ;BIOS statuses held here, also keyboard buffer
|
|
|
|
ORG 1AH
|
|
HEAD DW ? ;Unread chars go from Head to Tail
|
|
TAIL DW ?
|
|
BUFFER DW 16 DUP (?) ;The buffer itself
|
|
BUFFER_END LABEL WORD
|
|
|
|
ROM_BIOS_DATA ENDS
|
|
|
|
CODE_SEG SEGMENT
|
|
ASSUME CS:CODE_SEG
|
|
ORG 100H ;ORG = 100H to make this into a
|
|
;".com" file
|
|
FIRST: JMP LOAD_INT_21H
|
|
|
|
COPY_RIGHT DB '(C)1985 S Holzner'
|
|
BYPASS_FLAG DB 0 ;Bypass our checking for #? 1=Yes
|
|
ZERO_FLAG DB 0 ;Was there a zero in filename? 1=Yes
|
|
CR_FLAG DB 0
|
|
DTA DD ? ;The old disk transfer area address
|
|
OLD_INT_21H DD ? ;Address INT 21H uses normally
|
|
OLD_KEYBOARD_INT DD ? ;Location of old kbd interrupt
|
|
COUNT DW ?
|
|
FCB_OFFSET DW ? ;Offset of given filename to be deleted
|
|
FCB_SEG DW 0 ;Segment address of the same.
|
|
COMMAND_INDEX DW 0
|
|
DEL_Z DB 'DEL/Z',0
|
|
CRLF DB 13,10,'$' ;Carriage return, linefeed for messages
|
|
MSG DB ' ZEROED AND DELETED.',13,10,'$' ;The message
|
|
ERR DB 'Error deleting $' ;Error message
|
|
|
|
DELZ PROC FAR ;The function call interrupt will now come here.
|
|
|
|
PUSHF ;Save flags first (will get changed by CMPs)
|
|
CMP AH,13H ;Are we deleting?
|
|
JNE JUMP ;No, jump to function call Int and do not return
|
|
CMP ZERO_FLAG,1 ;Are we supposed to zero?
|
|
JNE JUMP ;If not, don't
|
|
TEST BYPASS_FLAG,1 ;We are deleting. Is bypass on?
|
|
JZ DEL_CHECK ;No, check if we should delete file.
|
|
JUMP: POPF ;Restore flags
|
|
JMP OLD_INT_21H ;Jump to function call Int. (CALL won't work).
|
|
DEL_CHECK: ;DS:DX are pointing to filename to be deleted (from delete call)
|
|
PUSH BX ;Save all used registers to be polite
|
|
PUSH CX
|
|
PUSH DX
|
|
PUSH SI
|
|
PUSH DI
|
|
PUSH AX ;Save AX last since will pop first and return status in it
|
|
|
|
MOV FCB_OFFSET,DX ;Save address of the file-to-be-deleted's FCB
|
|
MOV FCB_SEG,DS ;Ditto for the segment address
|
|
|
|
MOV AH,2FH ;Now get Disk Transfer Area (DTA) address
|
|
INT 21H ;This will work since AH is not equal to 13H
|
|
|
|
MOV DTA,BX ;Store DTA address Low part
|
|
MOV DTA[2],ES ;Ditto High part
|
|
PUSH DS ;Save file-to-be-deleted's FCB's Segment address
|
|
MOV AH,1AH ;Put the new DTA in our Program Segment Prefix,
|
|
MOV DX,80H ; CS:0080H (CS came from INT 21H vector we set)
|
|
PUSH CS ;Now move DS into CS to set DTA
|
|
POP DS
|
|
INT 21H ;Set Disk Transfer Area (DTA)
|
|
POP DS ;Restore Segment address of given file's FCB
|
|
|
|
MOV DX,FCB_OFFSET ;Restore the given file's FCB's address low part
|
|
MOV AH,11H ;Ask for DOS service 11H, which asks for the
|
|
INT 21H ; first match to the given file's FCB
|
|
|
|
TEST AL,1 ;Was a match found?
|
|
JZ TOP ;Yes, start checking if we should delete.
|
|
POP AX ;No, return status 0FF (not found) in AL
|
|
MOV AL,0FFH
|
|
JMP NONE_FOUND ;Over and out.
|
|
TOP:
|
|
MOV SI,81H ;the matching file's FCB is in DTA from search
|
|
MOV DI,0C0H ;We will move the name to print and scan for #
|
|
MOV CX,0BH ;11 characters per file
|
|
PUSH DS ;Get ready for string move, set up DS and ES
|
|
PUSH CS ;Set them both to CS (use program segment
|
|
POP DS ; prefix area)
|
|
MOV DX,80H ;Point to match to open file
|
|
MOV AH,0FH ;Select the correct DOS call
|
|
INT 21H
|
|
CMP AL,0 ;If error opening file, exit
|
|
JNE ERROR
|
|
MOV BX,80H ;Set record size to 512
|
|
MOV WORD PTR [BX+14],512
|
|
MOV AX,[BX+16] ;Find the file's size in sectors
|
|
XOR DX,DX
|
|
TEST AX,511 ;Do we have to add an add'l sector?
|
|
JZ SHIF ;No, do the shift
|
|
INC DX ;Yes, add 1
|
|
SHIF: MOV CL,9 ;Divide by 512
|
|
SHR AX,CL
|
|
MOV COUNT,AX ;Store in Count
|
|
ADD COUNT,DX ;And add possible add'l sector
|
|
MOV AX,[BX+18] ;Now for the high part of size
|
|
MOV CL,7 ;Mult by 65536 div by 512
|
|
SHL AX,CL
|
|
ADD COUNT,AX ;And add to what we already have
|
|
MOV DX,80H ;Now prepare for sequential write
|
|
MOV CX,COUNT ;Do COUNT sectors
|
|
MOV AH,15H
|
|
FILL: INT 21H ;Fill with copies of this prog.
|
|
CMP AL,0 ;Error writing?
|
|
JNE ERROR ;Yes, jump to ERROR
|
|
LOOP FILL ;No, go back for next one
|
|
MOV AH,10H ;Close the file now.
|
|
INT 21H
|
|
CMP AL,0 ;Error closing?
|
|
JNE ERROR ;Yep, go to ERROR
|
|
MOV AH,13H ;No, delete the file at last (Whew)
|
|
MOV BYPASS_FLAG,1 ;Don't intercept this call
|
|
INT 21H
|
|
MOV BYPASS_FLAG,0
|
|
CMP AL,0 ;Everything OK?
|
|
JNE ERROR ;No, go to ERROR
|
|
CALL PRINT_NAME ;Yes, print the name
|
|
LEA DX,MSG ;And the zeroed message
|
|
MOV AH,9
|
|
INT 21H
|
|
JMP SAVE
|
|
ERROR: MOV AH,10H ;First make sure file is closed
|
|
INT 21H
|
|
LEA DX,ERR ;Then print error message and go on
|
|
MOV AH,9 ; to next file
|
|
INT 21H
|
|
CALL PRINT_NAME
|
|
SAVE: POP DS ;Get segment of original given file's FCB
|
|
MOV DX,FCB_OFFSET ;Search for next match -- point to original FCB
|
|
MOV AH,12H ;Search for next match
|
|
INT 21H
|
|
TEST AL,1 ;Was a match found?
|
|
JNZ OUT
|
|
JMP TOP
|
|
|
|
OUT: POP AX ;At least one file deleted, set AL acordingly,
|
|
MOV AL,0 ; which means set it to 0
|
|
NONE_FOUND:
|
|
PUSH DS ;Now we have to reset the Disk Transfer Area
|
|
PUSH AX ;Save AX since it contains success status
|
|
MOV AH,1AH ;Function call 1AH will do want we want
|
|
MOV DX,DTA[2] ;Get original DTA's segment into DS
|
|
MOV DS,DX
|
|
MOV DX,DTA ;Now get offset inside that segment of same
|
|
INT 21H ;And reset DTA
|
|
POP AX ;Restore AX with status
|
|
POP DS ;And DS with original DS
|
|
|
|
POP DI ;And restore the other registers
|
|
POP SI
|
|
POP DX
|
|
POP CX
|
|
POP BX
|
|
POPF ;We musn't forget our original PUSHF
|
|
IRET ;An interrupt deserves an IRET
|
|
|
|
DELZ ENDP ;And that's it
|
|
|
|
PRINT_NAME PROC NEAR ;This small subroutine just prints
|
|
MOV BX,80H+1 ; file's name from the FCB
|
|
MOV AH,2 ;Use DOS service 2
|
|
MOV CX,11 ;Print all 11 letters
|
|
PRINT: MOV DL,[BX] ;Printing loop
|
|
INT 21H
|
|
INC BX ;Get next letter
|
|
LOOP PRINT
|
|
RET ;And return
|
|
PRINT_NAME ENDP
|
|
|
|
READ_KEY PROC NEAR ;The keyboard interrupt will now come here.
|
|
ASSUME CS:CODE_SEG
|
|
PUSH AX ;Save the used registers for good form
|
|
PUSH BX
|
|
PUSH CX
|
|
PUSH DX
|
|
PUSH DI
|
|
PUSH SI
|
|
PUSH DS
|
|
PUSHF ;First, call old keyboard interrupt
|
|
CALL OLD_KEYBOARD_INT
|
|
|
|
ASSUME DS:ROM_BIOS_DATA ;Examine the char just put in
|
|
MOV BX,ROM_BIOS_DATA
|
|
MOV DS,BX
|
|
MOV BX,TAIL ;Point to current tail
|
|
CMP BX,HEAD ;If at head, kbd int has deleted char
|
|
JNE CR ;So leave
|
|
JMP NOCR
|
|
CR: SUB BX,2 ;Point to just read in character
|
|
CMP BX,OFFSET BUFFER ;Did we undershoot buffer?
|
|
JAE NO_WRAP ;Nope
|
|
MOV BX,OFFSET BUFFER_END ;Yes -- move to buffer top
|
|
SUB BX,2
|
|
NO_WRAP:MOV DX,[BX] ;Char in DX now
|
|
|
|
CMP DL,'Z' ;Make sure we are in upper case
|
|
JBE CHAROK
|
|
SUB DL,'a'-'A' ;Make REALLY sure.
|
|
CHAROK: PUSH CS
|
|
POP DS ;Point to Code Seg with DS
|
|
ASSUME DS:CODE_SEG
|
|
CMP CR_FLAG,1 ;CR_Flag resets Zero_Flag
|
|
JNE CHECK
|
|
MOV CR_FLAG,0 ;Reset CR_Flag
|
|
MOV ZERO_FLAG,0 ;And Zero_Flag
|
|
MOV COMMAND_INDEX,0 ;As well as Command_Index
|
|
CHECK: LEA SI,DEL_Z ;Check the typed character
|
|
ADD SI,COMMAND_INDEX ;Find place in test string
|
|
CMP DL,[SI] ;Match?
|
|
JNE NOSET ;If not, forget it
|
|
INC COMMAND_INDEX ;Match! Move to next char next time
|
|
|
|
CMP DL,'/' ;For DOS 3+, delete the /Z from buffer
|
|
JNE NOTSLSH
|
|
ASSUME DS:ROM_BIOS_DATA ;Examine the char just put in
|
|
MOV CX,ROM_BIOS_DATA
|
|
MOV DS,CX
|
|
MOV TAIL,BX ;Erase character from buffer
|
|
MOV AH,10 ;Get ready to print the character
|
|
MOV CX,1
|
|
MOV AL,DL
|
|
XOR BX,BX ;Display page 0
|
|
INT 10H ;Print the '/'
|
|
MOV AH,3 ;Now prepare to move cursor over 1
|
|
INT 10H ;Get present position
|
|
ADD DX,1 ;Add 1
|
|
MOV AH,2 ;And reset cursor
|
|
INT 10H
|
|
|
|
NOTSLSH:CMP DL,'Z' ;For DOS 3+, delete the /Z from buffer
|
|
JNE NOTZ
|
|
ASSUME DS:ROM_BIOS_DATA ;Examine the char just put in
|
|
MOV CX,ROM_BIOS_DATA
|
|
MOV DS,CX
|
|
MOV TAIL,BX ;Erase character from the buffer
|
|
MOV AH,10 ;Prepare to type the 'Z'
|
|
MOV CX,1
|
|
MOV AL,DL
|
|
XOR BX,BX
|
|
INT 10H
|
|
MOV AH,3 ;And now adjust the cursor
|
|
INT 10H ;Moving it to the left 1 space
|
|
ADD DX,1
|
|
MOV AH,2
|
|
INT 10H
|
|
|
|
NOTZ: ASSUME DS:CODE_SEG
|
|
PUSH CS
|
|
POP DS
|
|
|
|
CMP BYTE PTR [SI+1],0
|
|
JNE NOSET
|
|
MOV ZERO_FLAG,1
|
|
MOV COMMAND_INDEX,0
|
|
NOSET: MOV CR_FLAG,0
|
|
CMP DX,1C0DH
|
|
JNE NOCR
|
|
MOV CR_FLAG,1
|
|
NOCR: POP DS
|
|
POP SI
|
|
POP DI
|
|
POP DX
|
|
POP CX
|
|
POP BX
|
|
POP AX
|
|
IRET
|
|
READ_KEY ENDP
|
|
|
|
LOAD_INT_21H PROC NEAR ;This subroutine installs DELZ
|
|
|
|
ASSUME DS:INTERRUPTS ;Now set DS to point to INTERRUPTS seg.
|
|
MOV AX,INTERRUPTS
|
|
MOV DS,AX
|
|
|
|
MOV AX,KEYBOARD_INT ;Get the old interrupt service routine
|
|
MOV OLD_KEYBOARD_INT,AX ;address and put it into our location
|
|
MOV AX,KEYBOARD_INT[2] ;OLD_KEYBOARD_INT so we can call it.
|
|
MOV OLD_KEYBOARD_INT[2],AX
|
|
|
|
MOV KEYBOARD_INT,OFFSET READ_KEY
|
|
MOV KEYBOARD_INT[2],CS ;routine into the keyboard interrupt
|
|
|
|
MOV AX,INT_21H ;Get the original function call INT's
|
|
MOV OLD_INT_21H,AX ;address and put it into our location
|
|
MOV AX,INT_21H[2] ;OLD_INT_21H so we can still jump there
|
|
MOV OLD_INT_21H[2],AX
|
|
|
|
MOV INT_21H[2],CS ;Install our delete filter's address
|
|
MOV INT_21H,OFFSET DELZ ; as new function call INT
|
|
|
|
PUSH CS ;Now point to CS in preparation for
|
|
POP DS ; terminate and stay resident call
|
|
|
|
MOV DX,OFFSET LOAD_INT_21H ;Set up everything but LOAD_INT_21H to
|
|
INT 27H ;stay and attach itself to DOS
|
|
|
|
LOAD_INT_21H ENDP ;End of loading subroutine
|
|
|
|
CODE_SEG ENDS ;End of Code Segment
|
|
|
|
END FIRST ;END "FIRST" so 8088 will go to FIRST first.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|