TURBO PASCAL

Новости

Программы   

Turbo Pascal 

Игры

Документация   

Странности

FAQ

Ссылки

Форум

Гостевая книга

Рассылка

Благодарности

Об авторе

 

 

П8. КОМПЛЕКС ПРОГРАММ SETFAG.PAS/FAG.ASM П8.1. Установщик фага SetFag.pas

Описание программы см. п. 6.4.1.

/

Эта программа устанавливает автономный антивирус-фаг на любой ЕХЕ-файл или

группу файлов. Код антивируса берется из файла FAG.PRG (или загрузочной част

любого другого ЕХЕ-файла). Формат обращения:

SetFag <EXE> [/F:<FAG>] [/NOBAK]

<ЕХЕ> - имя защищаемого ЕХЕ-файла (допускается групповое имя с использованием "*" и "?"; можно указать маршрут поиска; по умолчанию назначается расширение ЕХЕ) ;

<FAG> - имя ЕХЕ-файла с кодом фага; если ключ не задан, код берется из файла FAG.PRG

Ключ /NOBAK блокирует создание резервных копий защищаемых файлов (с расширением ВАК) .

}

Uses F_Anti,DOS;

const

LT » 63; {Длина строк сообщения}

NT - 20; (Количество строк сообщения}

Text: array [I..NT] of String [LT] °(

'Эта программа устанавливает автономный антивирус-фаг на любой',

'ЕХЕ-файл или группу файлов. Код антивируса берется из файла',

'FAG.PRG (или загрузочной части любого другого ЕХЕ-файла). ',

'Формат обращения:",

' SetFag <EXE> [/F:<FAG>] [/NOBAK]',

'<ЕХЕ> - имя защищаемого ЕХЕ-файла (допускается групповое имя',

' с использованием "*" и "?"; можно указать маршрут по-',

' иска; по умолчанию назначается расширение ЕХЕ);',

'<FAG> - имя ЕХЕ-файла с коДом фага; если ключ не задан» Код',

' берется из файла FAG.PRG;',

'Ключ /NOBAK блокирует создание резервных копий защищаемых фай-',

'лов (с расширением ВАК).',

'Примеры:',

' eetfag ffiyprog'^

'Файл MYPROG.EXE будет содержать защищенную программу,MYPROG.ВАК',

'- исходную. Код фага будет взят из файла FAG.PRG',

' setfag d:\dir\* /nobak /f:myfag.exe',

•Защищаются все ЕХЕ-файлы из каталога D:\DIR, резервные копии не',

' создаются, код фага берется из MYFAG.EXE.',

' (С) В.В.Фаронов, 1992');

type

HeadType » record case Byte of 1: (

Sign : Word? {Сигнатура 'MZ' - $5A4D)

PartPag: Word; fVaoa'b йвпалного сектора}

PageCnt: Word; {Количество секторов)

ReioCnt: Word; (Количество элементов в таблице перемещения)

HdrSize: Word; {Длина заголовка в параграфах)

MinMem : Word; (Минимальный размер кучи)

MaxMem : Word; {НаксчмальныЯ рааивр кучи)

Re^oSS : Word; {Начальное значение сегмента стека SS}

ExeSP : Word; {Начальное значение указателя стека SP)

ChkSum : Word; {Контрольная сумма всех слов файла)

ExelP : Word; {Смещение точки запуска программы)

ReloCS : Word; (Начальное значение сегмента кода CS)};

2:W: array [1..12] о- Word) end;

ТКеу = record

