Сборка и почерк.
С целью анализа был взят исходный код, утёкший в паблик 10.10.2023.
Программа написана на языке C++ (используется стандарт C++17 и поддержка Windows XP) и собиралась на MSVS 2015.
Теги файлов проекта свидетельствуют о создании исходного кода на русскоязычной ОС.

В путях включений сборки присутствуют:
c:\Projects\LockerInnocentFork\lk\NTRUEncrypt\include\
c:\Projects\LockerInnocentFork\lk\mbedtls\include
наводящие на мысль о форке некоего проекта LockerInnocent. Информации по нему я не нашел (но и не особо искал).
Алгоритм.
Математически шифрование реализовано с использованием библиотеки криптографических примитивов mbedtls и алгоритма NTRUEncrypt, который на данный момент считается устойчивым ко взлому квантовыми компьютерами. NTRU создан в 1996 году в качестве альтернативы RSA и алгоритмам на основе эллиптических кривых (ECC), и считается всё ещё плохо изученным.

что согласно приложенной документации является аналогом RSA с длиной ключа 1920 байт.

Публичный ключ длиной 1028 байт вшивается один для каждого отдельного билда HelloKitty.
Начальная стадия.
Исполнение шифровальщика начинается с запрета системе ронять программу при критических ошибках и запрета закрытия консоли по Ctrl + C.
Далее идёт формирование записки с выкупом с SHA256 публичного ключа.
Производится запись времени начала работы и по окончании шифрования отображается затраченное время.
Идёт проверка единственно поддерживаемого ключа командной строки: -path «путь». Если он указан, шифруется только эта папка.
Через WMI удаляются теневые копии и чистится содержимое корзины и создаются потоки.

Скорость, параллелизация и поиск.
Для обработки файлов заранее создаются потоки в кол-ве, равному числу ядер процессора * 2, что считается довольно эффективным. Файлы обрабатываются асинхронно с получением уведомления об окончании через I/O completion port, после чего происходит шифрование очередного блока или файла в том же потоке без создания нового.
Для замера и отображения скорости создаётся отдельный поток, который сбрасывает счётчик каждую 1 секунду и выводит замеры скорости чтения и записи в заголовок консольного окна.

В это время основной поток создаёт дополнительные потоки для поиска и начала обработки файлов, расположенных на локальных, съемных и сетевых дисках.

Для каждой буквы создаётся свой поток. Все они запустятся в параллели, что является очень не оптимальным и перегрузит диск запросами в случае, если физический диск не SSD и имеет много логических разделов.
Сам поиск реализован через FindFirstFileW и рекурсию, что при сравнительном анализе с другими шифровальщиками по данным исследователя Chuong Dong является сильно не оптимальным и может приводить к переполнению стека. Существуют линейные алгоритмы рекурсивного поиска без рекурсии на базе кучи, лишенные этого недостатка.
Следующие файлы и папки пропускаются:

Часть из них отвечает за этап загрузки системы и при их изменении ОС не сможет запуститься.
Файлы размером <= 1 МБ не обрабатываются. За документы небольшого размера можно не переживать
Если файл имеет атрибут «Только чтение», этот атрибут убирается.
Запись файла и его структура.
Файлы жертвы открываются через CreateFileW с шарингом прав на чтение и асинхронным режимом работы. Делается это сразу тем же потоком, что ищет файлы. А как мы помним выше, число этих потоков = числу дисков. Файлы могли бы открываться в бесконечном кол-ве. Для решения этой проблемы при перечислении файлов проверяется максимальное число уже открытых файлов. Поток уходит в бесконечный цикл повторной проверки этого значения, если оно выше 200.

Дескриптор файла ассоциируется с портом ввода-вывода.
Этот же поток читает первый блок данных, заполняя OVERLAPPED структуру первичными данными, нужными для шифрования (см. далее).
А за запись и чтение последующих блоков данных отвечают уже другие потоки (те, что = кол-ву ядер *2). Они вытаскивают из очереди I/O порта завершенные операции чтения/записи и обрабатывают очередной блок данных произвольного файла.
Если чтение первого блока неудачно, I/O операция отменяется.
В процессе шифрования блоков файла, I/O порт в указателе OVERLAPPED удерживает служебную структуру over_struct, имеющую всю информацию о ходе шифрования конкретного файла:

такую как путь к файлу, его размер, текущий блок данных, сид, контекст класса криптографии.
Файл обрабатывается блоками максимум по 160 КБ (DEFAULT_BLOCK_SIZE).
Поле operation держит текущее состояние объекта, по которому определяется, что делать дальше с текущим блоком или указателем файла.

*retry поля не задействованы, но по задумке, похоже, были ответственны за повторную попытку чтения/записи в случае неудачи.
Эта структура включает в себя другую структуру – enc_header, необходимую для обработки файла:

В качестве сигнатуры (магического числа) используется значение 0xBAADC0DE
encType содержит опциональные флаги, один из:
ENCTYPE_FULL_FILE = 2 (обработка файла целиком)
ENCTYPE_RANDOM_BLOCKS = 4 (обработка рандомных участков)
ENCTYPE_LIMIT_FILE_SIZE = 8 (ограничение длины файла для обработки)
ENCTYPE_USE_CRC32 = 16 (создание контрольной суммы самого заголовка для верификаци, что он не повреждён)
qwMaxEnctyptionSize – ограничение максимальной длины обработки файла (для флага ENCTYPE_USE_CRC32).
endBlockCrc32 – CRC32 заголовка (для флага ENCTYPE_USE_CRC32).
aes_key и aes_iv – ключ и вектор инициализации.
Затем байты структуры enc_header шифруются и записываются в конец файла (поле ntru_encrypted) в виде структуры enc_end_of_file_ntru:

туда же пишется cipherLen (размер ntru_encrypted в байтах) и сигнатура флага о завершении шифрования со значением ENCRYPTED_FILE_FLAG (0xABCCDCDA + 1).
В конце файл переименовывают, добавляя расширение .kitty
Конфигурация билда.
Конфигурация вшита в сам исходный код.

и содержит поля:
- configSignature (не используется, содержит число 0xAAEECCD0)
- реальный размер блока
- максимальная длина файла для обработки, в случае включения режима bFullFileEncrypt
- флаг включения записи и проверки контрольной суммы CRC32 заголовка хвоста
- флаг «Шифрования файла целиком»
- флаг «Шифровать рандомные блоки» (должен быть обязательно выбран либо этот флаг, либо шифрование целиком).
Стандартные настройки: шифрование рандомных блоков размером 160 КБ, и проверка CRC32.
Сетевые функции.
Перед запуском поиска на сетевых дисках, основной поток понижает свои права элегантным способом (обычно используют токен, украденный у другого процесса) – в данном случае в коде текущий процесс получает развернутую информацию о своем собственном токене, а именно связянный с ним limited token, т.к. по сути повышение прав осуществляется присоединением к текущему (limited) токену elevated токена.
Делается это, потому что обычно сетевые диски подключаются из-под ограниченных прав, и через evelated token не видны.
Поиск сетевых шар реализован через функцию WNetOpenEnumW. Для поиска файлов для каждой из шар создаётся отдельный поток, исполняющий тот же, код что и поиск локальных файлов. В дальнейшем их шифрованием занимается общий с локальными файлами набор потоков. Это может привести к блокировке всей процедуры шифрования, если к примеру, попадётся один сильно заторможенный сетевой ресурс.
Максимальное число потоков поиска (сетевых и локальных) жестко ограничено пределом в 64. В противном случае, все последующие сетевые ресурсы пропускаются.
Записка о выкупе.
В каждой директории создаётся вымогательская записка с именем read_me_lkdtt.txt
Для связи со злоумышленниками предлагается перейти на сайт, расположенный на сервисе в сети Tor.

Защита и снятие блокировок с файла
Если не получается открыть файл, программа проверяет, не занят ли он другими процессами, и снимает блокировки, используя Restart Manager. Код лишен доп. проверок и вполне может уронить систему в BSOD.
Самоудаление реализовано с задержкой через пинг на loopback и командой cmd, однако в исходнике вызов закомментирован.
Код никак не защищен от отладки или запуска внутри песочниц. Вероятно, используется дополнительный криптор, обрабатывающий уже готовую сборку либо иное решение, маскирующее процесс и его активность.
Работа шифровальщика завершается ожиданием всех потоков поиска и достижением кол-ва открытых дескрипторов файлов числа 0.
Мертвый код.
Вероятно, некоторые версии содержали защиту от повторного запуска шифрующей части. В коде есть никак не используемые строки

В исходнике закомментирован функционал, отвечающий за убийство процессов по списку. Что примечательно в комплекте идёт список из аж 1686 имён процессов.
Дешифровщик.
Создаёт мютекс «EnigmaThread«, чтобы предотвратить повторный запуск
Дешифровка выполняется в обратном порядке через извлечение структуры enc_end_of_file_ntru из хвоста файла и ее расшифровку приватным ключом, вшитым в конкретный билд.
Обработка файлов выполняется в многопоточном режиме через пул потоков и синхронное I/O с открытием файла через \\?\ префикс, а значит поддержкой длинных путей.
Если в конфиге билда была выбрана опция расчёта CRC32, то расшифрованный заголовок сверяется с контрольной суммой, правда уже после попытки расшифровать файл. В случае несовпадения, файл не будет переименован.
Дешифровщик также поддерживает ключ командной строки -path «» для выбора конкретного пути расшифровки.
Выводы делайте сами
Статья написана для развития умения читать чужой код, лучшего понимания техник работы современных зловредов и возможностей по поиску артефактов, коих в HelloKitty скажем, не так уж много; а также в виду желания выпустить хоть какую-то статью за долгое время перерыва, сделав ее в стиле статей блога эксперта реверсинга Chuong, который умеет писать также подробно и красиво, при этом не имея на руках исходного кода
В статье также оставлены небольшие намёки на недостатки, которыми так или иначе можно воспользоваться для предотвращения или минимизации эффективности шифрования, в том числе с созданием спец. инструментов. Последовательность развёртывания конкретных зловредов и понимание, какие функции исполняет процесс в конкретный момент времени может помочь авторам антивирусных решений правильно организовать защиту и подобрать правила поведенческого анализа для блокировки уже запущенного шифровальщика.
Статья составлена Dragokas и является интеллектуальной собственностью SafeZone.cc
Перепубликация на другие ресурсы без письменного разрешения администратора запрещена.