На заметку Исследование Loki-Bot


X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 082
Репутация
8 198
Всем привет, на "Хакере" интересная статья ( ), статья приватная, но думаю кому-то интересно будет почитать, автор статьи зареген тут, если нарушил авторское право, то удалю если-что.

Просто не у всех есть подписка на "Хакер".

Исследовать малварь не только весело, но и крайне познавательно. Недавно в мои цепкие руки попал банковский троян LOKI-bot. Подобные вредоносы обычно пишут настоящие профессионалы, и потому банкеры зачастую содержат достаточно интересные хаки. Так случилось и в этот раз — трой сопротивляется отладке и пытается «спалить» нас довольно нетривиальными способами.

Для начала загрузим семпл в DiE и посмотрим, что он покажет.

1573806615661.png

LOKI в анализаторе DiE

Как видно на скриншоте, DiE отчего-то уверен, что наш семпл написан на Delphi, но это, разумеется, не так. Ты наверняка знаешь, что существуют специальные тулзы, которые умеют склеивать один файл с другим. Создатели LOKI-bot именно так и поступили (обрати внимание на размер секции ресурсов rsrc в файле относительно его общего размера). Оригинальный LOKI запустится после того, как отработает его «обертка».
Все эксперименты с боевой малварью я настоятельно рекомендую проводить на изолированной от сети виртуальной машине, иначе есть риск получить заражение банкером LOKI-bot и лишиться своих данных!
Для того чтобы разобраться в механизме самозащиты этого бота, мы должны представлять себе, каким образом вообще малварь может сопротивляться отладке. Обычно так или иначе трой старается получить список процессов в системе, а после этого уже начинаются дальнейшие действия. Давай попробуем двигаться в эту сторону, чтобы изучить механизм самозащиты бота. Как известно, получить список процессов в Windows можно несколькими способами.
  1. Перечислить процессы при помощи функции PSAPI EnumProcesses.
  2. Создать снимок процессов при помощи функции CreateToolhelp32Snapshot.
  3. Перечислить процессы при помощи ZwQuerySystemInformation.
  4. Применить трюки со счетчиками производительности.
  5. Использовать Windows Management Instrumentation (WMI).
Безусловно, самый распространенный метод получения процессов — при помощи функции CreateToolhelp32Snapshot, поэтому мы начнем с установки брейк-пойнта на эту функцию (используется отладчик x64dbg, версия для архитектуры x86).
Исследование малвари или решение крякмисов может растянуться надолго: не всегда с первого раза ты ставишь брейк-пойнты на нужные функции, не всегда все идет по плану. В любом случае это вереница проб и ошибок. Когда ты читаешь статью, у тебя на это уходит максимум 10–15 минут, но это не значит, что любой взлом или снятие упаковки с файла будет занимать столько же времени. Просто есть «рабочие» моменты, рутина, которой не стоит утомлять читателя и которую автор пропускает. Поэтому создается впечатление, будто любой взлом быстр и элементарен, а автор с первого раза «угадывает», на какие функции WinAPI установить бряки, чтобы победить защиту. Но в жизни это не всегда так!
Итак, мы оказались в теле функции CreateToolhelp32Snapshot. Давай выполним ее и сделаем еще один шаг, чтобы вернуться по ret. В итоге мы попадаем в такой код:
Код:
01DC40D8 | 53                       | push ebx
01DC40D9 | 6A 02                    | push 2
01DC40DB | FF56 0C                  | call dword ptr ds:[esi+C]
01DC40DE | 8BD8                     | mov ebx,eax   <---------- мы стоим здесь
01DC40E0 | 83FB FF                  | cmp ebx,FFFFFFFF
01DC40E3 | 0F85 634C0000            | jne 1DC8D4C
01DC40E9 | 33C0                     | xor eax,eax
01DC40EB | E9 6AE5FFFF              | jmp 1DC265A
Мы стоим на 01DC40DE, но ты ведь помнишь, что мы только что вернулись из CreateToolhelp32Snapshot? Стало быть, call, который выше по коду, и есть вызов CreateToolhelp32Snapshot. Вспоминаем прототип CreateToolhelp32Snapshot:
Код:
HANDLE CreateToolhelp32Snapshot(
    DWORD dwFlags,
    DWORD th32ProcessID
);
Как видим, передаются два аргумента, один из которых — push 2, что говорит о передаче параметра TH32CS_SNAPPROCESS. Он заставляет CreateToolhelp32Snapshot сделать снимок всех процессов. Все указывает на вызов CreateToolhelp32Snapshot, но этот call не похож на стандартный вызов WinAPI в коде. Идем в ds:[esi+C] и смотрим, что там есть.

1573806591907.png

Вид «сырой» памяти в ds:[esi+C]

Видим какой-то код, похожий на мусор, среди которого прослеживаются имена функций WinAPI. Давай представим весь код в виде DWORD’ов.