Head.24: HeadType; {24 байта эталонного заголовка) Starts: Word; (OTHOWt'enlstaM сёТиент} StartO: Word; (и смещение точки запуска программы) Leng24: LongInt; {Длина незараженной программы минус 24 байта) Key : Word; {Клвч гжфрваки) end;

ByteArr = array [l..Max!nt] of Bytey var

H,HFag: HeadType;

HH : TKey;

fe, {ЕХЕ-файл} ft: file; (Файл с кодой фага} LFag: LongInt; {Длина кода фага) CFag: ^ByteArr; (Указатель на код фага} NE: String; (Имя исходного ЕХЕ-файла} NF: String; (Имя файла с кодом фага) S: SearchRec;

d: DirStr;

n: NameStr;

e: ExtStr;

LS: LongInt; {Смещение для фага} const

FagName = 'Fag.pre'; (Имя файла-фага BakFile : Buolean ~ True; (Признак ВАК-файлов} f., —————_-——-——;

Procedure Help;

{Выводит справку об использовании программы и завершает работу) const

Bord = 14;

var

к: Integer;

begin

for k := 1 to LT+Bord+1 do Write ('*');

WriteLn;

for k := 1 to NT do

WriteLn('*',' ':Bord div 2,

Text[k],1*1:LT+Bord div 2-Length(Text[k])) ;

for k := 1 to LT+Bord+1 do HriteC*') ;

WriteLn;

Halt

end; {Help} /————————————;

Procedure CheckParam;

{Проверяет параметры вызова и дает диагностику, если не задано имя защищаемого файла} var

k: Integer;

s,n,e: String;

Function UpStringts: String): string;

var

k: Integer;

begin

for k := 1 to Length(s) do s[k] := UpCase(s[k]);

UpString := s end; {UpString} begin {CheckParam} NE : = " ;

NF :== FagMame;

for k :° 1 to ParamCount do begin

s := UpString(ParamStr(te));

if s='/NOBAK" then

BakFile := False else

if pos C/F:',s)<>0 then

NF := copy(s,4,Length(s)-3) else

if NE='' then

NE := S else begin

WriteLn <'Ошибка в обращении') ;

Help end end;

if NE = '' then Help;

if pos (' : ',NF)=0 then

begin {He указан путь к фагу} s := ParamStr(0) ;

FSplit(s,s,n,e) ;

NF := s+NF {Взять путь к SetFag} end end; {CheckParam} ^————————————;

Procedure NotAccessis: String);

{Выводит сообщение 'Нет доступа...' и справку} begin

WriteLn('Нет доступа к файлу ',s);

Help

end; {NotAccess} ^————————-.. ..„^

Function CheckReadfs: String; k,n: Word):Boolean;

(Проверяет корректность чтения и дает сообщение, если требуемое количество К байт не равно прочитанному N байт} begin

if kon then begin

WriteLn('Ошибка чтения из файла ',s);

CheckRead := False end else

CheckRead :°= True end; {CheckRead} ^————————————;

Function CheckWrite(s: String; k,n: Word): Boolean;

{Проверяет корректность записи и дает сообщение, если требуемое количество К байт не равно записанному N байт} begin

if kon then begin

WriteLn('Ошибка записи в файл ',s);

CheckWrite := False end else

Checktorite :=» True end; {CheckRead} /————————————;

Procedure OpenFag;

{Открывает файл-фаг и считывает его код в динамическую память} var

k: Word;

begin

Assign(ff,NF) ;

{$!-}

Reset (ff,l) ;

{$!+}

if IQResult <> 0 then NotAccess(NF) ;

BlockRead(ff,HFag,SizeOf(HFag),k) ;

if not CheckRead(NF,SizeOf(HFag),k) then Halt;

LFag := File3ize(ff)-HFag.HdrSize*16-SizeOf(TKey);

GetMem(CFag,LFag) ;

Seek(ff,FileSize(ff)-LFag),•

BlockRead(ff, СЕ-ад", LFag, k) ;

if not CheckRead(NF,LFag,k) then Halt;

Close(ff) end; {OpenFag} _•————————————/

Function CheckFile: Boolean;

{Проверяет возможность установки защиты и готовит новый заголовок файла} va.s

г: registers;

s: String [1];

k: Word;

p: ^yteArr;

label

NotFag^ const

ЬхЫ"'Файл ';

txt2=' содержит оверлеи, архив или отладочную информацию.';

begin

CheckFile := False;

BlockRead(fe,H,SizeOf(H),k); {Читаем заголовок} if not CheckRead(NE,SizeOf(H),k) then

goto Not Fag; {Ошибка чтения} if H.Sign <> $5A4D then begin

WriteLn('Файл ',NE,' не является ЕХЕ-программой!');

NotFag:

WriteLn('Защита не установлена');

Exit end else begin

if FileSize(fe)-H.HdrSize*16>LFag then {Проверяем и блокируем повторную защиту} begin

GetMem(p,LFag);

Seek(fe,FileSize(fe)-LFag) ;

BlocltReadffetp^LFag,k) ;

k := LFag div 2; {Сравниваем, начиная с середины кода} repeat

