Образцы серьезной малвари и вымогателей часто содержат интересные методики заражения, скрытия активности и нестандартные отладочные приемы. В вирусах типа Potato или вымогателях вроде SynAsk используется простая, но мощная техника скрытия вызовов WinAPI. Об этом мы и поговорим, а заодно напишем рабочий пример скрытия WinAPI в приложении.

Итак, есть несколько способов скрытия вызовов WinAPI.

  1. Виртуализация. Важный код скрывается внутри самодельной виртуальной машины.
  2. Прыжок в тело функции WinAPI после ее пролога. Для этого нужен дизассемблер длин инструкций.
  3. Вызов функций по их хеш-значениям.

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

Наша задача — написать легко масштабируемый мотор для реализации скрытия вызовов WinAPI. Они не должны читаться в таблице импорта и не должны бросаться в глаза в дизассемблере. Давай напишем короткую программу для экспериментов и откомпилируем ее для x64.

#include <Windows.h>

int main() {
  HANDLE hFile = CreateFileA("C:\\test\\text.txt",
    GENERIC_READ,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL);
  Sleep(5000);
  return 0;
}

Как видишь, здесь используются две функции WinAPI — CreateFileA и Sleep. Функцию CreateFileA я решил привести в качестве примера не случайно — по ее аргументу "C:\\test\\text.txt" мы ее легко и найдем в уже обфусцированном виде.

Давай глянем на дизассемблированный код этого приложения. Чтобы листинг на ASM был выразительнее, программу необходимо откомпилировать, избавившись от всего лишнего в коде. Откажемся от некоторых проверок безопасности и библиотеки CRT. Для оптимизации приложения необходимо выполнить следующие настройки компилятора:

  • предпочитать краткость кода (/Os),
  • отключить проверку безопасности (/Gs-),
  • отключить отладочную информацию,
  • в настройках компоновщика отключить внесение случайности в базовый адрес (/DYNAMICBASE:NO),
  • включить фиксированный базовый адрес (/FIXED),
  • обозначить самостоятельно точку входа (в нашем случае это main),
  • игнорировать все стандартные библиотеки (/NODEFAULTLIB),
  • отказаться от манифеста (/MANIFEST:NO).

Эти действия помогут уменьшить размер программы и избавить ее от вставок неявного кода. В моем случае получилось, что программа занимает 3 Кбайт. Ниже — ее полный листинг.

public start
start proc near

dwCreationDisposition= dword ptr -28h
dwFlagsAndAttributes= dword ptr -20h
var_18= qword ptr -18h

sub   rsp, 48h
and   [rsp+48h+var_18], 0
lea   rcx, FileName       ; "C:\\test\\text.txt"
xor   r9d, r9d            ; lpSecurityAttributes
mov   [rsp+48h+dwFlagsAndAttributes], 80h ; dwFlagsAndAttributes
mov   edx, 80000000h      ; dwDesiredAccess
mov   [rsp+48h+dwCreationDisposition], 3 ; dwCreationDisposition
lea   r8d, [r9+1]         ; dwShareMode
call  cs:CreateFileA
mov   ecx, 1388h          ; dwMilliseconds
call  cs:Sleep
xor   eax, eax
add   rsp, 48h
retn

start endp

Как видишь, функции WinAPI явно читаются в коде и видны в таблице импорта приложения.

Приложение в программе PE-bear
Приложение в программе PE-bear

Теперь давай создадим модуль, который поможет скрывать от любопытных глаз используемые нами функции WinAPI. Напишем таблицу хешей функций.

static DWORD hash_api_table[] = {
  0xe976c80c, // CreateFileA
  0xb233e4a5, // Sleep
}

Как хешировать

В статье нет смысла приводить алгоритм хеширования — их десятки, и они доступны в Сети, даже в Википедии. Могу посоветовать алгоритмы, с возможностью выставления вектора начальной инициализации (seed), чтобы хеши функций были уникальными. Например, подойдет алгоритм MurmurHash.

Давай условимся, что у нас макрос хеширования будет иметь прототип HASH_API(name, name_len, seed), где name — имя функции, name_len — длина имени, seed — вектор начальной инициализации. Так что все значения хеш-функций у тебя будут другими, не как в статье!

Поскольку мы договорились писать легко масштабируемый модуль, определимся, что функция получения WinAPI у нас будет вида

LPVOID get_api(DWORD api_hash, LPCSTR module);

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

LPVOID parse_export_table(HMODULE module, DWORD api_hash) {
  PIMAGE_DOS_HEADER     img_dos_header;
  PIMAGE_NT_HEADERS     img_nt_header;
  PIMAGE_EXPORT_DIRECTORY     in_export;

  img_dos_header  = (PIMAGE_DOS_HEADER)module;
  img_nt_header = (PIMAGE_NT_HEADERS)((DWORD_PTR)img_dos_header + img_dos_header->e_lfanew);
  in_export = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)img_dos_header + img_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

По ходу написания этой функции я буду пояснять, что к чему, потому что путешествие по заголовку PE-файла — дело непростое (у динамической библиотеки будет именно такой заголовок). Сначала мы объявили используемые переменные, с этим не должно было возникнуть проблем. 🙂 Далее, в первой строчке кода, мы получаем из переданного в нашу функцию модуля DLL ее IMAGE_DOS_HEADER. Вот его структура:

Продолжение доступно только подписчикам

Вариант 1. Оформи подписку на «Хакер», чтобы читать все материалы на сайте

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

Вариант 2. Купи один материал

Заинтересовала информация, но нет возможности оплатить подписку? Тогда этот вариант для тебя! Обрати внимание: этот способ покупки доступен только для материалов, опубликованных более двух месяцев назад.


2 комментария

  1. hiddy

    17.12.2018 at 21:26

    А что на счет ручных системных вызовов? (syscall, sysenter).
    Хотелось бы побольше от Вас статей по защите/реверсу всего вот этого.
    И спасибо за качественный контент.

  2. le

    19.01.2019 at 21:08

    Ок, api_name вычислили, а name_len?

Оставить мнение

Check Also

Прицел на личные данные. Что мобильная реклама может разузнать о пользователе Android

В Google Play больше 40% приложений, которые будут показывать тебе рекламу. Используя таки…