Файл: О.А.Калашников. Ассемблер Это Просто. Учимся программировать.pdf
Добавлен: 16.02.2019
Просмотров: 29232
Скачиваний: 1689
Глава 17
Заражение файлов вирусом
17.1. Определение текущего смещения
выполняемого кода
Как уже упоминалось в прошлых главах, наш вирус теряется в адресах, если он
дописывается после кода заражаемого файла. Чтобы избежать этого, мы переноси-
ли код вируса в область 7-й видеостраницы, в которой он и выполнялся. Далее при-
веден один из вариантов определения текущего смещения выполняемого кода.
Причем этот вариант работает в любом случае, независимо от того, переместил ли
программист код собственной программы куда-либо по другому смещению или
нет. Помните, когда мы пытались записывать вирус в хвост файла, мы акцентиро-
вали ваше внимание на том, что команды вида
mov dx,offset Message
будут вы-
глядеть в отладчике как
mov dx,400h
. При перемещении вируса в хвост "програм-
мы-жертвы" мы, тем самым, перемещаем строку
Message
. В итоге получается, что
по смещению
400h
будет не наша строка, а что-то другое.
Получить же значение регистра
ip
обычной командой
mov
не удается. Поэтому
нам следует прибегнуть к более хитрым способам. Например, к способу из листин-
га 17.1.
Листинг 17.1. Получение значения регистра ip
...
;Якобы переходим на процедуру, при этом в стеке сохраняется адрес следующей
;за командой call инструкции (т. е. pop ax).
call Label_1
Label_1:
;И сразу же достаем этот адрес из стека.
pop ax
;Теперь в ax находится текущий адрес (смещение).
...
Глава 17. Заражение файлов вирусом
175
Почему команда
call Label_1
всегда заносит в стек текущее смещение, выяс-
нить можно в отладчике (рис. 17.1, 17.2 и 17.3). Тем не менее, мы позже рассмотрим
различие
mov dx,offset Message
и
call Label_1
. Мы выясним, почему в первом
случае в
dx
заносится константа (постоянное число), а во втором случае — "пла-
вающее". Так или иначе, считайте, что в приведенном выше примере мы получаем
всегда текущее смещение метки
Label_1
.
По этому принципу мы теперь получим текущее смещение в нашем вирусе, тем
самым, усложнив задачу и — как следствие — усовершенствовав сам код вируса.
Но об этом в следующем разделе.
Рис. 17.1. Перед вызовом лжеподпрограммы
Рис. 17.2. В стеке — смещение следующей команды
Часть III. Файловая оболочка, вирус, резидент
176
17.2. Вирус
Итак, открываем файл virus17.asm. Сразу же за меткой
Init
мы получаем сме-
щение, по которому расположена метка
Get_ip
(см. разд. 17.1). Из этого адреса
нужно вычесть смещение метки
Get_ip
.
Допустим, мы запускаем вирус первый раз. То есть все смещения содержат вер-
ные значения. Также допустим, что метка
Get_ip
находится по адресу
0203h
(лис-
тинг 17.2).
Листинг 17.2. Получаем текущее смещение в коде
...
(1) [1234:0200h] call Get_ip
(2) [1234:0203h] Get_ip:
(3) [1234:0203h] pop ax
(4) [1234:0204h] sub ax,offset Get_ip
...
На рис. 17.3 продемонстрировано, как приведенный выше фрагмент кода выгля-
дит в отладчике в случае, если вирус запускается первый раз.
Рис. 17.3. В ax — текущее смещение
Строка (1) заносит в стек число
0203h
, а (3) — достает из стека
0203h
. Таким об-
разом, мы оставили стек выровненным и получили текущий адрес (смещение) вы-
полняемого кода. Сегмент, как вы знаете, можно получить так:
mov ax,cs
, чего не
скажешь о
ip
. Загрузка и чтение напрямую числа в/из
ip
не допускается!
Далее происходит следующее: в строке (4) вычитаем из
203h
смещение метки
Get_ip
в памяти. В отладчике строка (4) будет всегда выглядеть так:
sub ax,0203h ;sub ax,offset Get_ip
ax=203h-203h=0
Глава 17. Заражение файлов вирусом
177
Теперь представим, что код вируса был перенесен самой программой в процессе
заражения в хвост некоторого файла, размером 1000h байт (обратите внимание на
смещения в скобках). Тот же самый код будет располагаться по такому адресу
(листинг 17.3).
Листинг 17.3. Код перемещен в хвост "файла-жертвы"
...
(1) [1234:1200h] call Get_ip
(2) [1234:1203h] Get_ip:
(3) [1234:1203h] pop ax
(4) [1234:1204h] sub ax,offset Get_ip
...
Тогда строка (1) заносит в стек число
1203h
.
Pop ax
достает его из стека. Затем
выполняется следующее действие:
sub ax,203h ;sub ax,offset Get_ip
ax=1203h-203h=1000h
Теперь в
ax
находится размер "файла-жертвы", т. е.
1000h
. Получили то, что нам
и нужно!
Вполне возможно, что принцип не до конца понятен. В таком случае особо не
переживайте, т. к. в главе 20, когда вирус будет полностью работоспособным, вы
увидите на практике, как именно подставляются необходимые нам значения. Сей-
час просто можете наблюдать за тем, как мы "играем" с адресами.
Итак, теперь в
ax
размер "файла-жертвы". Чтобы получить реальное смещение
того или иного адреса, нам достаточно прибавить к уже имеющемуся в регистре
"ненормальному" смещению то, что находится в
ax
, т. е. длину "файла-жертвы".
Вот что мы делаем в вирусе (листинг 17.4).
Листинг 17.4. Вычисление реального смещения
...
;Скопируем наш вирус в область 7-й видеостраницы, т. е. сделаем то,
;что уже делали в предыдущей главе, только "правильней".
push 0BF00h
pop es
;es — сегмент, куда будем перемещать код вируса,
mov di,offset Open_file
;di — смещение (адрес самой первой процедуры (см. файл-приложение))
mov si,di
;si должен содержать РЕАЛЬНЫЙ адрес (смещение), т. к. мы пока еще
;в сегменте "файла-жертвы"...
add si,ax
Часть III. Файловая оболочка, вирус, резидент
178
mov cx,offset Finish-100h
;Т. е. cx = длина нашего вируса в байтах
rep movsb ;Теперь в памяти две копии нашего вируса
...
Обратите внимание, как мы получаем адрес первой процедуры в памяти. Понят-
но, что после директивы
offset
не обязательно должен идти адрес метки или стро-
ки, например, для вывода с помощью функции
09h
. А так как метки и директивы
(инструкции программы-ассемблера) не занимают памяти, то в
di
мы занесем адрес
(смещение) первого байта процедуры
Open_file
, т. е.
mov ax,3D02h
(но не саму
команду
mov
, а ее смещение).
Копию кода в 7-й видеостранице мы сделали. Теперь можно и переходить
("прыгать") на нее. Но в данном случае мы не будем использовать оператор
jmp
.
Давайте чуть-чуть усложним задачу. Прежде чем разбирать, вспомним об операто-
рах возврата из подпрограмм и прерываний:
ret
,
retf
и
iret
(листинг 17.5).
Листинг 17.5. Переход по другому адресу с помощью retf
...
(1) mov bx,offset Lab_return
(2) add bx,ax
(3) push cs
(4) push bx
(5) retf
(6) Lab_jmp:
...
На рис. 17.4 и 17.5 показано, что процессор передает управление копии кода ви-
руса совсем в другой сегмент (следите за регистром
cs
).
Теперь, если вы разобрались с
retf
, то понять принцип работы приведенных
выше строк труда не составит. Команда
retf
достает из стека 2 слова (4 байта):
смещение и сегмент для возврата в то место, откуда дальняя процедура вызыва-
лась. Мы же имитируем вызов процедуры. Поверьте, процессору все равно, откуда
вызывалась процедура и куда нужно возвращаться. Более того, он вообще не сле-
дит за тем, вызывалась ли процедура. Однако при пользовании такими методами
нужно быть предельно внимательным, очень тщательно все подготовить и обяза-
тельно проверить несколько раз.
При вызове дальней процедуры в стек заносится сперва смещение, а затем сег-
мент для возврата. Делаем это в строках (1)—(4). Учитывая, что в компьютере дан-
ные хранятся наоборот, но стек растет снизу вверх, то в памяти сегмент и смеще-
ние будут располагаться именно так, как нам нужно. В строках (1), (2) мы