Консультация № 173536
22.10.2009, 01:14
25.00 руб.
0 2 1
Здравствуйте. Я пытаюсь модифицировать исходник примера с сайта О.А. Калашникова(http://kalashnikoff.ru/Assembler/issues/027.htm) так, чтобы при нажатии на какую-либо заданную клавишу(в данном примере выбрана F12) на экран выводилось текстовое сообщение о том, что программа загружена и работает(либо, как вариант, запускалась небольшая сторонняя программа выводящая подобное сообщение). Подскажите, пожалуйста, почему не работает мой вариант и что нужно для получения рабочей программы(Я также пропустил восстановление обработчика прерывания 09h - намереваюсь добавить его позже). Запускать программу планируется в эмуляции DOS под windows xp sp2.

Приложение:
;TASM.EXE resid27.asm
;TLINK.EXE resid27.obj /t/x

;-------------------------------------------------------------------------
; === Начало программы: ===
cseg segment
.286
assume cs:cseg, ds:cseg, ss:cseg, es:cseg
org 100h

Begin:
jmp Init ; На метку инициализации 

;--------------------------------------------------------------------------
; === Обработчик 21h-ого прерывания ===
Int_21h_proc proc
; ---
cmp ax,9988h ;Проверка на повторную загрузку?
jne No_test
xchg ah,al ;Даем наш "отклик".
iret ;И моментально выходим из прерывания...

; ---
No_test:
cmp ax,9999h ;Получаем информацию о нашем резиденте?
jne No_remove
mov ax,9998h ;Даем отклик и...

push cs ;...передаем в ES сегмент резидента...
pop es
mov dx,offset Int_21h_proc ;...в DX смещение...

;...BX - смещение оригинального обработчика...
mov bx,word ptr cs:[Int_21h_vect]
;...CX - сегмент оригинального обработчика...
mov cx,word ptr cs:[Int_21h_vect+2]
iret ;...и выходим из резидента.

; ---
No_remove:

;Передаем управление предыдущему (оригинальному) обработчику
;21h-ого прерывания без условия того, что потом опять вернемся сюда.

jmp dword ptr cs:[Int_21h_vect]

Int_21h_vect dd ?
Int_21h_proc endp
;---------------------------------------------------------------------------


;-----------------------------------------------------------------------------------
; === Обработчик 09h-ого прерывания ===
Int_09h_proc proc
cli ;Запрещаем все прерывания
pushf ;Дадим сперва управление...
call dword ptr cs:[Int_09h_vect] ;...оригинальному обработчику

pusha ;Сохраним регистры в стеке...
push es
push ds

in al,60h ;Получим СКАН-КОД нажатой клавиши.

cmp al,58h ;Нажали F12?
jnz Exit_09h ;Если да - выводим сообщение

mov dx,offset Prog_work
call Out_mess


Exit_09h:
pop ds ;Восстановим сохраненные регистры...
pop es
popa

sti ;Разрешим прерывания и
iret ;выходим из процедуры.

Int_09h_vect dd ?
Int_09h_proc endp
;-------------------------------------------------------------------------------




; === Инициализация (подготовка и настройка резидента) ===
Init:
mov es,word ptr cs:[2Ch] ;Получим сегмент окружения DOS.
mov ah,49h ;Функция освобождения памяти.
int 21h ;Освобождаем память...

call Get_cmd ;Проверим командную строку

;Итак,
;Если ничего в командной строке не введено, тогда пробуем установить резидент.
or al,al
jz Ok_cmd ;

;Если в командной строке введено '/u', то пробуем удалить программу из памяти.
cmp al,1
je Remove ;

;В противном случае выведем сообщение о неверной командной строке
;и завершимся...
Bad_cmd:
mov dx,offset Mess_badcmd
call Out_mess
ret


;На процедуру удаления программы из памяти...
Remove:
jmp Remove_prog ;


;Устанавливаем резидент.
Ok_cmd:
mov ax,9988h ;Проверка на повторную загрузку.
int 21h
cmp ax,8899h ;Получили наш отклик?
jne Next_step2 ;Нет. Тогда 

;Мы уже в памяти! Выведем соответствующую строку.
mov dx,offset Mess_memory
call Out_mess ;Универсальная процедура вывода строки.
ret ;Выйдем в DOS...

Next_step2:
; === 21h ===
;Все готово для перехвата прерывания и установки резидента.
mov ax,3521h
int 21h ;Получим и сохраним адрес (вектор) 21h прерывания
mov word ptr cs:[Int_21h_vect],bx ;Смещение...
mov word ptr cs:[Int_21h_vect+2],es ;Сегмент...

mov ax,2521h
mov dx,offset Int_21h_proc
int 21h ;"Повесим" нашу процедуру на 21h прерывание


; === 09h ===
mov ax,3509h
int 21h ;Получим и сохраним адрес (вектор) 09h прерывания
mov word ptr cs:[Int_09h_vect],bx ;Смещение...
mov word ptr cs:[Int_09h_vect+2],es ;Сегмент...

mov ax,2509h
lea dx,Int_09h_proc
int 21h ;"Повесим" нашу процедуру на 09h прерывание


;Выведем сообщение, что, мол, все в порядке!!! Программа загружена в память!
mov dx,offset Mess_hello
call Out_mess

;Оставляем резидентную часть в памяти и выходим в DOS.
mov dx,offset Init
int 27h



;------------------------------------------------------------------------------------------------------
; ======= Пошли подпрограммы =======

; --- Получим параметры в командной строке ---
Get_cmd proc
mov si,80h ;SI=смещение командной строки.
lodsb ;Получим кол-во символов.
or al,al ;Если 0 символов введено,
jz Got_cmd ;то все в порядке. 
cmp al,3 ;Иначе ввели не 3 символа? (пробел + /u)
jne No_string ;Да - на метку No_string 

inc si ;Теперь SI указывает на первый символ строки.

Next_char:
lodsw ;Получаем два символа
cmp ax,'u/' ;Это /u? Помним, что данные будут наоборот!!!
jne No_string ;Да - на выход... 

mov al,1 ;Сигнал того, что пора удалять программу из памяти
ret

Got_cmd:
xor al,al ;Сигнал того, что ничего не ввели в командной строке
ret ;Выходим из процедуры

No_string:
mov al,3 ;Сигнал неверного ввода командной строки
ret ;Выходим из процедуры
Get_cmd endp


; === Удаляем программу из памяти ===
Remove_prog:
;Прежде посылаем сигнал 21h-ому прерыванию, т.е. 9999h.
mov ax,9999h
int 21h

;Если в ответ получаем 9998h, то наш резидент "сидит" в памяти.
cmp ax,9998h
je In_mem ;Перейдем на соответствующую метку.

;Если мы не получили отклик (9998h), то наш резидент не загружен.
;Сообщим об этом пользователю и выйдем в DOS.
mov dx,offset Mess_badmem
call Out_mess
ret

;-------------------------------------------------------------------------------
;Итак, наш резидент сидит в памяти.

;Помимо отклика от нашего резидента мы также получаем (см. процедуру
;обрабоки прерывания 21h выше):
;* ES = сегмент, в который загрузился резидент;
;* DX = смещение резидента в данном сегменте;
;* CX = сегмент оринигального (прежнего) обработчика прерывания 21h;
;* BX = смещение оринигального (прежнего) обработчика прерывания 21h.

In_mem:
push es ;Сохраним некоторые регистры в стеке,..
push bx

mov Seg_21h,es ;...а также в переменных.
mov Off_21h,dx

push bx
push cx

mov ax,3521h
int 21h ;Получим адрес обработчика 21h-прерывания.

;Равен ли он тому, куда загружен наш обработчик?
;Если так, то никто не "повис" над нами. Т.е. можно смело удалять нашу
;программу из памяти.
mov ax,es
cmp ax,Seg_21h
jne Cannot_remove

cmp bx,Off_21h
jne Cannot_remove

;Вот и удаляем. Внимательно проследите, что мы загружаем в регистры!
cli
mov ax,2521h
pop ds
pop dx
int 21h

push cs
pop ds

mov ah,49h
int 21h
sti

;Программа удалена! Выведем сообщение об успешном удалении и вернемся в DOS.
mov dx,offset Remove_okmess

Exit_prog:
call Out_mess
int 20h


;Невозможно удалить программу, т.к. кто-то "повис" над нами.
Cannot_remove:
;Сообщим о случившейся беде пользователю и выйдем в DOS...
mov dx,offset Mess_cantremove
jmp short Exit_prog

Seg_21h dw ?
Off_21h dw ?

;--------------------------------------------------------------------------------------------
; === Вывод строки на экран ===
Out_mess proc
mov ah,9 ;Выводим строку. DX уже должен содержать ее адрес!
int 21h

mov ah,9 ;Выводим сообщение типа "нажмите любую клавишу".
mov dx,offset Any_key
int 21h

xor ah,ah ;Ждем нажатия на клавишу...
int 16h

ret
Out_mess endp

;----------------------------------------------------------------------------------------------
; === Сообщения ===
Mess_hello db 0Ah,0Dh,'Резидентная программа.',0Ah,0Dh
db 'Программа загружена в память',0Ah,0Dh,'$'

Prog_work db 0Ah,'Программа работает!',0Ah,0Dh,'$'

Mess_memory db 0Ah,0Dh,' Программа уже загружена в память ',0Ah,0Dh
db 'Для ее удаления из памяти укажите /u в командной строке.',0Ah,0Dh,'$'

Mess_badcmd db 0Ah,0Dh,'Неверно указан параметр в командной строке.',0Ah,0Dh
db 'Укажите /u, если хотите удалить программу из памяти.',0Ah,0Dh,'$'

Mess_badmem db 0Ah,0Dh,'Программы ведь нет в памяти. Удаление невозможно.',0Ah,0Dh,'$'

Remove_okmess db 0Ah,0Dh,'Программа успешно удалена из памяти.',0Ah,0Dh,'$'

Mess_cantremove db 0Ah,0Dh,'Не могу удалить резидент из памяти.',0Ah,0Dh,0Ah
db 'Какая-то программа перехватила 21h-ое прерывание после того,',0Ah,0Dh
db 'как загружен был RES.COM. Прежде необходимо удалить ее из памяти,',0Ah,0Dh
db 'а потом уже удалять RES.COM!',0Ah,0Dh,'$'

Any_key db 0Ah,'Для продолжения нажмите любую клавишу...$'

cseg ends
end Begin

Обсуждение

давно
Посетитель
7438
7205
22.10.2009, 10:39
общий
это ответ
Здравствуйте, dr213.
Исправлены все Ваши ошибки:
1) Строки, выводимые из резидента, должны быть доступны из резидента.
Не забывайте, что после
mov dx,offset Init
int 27h
все последующее за Init отсекается
2) Подпрограмма Out_mess должна быть доступна из резидента
3) Для вывода сообщения из прерывания лучше использовать int 10h
4) 9 вектор вообще построен неправильно. После того, как Вы отработаете старым вектором, in al,60h Вам уже не даст скан-код
5) Освобождение сегмента окружения надо делать после того, как Вы возьмете оттуда параметр командной строки

