Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Уроки ассемблеру. Быстро и просто. Урок 11


Всем доброго времени суток и хорошего настроения!

В 8-м уроке мы узнали, как можно записывать информацию в файлы. Давайте попробуем создать патч для обновления нашей программы. Допустим, у нас есть программа из второго урока, выводящая на экран строчку "Hello, world!". Нам нужно написать обновление для неё. Вместо строки "Hello, world!", после выполнения нашего патча, будем выводить строку "Goodbye, world!".

Из второго урока возьмём исходный код, сохраним в файле hello.asm в кодировке 866 и скомпилируем его в файл hello.com: ml hello.asm /AT.

Текст программы из урока 2:
;Всё, что следует за значком ";" - это комментарий.

.286 ;Разрешает ассемблирование непривилегированных инструкций
;процессора 80286 (реальный режим) и инструкций арифметического
;сопроцессора 80287.

CSEG segment ;Даём имя сегменту, а точнее определяем абсолютный
;сегмент в памяти программ по определённому адресу.
;Имя нашего сегмента будет CSEG.

assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG ;Задаём сегментные регистры, которые будем использовать для
;вычисления действующего адреса для всех меток и переменных, опре-
;делённых для сегмента или группы сегментов с указанным именем.
;У нас их четыре, - CS, DS, ES, SS и они будут указывать на наш
;единственный сегмент (мы его назвали CSEG).

org 100h ;Устанавливаем счётчик инструкций в текущем сегменте в соот-
;ветствии с адресом, задаваемым "выражением".
;Сейчас этот счётчик равен 100h - используется для всех программ
;типа .com

begin: ;Метка начала программы.

;Выводим фразу, используя функцию 9 прерывания 21h.
mov ah,9 ;Загружаем в регистр ah число 9 (указываем функцию).
mov dx,offset helloworld ;Указываем, что за фразу мы будем выводить.
int 21h ;Выводим фразу.

int 20h ;Выходим в DOS.

helloworld db 'Hello, world!$' ;Определяем переменную helloworld, доступную побайтно, с фразой
;"Hello, world!". В одинарных кавычках, после знака "!" ставим
;знак "$".

CSEG ends ;Указываем на завершение сегмента CSEG.
end begin ;Конец программы.



Теперь создаём программу test.com, позволяющую исправить наш файл, не считывая его в память:

;Всё, что следует за значком ";" - это комментарий.

.286 ;Разрешает ассемблирование непривилегированных инструкций
;процессора 80286 (реальный режим) и инструкций арифметического
;сопроцессора 80287.

CSEG segment ;Даём имя сегменту, а точнее определяем абсолютный
;сегмент в памяти программ по определённому адресу.
;Имя нашего сегмента будет CSEG.

assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG ;Задаём сегментные регистры, которые будем использовать для
;вычисления действующего адреса для всех меток и переменных, опре-
;делённых для сегмента или группы сегментов с указанным именем.
;У нас их четыре, - CS, DS, ES, SS и они будут указывать на наш
;единственный сегмент (мы его назвали CSEG).

org 100h ;Устанавливаем счётчик инструкций в текущем сегменте в соот-
;ветствии с адресом, задаваемым "выражением".
;Сейчас этот счётчик равен 100h - используется для всех программ
;типа .com

begin: ;Метка начала программы.


;Данная программа будет обновлять файл
;hello.com.

mov ax,3D02h ;Первоначально откроем файл для чтения-записи.
mov dx,offset File_name
int 21h
jc Message_bad

mov Handle,ax ;Если файл существует, сохраняем номер файла.
mov bx,ax

mov dx,9h ;Устанавливаем указатель с начала файла +9h.
mov cx,0h ;+9h мы указали потому, что просмотрели программу
mov ax,4200h ;hello.com в отладчике afdpro и увидели, что
int 21h ;начало строки "Hello, world!" находит по этому адресу.

mov ah,40h ;Записываем 16 символов (в т.ч.$) из смещения New_phrase
mov dx,offset New_phrase ;по адресу, указанному выше.
mov cx,16
int 21h

Message_ok: ;Подпрограмма успешного вывода строки и выхода.
mov bx,Handle ;Сначала сохраним файл, восстановив bx.
mov ah,3Eh ;Закроем файл.
int 21h

mov ah,9 ;Выводим строку.
mov dx,offset Mess_ok
int 21h
int 20h

Message_bad: ;Подпрограмма вывода сообщения об ошибке и выхода.
mov ah,9
mov dx,offset Mess_bad
int 21h
int 20h

File_name db 'hello.com',0,'!$' ;Имя файла.

Mess_ok db 'Файл обновлён. Всем спасибо.$' ;Успешное сообщение и сообщение об ошибке (ниже).

