Статья Как внедрить код в исполняемые файлы

  • 9
  • 17
Всем привет! В этой теме будет разбор патчнига исполняемого файла для внедрения нашего кода.

Введение

Есть много причин, по которым модифицируют / внедряют код в собранный исполняемый файл одни из которых:
1) Внедрить шелл-код в легальную программу, чтобы сделать ее вредоносной
2) Измените программу, чтобы исправить уязвимость, пока обновление недоступно (0day patch)
3) Взломать программу для обхода проверки
4) Сделайте изменение вредоносной программы или сигнатуры защиты на "FUD"

У нас есть 3 варианта реализации:
1. Изменить сам исполняемый файл
1) Byte patching​
2) Code caving​
3) Code caving через новую секцию​
4) Создание новой секции и указание на нее точки входа​
5) Создание нового импорта в DLL​

2. Изменение извне
1) Внедрение DLL и создание потока​
2) Внедрить DLL и создание хука существующей функции​

3. Без взаимодействия с исполняемым файлом
1) DLL Hijacking​
2) Другие способы связанные с COM, ….​

ПРИМЕЧАНИЕ: методы реализации через сам исполняемый файл могут быть использованы во время выполнения. Создание импорта может быть инъекцией DLL либо импортом, разрешенным при выполнении кода в исполняемом файле. И сделать точку входа, указывающую на другую функцию только во время выполнения, можно множеством способов, но это немного бесполезно

Byte patching
Самый простой способ изменить код приложения - это изменить «побайтно» коды операций, заменив один буфер кода операции другим. Этот метод очень распространен, но имеет большую проблему: вам нужно перезаписать код asm для выполнения вашего

Итак, это самый обычный пример исполняемого кода

Frame 5.png


Если хотим заменить инструкцию JNB (Jump if not below) на JMP (Jump), мы можем изменить ее байты и посмотреть, что получится
Frame 6.png


К счастью, инструкция JMP имеет тот же размер, что и инструкция JNB, поэтому наш код хорошо вписывается
Но если мы снова заменим эту инструкцию на инструкцию большего размера, у нас возникнет проблема

Frame 7.png


Тут мы заменили 2-байтовую инструкцию JMP на mov edi, 14 из 5 байт, наша инструкция здесь встала, без проблем, но следующая инструкция перекрывается нашей новой инструкцией.
Так что теперь вместо инструкции PUSH у нас ADD, потому что 2 оставшихся байта старой инструкции PUSH интерпретируются после нашего MOV
Мы можем просто заменить все на NOP, чтобы очистить его, но тогда мы потеряем инструкцию :-(
Frame 8.png

Это главная проблема такого изменения - РАЗМЕР. Но для этого есть решение, и об этом в следующей части

Code Caving
Решением проблемы размера байтовых патчей является code caving

По сути, code caving - это, как говорит само название, поиск участка для размещения нашего кода (пещеры). Буфер из нулевых байтов (0x00) в секции PE-файла (то есть в реальной памяти процесса), куда мы можем поместить наш код. И вместо того, чтобы писать наш asm-код в какой-либо функции, мы можем написать переход к участку (с нашим кодом в ней) и вернуться к коду функции позже

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

Посмотрим на примере исполняемого файла:
image 1.png

Итак, мы видим, что есть свободных 100 байт, мы могли бы использовать его для написания нашего кода

Давайте разберем на примере
Здесь у нас сверху функция, которую мы хотим изменить, а снизу участок, куда мы запишем наш код (add BYTE PTR [eax], al, соответствующий опкоду 0x00, 0x00)
Frame 9.png


Мы пишем наш код в участок
Frame 10.png


После, мы пишем прыжок в участок, конечно, последующие инструкции не сохраняются. Нам необходимо их затереть
Frame 11.png


После этого мы пишем инструкции, которые мы перезаписали, чтобы код продолжал работать корректно
И чтобы закончить, мы должны вернуться после кода исходной функции
Frame 12.png

Code caving через новую секцию
Это то же самое, что и code caving, но вместо того, чтобы писать наш код в существующем разделе, мы добавим раздел, чтобы внедрить наш код без ограничения размера
Новая секция всегда размещается после существующих секций, потому что они фиксированы смещениями, которые, если их изменить, приведут к поломке исполняемого файла

Добавление секции имеет некоторые ограничения, например, подпись исполняемого файла, потенциальные данные, используемые в EOF
Некоторые данные, такие как отладка или сертификат, должны находиться в последней секции исполняемого файла, поэтому добавление секции может нарушить структуру конечного файла (ссылка: Другое содержимое файла)

Вы можете добавить секцию с помощью множества инструментов (PEBear, CFF Explorer, PE Tools ...) или программно с помощью множества языков (pefile, pe-parse, ....).
Я показываю, как это сделать с помощью CFF Explorer менее автоматизированным способом
ПРИМЕЧАНИЕ: секция должна иметь флаг памяти Read и Execute для выполнения нашего кода

Во вкладке секций нужно нажать ПКМ по пустому пространству и выбрать добавление секции с пустым пространством
image 2.png


Затем необходимо указать размер нашей секции
image 3.png


Наша секция создалась, важно не забыть указать ее название
image 4.png


После добавления секции можно просто где-нибудь изменить инструкции и перейти к нашей секции
Frame 4 (1).png


Создание новой секции и указание на нее точки входа
Это то же самое, что добавить секцию с пустым участком, но вместо изменения кода для указания на наш участок, мы изменяем точку входа исполняемого файла для перехода непосредственно к нашему коду
Frame 13.png


Это можно сделать, просто изменив Entry Point
ПРИМЕЧАНИЕ: этот адрес является виртуальным
image 5.png


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

Многие инструменты могут это сделать из коробки, хотя они созданы для других целей, например, для исправления таблицы импорта после распаковки, но они делают то же самое. Я буду использовать функцию "Import Adder" из CFF Explorer, которая самая простая в использовании

(1-3) Вы можете добавлять новые модули, экспортирующие функции, перечисленные здесь
(4 + чекбокс) И выберите ту, которую вы хотите добавить в новую таблицу импорта. В конце просто отметьте "Create New Section" и нажмите Rebuild Import Table
(5) Новый раздел будет добавлен с новой таблицей импорта в нем
image 6.png

image 7.png


И каталог импорта в необязательном заголовке изменился на нашу новую секцию
image 8.png


Спасибо, всем кто дочитал! Жду фидбек, если он возможен)
 

Вложения

  • Frame 4.png
    Frame 4.png
    8.3 KB · Просмотры: 56
  • Frame 13.png
    Frame 13.png
    5.8 KB · Просмотры: 59
  • 1
  • 0
Контакты для связи отсутствуют.
Спасибо , очень полезный материал!
 
  • Юри
  • Маленький волк
  • 557
  • 1
  • 122
Материал конечно хороший, но ты же понимаешь, что это первый шаг к ратнику на пк?
 
  • Юри
  • Маленький волк
  • 557
  • 1
  • 122
Видимо для тебя любой патчинг и метод хука это уже целая трагедия и боязнь
А что если по этому гайду вошьют хорошо закриптованный хитро-мудро выебанный ратник и при проверке он у тебя заактивничает?
 
Сверху Снизу