Приложение:
;TASM.EXE resid27.asm
;TLINK.EXE resid27.obj /t/x

;-------------------------------------------------------------------------
;=== Начало программы: ===
cseg segment
.286
assume cs:cseg, ds:cseg, ss:cseg, es:cseg
org 100h

Begin:
jmp Init ; На метку инициализации 

Any_key db 0dh,0Ah,'Для продолжения нажмите любую клавишу...$'
Prog_work db 0dh,0Ah,'Программа работает!',0Ah,0Dh,'$'

;--------------------------------------------------------------------------
; === Обработчик 21h-ого прерывания ===
Int_21h_proc proc
; ---
cmp ax,9988h ;Проверка на повторную загрузку?
jne No_test
xchg ah,al ;Даем наш "отклик".
iret ;И моментально выходим из прерывания...

; ---
No_test:
cmp ax,9999h ;Получаем информацию о нашем резиденте?
jne No_remove
mov ax,9998h ;Даем отклик и...

push cs ;...передаем в ES сегмент резидента...
pop es
mov dx,offset Int_21h_proc ;...в DX смещение...

;...BX - смещение оригинального обработчика...
mov bx,word ptr cs:[Int_21h_vect]
;...CX - сегмент оригинального обработчика...
mov cx,word ptr cs:[Int_21h_vect+2]
iret ;...и выходим из резидента.