inc (k) until (k>LFag) or (p" [k] OCFag^ [k] ) ;

FreeMem(p,LFag) ;

if k > LFag then begin

WriteLn('Файл ',NE,' уже защищен!');

Exit end end;

{Проверяем размер программы}

Intr($12,r); {Получаем объем памяти} LS := FileSize(fe)+LFag+SizeOf(HH)+15;

if r.AX*lO24.0-LS-H.MinMem*16 <= 0 then begin

WriteLn('Файл ',NE,' слишком велик!') ;

goto NotFag end;

if (FileSize(fe)+15) div 16>H.PageCnt+H.HdrSize then begin

WriteLn(txtl,NE,txt2) ;

Write('Устанавливать защиту (У/М,умлч.У)? ');

ReadLn(s);

if s»1 ' then s : - ' Y ' ;

if UpCase(s[l]) = 'N' then

goto NotFag end;

HH.Head24 := H;

with HH,HH.Head24 do

begin

Starts := ReloCS;

StartO := ExelP;

ExelP := HFag.ExelP;

LS := (FileSize(fe)+15) div 16;

ReloCS := LS-HdrSize;

Leng24 := LS*16+LFag+SizeOf(HH)-24;

PageCnt := (Leng24+24+511) div 512;

PartPag :- (Leng24+24) mod 512;

if MinMem < 256 then MinMem :» 256;

if ExeSP < 4096 then ExeSP := 4096;

CheckFile := True end end end; {CheckFile} _•————————————;

Function CorrectName: Boolean;

(Определяет имя очередного защищаемого файла и открывает его} const

FirstName: Boolean = True;

begin

if FirstName then begin

FirstName := Falser-if post'.',NE)=0 then NE := NE+'.exe';

FSplit(NE,d,n,e) ;

FindFirst(NE,$2F,S) ;

if DOSErroroO then

NotAccess(NE) end else

FindNext(S);

if DOSError=0 then begin

NE := d+S.Name;

Assign(fe,NE);

{$!-} Reset(fe,l);

{$!+} if lOResult о 0 then NotAccess(NE) ;

CorrectName := CheckFile;

end else

CorrectName :» False;

end; (CorrectName} {.. —.—————————;

Procedure SetVirFag;

{Устанавливает защиту} vac

p: Pointer;

s: Word;

n: String;

L: Longint;

k: Word;

fb: file;

fff: file;

LOld: Longint;

label

NotBack,NotFag;

begin

LOld := FIleSize(fe); {Запоминаем ffvapya длину файла) if BakFile then begin

n :- copy(NE,l,pos('.',ME))+'BAK';

Assign(fb,n);

l$I-l Rewrite(fb,l) ;

{!+}

if lOResult <> 0 then NotBack:

WriteLn(1Невозможно соэдать резервную копию ',п) else begin

Seek(fe,0) ;

L := LOld;

repeat

s := 65520;

if L < s then s := L;

if MaxAvail < s then s :•= MaxAvail;

GetMemfp, s);

BlockReadtfetp^tStk);

if not CheckRead(NE,s,k) then Exit;

BlockWriteffb.p^tS.k);

if not CheckWrite(n,s,k) then goto NotBaek;

FreeMem(p,s);

dec(L,s) until L=0;

Close(fb) end end;

Seek(fe,0);

BlockWrite(fe,HH,SizeOf(H),k); (Пишем новый заголовок} if CheckWrite(ne,SizeOf(H),k) then begin

Randomize; (Инициируем генератор} HH.Key := Random($FFFF); (Получаем ключ} for k := 1 to 12 do with HH,HH.Head24 do

W[k] :° W[k] xor Key; (Шифруем заголовок} Seek(fe,LS*16); (Смещаем на границу параграфа} (Записываем ключ} BlockWrite(fe,HH,SizeOf(НН),k) ;

it CheckWrite(ne,SizeOf(HH),k) then begin

(Записываем фаг} Bloc]<Write(fe,CFag'',LFag,k) ;

if CheckWrite(ne,LFag,k) then

HriteLn(NE,' защищен') else

goto NotFag end

else

NotFag:

{Ошибка записи в конце ЕХЕ-файла. Восстанавливаем его:} begin

Seek(fe,0);

BlockWrite(fe,H,SizeOf(H)) ;

Seek(fe,L01d) ;

Truncate(fe) end;

end;

Close(fe) end; {SetVlrFag} /————————————;

begin

CheckParam; {Проверяем параметры обращения}

OpenFag; {Открываем файл-фаг}

while CorrectName do {Получаем имя очередного защищаемого файла}

SetVirFag {Устанавливаем защиту) end.

П8.2. Фаг Fag.asm

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

CSEG SEGMENT PARA PUBLIC 'CODE'

VirCheck PROC FAR

ASSUME CS:CSEG,DS:CSEG,S3:CSEG

; Следующие 17 слов готовятся программой-инсталлятором:

Head dw 12 dup(?) ;Зашифрованный ключ Starts dw ? Относительный сегмент StartO dw ? и смещение точки запуска Lengi dw ? Эталонная длина минус

24 байта - младшая часть Leng2 dw ? и старшая часть Key dw ? шифр ключа

; Константы и переменные ;

; ————————-—————;

PSP dw ? Сегмент PSP CSO dw ? Сохранение CS SPO dw ? Сохранение SP NameFE db 80 dup(?) ASCIIZ-имя ЕХЕ-файла NameF$ db 80 dup(?) ASCIIZ-имя $У$-файла NameFV db 80 dup(?) ASCIIZ-имя VIR-файла Buffer equ Word ptr NameF$ LName dw ? Длина имени без учета

расширения и нуля HandExe dw 0 Описатель ЕХЕ-файла TxtO db 13,10,'$' EOL Txtl db ^ 'Файл $'

Txt2 db ' заражен вирусом!',13,10,'$' Txt21 db 'Вирус поразил начало файла'

db ' - удаление невозможно!',13,10 Txt20 db 'Нажмите любую клавишу...$' Txt22 db 'Удалять (У/Я,умлч.У) ?$'

Тексты программ. 381

Vir db 'VIR',0

Ext$ db '$V$',0

HandVir dw 0

Txt3 db 'Ошибка доступа к файлу',

db 13,10,'$'

Txt4 db 'Вирус удален. Проверьте '

db 'другие файлы!',13,10,'$'

Mem dw ? ;Размер буфера

MemS dw ? ;Сегмент буфера

MemF dw 0 ;Признак распределения памяти

Основная программа

START:

push ax Спасаем AX mov ax,ds Запоминаем сегмент PSP mov bx,sp и указатель стека push cs Назначаем сегмент данных pop ds равным сегменту кода mov CSO,ds Сохраняем CS mov PSP,ax Спасаем DS = PSP mov SPO,bx Спасаем SP add ax,10h Сегмент начала программы add ax,Starts Сегмент точки запуска mov Starts,ax Готовим выход call GetExeName Получаем имя ЕХЕ-файла call ReadHead Читаем е^о заголовок call CheckHead Проверяем заголовок je Exit Кончаем, если норма call Dialog Сообщаем и запрашиваем je Exit Пользователь запретил удаление call NewFile Создаем $У$-файл call WriteHead Пишем в него эталонный заголовок call CopyFile Копируем незараженную часть call Rename Переименовываем файлы

Выход EXIT:

Выход из фага и передача управления защищаемой программе (метка EXIT) осуществляется следующим образом. Вначале фаг вызывает процедуру ClosePUes, чтобы закрыть все открытые файлы (файл считается открытым, если соответствующий ему дескриптор не равен 0). Затем вызывается процедура FreeMem, в которой контролируется размер MemF памяти, зарезервированной под буфер перезаписи с помощью функции ДОС $48. Если MemF не равна 0, фаг возвращает ДОС блок памяти длиной MemF байт. Перед завершением работы из стека извлекается значение регистра AX, a в регистры D5 и ?5 помещается значение сегмента PSP. Собственно передача управления осуществляется стандартным для ДОС образом: в стек заталкивается сегмент и смещение точки запуска программы, затем выполняется команда дальнего выхода из подпрограммы RETF. Эта команда извлекает два слова из вершины стека и помещает их в регистры IP и CS.

call CloseFiles Закрываем открытые файлы

call FreeMem Возвращаем зарезервированную память

mov sp,SPO Восстанавливаем SP

mov bx,Starts Готовим сегмент

mov cx,3tart0 и смещение точки запуска

mov es,PSP Восстанавливаем ES

mov ds,PSP Воссганавливаем DS

pop ax Восстанавливаем AX

push bx

push ex

retf ;Передаем управление программе ; Процедуры;

GetExeName PROC NEAR

; Анализируем окружение ДОС, выделяем ; имя программы и копируем его в буфер NameFE

