stihl не предоставил(а) никакой дополнительной информации.
Модуль 2 Maldev Academy Модуль под моей редакцией состоит из двух частей.
Часть 1 “Виртуальная память и ее хранение.”
Virtual Memory & Paging
Память в ОС не отображается напрямую в ОЗУ. Вместо этого виртуальные адреса памяти используются процессами, которые отображаются на адреса физической памяти. Для этого есть несколько причин, но в конечном итоге цель состоит в том, чтобы сэкономить как можно больше физической памяти. Виртуальная память может отображаться на физическую память, но также может храниться на диске. Благодаря адресации виртуальной памяти становится возможным, чтобы несколько процессов совместно использовали один и тот же физический адрес, имея при этом уникальный адрес виртуальной памяти. Виртуальная память основана на концепции страничного обмена памятью, которая делит память на фрагменты по 4 КБ, называемые «страницы».
Page State
Страницы, находящиеся в виртуальном адресном пространстве процесса, могут находиться в одном из трех состояний:
После того, как страницы будут выделены, им необходимо установить одну из опций защиты памяти. Основные примеры приведены ниже.
OS обычно имеют встроенные средства защиты памяти, чтобы препятствовать эксплойтам и атакам. Про это необходимо знать, может встречаться при создании или отладке малвари.
x86 vs x64 Memory Space
При работе с процессами Windows важно учитывать, является ли процесс x86 или x64. Процессы x86 имеют меньший объем памяти — 4 ГБ 0xFFFFFFFF, тогда как x64 имеет значительно больший объем памяти — 128 ТБ 0xFFFFFFFFFFFFFFFF.
Allocating Memory Example
Ниже примеры кода написанный на Си с использованием WinApi необходимый для выделения памяти. На Си приходится выделять память самостоятельно, так что надо быть очень аккуратным при написании такого кода и после использования памяти, ее необходимо очистить и закрыть.
//Выделение 100 bytes буфера памяти
// Метод 1 - Используем malloc()
PVOID pAddress = malloc(100);
// Метод 2 - Используем HeapAlloc()
PVOID pAddress = HeapAlloc(GetProcessHeap(), 0, 100);
// Метод 3 - Используем LocalAlloc()
PVOID pAddress = LocalAlloc(LPTR, 100);
Функции выделения памяти возвращают базовый адрес, который является просто указателем на начало выделенного блока памяти. Используя фрагменты выше, pAddress будет базовым адресом выделенного блока памяти. Используя этот указатель, можно выполнить несколько действий, таких как чтение, запись и выполнение. Тип действий, которые могут быть выполнены, будет зависеть от защиты, назначенной выделенной области памяти. Ниже показано, как pAddress выглядит в деббагере.
Writing To Memory Example
Ниже пример записи в память. В нем используется memcpy.
PVOID pAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 100);
CHAR* cString = "I'm a cool hacker. Fsociety";
memcpy(pAddress, cString, strlen(cString));
HeapAlloc использует HEAP_ZERO_MEMORY флаг, который инициализирует выделенную память нулем. Затем строка копируется в выделенную память с помощью memcpy. Последний параметр в memcpy — это количество байтов для копирования.
Freeing Allocated Memory
Когда приложение завершает работу с выделенным буфером, настоятельно рекомендуется освободить буфер, чтобы избежать утечек памяти. В зависимости от того, какая функция использовалась для выделения памяти, она будет иметь соответствующую функцию освобождения памяти.
Часть 2 “Основы WinApi”
Windows Data Types
В Windows API есть много типов данных помимо общеизвестных (например, int, float). Типы данных документированы и их можно посмотреть Для просмотра ссылки Войдиили Зарегистрируйся.
Некоторые из распространенных типов данных перечислены ниже:
PCSTR pcString = "Hello, world!";
PSTR pString = "Hello, world!";
PCWSTR pcwString = L"Hello, world!";
PWSTR pwString = L"Hello, world!";
wchar_t* wcString = L"Hello, world!";
Pointer = (ULONG_PTR)Pointer + 10;
Data Types Pointers
Windows API позволяет разработчику объявлять тип данных напрямую или указатель на тип данных. Это отражено в именах типов данных, где типы данных, начинающиеся с «P», представляют указатели на фактический тип данных, а те, которые не начинаются с «P», представляют сам фактический тип данных. будет очень полезным позже при работе с API Windows, имеющими параметры, которые являются указателями на тип данных. Примеры ниже показывают, как тип данных “P” выглядит без указателя.
Главное отличие, которое следует иметь в виду, заключается в том, что функции ANSI будут принимать типы данных ANSI в качестве параметров, тогда как функции Unicode будут принимать типы данных Unicode. Например, первый параметр для CreateFileA — это LPCSTR, который является указателем на постоянную строку с нулевым завершением из 8-битных символов Windows ANSI. С другой стороны, первый параметр для CreateFileW — это LPCWSTR, указатель на постоянную строку с нулевым завершением из 16-битных символов Unicode.
Кроме того, количество требуемых байтов будет различаться в зависимости от используемой версии.
char str1[] = "fsociety";// 9 байт (fsociety + нулевой байт ).
wchar str2[] = L"fsociety";// 18 байт, каждый символ — 2 байта (нулевой байт также 2 байта)
In and Out Parameters
API Windows имеют входные и выходные параметры. IN — это параметр, который передается в функцию и используется для ввода. В то время как OUT — это параметр, который используется для возврата значения обратно вызывающей функции. Выходные параметры часто передаются по ссылке через указатели.
Например, фрагмент кода ниже показывает функцию fsociety, которая принимает целочисленный указатель и устанавливает значение 111. Это считается выходным параметром, поскольку параметр возвращает значение.
BOOL fsociety(OUT int* num){
*num = 123;
return TRUE;
};
int main(){
int a = 0;
fsociety(&a);
};
Имейте в виду, что использование ключевых слов OUT и IN призвано облегчить разработчикам понимание того, что ожидает функция и что она делает с этими параметрами. Однако стоит отметить, что исключение этих ключевых слов не влияет на то, считается ли параметр выходным или входным параметром.
Analyze Return Type & Parameters
Следующим шагом будет просмотр параметров функции вместе с возвращаемым типом данных. В документации указано, что если функция завершается успешно, возвращаемое значение является открытым дескриптором указанного файла, устройства, именованного канала или почтового слота, поэтому CreateFileW возвращает HANDLE тип данных для указанного созданного элемента.
Кроме того, обратите внимание, что все параметры функции являются in. Это означает, что функция не возвращает никаких данных из параметров, поскольку все они являются in. Помните, что ключевые слова в квадратных скобках, такие как in, out, и optional, предназначены исключительно для справки разработчиков и не оказывают никакого фактического влияния.
HANDLE CreateFileW(
[in] LPCWSTR lpFileName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwShareMode,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD dwCreationDisposition,
[in] DWORD dwFlagsAndAttributes,
[in, optional] HANDLE hTemplateFile
);
Use The Function
В примере кода ниже представлен пример использования CreateFileW. Он создаст текстовый файл с именем fsociety.txt на рабочем столе текущего пользователя.
Handle hFile = INVALID_HANDLE_VALUE;
LPCWSTR filePath = L"C:\\Users\\maldevacademy\\Desktop\\fsociety.txt";
hFile = CreateFileW(filePath, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE){
printf("[-] CreateFileW Api Function Failed With Error : %d\n", GetLastError());
return -1;
};
Windows API Debugging Errors
Когда в функции возникает ошибка, они часто возвращают лог. Например, если в CreateFileW что-то пошло не так, возвращается сообщение INVALID_HANDLE_VALUE, указывающее на то, что файл не может быть создан. Чтобы получить больше информации о том, почему файл не может быть создан, необходимо получить код ошибки с помощью функции Для просмотра ссылки Войдиили Зарегистрируйся.
После извлечения кода его необходимо найти в Для просмотра ссылки Войдиили Зарегистрируйся. Ниже приведены некоторые распространённые коды ошибок:
Вспомним модуль архитектуры Windows : NTAPI в основном экспортируются из ntdll.dll. В отличие от API Windows, эти функции не могут получить свой код ошибки через GetLastError. Вместо этого они возвращают код ошибки напрямую, который представлен типом NTSTATUS данных. NTSTATUS используется для представления статуса системного вызова или функции и определяется как 32-битное целое число без знака. Успешный системный вызов вернет значение STATUS_SUCCESS, которое равно 0. Ниже фрагмент кода показывает, как выполняется проверка ошибок системных вызовов.
NTSTATUS STATUS = NativeSyscallExample(...);
if (STATUS != STATUS_SUCCESS){`
printf("[!] NativeSyscallExample Failed With Status : 0x%0.8X \n", STATUS);
};
NT_SUCCESS Macroc
Другой способ проверки возвращаемого значения NTAPI — через NT_SUCCESS макрос. Он возвращает, TRUE если функция выполнена успешно, и FALSE если она не выполнена.
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
Ниже пример такого макроса.
NTSTATUS STATUS = NativeSyscallExample(...);
if (!NT_SUCCESS(STATUS)){
printf("[!] NativeSyscallExample Failed With Status : 0x%0.8X \n", STATUS);
};
Надеюсь ваши мозги еще не поплавились и вы успешно доползли до конца этого модуля. Теперь дз или вы рассчитывали, что после прочтения данного модуля у вас и дз не будет?
Написать утилиту на Cи, которая выполняет следующие действия:
- Первая - это ознакомительный модуль с концепцией хранения и управления ОЗУ
- Вторая - это обзор минимально необходимого функционала WinApi

Часть 1 “Виртуальная память и ее хранение.”
Virtual Memory & Paging
Память в ОС не отображается напрямую в ОЗУ. Вместо этого виртуальные адреса памяти используются процессами, которые отображаются на адреса физической памяти. Для этого есть несколько причин, но в конечном итоге цель состоит в том, чтобы сэкономить как можно больше физической памяти. Виртуальная память может отображаться на физическую память, но также может храниться на диске. Благодаря адресации виртуальной памяти становится возможным, чтобы несколько процессов совместно использовали один и тот же физический адрес, имея при этом уникальный адрес виртуальной памяти. Виртуальная память основана на концепции страничного обмена памятью, которая делит память на фрагменты по 4 КБ, называемые «страницы».

Page State
Страницы, находящиеся в виртуальном адресном пространстве процесса, могут находиться в одном из трех состояний:
- Free — страница не зафиксирована и не зарезервирована. страница недоступна для процесса. Она доступна для резервирования, фиксации или одновременной фиксации и фиксации. Попытка чтения или записи на свободную страницу может привести к исключению нарушения доступа.
- Reserved — страница зарезервирована для будущего использования. Диапазон адресов не может использоваться другими функциями распределения. страница недоступна и не имеет физического хранилища, связанного с ней. Она доступна для фиксации.
- Committed - Затраты памяти были выделены из общего размера RAM и файлов подкачки на диске. страница доступна, и доступ контролируется одной из констант защиты памяти.Система инициализирует и загружает каждую зафиксированную страницу в физическую память только во время первой попытки чтения или записи на эту страницу.Когда процесс завершается, система освобождает хранилище для зафиксированных страниц.
После того, как страницы будут выделены, им необходимо установить одну из опций защиты памяти. Основные примеры приведены ниже.
- PAGE_NOACCESS - Отключает любой доступ к зафиксированной области страниц. Попытка чтения, записи или выполнения зафиксированной области приведет к нарушению доступа.
- PAGE_EXECUTE_READWRITE - Позволяет читать, писать и выполнять. Это крайне не рекомендуется использовать, и обычно это IoC, поскольку память редко бывает одновременно доступной для записи и выполнения.
- PAGE_READONLY - Разрешает доступ только для чтения к выделенному региону страниц. Попытка записи в выделенный регион приводит к нарушению прав доступа.
OS обычно имеют встроенные средства защиты памяти, чтобы препятствовать эксплойтам и атакам. Про это необходимо знать, может встречаться при создании или отладке малвари.
- DEP — это функция защиты памяти на системном уровне, встроенная в операционную систему, начиная с Windows XP и Windows Server 2003. Если параметр защиты страниц установлен на PAGE_READONLY, то DEP предотвратит выполнение кода в этой области памяти.
- ASLR — это метод защиты памяти, используемый для предотвращения эксплуатации уязвимостей повреждения памяти. ASLR случайным образом упорядочивает позиции адресного пространства ключевых областей данных процесса, включая базу исполняемого файла и позиции стека, кучи и библиотек.

x86 vs x64 Memory Space
При работе с процессами Windows важно учитывать, является ли процесс x86 или x64. Процессы x86 имеют меньший объем памяти — 4 ГБ 0xFFFFFFFF, тогда как x64 имеет значительно больший объем памяти — 128 ТБ 0xFFFFFFFFFFFFFFFF.
Allocating Memory Example
Ниже примеры кода написанный на Си с использованием WinApi необходимый для выделения памяти. На Си приходится выделять память самостоятельно, так что надо быть очень аккуратным при написании такого кода и после использования памяти, ее необходимо очистить и закрыть.
//Выделение 100 bytes буфера памяти
// Метод 1 - Используем malloc()
PVOID pAddress = malloc(100);
// Метод 2 - Используем HeapAlloc()
PVOID pAddress = HeapAlloc(GetProcessHeap(), 0, 100);
// Метод 3 - Используем LocalAlloc()
PVOID pAddress = LocalAlloc(LPTR, 100);
Функции выделения памяти возвращают базовый адрес, который является просто указателем на начало выделенного блока памяти. Используя фрагменты выше, pAddress будет базовым адресом выделенного блока памяти. Используя этот указатель, можно выполнить несколько действий, таких как чтение, запись и выполнение. Тип действий, которые могут быть выполнены, будет зависеть от защиты, назначенной выделенной области памяти. Ниже показано, как pAddress выглядит в деббагере.

Writing To Memory Example
Ниже пример записи в память. В нем используется memcpy.
PVOID pAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 100);
CHAR* cString = "I'm a cool hacker. Fsociety";
memcpy(pAddress, cString, strlen(cString));
HeapAlloc использует HEAP_ZERO_MEMORY флаг, который инициализирует выделенную память нулем. Затем строка копируется в выделенную память с помощью memcpy. Последний параметр в memcpy — это количество байтов для копирования.

Freeing Allocated Memory
Когда приложение завершает работу с выделенным буфером, настоятельно рекомендуется освободить буфер, чтобы избежать утечек памяти. В зависимости от того, какая функция использовалась для выделения памяти, она будет иметь соответствующую функцию освобождения памяти.
- Выделение с помощью malloc требует использования функции free.
- Выделение с помощью HeapAlloc требует использования функции HeapFree.
- Выделение с помощью LocalAlloc требует использования функции LocalFree.


Часть 2 “Основы WinApi”

Windows Data Types
В Windows API есть много типов данных помимо общеизвестных (например, int, float). Типы данных документированы и их можно посмотреть Для просмотра ссылки Войди
Некоторые из распространенных типов данных перечислены ниже:
- DWORD- 32-битное целое число без знака, используемое как в 32-битных, так и в 64-битных системах для представления значений от 0 до (2^32 - 1).
- size_t- Используется для представления размера объекта. Это 32-битное целое число без знака в 32-битных системах, представляющее значения от 0 до (2^32 - 1). С другой стороны, это 64-битное целое число без знака в 64-битных системах, представляющее значения от 0 до (2^64 - 1).
- VOID- Указывает на отсутствие определенного типа данных.
- PVOID- 32-битный или 4-байтный указатель любого типа данных на 32-битных системах. Альтернативно, 64-битный или 8-байтный указатель любого типа данных на 64-битных системах.
- HANDLE- Значение, указывающее конкретный объект, которым управляет операционная система (например, файл, процесс, поток).
- HMODULE- Дескриптор модуля. Это базовый адрес модуля в памяти. Примером МОДУЛЯ может быть файл DLL или EXE.
- LPCSTR/PCSTR- Указатель на постоянную строку с нулевым завершением из 8-битных символов Windows (ANSI). «L» означает «long», что произошло от периода 16-битного программирования Windows, в настоящее время это не влияет на тип данных, но соглашение об именовании все еще существует. «C» означает «constant» или переменная только для чтения. Оба эти типа данных эквивалентны const char*.
PCSTR pcString = "Hello, world!";
- LPSTR/PSTR- То же самое, что LPCSTRи PCSTR, единственное отличие в том, что LPSTRи PSTR не указывают на константную переменную, а вместо этого указывают на читаемую и записываемую строку. Оба эти типа данных эквивалентны char*.
PSTR pString = "Hello, world!";
- LPCWSTR\PCWSTR- Указатель на константную строку с нулевым завершением из 16-битных символов Windows Unicode (Unicode). Оба эти типа данных эквивалентны const wchar*.
PCWSTR pcwString = L"Hello, world!";
- PWSTR\LPWSTR- То же самое, что LPCWSTRи PCWSTR, единственное отличие в том, что ‘PWSTR’ и ‘LPWSTR’ не указывают на константную переменную, а вместо этого указывают на читаемую и записываемую строку. Оба эти типа данных эквивалентны wchar*.
PWSTR pwString = L"Hello, world!";
- wchar_t- То же самое, wcharчто используется для представления широких символов.
wchar_t* wcString = L"Hello, world!";
- ULONG_PTR- Представляет беззнаковое целое число того же размера, что и указатель на указанной архитектуре, то есть на 32-битных системах ULONG_PTR будет иметь размер 32 бита, а на 64-битных системах — 64 бита. На протяжении всего курса ULONG_PTR будет использоваться для манипулирования арифметическими выражениями, содержащими указатели (например, PVOID). Перед выполнением любой арифметической операции указатель будет подвергнут приведению типа к ULONG_PTR. Этот подход используется для того, чтобы избежать прямого манипулирования указателями, которое может привести к ошибкам компиляции.
Pointer = (ULONG_PTR)Pointer + 10;
Data Types Pointers
Windows API позволяет разработчику объявлять тип данных напрямую или указатель на тип данных. Это отражено в именах типов данных, где типы данных, начинающиеся с «P», представляют указатели на фактический тип данных, а те, которые не начинаются с «P», представляют сам фактический тип данных. будет очень полезным позже при работе с API Windows, имеющими параметры, которые являются указателями на тип данных. Примеры ниже показывают, как тип данных “P” выглядит без указателя.
- PHANDLE то же самое, что и HANDLE*.
- PSIZE_T то же самое, что и SIZE_T*.
- PDWORD то же самое, что и DWORD*.
Главное отличие, которое следует иметь в виду, заключается в том, что функции ANSI будут принимать типы данных ANSI в качестве параметров, тогда как функции Unicode будут принимать типы данных Unicode. Например, первый параметр для CreateFileA — это LPCSTR, который является указателем на постоянную строку с нулевым завершением из 8-битных символов Windows ANSI. С другой стороны, первый параметр для CreateFileW — это LPCWSTR, указатель на постоянную строку с нулевым завершением из 16-битных символов Unicode.
Кроме того, количество требуемых байтов будет различаться в зависимости от используемой версии.
char str1[] = "fsociety";// 9 байт (fsociety + нулевой байт ).
wchar str2[] = L"fsociety";// 18 байт, каждый символ — 2 байта (нулевой байт также 2 байта)
In and Out Parameters
API Windows имеют входные и выходные параметры. IN — это параметр, который передается в функцию и используется для ввода. В то время как OUT — это параметр, который используется для возврата значения обратно вызывающей функции. Выходные параметры часто передаются по ссылке через указатели.
Например, фрагмент кода ниже показывает функцию fsociety, которая принимает целочисленный указатель и устанавливает значение 111. Это считается выходным параметром, поскольку параметр возвращает значение.
BOOL fsociety(OUT int* num){
*num = 123;
return TRUE;
};
int main(){
int a = 0;
fsociety(&a);
};
Имейте в виду, что использование ключевых слов OUT и IN призвано облегчить разработчикам понимание того, что ожидает функция и что она делает с этими параметрами. Однако стоит отметить, что исключение этих ключевых слов не влияет на то, считается ли параметр выходным или входным параметром.

Analyze Return Type & Parameters
Следующим шагом будет просмотр параметров функции вместе с возвращаемым типом данных. В документации указано, что если функция завершается успешно, возвращаемое значение является открытым дескриптором указанного файла, устройства, именованного канала или почтового слота, поэтому CreateFileW возвращает HANDLE тип данных для указанного созданного элемента.
Кроме того, обратите внимание, что все параметры функции являются in. Это означает, что функция не возвращает никаких данных из параметров, поскольку все они являются in. Помните, что ключевые слова в квадратных скобках, такие как in, out, и optional, предназначены исключительно для справки разработчиков и не оказывают никакого фактического влияния.
HANDLE CreateFileW(
[in] LPCWSTR lpFileName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwShareMode,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD dwCreationDisposition,
[in] DWORD dwFlagsAndAttributes,
[in, optional] HANDLE hTemplateFile
);
Use The Function
В примере кода ниже представлен пример использования CreateFileW. Он создаст текстовый файл с именем fsociety.txt на рабочем столе текущего пользователя.
Handle hFile = INVALID_HANDLE_VALUE;
LPCWSTR filePath = L"C:\\Users\\maldevacademy\\Desktop\\fsociety.txt";
hFile = CreateFileW(filePath, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE){
printf("[-] CreateFileW Api Function Failed With Error : %d\n", GetLastError());
return -1;
};
Windows API Debugging Errors
Когда в функции возникает ошибка, они часто возвращают лог. Например, если в CreateFileW что-то пошло не так, возвращается сообщение INVALID_HANDLE_VALUE, указывающее на то, что файл не может быть создан. Чтобы получить больше информации о том, почему файл не может быть создан, необходимо получить код ошибки с помощью функции Для просмотра ссылки Войди

После извлечения кода его необходимо найти в Для просмотра ссылки Войди
- 5 - ERROR_ACCESS_DENIED
- 2 - ERROR_FILE_NOT_FOUND
- 87 - ERROR_INVALID_PARAMETERР
Вспомним модуль архитектуры Windows : NTAPI в основном экспортируются из ntdll.dll. В отличие от API Windows, эти функции не могут получить свой код ошибки через GetLastError. Вместо этого они возвращают код ошибки напрямую, который представлен типом NTSTATUS данных. NTSTATUS используется для представления статуса системного вызова или функции и определяется как 32-битное целое число без знака. Успешный системный вызов вернет значение STATUS_SUCCESS, которое равно 0. Ниже фрагмент кода показывает, как выполняется проверка ошибок системных вызовов.
NTSTATUS STATUS = NativeSyscallExample(...);
if (STATUS != STATUS_SUCCESS){`
printf("[!] NativeSyscallExample Failed With Status : 0x%0.8X \n", STATUS);
};
NT_SUCCESS Macroc
Другой способ проверки возвращаемого значения NTAPI — через NT_SUCCESS макрос. Он возвращает, TRUE если функция выполнена успешно, и FALSE если она не выполнена.
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
Ниже пример такого макроса.
NTSTATUS STATUS = NativeSyscallExample(...);
if (!NT_SUCCESS(STATUS)){
printf("[!] NativeSyscallExample Failed With Status : 0x%0.8X \n", STATUS);
};

Надеюсь ваши мозги еще не поплавились и вы успешно доползли до конца этого модуля. Теперь дз или вы рассчитывали, что после прочтения данного модуля у вас и дз не будет?
Написать утилиту на Cи, которая выполняет следующие действия:
- Статически выделяет память для хранения строки: “I’m a cool hacker. Soon I’ll fuck the whole world and I’ll have a cool botnet. fsociety”.
- Копирует эту строку в выделенную память.
- Получает путь к рабочему столу пользователя.
- Создает текстовый файл с именем fsociety.txt на рабочем столе.
- Записывает строку из статической памяти в созданный файл.
- Обрабатывает возможные ошибки (например, ошибка создания файла, ошибка получения пути к рабочему столу).