; ---
No_remove:

;Передаем управление предыдущему (оригинальному) обработчику
;21h-ого прерывания без условия того, что потом опять вернемся сюда.

jmp dword ptr cs:[Int_21h_vect]

Int_21h_vect dd ?
Int_21h_proc endp
;---------------------------------------------------------------------------
; === Вывод строки на экран ===
Out_mess proc
push si
mov si,dx
lll:
lods byte ptr cs:[si]
cmp al,'$'
je om_ret
mov ah,0eh
int 10h
jmp lll
om_ret:
; mov ah,9 ;Выводим строку. DX уже должен содержать ее адрес!
; int 21h

pop si
ret
Out_mess endp
;-----------------------------------------------------------------------------------
; === Обработчик 09h-ого прерывания ===
Int_09h_proc proc
cli ;Запрещаем все прерывания
push ax
in al,60h ;Получим СКАН-КОД нажатой клавиши.

cmp al,58h ;Нажали F12?
jnz to_old_09h ;Если да - выводим сообщение

pushf
call dword ptr cs:[Int_09h_vect] ;оригинальный обработчик

push dx
mov dx,offset Prog_work
call Out_mess
pop dx

pop ax
iret

to_old_09h:
pop ax
jmp dword ptr cs:[Int_09h_vect] ;на оригинальный обработчик

