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

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

Что такое процесс?

Самый простой способ определить процесс — посмотреть на него как на контейнер, в котором хранится код, который должен быть выделен в памяти и должен выполняться ЦП.

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

Как я сказал в начале объяснения, операционная система (Windows) использует процессы в качестве контейнеров, чтобы они могли управлять ресурсами и создавать разделение между другими процессами, есть много процессов, которые выполняются одновременно, и все они совместно используют одно и то же. ресурсы операционной системы, такие как память, ЦП и файловая система.

Одним из ресурсов, который является критическим для использования процессом, является память. При запуске процесса операционной системе необходимо выделить пространство памяти для процесса, когда память создает это выделение, процесс получает от операционной системы адрес, по которому процесс может быть загружены, процессы могут использовать один и тот же адрес и по-прежнему работать без риска коллизий, и это связано с тем, что они используют виртуальный адрес, например, если процесс Demo1.exeзагружается по адресу 0x00400000и процесс Demo2.exe загружается и получает тот же адрес 0x00400000ничего не произойдет, потому что это действительно важно физический адрес, который отличается для каждого процесса (мы обсудим виртуальные и физические адреса в другой статье).

Загрузчик ОС и структура PE

Итак, мы уже поняли, что процесс должен быть загружен в память, чтобы работать, но как на самом деле происходит эта загрузка?

Процесс построен на структуре, которая называется PE (Portable Executable). Эта структура представляет собой двоичный каркас Windows, обеспечивающий реальную загрузку в память, сам PE содержит все информация, необходимая загрузчику ОС для загрузки исполняемого файла и выделения памяти для процесса.

PE, насколько я понимаю, может быть разделен на две части: Заголовки и Разделы. Это изображение может помочь нам визуализировать это более четко:

Заголовки

Информация внутри заголовков — это все настройки исполняемой структуры (исполняемая архитектура, необходимая системная архитектура и т. д.), мы рассмотрим некоторые важные заголовки, которые помогут вам глубже разобраться в вашей карьере.

Заголовок Dos

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

e_magic: мы можем назвать его Заголовок MZ, это просто магическое число, которое говорит нам о типе файла, MZ — это сокращение от имя Марка Збиковски, человека, разработавшего MS-DOS, каждый файл Windows будет содержать этот заголовок MZ.

e_lfanew : это смещение следующего заголовка, который должен прочитать загрузчик ОС, который является заголовком NT.

Заголовок NT

Заголовок NT содержит еще два заголовка: Заголовок файла и Необязательный заголовок.

Мы начнем с заголовка файла, заглянув внутрь него, вот что у нас есть:

Machine. Заголовок Machine — это значение, которое сообщает нам, на какой архитектуре системы должен работать этот исполняемый файл (32-битная или 64-битная), если мы видим значение 014C мы знаем, что это x86 (32-битная), если мы видим 8664, это x64 (64-битная).

NumberOfSections: количество разделов в этом файле (позже мы перейдем к разделам, и вы это поймете)

TimeDateStemp: дату и время компиляции этого файла, конечно, можно легко изменить, но, например, для исследователя вредоносных программ это может быть некоторым показателем актуальности.

Характеристики: сохраняйте некоторые настройки файла, такие как:

Например, если значение 0x2000, мы можем понять, что это DLL, если значение 0x2100, поэтому мы знаем, что это 32-битная DLL, я думаю, вы получили смысл.

Необязательный заголовок

Не запутайтесь в названии «Необязательный заголовок», это обязательно.

Magic: значение Magic говорит нам об архитектуре файла, если значение равно 0x10B, двоичный файл 32-битный (x86), 0x20B 64-битный ( x64), помните, что существует разница между значением Magic и значением Machine

Машина относится к требуемой архитектуре системы (x86/x64).

Magic относится к двоичной файловой архитектуре (x86/x64).

Например, вы можете изменить значение Machine на 8664, и двоичный файл по-прежнему будет работать, поскольку 32-разрядная версия может работать поверх 64-разрядной архитектуры, но обратное не сработает.

AddressOfEntryPoint: имеется в виду RVA (относительный виртуальный адрес), куда загрузчик ОС должен перейти, чтобы запустить процесс в памяти.

ImageBase: ImageBase — это желаемый адрес в памяти, из которого наш процесс хочет загрузиться, когда процесс запускается, он должен запускаться из EP (Enrey Point) ,эта точка входа не относится к основной функции, поэтому, если вы хотите узнать точное местоположение точки входа, вы можете просто вычислить это: AddressOfEntryPoint+ImageBase= Точка входа

DLLCharacteristics: содержит настройки безопасности для процесса (я знаю, что это DLLCharacteristics, но оно относится к Dll и процессу), например ASLRзащита. (Рандомизация размещения адресного пространства). Я не буду вдаваться в методы защиты процессов, но настоятельно рекомендую вам понять концепции ASLR, DEP.

Разделы

Разделы содержат основное содержимое исполняемого файла, включая код, данные и ресурсы.

У нас есть пара разделов, которые нам нужно понять:

.Text : содержит все инструкции, которые ЦП должен выполнить в памяти.

.Data: содержит глобальные переменные, к которым можно получить доступ из любой части программы.

.Rdata: данные только для чтения, включая импорт (Dll) и экспорт.

.Rsrc : все ресурсы, такие как значки, меню и т. д.

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

Этапы загрузчика ОС:

  1. Загрузчик ОС загружает исполняемый файл, проверяет заголовок MZ и извлекает смещение для заголовка PE (e_lfanew).
  2. Загрузчик ОС проверяет правильность заголовка PE и переходит к таблице Section.
  3. Загрузчик ОС размещает таблицу разделов в памяти, отображая разделы из таблицы в системную память.
  4. Когда PE-файл отображается в памяти, динамический компоновщик (загрузчик PE) перемещается к логическим разделам PE-файла, а следующий переход — к таблице адресов импорта.

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

Когда PE сопоставлен с памятью, ему необходимо создать IAT, который представляет собой просто таблицу, содержащую указатели, указывающие на список функций, необходимых нашему процессу для правильной работы, >IAT — это способ, с помощью которого наш процесс может использовать различные функции Winapi после загрузки из DLL.

Чтобы упростить задачу, IAT представляет собой таблицу поиска, которая помогает находить функции, расположенные в разных модулях, используемых процессом.

Куда мне идти отсюда?

Так что вся эта информация очень важна, чтобы действительно иметь хорошее представление, я рекомендую вам скачать CFFExplorer, загрузить двоичный файл (Exe, Dll) и попытаться следовать заголовкам из этой статьи, это единственный способ, которым вы можете практиковаться, статически узнать об этом.

До встречи в следующей статье.