Резидентные программы
1
Содержание
Введение . 2
1 РЕЗИДЕНТНЫЕ ПРОГРАММЫ 4
2 ЗАЩИТА РЕЗИДЕНТНЫХ ПРОГРАММ ОТ ПОВТОРНОЙ УСТАНОВКИ .....8
3 ВЫГРУЗКА РЕЗИДЕНТНЫХ ПРОГРАММ ИЗ ПАМЯТИ 12
4 ОПИСАНИЕ ПРОГРАММЫ 13
Заключение 21
Литература 22
Приложение-листинг
Приложение-рисунок
Введение
В данной работе рассмотрено явление, которое в операционной системе MSDOS играет роль многозадачности. С истинной многозадачностью можно столкнуться, рассматривая программирование в операционной системе Windows. Многозадачность же, рассматриваемая в этой курсовой работе, может быть названа нелегальной. Термин TSR происходит от английской фразы Terminate State Resident, т.е. "завершить и остаться резидентным". Резидентная программа, находясь в памяти, выполняет свои функции через перехваченные прерывания. Связь с резидентной программой также осуществляется посредством прерываний. Часто такие программы играют роль драйверов -программ поддержки, каких-либо внешних устройств. Наиболее часто употребляемые резидентные драйверы обеспечивают русифицированную работу экрана и клавиатуры или поддерживают работу мыши. Популярны резидентные переводчики и различные резидентные детекторы, позволяющие на ранней стадии обнаруживать появление вируса. Наличие в памяти резидентных программ предполагает как бы некое подобие многозадачного режима, в котором, однако, есть главная задача, выполняющаяся в данный момент. Остальные же задачи получают управление время от времени через перехваченные ими прерывания либо "по милости" главной задачи. Таким образом, операционная система фактически не "знает" об их существовании. Многие авторы говорят в этой связи об изначальной порочности TSR-программ. Однако, надо заметить, что:
1. На TSR-программах держится значительная часть как чисто системной, так и прикладной программной поддержки в операционной системе MS DOS.
2. При написании любой программы нужно придерживаться определенных правил, а ошибка в программе приводит к полной и частичной ее неработоспособности.
3. Многие проблемы, рассмотренные в настоящей главе, носят общий характер для любых многозадачных сред.
1 РЕЗИДЕНТНЫЕ ПРОГРАММЫ
Структура типичной резидентной программы выглядит следующим образом:
text segment
assume CS:text,DS:text
org lOOh main proc
jmp init ;Переход на секцию инициализации
;Данные резидентной секции программы
entry: ;Текст резидентной секции программы
main endp
init proc ;Секция инициализации
mov DX,(init-main+10Fh)/16
mov АН,3100h ;Функция "Завершить и оставить в
int 21 h ; памяти"
init endp
text ends end main
Программа пишется в формате .СОМ, поэтому в ней предусматривается только один сегмент, с которым связываются сегментные регистры CS и DS; в начале сегмента резервируется lOOh байт для PSP. При запуске программы с клавиатуры управление передается (в соответствии с параметром директивы end) на начало процедуры main. Командой jmp сразу же осуществляется переход на секцию инициализации, которая может быть оформлена в виде отдельной процедуры или входить в состав процедуры main. В секции инициализации выполняются некоторые начальные действия, о которых мы поговорим позже. Последними строками секции инициализации вызывается функция DOS 3 lh, которая выполняет завершение программы с оставлением в памяти указанной ее части. Размер резидентной части программы (в параграфах) передается DOS в регистре DX. Определить этот размер можно, например, следующим образом. К разности смещений init-main, которая равна длине резидентной части программы в байтах, прибавляется размер PSP (lOOh) и еще число 15 (Fh), чтобы после целочисленного деления на 16 (для получения размера программы в параграфах) результат был округлен в большую сторону. С целью экономии памяти секция инициализации располагается в конце программы и отбрасывается при ее завершении. Функция 3lh, закрепив за резидентной программой необходимую для ее функционирования память, передает управление командному процессору (как и обычная функция завершения программы 4Ch), и вычислительная система переходит в исходное состояние. Наличие программы, резидентной в памяти, никак не отражается на ходе вычислительного процесса, за исключением того, что уменьшается объем свободной памяти. Одновременно в память может быть загружено любое число резидентных программ. Процесс первичного запуска резидентной программы, приводящего к ее загрузке в память, обычно называют установкой программы. На рисунке 1 показаны элементы резидентной программы и их взаимодействие.
PSP
Точка входа при
загрузке
Точка входа при актквнзадни —
Резидентные данные
entry:
> Резидентная часть программы
main endp
Резидентные шманды
init proc
: c*xw« инициализации mov AH, 31h
tot 21h init endp end main
Часть программы» > отбрасываемая после установки в памяти
Рис. 1. Взаимодействие элементов резидентной программы
Любая резидентная программа имеет по крайней мере две точки входа. При запуске с клавиатуры программы типа .СОМ управление всегда передается на первый байт после PSP (IP=100h). Поэтому практически всегда первой командой резидентной программы является команда jmp, передающая управление на начало секции инициализации. После отработки функции DOS 3 lh программа остается в памяти в пассивном состоянии. Для того чтобы активизировать резидентную программу, ей надо как-то передать управление. Вызвать к жизни резидентную программу можно разными способами, но наиболее употребительным является механизм аппаратных или программных прерываний. В этом случае в процессе инициализации необходимо заполнить соответствующий вектор адресом точки входа в программу (entry на рис.1). Адрес entry образует вторую точку входа в программу, через которую осуществляется ее активизация. Очевидно, что резидентная секция программы должна заканчиваться командой выхода из прерывания iret.
2 ЗАЩИТА РЕЗИДЕНТНЫХ ПРОГРАММ ОТ ПОВТОРНОЙ УСТАНОВКИ
Некоторые резидентные программы имеют серьезный недостаток: они не защищены от повторной загрузки. Вспомним, что для работы с резидентной программой ее следует прежде всего установить, запустив с клавиатуры как обычную программу. В этом случае управление передается на секцию инициализации, в которой подготавливаются условия для дальнейшей работы программы в резидентном состоянии. Как правило, в секции инициализации загружаются векторы прерываний, через которые программа будет активизироваться. Может получиться, что пользователь, установив программу, забудет об этом и запустит ту же программу повторно. В этом случае в память будет загружена и останется резидентной вторая копия той же программы. Это плохо не только потому, что понапрасну расходуется память; более неприятным является вторичный перехват тех же векторов. Если резидентная программа после ее активизации не обращается к старому содержимому перехваченных ею векторов, то вторая копия полностью лишит первую работоспособности и тогда повторная загрузка приведет только к расходованию памяти. Если, однако, как это обычно и имеет место, резидентная программа в процессе своей работы передает управление старому обработчику перехваченного ею прерывания, то новая копия резидентной программы, сохранившая в процессе инициализации адрес первой копии в качестве содержимого перехватываемого вектора, будет при каждой активизации вызывать и первую копию. В результате резидентная программа будет фактически выполняться при каждом вызове дважды. Во многих случаях такое повторное выполнение нарушит правильную работу программы. Поэтому обязательным элементом любой резидентной программы является процедура защиты ее от повторной загрузки (установки). Наиболее распространенным методом защиты резидентной программы от повторной установки является использование прерывания 2Fh, называемого мультиплексным и специально предназначенного для связи с резидентными программами. При вызове этого прерывания в регистре АН задается номер функции (от 00h до FFh), а в регистре AL - номер подфункции (в том же диапазоне). Функции с 00h по BFh зарезервированы для использования системой (например, функции 00h и Olh закреплены за резидентной программой DOS PRTNT.COM, а 1 Oh - за программой SHARE.EXE), а функции с COh по FFh могут использоваться прикладными программами. Для того чтобы резидентная программа могла отозваться на вызов прерывания int 2Fh, в ней должен иметься обработчик этого прерывания (рис. 1). Фактически все резидентные программы, как системные, так и прикладные, имеют такие обработчики, через которые осуществляется не только проверка на повторную установку, но и вообще вся связь с резидентной программой: смена режима ее работы или получение от нее требуемой информации. Задание действия, которое надлежит выполнить обработчику прерывания 2Fh конкретной резидентной программы, осуществляется с помощью номера подфункции, помещаемого перед вызовом прерывания в регистр AL. Обычно для проверки на наличие в памяти используется подфункция OOh.
9
DOS
entry: TSR (1)
■ Содержательная часть
• резидентной программы
jret
uew_2fti;
'. Обработчик прерывания 2Fh
Резидентная теть программы
Первый экземпляр TSE.COM, загруже ими й * память командой TSR,СОМ
Второй экземшяр TSR.COM, запущенны й повторной командой TSR.COM и приступивший к своей инициализаций
11
Признак наличия к памяти первого экземпляра программы
рис. 2. Проверка, осуществляемая запущенной с клавиатуры резидентной программой, на наличие в памяти ранее установленного экземпляра той же программы.
Резидентная программа на этапе инициализации должна выяснить, нет ли уже в памяти ранее установленного экземпляра той же программы. Для этого в секции инициализации выполняется команда int 2Fh (см. рис. 1). Предварительно в регистр АН помещается номер функции, присвоенный данной программе (из диапазона COh...FFh), а в регистр AL - номер подфункции, соответствующей запросу на наличие в памяти, например,ООп. Обработчик прерывания 2Fh резидентной программы должен прежде всего проверить номер функции в регистре АН; при обнаружении своей функции обработчик анализирует содержимое регистра AL и выполняет затребованные действия, после чего командой iret передает управление вызвавшей его программе. Если, однако, обработчик обнаружил в регистре АН чужую функцию, он должен командой jmp CS:old_2fh передать управление по цепочке тому обработчику, адрес которого был ранее в векторе 2Fh. В результате вызов int 2Fh из любой программы будет проходить по цепочке через все загруженные резидентные программы, пока не достигнет "своей" программы или йе вернет управление в вызвавшую программу через обработчик DOS (который, очевидно, всегда будет самым последним в цепочке).
4 ОПИСАНИЕ ПРОГРАММЫ
Данный резидентный обработчик должен выполнять следующее. После запуска программы на экране должны выводиться сообщения с текстом "Message" желтыми символами на зеленом фоне и прекращаться при нажатии клавиши "q" с клавиатуры.
Для того, чтобы останавливать вывод сообщений клавиатурой,
прописываем его для клавиши "q".
old_09 dw ?
dw? New_09 Proc pushf
call cs:dword ptr old_09 cli
mov ah, Olh int 16h jz exit_int
cmp al,'q'; clear Cnt, key combination 'q' jne exit_int mov cs:cnt,0 exit_int: sti iret
New_09 endp
Затем идет часть мультиплексной обработки прерывания:
New_2F Proc cli
cmp ax,08000h jne exit
mov ax, OABCDh
sti
iret
exit:
sti
jmp cs:[01d_2F]. 01d_2F DD 0 New_2F endp
Здесь сначала запрещаем на время все прерывания в операционной системе. Затем проверяем, нашу ли программу вызывают. Если же нет, то выйти из процедуры, а иначе отправить пароль «я здесь». После этого обратно разрешаем все прерывания в операционной системе и выходим из прерывания. А после того, как вышли из прерывания, разрешаем опять все прерывания в операционной системе и переходим по адресу старого обработчика прерывания F2.
После мультиплексного прерывания следует блок программы, который отсчитывает время 5 секунд до появления очередного сообщения на экране. New_lC Proc
cmp cs:Cnt,91 jb M2
mov cs:flg_timer,l
M2:
inc byte ptr cs:Cnt jmpcs:[01d_lC] OldJCDDO Cnt DB 0 flgjimer DB 0 New_lC endp
Проверяем, прошло ли 5 секунд. Если нет, то переход на увеличение счетчика jb. Сообщение можно выводить, поэтому установим в ячейке флага истечения контрольного времени 1. Увеличиваем счетчик времени на 1 и переходим по адресу обработчика прерывания F2.
New_28 proc pushf
cmp cs:flg_timer,l
jne Ml
push ax
push bx
push bp
push es
push cx
cli
В этой части программы сохраняем регистр флагов процессора. Проверяем, установлена ли 1 в ячейке флага контроля истечения заданного времени. Если не установлена, то время до вывода сообщения на экран еще не истекло и мы
переходим на старый обработчик прерывания 28h. В противном случае активизируем модуль вывода сообщения на экран. Для чего сначала сохраним в стеке регистры, которые сейчас начнем использовать. Иначе мы испортим в них данные прерванной нами программы. Запретим прерывания нашей программы другими программами.
Теперь идет блок, в котором прописывается вывод сообщений на экран.
mov ah, 01 ?h int lOh
mov ax,cs mov es,ax
mov bp,offset curtime
mov cl,25 call random mov dh,al mov cl,73 call random mov dl,al mov bl,2Eh mov cx,7 mov ax,1300h int lOh jmpql random:
push es push bx push dx push cx
mov ah,0 int lAh
mov es,cx mov bx,dx
mov al,byte ptr es:[bx] pop cx
cmp al,cl jb 12 sub al,cl jmp 11
pop dx pop bx pop es ret
Читаем текущий видеорежим и вызываем random, чтобы сообщения появлялись на экране в произвольных местах. Количество колонок - количество символов (80-7). Указываем атрибуты строки: желтые буквы на зеленом фоне mov bl,2Eh. После чего сохраняем в стек все регистры, и читаем часы.
3
ql:
mov cs:flg_timer,0 mov cs:cnt,0
sti
pop cx pop es pop bp pop bx pop ax
Ml:
popf
jmp cs:[old_28]
curtime db "Message" old_28 dd 0 New_28 endp
Здесь разрешаем все прерывания, восстанавливаем все сохраненные регистры и в заключение процедуры передаем управление старому обработчику.
И последний блок данного резидентного обработчика:
start:
mov ax,08000h int 2Fh
cmp ax,0ABCDh je analiz
mov ax,3528h int21h
mov word ptr old_28,bx mov word ptr old_28+2,es mov ax,2528h lea dx,new_28 int21h
mov ax,352Fh int21h
mov word ptr old_2F,bx mov word ptr old_2F+2,es mov ax,252Fh lea dx,New_2F int21h
mov ax,351Ch int21h
mov word ptr old_l C,bx mov word ptr old_lC+2,es mov ax,251Ch lea dx,New_lC int21h
mov ax,3509h" int21h
mov word ptr old_09,bx mov word ptr old_09+2,es mov ax,2509h lea dx,New_09 int21h
lea dx,start int 27h
analiz:
mov si,80h lodsb cmp al,3 jl bue inc si lodsw cmp ax,'u/' jne bue
mov ax,3528h int21h push ds
Ids dx,dword ptr es:old_09
mov ax,2509h
int21h
Ids dx,dword ptr es:old_l С
mov ax,251Ch
int21h
Ids dx,dword ptr es:old_28 mov ax,2528h int 21h
Ids dx,dword ptr es:old_2F
mov ax,252Fh
int 21h
pop ds
mov ah,49h
int 21h
mov es,word ptr es:[0()2ch] mov ah,49h int 21h
bue:
mov ax,4c00h
int 21h TSRprog endp code_seg ends end begin
Во-первых, сразу запросим прерывание 2F - нет ли нашей программы в памяти. Для этого занесем в АХ запланированный нами код функции (см. выше процедуру New_2F - мы там решили занять для своих нужд функцию 80 и подфункцию 00 . Проверим поступивший из прерывания «отзыв». Наша программа должна отозваться кодом 0ABCD (см.выше процедуру New_2F).
Если это так, то мы пытаемся второй раз установить программу - это не нужно, а может быть мы хоти выгрузить ее из памяти? Надо проверить ключ в строке параметров запуска программы, поэтому перейдем к метке analiz.
В противном случае начинаем установку программы резидентом в памяти. Для этого сначала сохраним адреса старых векторов прерываний, которые мы планируем заместить своими процедурами. Начнем с вектора 28h. Используя стандартную функцию 35h прерывания 21h получим этот вектор в ES:BX и сохраним его в специально подготовленной ячейке old_28 (см.выше).
С помощью стандартной функции 25п прерывания 21h на место старого вектора установим адрес нашей процедуры, и затем то же самое проделаем с вектором 2Fh и с вектором 1 Ch.
Разместим в регистре dx адрес начала не резидентной части программы с помощью прерывания 27h завершим нашу программу, оставив ее в памяти резидентной:
lea dx,start int 27h
Теперь удем анализировать содержимое строки параметров нашей программы. После запуска программы, содержимое командной строки операционная система размещает по смещению 80h в PSP программы. Точнее по самому смещению 80h находится байт, в котором размещается число, равное количеству символов в строке параметров. Получим его из PSP, для чего смещение интересующего нас байта занесем в SI. Потом загрузим этот байт в AL:
lodsb
и проверим, не меньше ли длина строки параметров 3-х символов. Если меньше, то выйти из программы , иначе перейдем на адрес следующего байта командной строки и возьмем из нее в АХ два байта. Проверим, это наш ключ /и:
cmp ах,'и/'
если не равно, то это не наш ключ и программу завершаем. В противном случае начинаем модуль выгрузки программы из памяти. Для начала загрузим регистры ES:BX адресом нашей программы в памяти для чего возьмем вектор любого из контролируемых ею прерываний, например, 28h:
mov ax,3528h int 21h
Теперь сохраним регистр ds.
Загрузим регистры DS:DX адресом старого вектора прерывания 1С взятого из памяти нашей программы, которая сейчас стоит резидентом:
Ids dx,dword ptr es:old_09
mov ax,2509h
int21h
Ids dx,dword ptr es:old_lC
To же сделаем с прерыванием 28h и прерыванием 2F. Восстановим регистр DS. Используя стандартную функцию 49h прерывания 21h, освободим память, занимаемую нашей резидентной программой. Функция освобождает память, сегмент которой стоит в ES. А там как раз адрес начала нашей резидентной программы:
mov ah,49h int 21h
Теперь надо освободить память, занятую блоком окружения программы. Возьмем оттуда этот адрес и поместим его в ES:
mov es,word ptr es:[002ch] после чего дадим команду освободить этот блок:
mov ah,49h
int 21h
Когда блок окружения программы свободен, завершить программу и возвратиться в операционную систему: bue:
mov ax,4c00h
int 21h TSRprog endp code_seg ends end begin
Заключение
Многие программы, обеспечивающие функционирование вычислительной системы (драйверы устройств, программы сжатия или шифрования данных, русификаторы, интерактивные справочники и др.), должны постоянно находиться в памяти и быстро реагировать на запросы пользователя или на какие-то события, происходящие в вычислительной системе. Такие программы носят название программ, резидентных в памяти (Terminate and Stay Resident, TSR), или просто резидентных. Сделать резидентной можно любую программу, однако ввиду того, что резидентная программа должна быть максимально компактной, чаще всего в качестве резидентных используют программы типа .СОМ.
Литература
1. Пирогов В.Ю. ASSEMBLER Учебный курс Москва Издатель Молгачева СВ., 2001
2. Зубков С. В. ASSEMBLER -язык неограниченных возможностей Издательство «ДМК Пресс», 1999
3. АССЕМБЛЕР И ПРОГРАММИРОВАНИЕ ДЛЯ IBM PC Под редакцией Ершова В.Г.
4. В. И. Юров ASSEMBLER Учебник для вузов. 2-е изд. / В. И. Юров — СПб.: Питер, 2003. —637 с: ил.
5. Рудаков П. И., Финогенов К. Г. Язык ассемблера: уроки программирования. - М.: ДИАЛОГ-МИФИ, 2001. - 640 с.
6. Интернет.
Приложение-листинг
code_seg segment TSRprog proc far
assume cs:code_seg
org lOOh begin:
jmp start
old_09 dw ? dw?
New_09 Proc pushf
call cs:dword ptr old_09 cli
mov ah, 0lh int 16h jz exit_int
cmp al,'q'; clear Cnt, key combination 'q'
jne exit_int
mov cs:cnt,0
exit_int:q sti iret
New_09 endp
New_2F Proc
;обработка мультиплексного прерывания
24
24
exit:
cli
cmp ax,08000h
jne exit
mov ax, OABCDh
sti
iret
sti
jmp cs:[O1d_2F] Old 2F DD 0
;запретим на время все прерывания в ОС ; проверим, а нашу ли программу вызывают? ;если нет, то выйти из процедуры. ;иначе - отправить пароль «я здесь» разрешить опять все прерывания в ОС. ; выйти из прерывания
разрешить опять все прерывания в ОС. ;и перейти по адресу старого обработчика прерывания 2F.
;это место под адрес старого обработчика прерывания 2F.
21
New_2F endp
New 1С Proc
21
cmpcs:Cnt,9 1 ;проверяем прошло ли 5 сек.?
jb М2 ;если нет, то переход на увеличение счетчика jb
mov cs:flg_timer,l
M2:
;проверяем, прошло ли 5 секунд?
;если нет, то переход на увеличение счетчика jb
;сообщение можно выводить, поэтому установим
;в ячейке флага истечения
контрольного времени 1
inc byte ptr cs:Cnt увеличить счетчик времени на 1
jmp cs:[O1d_lC] ;перейти по адресу обработчика прерывания 2F.
01d_lC DD 0 ;адрес обработчика прерывания 2F.
Cnt DB 0 ;ячейка для счетчика тиков
21
flg_timer DB 0 ;ячейка для флага истечения заданного времени
New_lC endp
New_28 proc
;сохраним регистр флагов процессора
pushf
; проверим - в ячейке флага контроля истечения заданного времени установлена ли единица?
cmp cs:flg_timer,l
;если нет, то время до вывода сообщения на экран ;еще не истекло и мы переходим на старый ;обработчик прерывания 28h
jne Ml
;в противном случае активизируем модуль вывода
;сообщения на экран. Для чего
;сначала сохраним в стеке регистры, которые
;сейчас начнем использовать. Иначе
;мы испортим в них данные прерванной нами
программы.
push ах push bx push bp push es push cx
;3апретим прерывания нашей программы другими программами
mov ah, OFh ;Читаем текущий видео режим
int 1 Oh
mov ax,cs
mov es,ax ;Выводимая строка
;Количество строк
;Вызов random
;Строка начала вывода
mov bp,offset curtime
mov cl,25
call random
mov dh,al
mov cl,73 ;Количество колонок - количество символов
;(80-7)
call random ;Вызов random
mov dl,al ;Колонка начала вывода
mov bl,2Eh ;Атрибуты строки: желтые буквы на
;зеленом фоне
mov сх,7 ;Длина строки
mov ax,1300h ;Вывод
int 10h
jmpql
random:
;Сохраняем в стек регистры
push es
push bx push dx push cx
mov ah,0 ;Читаем часы
int lAh ;cx - старшая часть значения
;dx - младшая часть значения
mov es,cx mov bx,dx
mov al,byte ptr es:[bx] ;Возвращаемое значение
11:
12:
pop cx
cmp al,cl jb 12
sub al,cl jmp 11
;Восстанавливаем
; Сравнение
;Если ниже
;Иначе CL - AL ;Повторная проверка
;Восстанавливаем сохраненные регистры
pop dx
pop bx
pop es
ret
mov cs:flg_timer,0 mov cs:cnt,0
;разрешим внешние прерывания
Ml:
sti
pop cx pop es pop bp pop bx pop ax
popf
восстановим все сохраненные регистры
jmp cs:[old_28]
;В заключение процедуры передадим управление старому обработчику
;А это область данных нашей процедуры New_28. ; Здесь хранится текстовая строка выводимая на ;экран ПЭВМ
curtime db "Message'
old_28 dd 0
New_28 endp
start:
mov ax,08000h
;Во-первых, сразу запросим прерывание 2F - нет
ли нашей программы в памяти?
;Для этого занесем в АХ запланированный нами
;код функции (см. выше процедуру
;New_2F - мы там решили занять для своих нужд
функцию 80 и подфункцию 00
; Обратимся к функции 2F
int 2Fh
;Проверим поступивший из прерывания «отзыв». ;Наша программа должна отозваться кодом
;0ABCD (см.выше процедуру New_2F)
cmp ax,0ABCDh
;Если это так, то мы пытаемся второй раз
установить программу - это не нужно,
;а может быть мы хоти выгрузить ее из памяти?
;Надо проверить ключ в строке
параметров запуска программы, поэтому
перейдем к метке ANALIZ
je analiz
;В противном случае начинаем установку программы резидентом в памяти ;Для этого сначала сохраним адреса старых ; векторов прерываний, которые мы ;планируем заместить своими процедурами. ;Начнем с вектора 28h. Используя стандартную функцию 35h прерывания 21h ;получим этот вектор в ES:BX
mov ax,3528h
int 21h
;и сохраним его в специально подготовленной ;ячейке old_28 (см.выше)
mov word ptr old_28,bx
mov word ptr old_28+2,es
;C помощью стандартной функции 25h прерывания 21h на место старого вектора установим адрес нашей процедуры
mov ax,2528h
lea dx,new_28
int21h
; Теперь то же самое проделаем с вектором 2Fh
mov ax,352Fh
int 21h
mov word ptr old_2F,bx
mov word ptr old_2F+2,es
mov ax,252Fh
lea dx,New_2F
int21h
;и с вектором lCh
mov ax,351Ch
int21h
mov word ptr old_lC,bx
mov word ptr old_lC+2,es
mov ax,251Ch
lea dx,New_lC
int21h
mov ax,3509h
int21h
mov word ptr old_09,bx
mov word ptr old_09+2,es
mov ax,2509h
lea dx,New_09
int21h
27
27
lea dx, start
int 27h analiz:
;Разместим в регистре dx адрес начала НЕ РЕЗИДЕНТНОЙ части программы
;с помощью прерывания 27h завершим нашу
программу, оставив
;ее в памяти резидентной
27
занесем в SI
mov si,80h
lodsb
cmp al,3
jl bue
;Будем анализировать содержимое строки
щараметров нашей программы.