Int_09h_vect dd ?
Int_09h_proc endp
;-------------------------------------------------------------------------------

; === Инициализация (подготовка и настройка резидента) ===
Init:
call Get_cmd ;Проверим командную строку

;Итак,
;Если ничего в командной строке не введено, тогда пробуем установить резидент.
or al,al
jz Ok_cmd

;Если в командной строке введено '/u', то пробуем удалить программу из памяти.
cmp al,1
je Remove

;В противном случае выведем сообщение о неверной командной строке
;и завершимся...
Bad_cmd:
mov dx,offset Mess_badcmd
call Out_mess
ret

;На процедуру удаления программы из памяти...
Remove:
jmp Remove_prog

;Устанавливаем резидент.
Ok_cmd:
mov ax,9988h ;Проверка на повторную загрузку.
int 21h
cmp ax,8899h ;Получили наш отклик?
jne Next_step2 ;Нет. Тогда 

;Мы уже в памяти! Выведем соответствующую строку.
mov dx,offset Mess_memory
call Out_mess ;Универсальная процедура вывода строки.
ret ;Выйдем в DOS...

Next_step2:
; === 21h ===
;Все готово для перехвата прерывания и установки резидента.
mov ax,3521h
int 21h ;Получим и сохраним адрес (вектор) 21h прерывания
mov word ptr cs:[Int_21h_vect],bx ;Смещение...
mov word ptr cs:[Int_21h_vect+2],es ;Сегмент...

mov ax,2521h
mov dx,offset Int_21h_proc
int 21h ;"Повесим" нашу процедуру на 21h прерывание

; === 09h ===
mov ax,3509h
int 21h ;Получим и сохраним адрес (вектор) 09h прерывания
mov word ptr cs:[Int_09h_vect],bx ;Смещение...
mov word ptr cs:[Int_09h_vect+2],es ;Сегмент...

mov ax,2509h
lea dx,Int_09h_proc
int 21h ;"Повесим" нашу процедуру на 09h прерывание

;Выведем сообщение, что, мол, все в порядке!!! Программа загружена в память!
mov dx,offset Mess_hello
call Out_mess

mov es,word ptr cs:[2Ch] ;Получим сегмент окружения DOS.
mov ah,49h ;Функция освобождения памяти.
int 21h ;Освобождаем память...

;Оставляем резидентную часть в памяти и выходим в DOS.
mov dx,offset Init
int 27h

;------------------------------------------------------------------------------------------------------
; ======= Пошли подпрограммы =======

; --- Получим параметры в командной строке ---
Get_cmd proc
mov si,80h ;SI=смещение командной строки.
lodsb ;Получим кол-во символов.
or al,al ;Если 0 символов введено,
jz Got_cmd ;то все в порядке. 
cmp al,3 ;Иначе ввели не 3 символа? (пробел + /u)
jne No_string ;Да - на метку No_string 

inc si ;Теперь SI указывает на первый символ строки.

Next_char:
lodsw ;Получаем два символа
cmp ax,'u/' ;Это /u? Помним, что данные будут наоборот!!!
jne No_string ;Да - на выход... 

mov al,1 ;Сигнал того, что пора удалять программу из памяти
ret

Got_cmd:
xor al,al ;Сигнал того, что ничего не ввели в командной строке
ret ;Выходим из процедуры

No_string:
mov al,3 ;Сигнал неверного ввода командной строки
ret ;Выходим из процедуры
Get_cmd endp