Процедура GetExeName отыскивает имя программы в окружении ДОС и переносит его в переменную NameFE. Если имя выделить не удалось (не найдены два последовательных нуля в буфере длиной 1000 байт или количество переменных в расширенной части равно 0)i фаг не может определить местоположение и имя защищаемой файла — в этом случае он просто завершает свою работу и без каких-либо сообщений передает управление защищаемой программе. Таким образом, непременным условием работоспособности фага является использование версии ДОС 3.0 и выше. Фактически все, что делает процедура GetExeName, реализуется единственной строкой на Турбо Паскале:

NameFE := PararnStr (0);

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

mov es,PSP ES = PSP

mov es,es:[2Ch] ES - сегмент окружения ; Ищем начало дополнительнык параметров

mov сх,1000 Ищем в буфере

длиной 1000 байт

mov Ьх,-1 Начинаем с первого байта SEARCH:

inc bx Текущий байт

mov ax,es:[bx] Текущее слово

стр ах, 0 Ищем два нуля

jz EndSearch Переходим, если нашли

loop Search Повторяем для следующего байта

jmp Exit He найдено имя - выходим! ; Переносим имя файла endSEARCH:

add bx,2 Смещение количества параметров

mov ax,es:[bx] Получаем количество параметров

crop ax,0 Есть параметры?

je Exit Нет, выходим

add Ьх, 2 Начало имени

lea di,NameFE Индекс копии

mov сх,0 Готовим счетчик длины LAB1:

mov ah,es:[bx] Очередной символ

mov [di],ah Переносим его

inc bx Наращиваем указатели

inc di

inc сх и счетчик

cmp ah,О

jnz Labi Повторяем до символа О

add сх,-4 СХ - длина имени без расширения и нуля

mov LName,cx

ret

ReadHead PROC NEAR

; Открываем ЕХЕ-файл, читаем из него заголовок

Процедура ReadHead открывает ЯХС—файл и читает его заголовок. В ней (как и всюду в фаге) используется метод доступа к файлам с помощью так называемых дескрипторов (описателей) файлов, файл открывается с помощью функции ДОС $3D, а заголовок читается функцией $ЗР.

; Открываем файл с именем в NAMEF

lea dx,NameFE Начало имени

mov ah,3Dh Код операции

mov al,0 Открываем для чтения

int 21h Обращаемся к DOS

jnc LabO Если нет ошибки

jmp Exit Выходим, если ошибка LABO:

mov HandExe,ax Сохраняем описатель файла ; Читаем заголовок файла

mov Ьх,ах ВХ - описатель файла

mov ah,3Fh Код операции

lea dx,Buffer DS:DX - адрес буфера

mov cx,24 Читаем 24 байт (заголовок)

int 21h

jnc п>0

jmp Exit Если ошибка m0: ret

CheckHead PROC NEAR

; Дешифруем ключ и проверяем заголовок файла

Процедура CheckHead расшифровывает эталонный заголовок Head и сравнивает его с только что прочитанным. Если нет отличий (в этом случае при выходе из процедуры флаг ZF=l), фаг закрывает ЯХЕ—фаил и запускает защищаемую программу. Если обнаружено отличие заголовка от эталона [ZF=0], вызывается процедура Dialog. В ней на экран выводится сообщение о факте заражения файла и проверяется расположение вируса в теле файла. Если относительный сегмент точки запуска зараженной программы ReloCS меньше, чем в эталоне, значит вирус пристыкован в начало программы. В этом случае фаг сообщает о невозможности ликвидации вируса и завершает работу. Если ReloCS больше, чем в эталоне, вирус расположен в конце файла и его можно «выкусить». Фаг запрашивает согласие пользователя и, если оно получено, приступает к ликвидации вируса. ; Дешифруем ключ

mov сх,12 ;Счатчик повторений

lea bx,Head »BX - смещение эталонного заголовка LAB2:

mov ах,[Ьх] ?Берем очередное слово

xor ax,Ksy Дешифруем его

mov [Ьх],ах Помещаем назад

add bx,2 Переходим к следующему слову

loop Lab2 Повторяем расшифровку ; Сравниваем заголовок с эталонным

mov сх,12 Длина заголовка в словах

mov es,cs0 ES - сегмент кода

lea si,Head Эталон

lea di,Buffer ;Заголовок

eld ;Направление - вперец repe cmpsb ;Сравниваем

ret

! ~ ———————— —-.———• ———————— ___—~_ ——__•._—_.••-—_—•. —_«.—

Dialog PROC NEAR

; Сообщаем пользователю о наличии вируса

; и запрашиваем его действия ; Сообщаем пользователю lea dx,Txtl

call Print ; 'Файл '

lea bx,NameFE

add bx,LName

add bx,3 ;BX - смещение нуля в конце имени

mov Mem,bx ;Сохраняем в Mem

mov ah,24h ,-Помещаем '$' в конец имени

mov [bx],ah

lea dx,NameFE

call Print ; Выводим имя файла

mov bx,Mem ;Восстанавливаем

mov ah,0 ;ноль в конце имени

mov [bx] ,ah

lea dx,Txt2

call Print ; ' заражен вирусом!' ; Проверяем расположение вируса

mov ax,Word ptr [Buffer+l6h]

cmp ax,Word ptr [Head+16h]

ja Delete /Вирус в конце файла

; Вирус в начале файла. Сообщаем и завершаем работу

lea dx,Txt21

call Print

mov ah, 1

int 21h ;Ждем реакцию пользователя

lea dx,TxtO

call Print ;EOL

jmp Exit ;3ааершаем работу

; Вирус в конце файла DELETE:

lea dx,Txt22

call Print ;Спрашиваем пользователя ; Вводим ответ пользователя

mov ah,1

int 21h

push ax

lea dx,TxtO

call Print ; EOL

pop ax

cmp al,'N'

je ExitP ;Выходим, если ответ 'N'

cmp al, 'n' ; Сравниваем с 'п'

EXITP: ret

Print PROC NEAR

; Выводим на экран текстовую строку ; При входе DX - смещение строки mov ah, 9

int 21h ret

NewFile PROC NEAR

; Подготавливаем имена $V$ и VIR-файлов ; и создаем $У$-файл

В процедуре NewFile подготавливаются имена файлов с расширениями $V$ и VJR: в файл с расширением $V$ помещается восстановленная копия файла, расширение VIR писваивается зараженной копии.

lea di,NameF$

lea bx,ext$

call CopyName -Имя $У$-файла

lea di,NameFV

lea bx,Vir

call CopyName ;Р1мя VIR-файла ; Создаем $У$-файл

mov ah,3Ch

mov al,l

lea dx,NameF$

int 21h

jnc я1

jmp Error ml: mov HandVir,ax ;0писатель незараженного файла

ret

CopyName PROC NEAR ;Копируем строку имени файла с нужным расширением ; При входе DI - смещение приемника, ; ВХ - смещение расширения

mov es,cs0

lea si,NameFE

mov ex,LName

eld rep movsb ;Копируем до расширения

mov si,bx \

mov ex,4 rep movsb ;Копируем расширение

ret

WriteHead PROC NEAR

; Записываем в $У$-файл эталонный заголовок

С помощью процедуры WriteHead в $У$—файл записываются 24 байта эталонного заголовка из переменной Head. Процедура CopyFile копирует в него остальные Leng24 байта из исходного файла. Для убыстрения копирования процедура пытается выделить буферную память максимально возможного размера 64 Кбайт за концом программы. Если в конце программы нет свободной памяти, процедура пытается зарезервировать память вне программы с помощью обращения к функции ДОС $48, а если ДОС отвечает отказом, использует в качестве буфера незанятую часть стека. В подавляющем большинстве случаев программа после загрузки получает в свое распоряжение всю незанятую память, поэтому обращение к ДОС или использование стека осуществляется крайне редко.

mov ah,40h

mov cx,24 ;Длина заголовка 24 байт lea dx,Head mov bx,HandVi r int 21h jnc m2 jmp Error m2: ret

CopyFile PROC NEAR ; Определяем длину буфера ;и копируем незараженную часть ; Находим длину буфера

mov ax,SPO ;Вершина стека (в стеке АХ!)

cmp ax,OFFDOh ;Стек 64 Кбайт?

ja LargStack ;-да

add ax,17 ;Округляем с избытком

mov cl,4

shr ax,cl ;Переводим в параграфы

jmp GetSize LargStack:

aov ax,1000h 64 К » 4096 параграфов GetSize:

ШОУ ex, as

add ax,ex AX - сегмент буфера

mov es,PSP Сегмент PSP

mov bx,es:[2] Получаем вершину памяти

sub bx,ax BX - доступная память в параграфах

crop bx,1000h Больше 64 Кбайт?

ja LargMem -Да

mov cl,4

shi bx,cl BX - доступная память в байтах

jmp m5 LargMem:

