Два простых способа идентификации неизвестной малвари
Перед изучением неизвестного вредоносного файла, обычно пытаются выяснить
- Является ли малварь уже известной сообществу (проанализированной)
- Принадлежность к определенному семейству (вариант известного RAT/бэкдора)
- Принадлежность к конкретной APT группе
- Не все сэмплы загружаются в общедоступные базы. Бывают ситуации, когда антивирусным компаниям/исследователям сливать файл нельзя - разработчикам малвари будет известно, что их обнаружили. Они могут распространять разные версии малвари для разных регионов/организаций и увидев в публичном доступе свой файл, понять конкретное место утечки
- Разработчики малвари могут билдить варианты одной программы, с разными ip, набором ресурсов, payload, текстом на разных языках (в случае шифровальщиков), адреса btc кошельков и т.д. В этом случае, несмотря на то, что малварь по сути одна и та же, хэш будет отличаться.
imphash
imphash - это хэш, который вычисляется от списка импортируемых функций в PE файле. Пример импортируемых функций в PE файле:
Данный вид хэша решает две проблемы, описанные ранее:Официальная документация PE формата
Более расширенное описание PE формата: 1 часть, 2 часть
Впервые, метод был описан в отчете от FireEye в 2014 году
- Практически все общедоступные базы малвари прикладывают значение imphash. Поэтому вы можете найти связанные сэмплы поиском по imphash, не выдавая свой:
Пример поиска по imphash на Virusshare:Анонс Virustotal о вводе imphash в своей базе
- Каждый новый вариант малвари может содержать новые ip адреса, секции, ресурсы и т.д., но набор используемых функций будет неизменен
Авторы идеи imphash закоммитили патч, с алгоритмом вычисления, для популярного инструмента, для анализа PE файлов - pefile.
Алгоритм вычисления imphash:
Код:
#DIRECTORY_ENTRY_IMPORT (list of ImportDescData instances)
def get_imphash(self):
impstrs = []
exts = ['ocx', 'sys', 'dll']
if not hasattr(self, "DIRECTORY_ENTRY_IMPORT"):
return ""
for entry in self.DIRECTORY_ENTRY_IMPORT:
if isinstance(entry.dll, bytes):
libname = entry.dll.decode().lower()
else:
libname = entry.dll.lower()
parts = libname.rsplit('.', 1)
if len(parts) > 1 and parts[1] in exts:
libname = parts[0]
for imp in entry.imports:
funcname = None
if not imp.name:
funcname = ordlookup.ordLookup(entry.dll.lower(), imp.ordinal, make_name=True)
if not funcname:
raise Exception("Unable to look up ordinal %s:%04x" % (entry.dll, imp.ordinal))
else:
funcname = imp.name
if not funcname:
continue
if isinstance(funcname, bytes):
funcname = funcname.decode()
impstrs.append('%s.%s' % (libname.lower(),funcname.lower()))
return md5( ','.join( impstrs ).encode() ).hexdigest()
- Парсинг Import Directory Table
- Создание списка из элементов вида “имя_библиотеки.имя_функции”
- Вычисление md5 хэш списка
Backdoor.Win32.Agent.vuz.08248dd1e494f0cd7370067f430b3492. Вот так выглядит его таблица адресов импортируемых функций (IAT):
imphash в этом случае будет вычислен от следующей строки, которая является конкатенацией имен функций:
Код:
kernel32.getprocaddress,kernel32.loadlibrarya,kernel32.closehandle,kernel32.writefile,kernel32.createdirectorya,kernel32.gettemppatha,kernel32.readfile,kernel32.setfilepointer,kernel32.createfilea,kernel32.getmodulefilenamea,kernel32.getstringtypea,kernel32.lcmapstringw,kernel32.lcmapstringa,kernel32.heapalloc,kernel32.heapfree,kernel32.getmodulehandlea,kernel32.getstartupinfoa,kernel32.getcommandlinea,kernel32.getversion,kernel32.exitprocess,kernel32.heapdestroy,kernel32.heapcreate,kernel32.virtualfree,kernel32.virtualalloc,kernel32.heaprealloc,kernel32.terminateprocess,kernel32.getcurrentprocess,kernel32.unhandledexceptionfilter,kernel32.freeenvironmentstringsa,kernel32.freeenvironmentstringsw,kernel32.widechartomultibyte,kernel32.getenvironmentstrings,kernel32.getenvironmentstringsw,kernel32.sethandlecount,kernel32.getstdhandle,kernel32.getfiletype,kernel32.rtlunwind,kernel32.getcpinfo,kernel32.getacp,kernel32.getoemcp,kernel32.multibytetowidechar,kernel32.getstringtypew,user32.messageboxa,user32.wsprintfa
Вы можете возразить, что идентификация упакованной малвари будет бесполезна, так как пакеры часто используют один и тот же набор функций (VirtualAlloc, GetProcAddress, LoadLibrary, …). Предполагается, что анализ проводится над уже распакованным/сдампленым сэмплом.
Минусы идентификации по imphash:
- Много false positive. Одни и те же API могут использоваться в никак не связанных между собой программах
- Маленькая точность. Даже небольшое изменение списка функций координально изменяет хэш
В отличие от обычных PE программ, .NET программы импортируют только одну библиотеку “mscoree.dll”, поэтому imphash к ним неприменим. Зато у .NET программ есть Metadata table. Metadata table содержит все используемые программой типы и неймспейсы. Их можно использовать для хэширования, вместо таблицы импортов классического PE файла. Такой хэш назвали TypeRefHash.
В отличие от PE, у Mach-O файлов (формат в MacOS) совершенно другая структура. Поэтому для него придумали SymHash (аналог imphash).
Для ELF файлов существует несколько разных модификаций алгоритма вычисления хэша импортируемых функций. Одним из самых лучших на мой взгляд, является реализация от Trend Micro - Trend Micro ELF Hash. Они используют комбинированный подход с fuzzy hash (об этом позже).
Предотвращение идентификации по imphash
Первый - это вызов и получение функций в рантайме, с помощью LoadLibrary/GetProcAddress. Таким образом список импортируемых функций будет похожим на списки огромного количества других программ.Второй - вручную менять порядок определений/вызовы функций в исходном коде или менять очередность передачи файлов компилятору. Если вы обратите внимание на оригинальный алгоритм, то в нем нет сортировки списка перед хэширование, из-за этого и появился такой способ обхода. К сожалению, изначальный алгоритм из скрипта pefile стал каноном и используется повсеместно (от всех известных инструментов до баз с малварью).
В статье “Tracking Malware with Import Hashing” утверждается, что функции, в таблице импортов PE файла, располагаются в том порядке, в котором они появляются в исходном коде. Я проверил и оказалось, что это справедливо только для программ, скомпилированных и слинкованных visual c++ build tools версий меньше 2013 (включительно). Если вы попытаетесь сделать то же самое, используя более новые build tools, то у вас ничего не получится. Поэтому этот способ я считаю устаревшим.Способ обхода подробно описан в статье Tracking Malware with Import Hashing
Модифицированный скрипт, который сортирует
Третий - изменение таблицы импортов в уже скомпилированной программе. Алгоритм следующий (псевдокод):
Код:
thunk_data = [] # vector of (orig_thunk.addressofdata, thunk)
thunk_data_copy = [] # holds duplicate of thunk_data
original_to_new = {} # map<firstthunk, firstthunk>
for entry in DIRECTORY_ENTRY_IMPORT:
for import in entry.imports:
thunk_data.append((import.original_thunk.addressofdata,
import.firstthunk))
thunk_data_copy = thunk_data.clone()
random_shuffle(thunk_data)
for i in range(thunk_data.length):
original_to_new[thunk_data[i].second] = thunk_data_copy[i].second
for entry in DIRECTORY_ENTRY_IMPORT:
i = 0
for import in entry.imports:
import.original_thunk.addressofdata =
import.firstthunk.addressofdata = thunk_data[i].first
i = i+1
for relocation_block in DIRECTORY_ENTRY_BASERELOC:
for patch_address in relocation_block:
lookup = patch_address - imagebase
value = original_to_new.find(lookup)
if value:
relocation_block[patch_address] = value + imagebase
Алгоритм:
- Парсинг Import Directory Table
- Создание списка из элементов вида “OriginalFirstThunk + FirstThunk”
- Рандомное перемешивание списка
- Обновление таблицы импортов новыми значениями из списка
- Обновление relocation структур
fuzzyhash
fuzzyhash (или по другому Context triggered piecewise hashes (CTPH))- алгоритм выявления похожих файлов. Алгоритм сравнивает хэши разных последовательностей байт двух файлов и выдает процент схожести между всеми последовательностями. Алгоритм, изначально, был не был нацелен на идентификацию малвари, но исследователи малвари увидели в нем потенциал для своих задач. Таким образом его можно использовать, для выявления разных модификаций одной и той же малвари.Изначальный алгоритм, использовавшийся в утилите dcfldd, хэшировал файл кусками фиксированной длины. Например, брали первые 512 байт файла, хэшировали, потом следующие 512 байт и так далее. Также поступаем и со сравниваемым файлом. Далее сравниваем эти два массива хэшей (фазим) и получаем процент схожести двух файлов. Такой способ называется хэшированием по кускам (Piecewise hashing).Интервью с чуваком, который использовал fuzzyhash для малвари
Потом, этот алгоритм улучшили, и начали хэшировать не фиксированными кусками, а проходясь по изменяющимся последовательностям байтов с конца файла (rolling hashing). Этот улучшенный алгоритм и назвали fuzzyhash.
Я взял 80 гигабайт сэмплов малвари с сайта vx-underground и сделал сравнение по fuzzyhash, программой ssdeep. Сэмплы с этого сайта хороши тем, что они правильно проименованы.Об этом всем подробно читайте в исследовании Identifying almost identical files using context triggered piecewise hashing
Часть результата, в скобках процент схожести:
Код:
Backdoor.Win32.Agent.bkn.4d956a843fa431b608f426e15fb69520 matches Backdoor.Win32.Agent.bkn.026e279f2c16d654e17f6fb5c45e1741 (57)
Backdoor.Win32.Agent.bkoh.6a612dc284ef8d7f38304680a0e28693 matches Backdoor.Win32.Agent.bjup.961014b078122e628dbef050e58c9cd0 (99)
Backdoor.Win32.Agent.bkpe.f777700b6dfd3e11a808f81fdd494fb3 matches Backdoor.Win32.Agent.bjup.961014b078122e628dbef050e58c9cd0 (96)
Backdoor.Win32.Agent.bkpe.f777700b6dfd3e11a808f81fdd494fb3 matches Backdoor.Win32.Agent.bkoh.6a612dc284ef8d7f38304680a0e28693 (96)
Backdoor.Win32.Agent.bkqk.dafedf9521d55dbe25c68f3fcc9f970d matches Backdoor.Win32.Agent.bjup.961014b078122e628dbef050e58c9cd0 (97)
Backdoor.Win32.Agent.bkqk.dafedf9521d55dbe25c68f3fcc9f970d matches Backdoor.Win32.Agent.bkoh.6a612dc284ef8d7f38304680a0e28693 (97)
Backdoor.Win32.Agent.bkqk.dafedf9521d55dbe25c68f3fcc9f970d matches Backdoor.Win32.Agent.bkpe.f777700b6dfd3e11a808f81fdd494fb3 (96)
Backdoor.Win32.Agent.lei.08305b81ebfe3cfe37bcb1ec5e05531d matches Backdoor.Win32.Agent.hmv.845f009f63a39c2526ea8caba15c94a4 (55)
Backdoor.Win32.Agent.tyx.dcee8fe38cb66cefbb1cbee3f35b8107 matches Backdoor.Win32.Agent.qgq.734c8a4e104990bd785b1dc4ade1d5ac (61)
Backdoor.Win32.Agent.ucs.3dd15d055bb58c47ad6d48fa2009dca9 matches Backdoor.Win32.Agent.bkk.c29bc042c29b26ba7b43e035069cc673 (38)
Backdoor.Win32.Agent.ucv.d79281a60f0429c1846372a28084cfd0 matches Backdoor.Win32.Agent.ucq.5bee36b9e85e3d6afd5babf809eb4c9e (99)
impfuzzyhash
По названию можно догадаться, что это комбинация imphash и fuzzyhash. Если раньше, два файла с почти одинаковой таблицой импорта, имели разные imphash, то теперь сравниваются куски этой таблицы (fuzzyhash). То есть, вычисляется процент схожести одной импорт таблицы с другой. Данный подход убирает минусы оригинального алогритма и намного лучше выявляет схожесть.Придумали этот способ в JPCERT. Статья Classifying Malware using Import API and Fuzzy Hashing
Еще более улучшенный алгоритм impfuzzyhash от Trend Micro, под название Call Graph Pattern, при котором сравниваются хэши вершин графа вызовов функций
Автор Thatskriptkid
orderofsixangles.com