; === Удаляем программу из памяти ===
Remove_prog:
;Прежде посылаем сигнал 21h-ому прерыванию, т.е. 9999h.
mov ax,9999h
int 21h

;Если в ответ получаем 9998h, то наш резидент "сидит" в памяти.
cmp ax,9998h
je In_mem ;Перейдем на соответствующую метку.

;Если мы не получили отклик (9998h), то наш резидент не загружен.
;Сообщим об этом пользователю и выйдем в DOS.
mov dx,offset Mess_badmem
call Out_mess
ret

;-------------------------------------------------------------------------------
;Итак, наш резидент сидит в памяти.

;Помимо отклика от нашего резидента мы также получаем (см. процедуру
;обрабоки прерывания 21h выше):
;* ES = сегмент, в который загрузился резидент;
;* DX = смещение резидента в данном сегменте;
;* CX = сегмент оринигального (прежнего) обработчика прерывания 21h;
;* BX = смещение оринигального (прежнего) обработчика прерывания 21h.

In_mem:
push es ;Сохраним некоторые регистры в стеке,..
push bx

mov Seg_21h,es ;...а также в переменных.
mov Off_21h,dx

push bx
push cx

mov ax,3521h
int 21h ;Получим адрес обработчика 21h-прерывания.

;Равен ли он тому, куда загружен наш обработчик?
;Если так, то никто не "повис" над нами. Т.е. можно смело удалять нашу
;программу из памяти.
mov ax,es
cmp ax,Seg_21h
jne Cannot_remove

cmp bx,Off_21h
jne Cannot_remove

;Вот и удаляем. Внимательно проследите, что мы загружаем в регистры!
cli
mov ax,2521h
pop ds
pop dx
int 21h

push cs
pop ds

mov ah,49h
int 21h
sti

;Программа удалена! Выведем сообщение об успешном удалении и вернемся в DOS.
mov dx,offset Remove_okmess

Exit_prog:
call Out_mess
int 20h


;Невозможно удалить программу, т.к. кто-то "повис" над нами.
Cannot_remove:
;Сообщим о случившейся беде пользователю и выйдем в DOS...
mov dx,offset Mess_cantremove
jmp short Exit_prog

Seg_21h dw ?
Off_21h dw ?

;----------------------------------------------------------------------------------------------
; === Сообщения ===
Mess_hello db 0Ah,0Dh,'Резидентная программа.',0Ah,0Dh
db 'Программа загружена в память',0Ah,0Dh,'$'

Mess_memory db 0Ah,0Dh,' Программа уже загружена в память ',0Ah,0Dh
db 'Для ее удаления из памяти укажите /u в командной строке.',0Ah,0Dh,'$'

Mess_badcmd db 0Ah,0Dh,'Неверно указан параметр в командной строке.',0Ah,0Dh
db 'Укажите /u, если хотите удалить программу из памяти.',0Ah,0Dh,'$'

Mess_badmem db 0Ah,0Dh,'Программы ведь нет в памяти. Удаление невозможно.',0Ah,0Dh,'$'

Remove_okmess db 0Ah,0Dh,'Программа успешно удалена из памяти.',0Ah,0Dh,'$'

Mess_cantremove db 0Ah,0Dh,'Не могу удалить резидент из памяти.',0Ah,0Dh,0Ah
db 'Какая-то программа перехватила 21h-ое прерывание после того,',0Ah,0Dh
db 'как загружен был RES.COM. Прежде необходимо удалить ее из памяти,',0Ah,0Dh
db 'а потом уже удалять RES.COM!',0Ah,0Dh,'$'


cseg ends
end Begin
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
22.10.2009, 11:44
общий
Ответ перенесен из формы ответа.

Здравствуйте, dr213.

после вызова перехваченного обработчика это ставить нельзя, лучше перед и выставить флаг в к-л переменной
Код:

pushf ;Дадим сперва управление...
call dword ptr cs:[Int_09h_vect] ;...оригинальному обработчику

pusha ;Сохраним регистры в стеке...
push es
push ds
in al,60h ;Получим СКАН-КОД нажатой клавиши.

cmp al,58h ;Нажали F12?
jnz Exit_09h ;Если да - выводим сообщение

Ответил: Airyashov, Студент
Ответ отправлен: 22.10.2009, 07:12
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Форма ответа