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

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

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

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

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

Категории

Все категории
Loading categories
ExportHider — ExportHider: генерация таблицы экспорта во время выполнения для скрытия экспортируемых функций из DLL-файла. | Kitploit
Инструменты/GitHubGitHub/frkngksl/exporthider
Динамический анализ кода (DAST)ЭксплуатацияОбратная инженерияАнализ вредоносных программАнализ Бинарных ФайловRed TeamingРазработка Полезной Нагрузки
GitHubfrkngksl/exporthider

ExportHider

ExportHider: генерация таблицы экспорта во время выполнения для скрытия экспортируемых функций из DLL-файла.

Репозиторий
332195 месяцев назадПроверено Kitploit

Популярное

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

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

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

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

Смотреть все инструменты →
Поделиться

ExportHider

ExportHider генерирует шаблон DLL на C++, содержащий заглушку кода, которая позволяет скрыть экспортируемые функции из каталога экспорта DLL на файловой системе. После добавления определений функций и компиляции файла вы не увидите скрытые экспортируемые функции через программы просмотра PE-файлов, такие как CFF Explorer. Однако, поскольку заглушка кода в шаблоне воссоздает каталог экспорта во время выполнения, легитимные вызовы GetProcAddress будут выполняться успешно. Этот метод работает только для динамической загрузки DLL или случаев использования пользовательского загрузчика DLL.

Как это работает?

Обычно, когда вы хотите определить экспортируемую функцию в файлах DLL (на C или C++), вы просто ставите ключевое слово __declspec(dllexport) перед именем функции или создаете .def-файл. После компиляции компилятор создает специальную таблицу, называемую каталогом экспорта (Export Directory), для хранения информации, связанной с экспортируемыми функциями. Структура каталога экспорта показана ниже:

Когда процесс хочет использовать функцию из DLL-файла, загрузчик Windows (Windows Loader) просто анализирует эту структуру и импортирует запрошенные функции, используя массивы AddressOfFunctions, AddressOfNames и AddressOfNameOrdinals.

Конкретная процедура импорта функции подробно описана в статье блога ferreirasc, но кратко: для функции, импортируемой по имени, загрузчик перебирает массив AddressOfNames (значения этого массива — это просто RVA) и ищет заданное имя. Как только загрузчик находит совпадение на позиции "i", он обращается к i-му индексу массива AddressOfNameOrdinals и получает порядковый номер (ordinal), связанный с этой функцией. Имея порядковый номер, загрузчик обращается к массиву AddressOfFunctions по позиции значения порядкового номера, чтобы наконец получить RVA, связанный с импортируемой функцией.

Ключевой момент здесь заключается в том, что все эти операции поиска и доступа со стороны загрузчика Windows при вызове LoadLibrary выполняются после того, как DLL отображена в адресное пространство процесса. Во время отображения DLL весь файл DLL, включая его PE-заголовки, записывается в память, и загрузчик анализирует заголовки в памяти для доступа к каталогу экспорта. Это означает, что если сама DLL сможет перезаписать свои PE-заголовки в памяти, чтобы изменить адрес каталога экспорта (просто 0-й индекс DataDirectory) после присоединения к процессу, загрузчик сможет искать функции для импорта в произвольном каталоге экспорта, а не в том, который был добавлен компилятором.

Параметры командной строки


   __                       _          _     _
  /__\_  ___ __   ___  _ __| |_  /\  /(_) __| | ___ _ __
 /_\ \ \/ / '_ \ / _ \| '__| __|/ /_/ / |/ _` |/ _ \ '__|
//__  >  <| |_) | (_) | |  | |_/ __  /| | (_| |  __/ |
\__/ /_/\_\ .__/ \___/|_|   \__\/ /_/ |_|\__,_|\___|_|
          |_|
                     от @R0h1rr1m

Использование: C:\Users\Public\DLLDemo\ExportHider.exe:

    -h | --help                                 Показать справку.
    -i | --input <Путь к входному файлу>        Путь к файлу со списком имен функций, которые нужно скрыть. (Обязательно)
    -o | --output <Путь к выходному файлу>      Путь для сохранения шаблона DLL. (Обязательно)
    -n | --name <Имя DLL>                       Имя DLL для каталога экспорта. (Обязательно)
    -c | --count <Количество других функций>    Количество других экспортируемых функций, которые не будут скрыты.

Что касается параметра -i | --input <Input Path>, вам нужно указать путь к входному файлу, в котором построчно хранятся имена функций для скрытия. Пример содержимого такого файла:

TestFunction1
TestFunction2
TestFunction3

Что касается параметра -c | --count, если вы не хотите скрывать все экспортируемые функции (т.е. есть некоторые функции, экспортированные с помощью __declspec(dllexport) или .def-файла, и они отображаются в каталоге экспорта DLL на файловой системе через программы просмотра PE-файлов), просто укажите, сколько их, используя этот параметр, так как инструменту нужна эта информация для вычислений в памяти.

Быстрое демонстрационное видео

Обходные пути

Если вы хотите поэкспериментировать с техникой, есть два интересных момента, с которыми я столкнулся при разработке проекта. Возможно, вам нужно знать их перед изменением проекта:

  • Загрузчик Windows использует алгоритм, похожий на бинарный поиск, для поиска экспортируемой функции, если вы вызываете GetProcAddress с именем. Из-за этого имена всех экспортируемых функций, включая скрытые, должны быть отсортированы в массиве AddressOfNames. В противном случае функция GetProcAddress возвращает NULL. Поэтому я использовал алгоритм пузырьковой сортировки для сортировки элементов этого массива.
  • Как я сказал выше, массив AddressOfNames, массив AddressOfFunctions, массив DataDirectory и некоторые другие поля требуют значений в относительных виртуальных адресах (RVA). Вдобавок к этому, они хранят эти значения RVA в полях размером DWORD. Когда вы выделяете область памяти для нового произвольного каталога экспорта с помощью функций динамического выделения памяти, таких как VirtualAlloc или HeapAlloc, полученные адреса будут далеки от отображенной области DLL, и значения RVA не помещаются в поля размером DWORD, что приводит к целочисленному переполнению. Поэтому я использовал глобальные переменные типа массива байтов в шаблоне DLL для удовлетворения потребностей в памяти.

Случай статически импортированной DLL (также известный как DLL Sideloading)

Моей первой целью при создании этого проекта было сгенерировать DLL, у которой отсутствуют экспорты (или полностью отсутствует таблица экспорта), но которая все равно успешно загружается вновь созданным процессом. Я думал, что такое поведение может предоставить новую среду для полезных нагрузок DLL sideloading. Однако я не смог найти ни функции, ни способа, при котором заглушка исправления каталога экспорта выполняется до того, как загрузчик Windows проверит экспортируемые функции DLL.

dll_timing_problem (1)

Более технически, я заметил следующий поток и вызов функций для каждой DLL в разделах кода, связанных с загрузчиком Windows в NTDLL (собрано из моих старых заметок; могут быть некоторые ошибки, так как я не эксперт по реверс-инжинирингу):

Скачать инструмент