[Назад] [Далее]

5.9.1. Пассивная резидентная программа

В качестве первой резидентной программы рассмотрим именно пассивный резидент, который будет активироваться при попытке программ вызывать 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 байт.


п»ї
"target=_blank><\/a>") //-->