| [Назад] [Далее] | ![]() |
В качестве первой резидентной программы рассмотрим именно пассивный резидент, который будет активироваться при попытке программ вызывать INT 21h и запрещать удаление файлов с указанного диска.
; tsr.asm
; Пример пассивной резидентной программы.
; Запрещает удаление файлов на диске, указанном в командной строке, всем
; программам, использующим средства DOS
.model tiny
.code
org 2Ch
envseg dw ? ; сегментный адрес копии окружения DOS
org 80h
cmd_len db ? ; длина командной строки
cmd_line db ? ; начало командной строки
org 100h ; СОМ-программа
start:
old_int21h:
jmp short initialize ; эта команда занимает 2 байта, так что
dw 0 ; вместе с этими двумя байтами получим
; old_int21h dd ?
int21h_handler proc far ; обработчик прерывания 21h
pushf ; сохранить флаги
cmp ah,41h ; Если вызвали функцию 41h (удалить
je fn41h ; файл)
cmp ax,7141h ; или 7141h (удалить файл с длинным именем),
je fn41h ; начать наш обработчик,
jmp short not_fn41h ; иначе - передать управление
; предыдущему обработчику
fn41h:
push ax ; сохранить модифицируемые
push bx ; регистры
mov bx,dx
cmp byte ptr ds:[bx+1],':' ; если второй символ ASCIZ-строки,
; переданной INT 21h,
; двоеточие - первый символ
; должен быть именем диска,
je full_spec
mov ah,19h ; иначе:
int 21h ; функция DOS 19h - определить текущий диск,
add al,'А' ; преобразовать номер диска к заглавной букве,
jmp short compare ; перейти к сравнению
full_spec:
mov al,byte ptr [bx] ; AL = имя диска из ASCIZ-строки
and al,11011111b ; преобразовать к заглавной букве
compare:
cmp al,byte ptr cs:cmd_line[1] ; если диски
je access_denied ; совпадают - запретить доступ,
pop bx ; иначе: восстановить
pop ax ; регистры
not_fn41h:
popf ; и флаги
jmp dword ptr cs:old_int21h ; и передать управление
; предыдущему обработчику INT 21h
access_denied:
pop bx ; восстановить регистры
pop ax
popf
push bp
mov bp,sp
or word ptr [bp+6],1 ; установить флаг переноса
; (бит 0) в регистре флагов,
; который поместила команда INT в стек
; перед адресом возврата
pop bp
mov ax,5 ; возвратить код ошибки "доступ запрещен"
iret ; вернуться в программу
int21h_handler endp
initialize proc near
cmp byte ptr cmd_len,3 ; проверить размер командной строки
jne not_install ; (должно быть 3 - пробел, диск, двоеточие),
cmp byte ptr cmd_line[2],':' ; проверить третий символ
jne not_install ; командной строки (должно быть двоеточие),
mov al,byte ptr cmd_line[1]
and al,11011111b ; преобразовать второй
; символ к заглавной букве,
cmp al,'А' ; проверить, что это не
jb not_install ; меньше "А" и не больше
cmp al,'Z' ; "Z",
ja not_install ; если хоть одно из этих условий
; не выполняется - выдать информацию
; о программе и выйти, иначе - начать
; процедуру инициализации
mov ax,3521h ; АН = 35h, AL = номер прерывания
int 21h ; получить адрес обработчика INT 21h
mov word ptr old_int21h,bx ; и поместить его в old_int21h
mov word ptr old_int21h+2,es
mov ax,2521h ; AH = 25h, AL = номер прерывания
mov dx,offset int21h_handler ; DS:DX - адрес нашего обработчика
int 21h ; установить обработчик INT 21h
mov ah,49h ; AH = 49h
mov es,word ptr envseg ; ES = сегментный адрес блока с нашей
; копией окружения DOS
int 21h ; освободить память из-под окружения
mov dx,offset initialize ; DX - адрес первого байта за концом
; резидентной части программы
int 27h ; завершить выполнение, оставшись
; резидентом
not_install:
mov ah,9 ; АН = 09h
mov dx,offset usage ; DS:DX = адрес строки с информацией об
; использовании программы
int 21h ; вывод строки на экран
ret ; нормальное завершение программы
; текст, который выдает программа при запуске с неправильной командной строкой:
usage db "Использование: tsr.com D:",0Dh,0Ah
db "Запрещает удаление на диске D:",ODh,OAh
db "$"
initialize endp
end start
Если запустить эту программу с командной строкой D:, никакой файл на диске D нельзя будет удалить командой Del, средствами оболочек типа Norton Commander и большинством программ для DOS. Действие этого запрета, однако, не будет распространяться на оболочку Far, которая использует системные функции Windows API, и на программы типа Disk Editor, обращающиеся с дисками при помощи функций BIOS (INT 13h). Несмотря на то что мы освободили память, занимаемую окружением DOS (а это могло быть лишних 512 или даже 1024 байта), наша программа все равно занимает в памяти 352 байта из-за того, что первые 256 байт отводятся для блока PSP. Существует возможность оставить программу резидентной без PSP — для этого инсталляционная часть программы должна скопировать резидентную часть с помощью, например, movs в начало PSP. Но при этом возникает сразу несколько проблем: во-первых, команда INT 27h, так же как и функция DOS 31h, использует данные из PSP для своей работы, во-вторых, код резидентной части должен быть написан для работы с нулевого смещения, а не со 100h, как обычно, и, в-третьих, некоторые программы, исследующие выделенные блоки памяти, определяют конец блока по адресу, находящемуся в PSP программы — владельца блока со смещением 2. С первой проблемой можно справиться вручную, создав отдельные блоки памяти для резидентной и инсталляционной частей программы, новый PSP для инсталляционной части и завершив программу обычной функцией 4Ch или INT 20h. Реальные программы, делающие это, существуют (например, программа поддержки нестандартных форматов дискет PU_1700), но мы не будем чрезмерно усложнять наш первый пример и скопируем резидентную часть не в позицию 0, а в позицию 80h, то есть, начиная с середины PSP, оставив в нем все значения, необходимые для нормальной работы функций DOS.
Прежде чем это сделать, заметим, что и номер диска, и адрес предыдущего обработчика INT 21h изменяются только при установке резидента и являются константами во время всей его работы. Более того, каждое из этих чисел используется только по одному разу. В этих условиях оказывается, что можно вписать номер диска и адрес перехода на старый обработчик прямо в код программы. Более того, после этого наш резидент не будет больше ссылаться ни на какие переменные с конкретными адресами, а значит, его код становится перемещаемым, то есть его можно выполнять, скопировав в любую область памяти.
; tsrpsp.asm
; Пример пассивной резидентной программы с переносом кода в PSP.
; Запрещает удаление файлов на диске, указанном в командной строке,
; всем программам, использующим средства DOS
.model tiny
.code
org 2Ch
envseg dw ? ; сегментный адрес копии окружения DOS
org 80h
cmd_len db ? ; длина командной строки
cmd_line db ? ; начало командной строки
org 100h ; СОМ-программа
start:
old_int21h:
jmp short initialize ; переход на инициализирующую часть
int21h_handler proc far ; обработчик прерывания 21h
pushf ; сохранить флаги
cmp ah,41h ; Если вызвали функцию 41h
; (удалить файл)
je fn41h
cmp ax,7141h ; или 7141h (удалить файл
; с длинным именем),
je fn41h ; начать наш обработчик,
jmp short not_fn41h ; иначе - передать
; управление предыдущему обработчику
fn41h:
push ax ; сохранить модифицируемые
push bx ; регистры
mov bx,dx ; можно было бы использовать
; адресацию [edx+1], но в старшем
; слове EDX совсем не обязательно 0,
cmp byte ptr [bx+1],':' ; если второй символ
; ASCIZ-строки, переданной INT 21h,
; двоеточие, первый символ должен
; быть именем диска,
je full_spec
mov ah,19h ; иначе:
int 21h ; функция DOS 19h - определить
; текущий диск
add al,'А' ; преобразовать номер диска
; к заглавной букве
jmp short compare ; перейти к сравнению
full_spec:
mov al,byte ptr [bx] ; AL = имя диска из ASCIZ-строки
and al,11011111b ; преобразовать к заглавной букве
compare:
db 3Ch ; начало кода команды CMP AL,число
drive_letter: db 'Z' ; сюда процедура инициализации
; впишет нужную букву
pop bx ; эти регистры больше не
pop ax ; понадобятся, если диски совпадают -
je access_denied ; запретить доступ
not_fn41h:
popf ; восстановить флаги и передать
; управление предыдущему
; обработчику INT 21h:
db 0EAh ; начало кода команды
; JMP, FAR-число
old_int21h dd 0 ; сюда процедура инициализации
; запишет адрес предыдущего
; обработчика INT 21h
access_denied:
popf
push bp
mov bp,sp ; чтобы адресоваться в стек
; в реальном режиме,
or word ptr [bp+6],1 ; установить флаг
; переноса (бит 0) в регистре
; флагов, который поместила команда
; INT в стек перед адресом возврата
pop bp
mov ax,5 ; возвратить код ошибки
; "доступ запрещен"
iret ; вернуться в программу
int21h_handler endp
tsr_length equ $-int21h_handler
initialize proc near
cmp byte ptr cmd_len,3 ; проверить размер
; командной строки
jne not_install ; (должно быть 3 -
; пробел, диск, двоеточие)
cmp byte ptr cmd_line[2],':' ; проверить
; третий символ командной
jne not_install ; строки (должно быть двоеточие)
mov al,byte ptr cmd_line[1]
and al,11011111b ; преобразовать второй
; символ к заглавной букве
cmp al,'A' ; проверить, что это не меньше "А"
jb not_install ; и не больше
cmp al,'Z' ; "Z",
ja not_install ; если хоть одно из
; этих условий
; не выполняется - выдать информацию о программе и выйти,
; иначе - начать процедуру инициализации
mov byte ptr drive_letter,al ; вписать имя
; диска в код резидента
push es
mov ax,3521h ; АН = 35h,
; AL = номер прерывания
int 21h ; получить адрес обработчика INT 21h
mov word ptr old_int21h,bx ; и вписать его
; в код резидента
mov word ptr old_int21h+2,es
pop es
cld ; перенос кода резидента,
mov si,offset int21h_handler ; начиная
; с этого адреса,
mov di,80h ; в PSP:0080h
rep movsb
mov ax,2521h ; AH = 25h,
; AL = номер прерывания
mov dx,0080h ; DS:DX - адрес нашего обработчика
int 21h ; установить обработчик INT 21h
mov ah,49h ; AH = 49h
mov es,word ptr envseg ; ES = сегментный адрес блока
; с нашей копией окружения DOS
int 21h ; освободить память из-под
; окружения
mov dx,80h+tsr_length ; DX - адрес первого
; байта за концом резидентной части
; программы
int 27h ; завершить выполнение,
; оставшись резидентом
not_install:
mov ah,9 ; АН = 09h
mov dx,offset usage ; DS:DX = адрес строки
; с информацией об
; использовании программы
int 21h ; вывод строки на экран
ret ; нормальное завершение
; программы
; текст, который выдает программа при запуске
; с неправильной командной строкой:
usage db "Usage: tsr.com D:",0Dh,0Ah
db "Denies delete on drive D:",0Dh,0Ah
db "$"
initialize endp
end start
Теперь эта резидентная программа занимает в памяти только 208 байт.