/* В 1988 году и к нам пришла эпидемия компьютерных вирусов. В связи с этим весьма актуально научиться с ними бороться. Данная программа предназначена для лечения дисков от двух типов вирусов и обнаружения наличия неизвестных вирусов определенного класса. По мере обнаружения вирусов новых типов ее довольно просто можно приспосабливать для борьбы и с ними. Для начала попробуем дать определение этой болезни. Назовем компьютерным вирусом программный фрагмент, обладающий следующими свойствами: - способность к размножению не только в одной ЭВМ, но и к заражению других; - наличие более или менее значительного инкубационного периода, т.е. интервала от момента заражения машины до какого либо открытого проявления. При отсутствии способности к размножению можно говорить только о наличии "бомбы" - скрытой в программе пакости, которая должна сработать через некоторое время, для отмщения врагам, не оценившим таланты автора программы. Из необходимости наличия способности к размножению следует обязательная способность вируса заражать программные модули типа COM или EXE. Это связано с тем, что именно модули таких типов обычно копируют с одной машины на другую. При отсутствии у вируса инкубационного периода сравнительно легко можно определить источник заражения и, следовательно, появляется реальная опасность для автора вируса. Заметим, что для автора вируса необходимо наличие двух способностей: - уметь программировать на ассемблере и иметь основные понятия об операционной системе; - уметь получать удовольствие, делая людям пакости. К счастью сочетание обоих этих достоинств встречается в людях достаточно редко. Принцип организации любого типа вируса состоит в том, что при запуске зараженной программы управление получает именно вирус. Первым делом он обычно пытается заразить другие программы, а затем обеспечить правильную работу программы. Заражение COM-модулей состоит в дописывании себя в конец программного файла и замене начала (первых трех байт) на команду передачи управления на собственное начало. При этом старое значение замененных трех байтов сохраняется для обеспечения правильной работы зараженной программы в течении инкубационного периода. Для лечения зараженных модулей необходимо только знать, где в вирусе спрятано старое значение трех замененных байтов и какова длина вируса. Знание прочих подробностей о его организации отнюдь не необходимо. Программа написана для транслятора "Turbo-C". Возможно использование и Microsoft-C версии не ниже 5.0. При этом необходимо заменить функции findfirst и findnext на _dos_findfirst и _dos_findnext, а также имена используемой с ними структуры и ее полей. Некоторые (но далеко не все) места, которые необходимо изменить для перехода на MS-C, отмечены в комментариях. Общая схема организации программы заключается в организации просмотра всех каталогов (directory) диска, проверке всех имеющихся COM и EXE файлов на наличие в них известных вирусов и лечение больных. Хотя программа умеет работать только с модулями типа COM, приходится проверять и модули с расширением EXE, поскольку тип модуля определяется не расширением, а внутренним форматом. Кроме того для всех модулей, начинающихся с команды JMP, вычисляется разность между адресом перехода по этой команде и длиной файла и ведется статистика этих разностей. При многократном повторении одинаковых значений этой разности можно предполагать наличие вируса. Впрочем, не так сложно сделать вирус, который не обнаруживаются таким способом. */ #include #include #include #include #include #include #include /*Дипазон сбора статистики о разностях (пределы длин вирусов)*/ #define MIN_VIRUS 0x0100 #define MAX_VIRUS 0x2000 /*Критическое число повторений разности для тревоги*/ #define critical_no 3 char path[10][13], /* имена отдельных уровней каталогов */ full_path[64], /* буфер для полного пути */ file_name[64], /* буфер для полного имени файла */ disk[4]; /* структуры для функций поиска в каталоге */ /* для MS-C 5.0 вместо ffblk нужно find_t */ struct ffblk block, dbl[10]; #define dblock (dbl[level]) int fixup=0, and_exe=0, by_date=0; int *len_index, second_pass=0; int resident_virus=0; /* обнаружен вирус, остающийся в памяти */ void (far * restart)()=(void far *)0xf000fff0L; main(int argc, char *argv[]) { char *wc, *extention, *extpos; int i,fff, dff, w, /* счетчики для отчета о проделанной работе */ total=0, totvir=0, totcorr=0, totkill=0, totrest=0, totqq=0, level=0; /* текущий уровень каталога (0 - корневое) */ wc=NULL; for (i=1; i0); /* повторение цикла для нового каталога */ if(second_pass) { /* об этой переменной - ниже */ printf("Вопросы по тел. Москва - 292.40.76 - Лозинский Д.Н."); goto end_of_program; } printf("\n\ Всего проверено %d\n\ Заражено: %d\n",total,totvir); if(fixup) printf("\n\ Исправлено: %d\n\ Стерто: %d\n\ Не удалось исправить -\n\ всего: %d\n\ опасных: %d\n",totcorr,totkill,totrest+totqq,totqq); /* просмотр массива со статистикой разностей */ for(i=MIN_VIRUS; i critical_no ) { printf("Длину хвоста =%X имеет %d модулей\n",i,w); second_pass=1; /* статистика неприятная */ } /* пойдем на повторный просмотр диска для вывода */ /* имен всех подозрительных файлов */ level=0; if (second_pass) goto for_second; end_of_program: if(resident_virus) { printf("\n\ Обнаружен вирус, который делает себя резидентным в памяти\n\ и заражает все загружаемые программы !\n\ Для надежной очистки машины будет произведена перезагрузка системы.\n\ Если считаете это излишним, нажмите \"N\", но пеняйте потом на себя!\n"); i=getch() | 0x20; if(i != 'n') { *(int far *)0x472L = 0x1234; restart(); } } } /* Всевозможная работа с одним файлом - проверка, лечение, стирание, вывод подозрительных. Параметры: полное имя каталога структура, используемая в функциях поиска */ test_virus(char *path, struct ffblk block) { unsigned int flen, i, jmpadr, vir_adr, vir_len, access, drive, exit_code, basadr; int vir_no, LEN_BAS, BEF_LEN; unsigned char filename[64], oldstart[3], *VIR_BEG, *msg="", *type_code, *wc=NULL; long wl; /* Это команда ухода на перезагрузку системы, которую пишет в начало некоторых модулей один из вирусов. Такие модули приходится стирать. */ char *trup="\xEA\xF0\xFF\x00\xF0"; /* массивы данных, используемых при опознании вирусов */ static unsigned len_jmp[]={0x28B, 0x6A7 }, len_bas[]={0x8F , 0 }, before_len[]={0 , 1 }; static char *vir_beg[]={"\x51\xBA","\xFA\x8B\xEC\xE8"}; #define no_of_vir 2 #define near_jmp 0xE9 /* образуем полное имя файла */ strcpy(filename,path); strcat(filename,block.ff_name); outfname(filename); /* вывод имени на экран */ flen = block.ff_fsize; /* длина файла */ /* если файл только для чтения, пока так его и откроем, а иначе сразу и для записи, на случай необходимости исправления */ access = ( block.ff_attrib & FA_RDONLY ) ? O_RDONLY : O_RDWR; drive = open(filename, access | O_BINARY); if (drive == -1) return 0; /* открыть не удалось - на выход */ read(drive, oldstart, 5); /* читаем первые 5 байт */ if ( !memcmp(oldstart,"MZ",2) ) goto EXE; /* это признак EXE-программ*/ if ( !memcmp(oldstart,trup,5) ) goto TRUP;/* модуль испорчен вирусом */ /* если первая команда - не переход, считаем, что вируса нет */ if ( oldstart[0] != near_jmp ) goto StartNotJmp; /* из команды перехода изымаем адрес */ jmpadr = * (int*) (oldstart+1); /* при втором просмотре диска - на проверку по массиву статистики */ if(second_pass) goto for_second; /* первый контроль на известные вирусы по разности длины файла и адреса перехода */ for ( vir_no=0; vir_no=MIN_VIRUS && vir_len=MIN_VIRUS && vir_len critical_no ) printf("%s - подозрителен\n",filename); return 0; } /* замена атрибутов файла */ /* библиотека MS-C 5.0 имеет такую функцию под именем _dos_setfileattr */ setfattr(char *filename, int attrib) { union REGS inregs, outregs; struct SREGS segregs; inregs.x.ax = 0x4301; inregs.x.cx = attrib; inregs.x.dx = FP_OFF(filename); segregs.ds = FP_SEG(filename); intdosx( &inregs, &outregs, &segregs); } /* В зависимости от первого параметра сохраняется (1) или восстанавливается (0) позиция курсора. Позиции хранятся в стеке на 10 значений, хотя для данной программы нужно только одно. При сохранении позиции и ненулевом втором параметре стирается строка, содержащая курсор. Для MS-C в этой функции придется сделать много изменений */ CurSavRest(int save, int clear) { static int pointer=0, cstack[10]; union REGPACK regs; if(save) { regs.r_ax=0x300; regs.r_bx=0; intr(0x10, ®s); cstack[pointer]=regs.r_dx; if(clear) { regs.r_cx=regs.r_dx; regs.r_dx &= 0xFF00; regs.r_dx += 79; regs.r_ax = 0x600; regs.r_bx = 0x700; intr(0x10, ®s); } if(pointer<9) pointer++; } else { if(pointer>=0) pointer--; regs.r_ax=0x200; regs.r_bx=0; regs.r_dx=cstack[pointer]; intr(0x10, ®s); } } /* вывод имени файла на экран с возвратом курсора на старое место */ outfname(char *filename) { CurSavRest(1,1); printf(filename); CurSavRest(0,1); } /* если файл был открыт только для чтения, у него меняются атрибуты и он открывается заново на чтение и запись */ int att_reopen(int drive, char *filename, char attrib, char **msg, char acc) { if(acc == O_RDONLY) { close(drive); setfattr(filename, attrib & ~(FA_RDONLY|FA_SYSTEM)); drive=open(filename, O_RDWR | O_BINARY); if (drive==-1) *msg = "!!! ОШИБКА ДОСТУПА !!!"; } return drive; } /* Относительно несложно написать программу, которая обнаруживает появление на диске почти любого вируса. Для этого нужно воспользоваться тем, что при заражении программы необходимо, чтобы вирус начинал работу до зараженной программы. При этом практически во всех случаях необходимо внести некоторые изменения в начале программного модуля. Для программ формата COM вместо замены первых трех байт, как это происходит в вирусах, с которыми умеет бороться данная программа, автор выруса может воспользоваться тем, что почти все программы формата COM сами начинаются с команды перехода. В этом случае подстановка перехода в вирус может производиться в том месте, куда делается первый переход. Для программ в формате EXE возможность для вируса обойтись без изменения первых 32 байт программного файла, в которых содержится основная управляющая информация, представляется весьма проблематичной. Из этих фактов следует идея организации программы слежения за проникновением на диск какого либо вируса. Программа должна вести специальный файл, содержащий для всех программных файлов параметры, которые заведомо должны измениться при заражении. Эти параметры могут, например, включать длину файла, сумму первых 32 байт и, для COM-программ, сумму нескольких байт за первым переходом. Эта информация должна ежедневно сличаться с текущим состоянием диска и обо всех замеченных расхождениях должно выдаваться сообщение. */