Архитектура любой ОС, очень сложная система, так наскоком не изучишь.
Тут на форуме есть целый раздел:Системное программирование и разработка
Но т.к. разработка малвари - Это системная разработка, какое-то понимание этой самой архитектуры нужно иметь, поэтому данная шпаргалка даст небольшое понимание, но к сожалению не более того.:(
Процессор внутри компьютера, на которой работает операционная система Windows, может работать в двух разных режимах: режиме пользователя и режиме ядра.
Приложения работают в режиме пользователя, а компоненты операционной системы работают в режиме ядра.
Когда приложение хочет выполнить задачу, например, создать файл, оно не может сделать это самостоятельно.
Единственная сущность, которая может выполнить задачу - это ядро, поэтому приложения должны следовать определенному порядку вызова функций.
Ниже представлена диаграмма, показывающая высокий уровень этого потока.
Процессы пользователя:
Затем, createFile вызывает свою эквивалентную функцию NTAPI, NtCreateFile, которая предоставляется через ntdll.dll.
Ntdll.dll затем выполняет команду sysenter (x86) или syscall (x64), которая переводит выполнение в режим ядра.
Затем используется функция ядра ntcreaterFile, которая вызывает драйверы ядра и модули для выполнения запрашиваемой задачи.
Пример стека вызова функций
Этот пример показывает стека вызова функций через отладчик. Это делается путем подключения отладчика к бинарному файлу, который создает файл через API Windows createFilew.
Пользовательское приложение вызывает функцию createrFilew WinAPI.
Далее, вызов из CreateFileW функции NtCreateFile.
Наконец, функция NtCreateFile использует инструкцию syscall для перехода из режима пользователя в режим ядра.
Ядро затем создает файл.
Прямой вызов Native API (NTAPI)
Важно отметить, что приложения могут вызывать syscalls (т.е. функции NTDLL) напрямую, не обращаясь к API Windows.
API Windows просто действует как оболочка для Native API. Сказав это, стоит отметить, что Native API сложнее в использовании, потому что он официально не документирован Microsoft.
Более того, Microsoft советует не использовать функции Native API, потому что они могут быть изменены в любое время без предупреждения.
Теперь рассмотрим управление памятью в Windows
Виртуальная память и разбиение на страницы
Память в современных операционных системах не отображается напрямую на физическую память (т.е. оперативную память). Вместо этого процессы используют виртуальные адреса памяти, которые отображаются на физические адреса памяти. Причин для этого несколько, но в конечном итоге цель состоит в том, чтобы сохранить как можно больше физической памяти.
Виртуальная память может быть отображена на физическую память, но может также храниться на диске. С помощью виртуального адресования памяти становится возможным для нескольких процессов делить один и тот же физический адрес, имея уникальный виртуальный адрес памяти.
Виртуальная память опирается на концепцию разбиения памяти на страницы, которое делит память на блоки по 4КБ, называемые "страницами".
Смотрите изображение ниже из книги "Windows Internals 7th edition - part 1".
Страницы, находящиеся в виртуальном адресном пространстве процесса, могут находиться в одном из 3 состояний:
Современные операционные системы обычно имеют встроенные механизмы защиты памяти для предотвращения эксплуатации и атак. Это также важно учитывать, так как они, скорее всего, будут встречаться при создании или отладке вредоносного ПО.
Пример выделения памяти
В этом примере рассматриваются небольшие фрагменты кода, чтобы лучше понять, как можно взаимодействовать с памятью Windows через функции С и Windows API. Первый шаг во взаимодействии с памятью - это выделение памяти. Ниже приведен фрагмент кода, который демонстрирует несколько способов выделения памяти, что в сущности сводится к резервированию памяти внутри выполняющегося процесса.
Функции выделения памяти возвращают базовый адрес, который является просто указателем на начало выделенного блока памяти. Используя приведенные выше фрагменты, pAddress будет базовым адресом выделенного блока памяти. Используя этот указатель, можно выполнить несколько действий, таких как чтение, запись и выполнение. Типы действий, которые могут быть выполнены, будут зависеть от защиты, назначенной выделенному региону памяти.
Ниже приведен образец того, как выглядит pAddress в отладчике.
Когда память выделена, она может быть либо заполнена нулями, либо содержать случайные данные. Некоторые функции выделения памяти предоставляют опцию обнуления региона памяти в процессе выделения.
Следующим шагом после выделения памяти обычно является запись в этот буфер. Для записи в память можно использовать несколько функций, но в этом примере используется memcpy.
HeapAlloc использует флаг HEAP_ZERO_MEMORY, который приводит к инициализации выделенной памяти нулями. Затем строка копируется в выделенную память с помощью memcpy. Последний параметр в memcpy - это количество байтов, которые нужно скопировать.
Отмечу что указанный пример не безопасен, т.к. strlen не учитывает нулевой символ в конце, лучше использовать специализированные функции для работы со строками, например strcpy.
Но для демонстрации работы с памятью, пример сойдет.)
Когда приложению больше не нужен выделенный буфер, настоятельно рекомендуется освободить буфер, чтобы избежать утечек памяти. В зависимости от того, какая функция использовалась для выделения памяти, у нее будет соответствующая функция освобождения памяти. Например:
Также рекомендуется после освобождения памяти устанавливать значение указателю nullptr, что-бы не было возможности повторно использовать невалидный адрес в программе.)
В следующей части рассмотрим работу с Windows API.
Также планирую обновить свой репозиторий в гите, выложить туда части статей и итоговый как закончу цикл.)
Тут на форуме есть целый раздел:Системное программирование и разработка
Но т.к. разработка малвари - Это системная разработка, какое-то понимание этой самой архитектуры нужно иметь, поэтому данная шпаргалка даст небольшое понимание, но к сожалению не более того.:(
Процессор внутри компьютера, на которой работает операционная система Windows, может работать в двух разных режимах: режиме пользователя и режиме ядра.
Приложения работают в режиме пользователя, а компоненты операционной системы работают в режиме ядра.
Когда приложение хочет выполнить задачу, например, создать файл, оно не может сделать это самостоятельно.
Единственная сущность, которая может выполнить задачу - это ядро, поэтому приложения должны следовать определенному порядку вызова функций.
Ниже представлена диаграмма, показывающая высокий уровень этого потока.
Процессы пользователя:
- Процессы пользователя - это программа/приложение, выполненное пользователем, такое как Notepad, Google Chrome или Microsoft Word.
- Библиотеки DLL подсистемы - DLL, которые содержат функции API, вызываемые процессами пользователя. Примером этого может служить kernel32.dll, экспортирующая функцию CreateFile Windows API (WinAPI), другими общими DLL подсистемы являются ntdll.dll, advapi32.dll и user32.dll.
- Ntdll.dll - это системная DLL, которая является самым низким слоем, доступным в режиме пользователя. Это специальная DLL, которая создает переход из режима пользователя в режим ядра. Это часто называют Native API или NTAPI.
- Executive Kernel - это то, что известно как Windows Kernel, и оно вызывает другие драйверы и модули, доступные в режиме ядра, чтобы выполнить задачи. Ядро Windows частично хранится в файле под названием ntoskrnl.exe по пути "C:\Windows\System32".
Затем, createFile вызывает свою эквивалентную функцию NTAPI, NtCreateFile, которая предоставляется через ntdll.dll.
Ntdll.dll затем выполняет команду sysenter (x86) или syscall (x64), которая переводит выполнение в режим ядра.
Затем используется функция ядра ntcreaterFile, которая вызывает драйверы ядра и модули для выполнения запрашиваемой задачи.
Пример стека вызова функций
Этот пример показывает стека вызова функций через отладчик. Это делается путем подключения отладчика к бинарному файлу, который создает файл через API Windows createFilew.
Пользовательское приложение вызывает функцию createrFilew WinAPI.
Далее, вызов из CreateFileW функции NtCreateFile.
Наконец, функция NtCreateFile использует инструкцию syscall для перехода из режима пользователя в режим ядра.
Ядро затем создает файл.
Прямой вызов Native API (NTAPI)
Важно отметить, что приложения могут вызывать syscalls (т.е. функции NTDLL) напрямую, не обращаясь к API Windows.
API Windows просто действует как оболочка для Native API. Сказав это, стоит отметить, что Native API сложнее в использовании, потому что он официально не документирован Microsoft.
Более того, Microsoft советует не использовать функции Native API, потому что они могут быть изменены в любое время без предупреждения.
Теперь рассмотрим управление памятью в Windows
Виртуальная память и разбиение на страницы
Память в современных операционных системах не отображается напрямую на физическую память (т.е. оперативную память). Вместо этого процессы используют виртуальные адреса памяти, которые отображаются на физические адреса памяти. Причин для этого несколько, но в конечном итоге цель состоит в том, чтобы сохранить как можно больше физической памяти.
Виртуальная память может быть отображена на физическую память, но может также храниться на диске. С помощью виртуального адресования памяти становится возможным для нескольких процессов делить один и тот же физический адрес, имея уникальный виртуальный адрес памяти.
Виртуальная память опирается на концепцию разбиения памяти на страницы, которое делит память на блоки по 4КБ, называемые "страницами".
Смотрите изображение ниже из книги "Windows Internals 7th edition - part 1".
Страницы, находящиеся в виртуальном адресном пространстве процесса, могут находиться в одном из 3 состояний:
- Свободная - Страница ни в чем не участвует, она недоступна для процесса. Она доступна для резервирования или коммитирования.
- Зарезервированная - Страница зарезервирована для будущего использования. Диапазон адресов не может быть использован другими функциями выделения. Страница не доступна и не связана с физическим хранилищем. Она доступна для коммитирования.
- Закоммитированная - Страница доступна и доступ к ней контролируется одной из констант защиты памяти. Система инициализирует и загружает каждую коммитированную страницу в физическую память только при первой попытке чтения или записи на эту страницу. Когда процесс завершается, система освобождает хранилище для закоммитированных страниц.
Вы должны зарегистрироваться, чтобы увидеть внешние ссылки
, но ниже приведены некоторые примеры.- PAGE_NOACCESS - отключает все доступы к закоммитированному региону страниц. Попытка чтения из этого региона, записи в него или выполнения команд приведет к нарушению доступа.
- PAGE_EXECUTE_READWRITE - включает чтение, запись и выполнение. Этот параметр крайне не рекомендуется к использованию и обычно является плохим показателем, поскольку память редко может быть одновременно доступна для записи и выполнения.
- PAGE_READONLY - включает только чтение в закоммитированном регионе страниц. Попытка записи в закоммитированный регион приведет к нарушению доступа.
Современные операционные системы обычно имеют встроенные механизмы защиты памяти для предотвращения эксплуатации и атак. Это также важно учитывать, так как они, скорее всего, будут встречаться при создании или отладке вредоносного ПО.
- Предотвращение выполнения данных (DEP) - DEP это функция защиты памяти на уровне системы, которая встроена в операционную систему, начиная с Windows XP и Windows Server 2003. Если параметр защиты страницы установлен в PAGE_READONLY, то DEP предотвратит выполнение кода в этом регионе памяти.
- Случайное расположение адресного пространства (ASLR) - ASLR это техника защиты памяти, используемая для предотвращения эксплуатации уязвимостей направленные на адреса памяти. ASLR случайным образом меняет положение ключевых областей данных процесса в адресном пространстве, включая базу исполняемого файла и положения стека, кучи и библиотек.
Пример выделения памяти
В этом примере рассматриваются небольшие фрагменты кода, чтобы лучше понять, как можно взаимодействовать с памятью Windows через функции С и Windows API. Первый шаг во взаимодействии с памятью - это выделение памяти. Ниже приведен фрагмент кода, который демонстрирует несколько способов выделения памяти, что в сущности сводится к резервированию памяти внутри выполняющегося процесса.
C:
// Allocating а memory buffer 100 bytes
// Method 1 — Using malloc()
PVOID pAddress = malloc(100);
// Method 2 — Using НеарА11ос ()
PVOID pAddress = HeapAlloc (GetProcessHeap (), 0, 100);
// Method 3 — Using LocalAlloc ()
PVOID pAddress = LocalAlloc(LPTR, 100);
Функции выделения памяти возвращают базовый адрес, который является просто указателем на начало выделенного блока памяти. Используя приведенные выше фрагменты, pAddress будет базовым адресом выделенного блока памяти. Используя этот указатель, можно выполнить несколько действий, таких как чтение, запись и выполнение. Типы действий, которые могут быть выполнены, будут зависеть от защиты, назначенной выделенному региону памяти.
Ниже приведен образец того, как выглядит pAddress в отладчике.
Когда память выделена, она может быть либо заполнена нулями, либо содержать случайные данные. Некоторые функции выделения памяти предоставляют опцию обнуления региона памяти в процессе выделения.
Следующим шагом после выделения памяти обычно является запись в этот буфер. Для записи в память можно использовать несколько функций, но в этом примере используется memcpy.
C:
PVOID pAddress = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, 100);
CHAR* cString = "Malware Academy Is The Best";
memcpy (pAddress, cString, strlen(cString));
HeapAlloc использует флаг HEAP_ZERO_MEMORY, который приводит к инициализации выделенной памяти нулями. Затем строка копируется в выделенную память с помощью memcpy. Последний параметр в memcpy - это количество байтов, которые нужно скопировать.
Отмечу что указанный пример не безопасен, т.к. strlen не учитывает нулевой символ в конце, лучше использовать специализированные функции для работы со строками, например strcpy.
Но для демонстрации работы с памятью, пример сойдет.)
Когда приложению больше не нужен выделенный буфер, настоятельно рекомендуется освободить буфер, чтобы избежать утечек памяти. В зависимости от того, какая функция использовалась для выделения памяти, у нее будет соответствующая функция освобождения памяти. Например:
- При выделении памяти с помощью malloc требуется использование функции free.
- При выделении памяти с помощью HeapAlloc требуется использование функции HeapFree.
- При выделении памяти с помощью LocalAlloc требуется использование функции LocalFree.
Также рекомендуется после освобождения памяти устанавливать значение указателю nullptr, что-бы не было возможности повторно использовать невалидный адрес в программе.)
В следующей части рассмотрим работу с Windows API.
Также планирую обновить свой репозиторий в гите, выложить туда части статей и итоговый как закончу цикл.)
Последнее редактирование: