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

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


Приветствуем всех!
В прошлый раз мы писали программу по модификации 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 должен выводить строчку, как и раньше.

Поехали!

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

.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,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

mov si,offset Finish
mov byte ptr cs:[si],0EBh ;Первый байт hello.com = jmp

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 ;Определяем переменную (для идентификатора файла).

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

CSEG ends
end begin

В избранное