Mess_bad db 'Файл не найден. Поместите hello.com в каталог с программой.$'

Handle dw 0FFFFh ;Определяем переменную (для идентификатора файла).

New_phrase db 'Goodbye, world!$' ;Новая фраза, которая заменяет "Hello, world!".


CSEG ends
end begin

Комментариев мы уже используем чуть меньше, но думаю, если вы разобрались с 8-м уроком, то здесь всё понятно. Теперь усложним задачу.
Задача будет аналогичная, но файл должен быть загружен в память, и там изменён. Жирным шрифтом, по традиции, указываем отличия от предыдущего листинга.

;Всё, что следует за значком ";" - это комментарий.

.286 ;Разрешает ассемблирование непривилегированных инструкций
;процессора 80286 (реальный режим) и инструкций арифметического
;сопроцессора 80287.

CSEG segment ;Даём имя сегменту, а точнее определяем абсолютный
;сегмент в памяти программ по определённому адресу.
;Имя нашего сегмента будет CSEG.

assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG ;Задаём сегментные регистры, которые будем использовать для
;вычисления действующего адреса для всех меток и переменных, опре-
;делённых для сегмента или группы сегментов с указанным именем.
;У нас их четыре, - CS, DS, ES, SS и они будут указывать на наш
;единственный сегмент (мы его назвали CSEG).

org 100h ;Устанавливаем счётчик инструкций в текущем сегменте в соот-
;ветствии с адресом, задаваемым "выражением".
;Сейчас этот счётчик равен 100h - используется для всех программ
;типа .com

begin: ;Метка начала программы.


;Данная программа будет обновлять файл
;hello.com.

mov ax,3D02h ;Первоначально откроем файл для чтения-записи.
mov dx,offset File_name
int 21h
jc Message_bad

mov Handle,ax ;Если файл существует, сохраняем номер файла.
mov bx,ax

mov ah,3Fh ;Чтение в память.
mov cx,19h ;Определяем количество читаемых байт (напр.25 - будущая длина hello.com).
mov dx,offset Finish ;по адресу памяти за нашей программой.
int 21h

;Файл в нашей памяти. Строка Hello, world находится в db
;в смещении 109h от начала программы hello, т.е. через 9 байт
;У нас она располагается тоже через 9 байт, но после конца (Finish equ $)
;Определим её настоящий адрес:

add dx,9h

mov si,offset New_phrase ;Переносим 16 букв c адреса смещения переменной New_phrase
mov di,dx ;на новый адрес dx (конец нашей програмы + 9 байт).
mov cx,16
rep movsb

mov dx,0h ;Устанавливаем указатель с начала файла +0h.

mov cx,0h ;+0h мы указали потому, что будем
mov ax,4200h ;перезаписывать файл hello.com с первого байта.
int 21h ;

mov ah,40h ;Записываем 25 символов (в т.ч.$) из смещения Finish
mov dx,offset Finish ;по адресу, указанному выше.
mov cx,25

int 21h ;Смотрим отладчик - файл прочтён и строка в памяти заменилась!

Message_ok: ;Подпрограмма успешного вывода строки и выхода.
mov bx,Handle ;Сначала сохраним файл, восстановив bx.
mov ah,3Eh ;Закроем файл.
int 21h

mov ah,9 ;Выводим строку.
mov dx,offset Mess_ok
int 21h
int 20h

Message_bad: ;Подпрограмма вывода сообщения об ошибке и выхода.
mov ah,9
mov dx,offset Mess_bad
int 21h
int 20h

File_name db 'hello.com',0,'!$' ;Имя файла.

Mess_ok db 'Файл обновлён. Всем спасибо.$' ;Успешное сообщение и сообщение об ошибке (ниже).

Mess_bad db 'Файл не найден. Поместите hello.com в каталог с программой.$'

Handle dw 0FFFFh ;Определяем переменную (для идентификатора файла).

New_phrase db 'Goodbye, world!$' ;Новая фраза, которая заменяет "Hello, world!".

Finish equ $ ;Метка конца нашей программы!

CSEG ends
end begin

Новое в программе:
mov ah,3Fh — Изучаем функцию 3fh прерывания 21h: HELP.EXE -> Указатель функций DOS/BIOS -> Функции DOS -> 3fH Read File.
rep movsb и предыдущие три строки — программка переноса данных из одного места в другое. Разберёмся с ней чуть позже.
Finish equ $ — Новая для нас инструкция. Является меткой конца файла. При использовании mov dx,offset Finish в dx записывается адрес смещения данной метки — т.е. конца программы. Рекомендуем проверить это в отладчике AFDPro.

В избранное