mov bx,OFFFFh Длина буфера -=64 Кбайт той: сюр bx,0 Есть свободная память?

jne SetMem -Да ; Нет свободной памяти - резервируем ее

mov ah,48h

mov bx,1000h

int 21h ;Резервируем 64 К

jnc OKay .-Удачно

cmp bx,0 ;Есть хоть сколько-нибудь памяти?

jne GetLowMem ;-Да ; Выделяем часть стека GetStack:

mov ax, ss

mov bx,sp

add bx,-2

jmp SetMem GetLowMem:

mov ah,48h ;Просим у ДОС блок памяти

int 21h ;длиной BX параграфов

jc GetStack ;He получаем - используем стек Okay:

piov MemF,bx ; Размер зарезервированной mov cl,4 ;памяти в параграфах shi Ьх,с1 ;Переводим в байты SetMem:

mov Mem,bx ;Запоминаем в MEM длину буфера

TOOV MemS,ax ;и в MemS сегмент

; Копируем незараженную часть файла

Copy:

mov ax,Leng2 AX - старшая часть остатка

mov сх,Меш СХ - максимальный буфер

cmp ах,0 Осталось больше длины буфера?

jnz Read -Да

cmp cx,Lengl Сравниваем с длиной буфера

jb Read -Буфер меньше остатка

mov cx,Lengl Копируем остаток целиком

; Читаем файл

Read:

mov ah,3Fh

mov dx,0 »Смещение буфера » О

mov bx,HandExa lЧитаем из зараженного файла

push ds ; Сохраняем DS

mov ds,HemS ;вуфер за концом программы

int 21h

pop ds

jnc Write

jmp Error ;Если ошибка чтения ; Записываем файл Write:

mov ex,ax ;Число фактически прочитанных байт

mov ah,4 Oh

mov dx,0

mov bx,HandVir ;Пишем в $У$-файл

push ds

mov ds,MemS

int 21h

pop' ds

jnc m3

jmp Error ;Если ошиГха записи

; Изменяем длину остатка

m3: mov dx,Lengl ;DX - младшая часть длины

mov cx,Leng2 ;СХ - старшая часть длины

sub dx,ax ;AX - длина записанного

sbb cx,0 ;CX:DX - длина остатка

mov Leng2,cx

mov Lengl,dx

cmp ex,0

jnz Copy ; Повторяем

cmp dx, 0

jnz Copy ;пока остаток не станет нулевым

ret

Rename PROC NEAR

; Переименовываем ЕХЕ-файл в VIR-файл ;и $У$-файл в ЕХЕ-файл. Сообщаем о конце работы

После завершения копирования процедура Rename закрывает оба открытых файла и переименовывает их: исходный (зараженный) файл переименовывается в файл с расширением VIR, вновь созданный $У$—файл получает расширение ЕХЕ.

call CloseFiles ;Закрываем файлы mov ah,41h lea dx,NameFV

int 21h ;Удаляем VIR-файл mov ah,56h lea dx,NameFE push ds pop es lea di,NameFV

int 21h ;Переименовываем ЕХЕ в VIR jc Error mov ah,56h lea dx,NameF$ lea di,NameFE

int 21h ;Переименовываем $V$ в ЕХЕ jc Error ; Сообщаем о конце работы lea dx,Txt4

call Print ; 'Вирус удален...' lea dx,Txt20

call Print ; 'Нажмите...' mov ah,l

int 21h ;Ждем реакцию пользователя lea dx,TxtO call Print ret

CloseFiles PROC NEAR ; Закрываем открытые файлы

mov bx,HandExe

cmp bx,0

3e Next

mov ah,3Eh

int 21h

30 Error

mov HandExe,0 Next:

mov bx,HandVir

cmp bx, 0

3e All

mov ah,3Eh

int 21h

30 Error

mov HandVir,0 All:ret

FreeMem PROC NEAR

; Возвращаем зарезервированную память

mov bx,MemF

cmp bx,0

30 Return

mov ah,49h

mov es,Mem3

int 21h

Return: ret

; Ошибка доступа к диску ERROR:

lea dx,Txt3 •" • ' ""

call Print ; 'Ошибка доступа к диску'

jmp Exit CSEG endS

end START

 

 Оглавление

На первую страницу

Rambler's Top100 Rambler's Top100
PROext: Top 1000

(с)Все права защищены

По всем интересующим вопросам прошу писать на электронный адрес

Hosted by uCoz