Приветствуем всех! В прошлый раз мы писали программу по модификации com-файлов. Немного огорчим — она будет работать не со всеми программами, а простыми вроде hello.com (можете проверить). Давайте напишем другую программу, которая будет работать по следующему алгоритму (на основе всё той же программы из 16 урока):
1. Считает файл hello.com себе в память позади основного кода. 2. Сохранит первые три байта программы hello.com в нашей памяти. 3. Заменит первые три байта программы hello.com,
находящейся в памяти, конструкцией jmp xx, где xx - вычисленное
значение в памяти позади считанной программы и записанных за ней 3-х
байт. 4. Запишет далее в хвост произвольный код, содержащий как минимум программу восстановления файла hello.com в частности и большинства других файлов в целом. 5. Сохранит новый файл hello.com таким образом, чтобы при запуске он переходил по адресу нашей программы, восстанавливал первые 3 байта и запускал её. Файл hello.com должен выводить строчку, как и раньше.
Поехали!
;Всё, что следует за значком ";" - это комментарий.
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,100h ;Определяем количество читаемых байт (напр.255). mov dx,offset Finish ;по
адресу памяти за нашей программой. int 21h
push ax ;В стеке - число реально прочитанных байт.
mov si,offset Finish ;Переносим 3 байта c начала программы hello.com в конец. add ax,offset Finish ;Число прочитанных байт прибавляем к адресу начала программы. mov di,ax ;В di - адрес области за всем файлом
hello.com. mov cx,3 ;Перемещаем (сохраняем) 3 байта. rep movsb
add ax,3 ;В ax хранится адрес начала области нашей программы восстановления. push ax
pop ax pop cx push ax ;Нехитрой комбинацией устанавливаем в cx количество байт программы.
add cx,1 ;Плюс два
(!) перенесённых байта. add cx,100h ;Теперь в cx адрес (куда прыгать) в самом hello.com.
mov si,offset Finish inc si mov word ptr cs:[si],cx ;Ещё два байта hello.com = адрес, куда "прыгать".
pop ax push ax ;В ax хранится адрес начала области нашей программы восстановления.
mov si,offset Add_nop ;Переносим
х байт c адреса смещения Add_nop mov di,ax ;в конец нашей программы, за нашими сохранёнными байтами. ;Сколько байт переносить? mov ax,offset Add_nop mov cx,offset End_nop sub cx,ax ;В cx - длина тела нашей программы.
pop ax ;В ax хранится адрес начала области нашей программы восстановления. sub ax,offset Finish add ax,cx ;В ax - новая
длина файла hello.com. push ax ;Запоминаем ax.
rep movsb
mov dx,0h ;Устанавливаем указатель с начала файла +0h. mov cx,0h ;+0h мы указали потому, что будем mov ax,4200h ;перезаписывать файл hello.com с первого байта. int 21h
mov ah,40h ;Записываем программу (в т.ч.$) из смещения Finish mov dx,offset Finish ;по адресу, указанному выше. pop cx ;В cx - длина записываемой нами программы. Стек обнулён. 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
Add_nop equ $ ;Будущие строчки в конце файла hello.com. push cx ;Сохраняем cx - там длина программы hello.com push si push di
mov si,word ptr cs:[0101h] ;Заносим в SI адрес начала программы восстановления. ;Мы ведь оттуда
"прыгнули"! sub si,1 ;А первый наш байт находится выше.
mov di,100h ;в начало программы mov cx,3 rep movsb
NOP NOP NOP
pop di ;Восстанавливаем регистры. pop si pop cx
mov ax,100h jmp ax ;Безусловный переход в начало программы.
End_nop equ $ ;Конец нашей внедряемой программы.
File_name db 'hello.com',0,'!$' ;Имя
файла.
Mess_ok db 'Файл обновлён. Всем спасибо.$' ;Успешное сообщение и сообщение об ошибке (ниже).
Mess_bad db 'Файл не найден. Поместите hello.com в каталог с программой.$'
Handle dw 0FFFFh ;Определяем переменную (для идентификатора файла).