Привет. Это снова Алексей Маланов из «Лаборатории Касперского». В прошлый раз я рассказывал про опыт найма вирусных аналитиков, а сегодня расскажу про то, что делают вирусописатели, чтобы их работа не была замечена, и что делаем мы, чтобы их труд в итоге оказался напрасен.
Вообще говоря, злоумышленник пишет вредоносную программу, почти всегда заранее зная, что она рано или поздно попадет на «операционный стол» вирусному аналитику. И вся информация из зловреда может быть использована против автора. А чего ему скрывать? Ну, во-первых свою личность. Порой в зловредах встречаешь строки, типа C:UsersVasiliy IvanovDocumentsVisual Studio 2005заработокtrojanReleasetrojan.pdb. Во-вторых, довольно много информации, облегчающей анализ зловреда. Давайте рассмотрим некоторые приемы вирусописателей, и выясним, почему же они бесполезны.
Что скрывают:
Интернет-ссылки
Экземпляры Trojan-Downloader в первую очередь скачивают/обновляют другое вредоносное ПО по одной или нескольким ссылкам. Очевидно, что если ссылки хранить в явном виде, то система автообработки любой антивирусной компании с легкостью извлечет их, добавит в список на периодическое скачивание и забанит доступ к этим ресурсам. Смысл Trojan-Downloader'а пропадает напрочь. Зловредам других классов также свойственно скачивать новые версии своих модулей.
Компоненты
Зловреды нынче сложные, многокомпонентые. Один модуль отвечает за обновление, второй — драйвер-руткит, третий обеспечивает сбор паролей, четвертый ответственен за прием команд из центра управления и передачу результатов. В общем, очень часто все это оборачивается в один «инсталлятор». Это может быть специальный Trojan-Dropper стороннего «разработчика», а то и просто очередной компонент того же автора. Так же, как и с URL'ами, если внутренние PE-EXE модули не спрятать надёжнее, их легко выдерут автоматические средства, и все файлы отправятся на автоанализ и автодетектирование. Причем, если хотя бы один из модулей будет признан вредоносным, то вся пачка уже точно не потребует ручного анализа, и все будет успешно задетектировано.
Пароли
Вы не поверите, но во вредоносных файлах авторы зашивают даже свои пароли. Не часто, конечно, но бывает. Например, если Trojan-Spy делает много снимков экрана (или даже снимает видео камерой жертвы), то обычный способ доставки материала автору — заливка на FTP. Вирусописатель создает один аккаунт с доступом на чтение и запись. И чтобы никто лишний не забрел на его FTP, выставляет пароль, который вписывается в код зловреда. В результате туда забредает не абы кто, а конкретный независимый исследователь, стирает все награбленное и оставляет записочку горе-автору. Замечу, что это «неправомерный доступ к компьютерной информации», вот только преступник не побежит писать заявление.
Другой пример — пароль от почтового ящика. Автору нужно переслать себе собранные у жертвы пароли. Но почтовый сервис, на котором у него зарегистрирован ящик, не позволяет отправлять письма непонятным людям и требует аутентификации при отправке. Понятно, что «серьезные» малварщики так не ошибаются, особенно те, кто нацелен на воровство банковской информации. Другие же, напротив, иногда намеренно рассчитывают, что их код будет просмотрен аналитиком. Сейчас-то почти все идет на автоматику, а вот 10 лет назад, когда я сам анализировал файлы, встречалось мне такое обращение к вирусному аналитику внутри сампла: «Это никем не детектируемый Pinch, и мне бы хотелось, чтобы он оставался таким. Вам для этого ничего не нужно делать. Спасибо.»
Тривиальное шифрование:
Отсюда вывод: вирусописателям есть что скрывать внутри своих творений, и они пытаются это делать. Если вы отлично знаете, что такое битовые операции XOR, ROL и понимаете, как при помощи них можно преобразовать данные, вы можете пропустить весь этот раздел.
Предположим, у вас есть строка secret.url, и вы хотите спрятать ее в коде от просмотра глазами. При этом алгоритм расшифровки и ключ тоже будут находиться в этом же коде, ведь вам самим эта строка понадобится. Пожалуй, самым-самым простым способом будет следующий.
Not
Представим строку в виде цепочки битов 110100001110100… Заменим каждый бит на противоположный 001011110001011… Получим строку "ЧЛЛП┼╨╨МЪЬНЪЛ╤КНУ". Теперь невооруженным глазом такую строку уже не узнать. Зато программно можно. Применим ко всему файлу операцию NОТ: то, что было зашифровано, расшифруется обратно. А что нет — зашифруется. В получившемся файле URL виден автоматике и глазам.
Xor
Слегка усложним. При шифровании инвертировать можно не каждый бит, а, например, каждый четвертый (чтобы никто не догадался). Такое вот инвертирование некоторых битов называется так же операцией XOR. Записывается a := a XOR key, где a — это преобразуемый байт, а key — ключ, в котором записано, какие биты меняем.
При этом, операция NOT эквивалентна a := a XOR FF. Шестнадцатеричное FF равно двоичному 11111111 — инвертируем каждый бит.
Для расшифровки нужно «поксорить» данные еще раз с тем же ключом. Убедиться, что при этом получится именно оригинал, я предлагаю любопытному читателю самостоятельно. Не забывайте, что в отличие от предыдущего метода, в этом вам придется хранить ключ в программе, если вы хотите расшифровать свои данные при исполнении. И аналитику тоже придется искать/перебирать ключ, чтобы дешифровать строку. Есть, впрочем, еще один способ, но об этом ниже.
Другие методы
Что еще можно сделать:
- Применять к каждому байту операцию ADD, то есть добавлять некое число — ключ
- Менять ключ от байта к байту. Например, первый байт «ксорить» с ключом 71, второй — с 73, третий — с 75 и т.д. Тогда ключ нельзя будет уже просто перебрать, перебирать придется уже 2 ключа, а это в 256 раз дольше
- Можно увеличить длину ключа. Например, первый DWORD «ксорить» с четырехбайтовым ключом, второй DWORD «ксорить» с ним же и т.д. Тогда для расшифровки без ключа вам придется перебирать уже 4 миллиарда вариантов. А если вы (или программа-выдиралка) не знаете, где точно расположена искомая информация в файле, то такой перебор придется производить для каждого байта (размер зловреда обычно составляет несколько сотен килобайт)
- Можно применить операцию циклического сдвига ROL на каждый байт. Но только при побайтовой шифрации «ролить» имеет смысл только на 1-7 битов. При циклическом сдвиге байта на 8 бит мы получаем тот же самый байт.
- Можно «ксорить» два раза. Один малварщик зашифровал строку. Ему показалось, что этого мало, он еще раз «поксорил» ее с тем же ключом. Что из этого получилось, догадайтесь сами.
- Строчку можно записать задом наперед
- Строчку можно записать через байт. Для ASCII-символов получится Unicode.
- Строчку можно сдвинуть на пол алфавита, то есть a->n, b->o, ..., z->m, 0->5, 1->6...
- Записать символы в hex-виде, то есть 687474703A2F2F
- Разместить строку на стеке
…
push 'w//:'
push 'ptth' - И тому подобное
Все обозначенные способы, очевидно, можно комбинировать для пущей конспирации :)
Расшифровка:
Обозначим задачу. Есть файл длиной в 1 Мб. Внутри в неизвестном месте зашифрована одним из описанных выше методов строка, начинающаяся с http://. Ключ неизвестен. Надо написать программу, которая обработает файл и, без эпического перебора, извлечет эту строку. Зашифровано может быть что-то еще, например, MZ-PE заголовок внутреннего файла, но с условием: вам известно, что именно вы ищете. А то искать неизвестный мусор, зашифрованный неизвестно чем, в неизвестном месте – не очень-то благодарное занятие.
Небольшое отступление. В ЛК сейчас используются такие методы автоанализа, которым все равно, что зашифровано, и как. Хотя бы даже очень стойким алгоритмом (в отличие от описанных). Если данные используются самой программой, они все равно будут нами извлечены. Ниже я лишь продемонстрирую, что описанные методы шифрования не защитят вирусописателя и не составят трудности даже для аналитика-любителя. Никаких откровений. Следите за руками.
Xor
Вы помните урл, к которому применили операцию NOT? "ЧЛЛП┼╨╨". Увидев эти буквы вновь, вы всегда угадаете, что это урл. А вот строка, поксоренная с ключом F0: "ШДДА╩▀▀". Или с 0F: "g{{⌂5 ". Обратите внимание, второй и третий, а также последний и предпоследний символы равны. «Полезное» свойство описанных байтовых преобразований — они переводят равные байты в равные. Но у XOR есть еще полезное свойство — операция обратима. То есть, из 'Ш' = 'h' XOR key
следует, что key = 'Ш' XOR 'h'. А это значит, что если есть последовательность "ЙЦУКЕНГ", то key := 'Й' XOR 'h'. Далее проверяем, что
'Ц' == 't' XOR key
'К' == 'p' XOR key
'Е' == ':' XOR key...
Если так и есть, то мы нашли урл и знаем ключ.
Add
Аналогично, если 'Й' = 'h' + key, то key = 'Й' — 'h'. И проверяем, что оставшаяся часть урла действительно является урлом.
Но вот что делать, если злоумышленник линейно менял ключ от байта к байту?
'Й' = 'h' + key
'Ц' = 't' + (key + d)
'У' = 't' + (key + 2d)
'К' = 'p' + (key + 3d)...
Все то же самое:
key = 'Й' — 'h'
key + d = 'Ц' — 't'
Если автор использовал четырехбайтовый ключ:
'ЙЦУК' = 'http' XOR key32
То мы все равно сможем вычислить ключ и даже провалидировать его на оставшихся трех известных байтах. Но если ключ длиной в саму искомую строку, то описанный подход не работает. Почему-то малварщики делают что угодно, но только не то, что на самом деле помогло бы им.
Комбинирование
Например, они комбинируют методы. Запишут урл задом наперед, да еще через байт, а потом еще поксорят с переменным ключом. Да, некоторую автоматику это может сбить с толку. По факту же это не мешает успешно детектировать такие файлы. А как только зловреда разберет вирусный аналитик, он добавит в дешифровщик метод Reverse(Unicode(LinearXor(stream))), после этого все старые и новые версии расшифровываются автоматически.
Водяные знаки
Еще одно применение описанных методов шифрации: водяные знаки в программе. Предположим, вы честный программист и отдаете копию программы клиентам Василию и Георгию. Нет, лучше Эдуарду и Григорию. И хотите знать, а не выкладывает ли один из них программу в публичный доступ. В исходном коде вы можете написать:
#pragma data_seg(".text")
extern char WATERMARK[]= «uQPW►▀}_▲WTW►»; // «Копия Эдуарда» XOR FF
После компиляции зашифрованная строчка будет в исполняемом файле, но обнаружить ее непросто. Ведь неизвестно, что искать и где.
Однако, как мы только что выяснили, если копия Эдуарда все-таки попадет в публичный доступ, то у Григория окажутся два варианта программы, и он без труда найдет разницу в них и разгадает шифр. Получится только хуже для вас, Григорий сможет подделывать чужие копии.
Заключение:
Вот в целом и все. Мы познакомились с некоторыми тривиальными методами шифрации и выяснили, почему же они бесполезны. Если у вас есть вопросы и комментарии — отлично, очень жду. Если же материал показался вам слишком простым, то вот вам жизненный пример из области анализа вирусов на эту же тему.
Вирус (инфектор) встраивает свое тело в заражаемый файл. Тело у него постоянно, но он его ксорит по DWORD'ам с псевдослучайными числами так:
srand(key);
crypted[0] = body[0] XOR rand();
crypted[1] = body[1] XOR rand();
…
Дешифратор тела с ключом «размазан» по коду жертвы (entry-point obscuring), поэтому детектировать придется по зашифрованному телу. Для примера можете считать, что телом вируса является секция кода notepad.exe.
Известно, что в качестве генератора псевдо-случайных чисел используется самый популярный в компиляторах алгоритм:
seed = seed * 1103515245 + 12345;
return (seed % ((unsigned int)RAND_MAX + 1));
/*
* This file is part of the libpayload project.
*
* It was originally taken from the OpenBSD project.
*
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <libpayload.h>
static unsigned int next = 1;
int rand_r(unsigned int *seed)
{
*seed = *seed * 1103515245 + 12345;
return (*seed % ((unsigned int)RAND_MAX + 1));
}
int rand(void)
{
return (rand_r(&next));
}
void srand(unsigned int seed)
{
next = seed;
}
Реально ли быстро в уме программно задетектировать этот вирус?
Автор: Kaspersky_Lab