Книга: О.А.Калашников. Ассемблер Это Просто. Учимся программировать

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам
background image

Глава 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 находится текущий адрес (смещение). 

... 

background image

Глава 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. В стеке — смещение следующей команды 

background image

Часть 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 

background image

Глава 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 

background image

Часть 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)  мы 

Источник: https://files.student-it.ru/previewfile/2839