1573806675953.png

Имена функций, получаемых динамически

Перед нами список функций, которые LOKI получает динамически. По этому списку мы можем судить о том, как работает банкер. В дальнейшем вызовы используемых WinAPI будут выполняться подобными call’ами. Итак, со списком WinAPI разобрались, теперь вернемся в код. Чтобы ты понимал его структуру, приведу листинг части функции поиска процессов (обрати внимание на адреса!):
Код:
01DC40DB | FF56 0C                  | call dword ptr ds:[esi+C]       | CreateToolhelp32Snapshot
01DC40DE | 8BD8                     | mov ebx,eax                     |
01DC40E0 | 83FB FF                  | cmp ebx,FFFFFFFF                |
01DC40E3 | 0F85 634C0000            | jne 1DC8D4C                     |
01DC40E9 | 33C0                     | xor eax,eax                     |
01DC40EB | E9 6AE5FFFF              | jmp 1DC265A                     |

00228D4C | 8D85 D4FDFFFF            | lea eax,dword ptr ss:[ebp-22C]  |
00228D52 | 50                       | push eax                        |
00228D53 | E9 53ABFFFF              | jmp 2238AB                      |

002238AB | 53                       | push ebx                        |
002238AC | 89BD D4FDFFFF            | mov dword ptr ss:[ebp-22C],edi  |
002238B2 | FF56 10                  | call dword ptr ds:[esi+10]      | Process32FirstW
002238B5 | E9 97EDFFFF              | jmp 222651
Как видишь, код сильно фрагментирован и связан переходами (jmp). Это нас не запугает: ставим точку останова на конец функции получения списка процессов и жмем на выполнение. Далее нас ждет такой код (некоторые не особо интересные функции я буду называть, но не стану в них заглядывать — по названию будет все понятно):
Код:
007C5D83 | 8D8D F8FDFFFF            | lea ecx,dword ptr ss:[ebp-208]          | Имя процесса
007C5D89 | E8 4AD8FFFF              | call <str_len>                          | Считаем число символов имен процессов
007C5D8E | 83F8 40                  | cmp eax,40                              | Сравниваем размер имени процесса с числом 40h (64)
007C5D91 | 0F82 08000000            | jb 7C5D9F                               | Все хорошо, продолжаем корректное выполнение трояна
007C5D97 | 6A 00                    | push 0                                  | Все плохо, завершаемся
007C5D99 | FF96 AC000000            | call dword ptr ds:[esi+AC]              | ExitProcess
007C5D9F | E9 E1D1FFFF              | jmp 7C2F85                              |
Здесь считается размер имени каждого процесса и сравнивается с числом 40h, или 64 в десятеричной системе. Если процесс с таким длинным именем найден, то выполняется выход. В чем тут был замысел?

Все дело в том, что обычно исследователи в вирусных лабораториях дают имена файлам семплов вредоносных программ в виде их хешей SHA-256, которые как раз имеют длину 64 символа. Если такой файл запустить, LOKI поймет это по числу символов процесса и выполнит выход. Или не поймет? Думаю, что внимательный читатель уже догадался: дело в том, что получаемое при помощи функции CreateToolhelp32Snapshot имя процесса также содержит его расширение, которое добавляет еще четыре символа к имени — .exe. Очевидно, разработчик забыл об этом, поэтому его замысел не сработает. Хотя идея достаточно оригинальна. Вывести из строя эту защиту банкера можно, проследив, чтобы в системе не было процессов с именами из 64 символов, включая расширение.

На очереди следующая проверка.
Код:
002567B4 | 85C0                     | test eax,eax                            |
002567B6 | 0F85 07000000            | jne 2567C3                              |
002567BC | 53                       | push ebx                                |
002567BD | FF96 AC000000            | call dword ptr ds:[esi+AC]              | ExitProcess
002567C3 | 56                       | push esi                                |
002567C4 | E8 D4DBFFFF              | call <check_name>                       |
002567C9 | 59                       | pop ecx                                 |
002567CA | 85C0                     | test eax,eax                            |

Я обозначил нужный call меткой <check_name>, чтобы было понятнее. Если прыгнуть в него, можно увидеть такой код (я добавил комментарии, хоть код и простой):
Код:
004C37B0 | 66:8945 D8               | mov word ptr ss:[ebp-28],ax             | Положили в память символ по адресу ss:[ebp-28]
004C37B4 | 58                       | pop eax                                 |
004C37B5 | 6A 6D                    | push 6D                                 | Взяли символ
004C37B7 | 8BD0                     | mov edx,eax                             |
004C37B9 | 66:8955 DA               | mov word ptr ss:[ebp-26],dx             | Положили в память по адресу ss:[ebp-26]
004C37BD | 5A                       | pop edx                                 |
004C37BE | 6A 70                    | push 70                                 | Взяли символ
004C37C0 | 66:8955 DC               | mov word ptr ss:[ebp-24],dx             |
004C37C4 | 5A                       | pop edx                                 | Положили в память по адресу ss:[ebp-24]
004C37C5 | E9 6B2D0000              | jmp 4C6535                              |
Очевидно, в память помещаются значения, являющиеся кодами символов. Давай пройдем этот код (это только фрагмент, код связан jmp’ами) и посмотрим на окно дампа, например в окрестностях [ebp-26].

1573806819772.png

Генерация строк в памяти

В памяти сгенерированы слова self, sample, sandbox, virus, malware. Такой подход к генерации нужных строк в памяти используется для того, чтобы строковые константы не бросались в глаза в исполняемом файле LOKI.

1573806871897.png

Строки сгенерированы, это видно в дампе

Далее идет вызов WinAPI GetModuleFileName:
Код:
004C7B13 | 6A 00                    | push 0                                  |

004C7B15 | FF50 4C                  | call dword ptr ds:[eax+4C]              | GetModuleFileName

004C7B18 | 85C0                     | test eax,eax                            |

004C7B1A | E9 73F4FFFF              | jmp 4C6F92                              |
Так LOKI проверяет, не содержит ли имя его исполняемого файла этих слов, и если содержит, то он сразу завершает работу. Кроме того, подобным образом генерируются названия процессов популярных исследовательских программ, например ollydbg.exe, procmon.exe, procmon64.exe, windbg.exe, procexp64.exe, и названия процессов антивируса Avast — avgsvc.exe, iavgui.exe, avastsvc.exe и так далее.

После этого сгенерированные названия сверяются с полученным ранее при помощи функции CreateToolhelp32Snapshot списком процессов.

Что интересно, проверяются процессы только антивируса Avast, другие LOKI почему-то не просматривает. Чтобы успешно
пройти и эту проверку, переименуй все приложения из списка или просто не запускай их, иначе это спугнет LOKI и он завершится. А мы подходим к
следующему антиотладочному трюку.

После проверки имен процессов мы видим такой код:
Код:
01D16CB7 | FF96 8C000000            | call dword ptr ds:[esi+8C]              | ZwQueryInformationProcess

01D16CBD | 397D FC                  | cmp dword ptr ss:[ebp-4],edi            |

01D16CC0 | E9 A8130000              | jmp 1D1806D                             |
Очевидно, вызов ZwQueryInformationProcess здесь используется не просто так. Вспоминаем, что есть антиотладочный трюк, основанный на вызове WinAPI ZwQueryInformationProcess. Смотрим, какие аргументы передаются в эту функцию, попутно вспоминая ее прототип:
Код:
NTSTATUS WINAPI ZwQueryInformationProcess(
    _In_      HANDLE           ProcessHandle,
    _In_      PROCESSINFOCLASS ProcessInformationClass,
    _Out_     PVOID            ProcessInformation,
   _In_      ULONG            ProcessInformationLength,
    _Out_opt_ PULONG           ReturnLength
);
Больше всего нас будут интересовать поля ProcessHandle и ProcessInformationClass. Первое поле указывает на процесс, о котором мы получаем информацию, второе — интересующий нас флаг. Отладчик показывает, что первое поле имеет значение FFFFFFFF, а второе — 0x1f. Чтобы в этом убедиться, посмотри, какие параметры передаются в функцию через стек, на скриншоте.

Переданное значение FFFFFFFF в поле ProcessHandle говорит о том, что мы работаем со своим собственным процессом (GetCurrentProcess();). Значение поля ProcessInformationClass 0x1f указывает на использование ProcessDebugFlags, который сигнализирует о том, что процесс находится под отладкой. Также будет проверен флаг 0x1E (ProcessDebugObjectHandle), который тоже указывает на отладку.

1573806961746.png

Аргументы ZwQueryInformationProcess в стеке и ее вызов

Кстати, в «Хакере» была отдельная , которая описывала этот (и другие) методы определения отладки.

Ну а далее, разумеется, мы обнаруживаем условный переход в зависимости от состояния указанных выше флагов. Как его преодолевать, ты и сам знаешь: либо перескочить, установив регистр eip на нужный адрес, либо забить nop’ами, либо поменять значение флага перехода в отладчике. Лично я просто поменял значение регистра eip на следующую после условного перехода команду.

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

1573806997383.png

Строки в дампе LOKI

Очевидно, LOKI собрался стилить мои пароли из браузера Firefox.

В этой статье мы рассмотрели, какие уловки вирмейкеры применяют против средств анализа бинарных файлов, на примере банкера LOKI-bot. Какие-то способы были уже много раз описаны, какие-то достаточно нетривиальны по своему подходу. Но, как видишь, при помощи отладчика и дизассемблера они преодолеваются быстро и эффективно.
 
Автор темы Похожие темы Форум Ответы Дата
denis7656 Трояны и шпионские программы 8
Верх Низ