Статья Разбор вредоносного образца [1/2]

  • 9
  • 17
[НАЧАЛЬНЫЙ СТАТИЧЕСКИЙ АНАЛИЗ]

Давайте посмотрим на образец в DiE, чтобы понять, с чем мы имеем дело

1632169400609.png


Здесь мы видим, что это обычный консольный исполняемый файл windows. Кроме того, энтропия равна 7,434, поэтому вполне вероятно, что этот исполняемый файл упакован, о чем соответственное говорит наша программа.

Энтропия - это концепция информатики, которая измеряет степень случайности в системе, поэтому высокая энтропия означает, что данные внутри действительно случайны и беспорядочны, что означает, что автор, возможно, запутал некоторую часть.

Рассмотрим секции и их энтропию.

1632169440094.png


Мы видим, что раздел ресурсов (.rsrc) имеет очень высокую энтропию, поэтому мы можем с уверенностью предположить, что второй этап файла хранится там.

1632169480185.png


Стоит заметить, что есть много действительных импортов из kernel32.dll, так что это не так сильно запаковано, как можно подумать. Давайте попробуем распаковать его динамически.


[ДИНАМИЧЕСКИЙ АНАЛИЗ]

Во-первых, нам нужно настроить точки остановки в x32dbg. Мы поставим их на VirtualAlloc, VirtualProtect, CreateProcessInternalW, WriteProcessMemory и ResumeThread, чтобы мы могли поймать, когда вредоносная программа записывает распакованный исполняемый файл в память и запускает процесс для его выполнения.
Кроме того, на всякий случай у нас должна быть точка останова на IsDebuggerPresent (only pasters use advanced usermode anti-anti-debugger)

После их установки мы можем запускать программу и попадаем на наш первый VirtualAlloc.

1632169541146.png


В стеке возвращаемое значение 0x01A00000. Открываем в дампе и идем дальше.

1632169580988.png


Когда мы останавливаемся на вызове WriteProcessMemory, мы видим, что ранее возвращенный буфер был записан с помощью исполняемого файла.
После его выгрузки, мы увидим, что это второй этап этого образца.


[ПОЛНОЦЕННЫЙ СТАТИЧЕСКИЙ АНАЛИЗ]

Поскольку это полноценный анализ всего файла, давайте не будем просто переходить ко второму этапу. Вместо этого мы можем реконструировать этот образец, чтобы увидеть, как он генерирует следующий этап, а так же как он внедряет новый исполняемый файл в новый процесс.

Первое, что мы видим в функции main, что в качестве параметра функции sub_401300 используются странные строки. Это почти 99% функция для расшифровки этих строк. Кроме того, мы видим, что эти строки после расшифровки в sub_401300 будут отправлены в LoadLibraryA и GetProcAddress, поэтому они представляют собой строки, содержащие имена dll / api.

1632169663640.png


Первое, что мы видим в функции main, это это. В качестве параметра функции sub_401300 используются странные строки. Это почти 99% функция для расшифровки этих строк. Кроме того, мы видим, что эти строки после расшифровки в sub_401300 будут отправлены в LoadLibraryA и GetProcAddress, поэтому они представляют собой строки, содержащие имена dll / api.

1632169685522.png


Разберем функцию дешифрования.

Сначала проверяется длина зашифрованной строки, чтобы убедиться, что она не равна нулю. Затем, как мы видим, создается ключ дешифрования.
Затем выполняется цикл для перебора каждого символа в зашифрованной строке. Каждый из этих символов проверяется на предмет индекса, в котором они указаны в ключе дешифрования.

К индексу добавляется 0xD (13), и если этот результат больше, чем длина ключа дешифрования, результат в основном модифицируется с длиной для переноса с передней стороны.
Этот результат используется в качестве индекса в ключе дешифрования для создания расшифрованного символа.

В целом, это всего лишь алгоритм ROT13. Мы можем быстро создать небольшой скрипт на python, который с этого момента поможет нам расшифровывать строки.

Python:
def decrypt(string):
    decrypt_key = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890./='
    result_str = ''
    for character in string:
        index = decrypt_key.find(character)
        result_str += decrypt_key[(index + 0xD) % len(decrypt_key)]

    print(stringresult_str)


encrypted = ["F5gG8e514pbag5kg",
             ".5ea5/QPY4//",
             "pe51g5Ceb35ffn",
             "I9egh1/n//b3rk",
             "E5fh=5G8e514",
             "Je9g5Ceb35ffz5=bel",
             "I9egh1/n//b3",
             "E514Ceb35ffz5=bel",
             "t5gG8e514pbag5kg",
             ".5ea5/QPY4//",
             "F9m5b6E5fbhe35",
             "s9a4E5fbhe35n",
             "I9egh1/n//b3",
             "yb3.E5fbhe35",
             "yb14E5fbhe35"]

for encrypted_string in encrypted:
    decrypt(encrypted_string)

Код:
> F5gG8e514pbag5kg - SetThreadContext
> .5ea5/QPY4// - kernel32.dll
> pe51g5Ceb35ffn - CreateProcessA
> I9egh1/n//b3rk - VirtualAllocEx
> E5fh=5G8e514 - ResumeThread
> Je9g5Ceb35ffz5=bel - WriteProcessMemory
> I9egh1/n//b3 - VirtualAlloc
> E514Ceb35ffz5=bel - ReadProcessMemory
> t5gG8e514pbag5kg - GetThreadContext
> F9m5b6E5fbhe35 - SizeofResource
> s9a4E5fbhe35n - FindResourceA
> I9egh1/n//b3 - VirtualAlloc
> yb3.E5fbhe35 - LockResource
> yb14E5fbhe35 - LoadResource

Расшифровав все, мы можем продолжить, задокументировать это в IDA и двигаться дальше.

1632169962589.png


В основном он расшифровывает kernel32.dll, FindResourceA, LoadResource, SizeofResource и LockResource и находит адреса этих функций в kernel32.dll.
Затем он вызывает эту функцию, чтобы наконец получить указатель на ресурс с помощью LockResource.

1632169977975.png


(Заранее сделал структуру для наглядности)
Файл извлекает длину ресурса по индексу 0x8, умножает ее на 10, предположим, что это длина нового исполняемого файл, т.к. это значение передается в VirtualAlloc и memmove.
Кроме того, поскольку из ресурса копируется значение с индексом 0x1C, мы также можем предположить, что зашифрованный исполняемый файл начинается с 0x1C.

1632170005885.png


Далее мы можем увидеть очевидный алгоритм дешифрования RC4, реализованный здесь.
На втором этапе RC4 KSA мы видим, что ключ используется для шифрования / дешифрования. В IDA это resource_ptr->RC4_key[j % 0xF] через каждую итерацию.

Поскольку j увеличивается каждый раз и получается остаток от 0xF, то j будет находиться в диапазоне от 0 до 15. Отсюда мы можем предположить, что длина ключа составляет 15 байт, и он хранится с индексом 0xC в разделе ресурсов.

Если мы посмотрим на ресурс в Resource Hacker, то увидим следующие:

1632170036536.png


Ключ RC4 показан как kkd5YdPM24VBXmi, а все, что находится после 0x1B, является зашифрованным исполняемым файлом, как мы обсуждали выше.
На этом этапе ясно, как распаковывается исполняемый файл, мы можем просто выгрузить этот ресурс и написать небольшой скрипт python для его расшифровки.

Python:
import arc4

def RC4_decrypt(encrypted_data):
    return arc4.ARC4('kkd5YdPM24VBXmi').decrypt(encrypted_data)

Наконец, мы видим вызов sub_401000(allocated_second_stage) перед возвращением.
Вероятно, это функция, которая выполняет инъекцию для запуска следующего состояния.
Когда мы заходим внутрь этой функции, легко понять, что классический Process Hollowing или RunPE используется здесь для внедрения процесса.
Мне лень объяснять полноценно, что это такое (могу сделать отдельную статью), поэтому завершим разбор первого этапа на скринах из IDA с комментариями.

1632170101816.png
1632170107266.png


Разбор второй части этого образца будет в отдельной статье.
 
  • 1
  • 1
Контакты для связи отсутствуют.
Кроме того, энтропия равна 7,434, поэтому вполне вероятно, что этот исполняемый файл упакован, о чем соответственное говорит наша программа.
Привет, я новичок в реверсе, не подскажешь какая энтропия является нормальной для неупакованного файла? По скрину выше могу предположить что от 2 до 5, но мне нужно подтверждение моих слов.
 
Последнее редактирование:
  • 9
  • 17
Привет, я новичок в реверсе, не подскажешь какая энтропия является нормальной для неупакованного файла? По скрину выше могу предположить что от 2 до 5, но мне нужно подтверждение моих слов.
В среднем значение энтропии находится в диапазоне 4.8 до 7.2, но меньше тоже считается нормой (редко)
Вот примерная гистограмма для понимания

1634490379228.png
 
Активность
Пока что здесь никого нет
Сверху Снизу